Merge pull request #7631 from beakerbrowser/webview-policy-flags2

Add 'webpreferences' attribute to webview
This commit is contained in:
Kevin Sawicki 2016-10-25 12:14:25 +09:00 committed by GitHub
commit 39a5c7dab9
8 changed files with 91 additions and 14 deletions

View file

@ -194,6 +194,20 @@ value will fail with a DOM exception.
If "on", the guest page will be allowed to open new windows. If "on", the guest page will be allowed to open new windows.
### `webpreferences`
```html
<webview src="https://github.com" webpreferences="allowDisplayingInsecureContent, javascript=no"></webview>
```
A list of strings which specifies the web preferences to be set on the webview, separated by `,`.
The full list of supported preference strings can be found in [BrowserWindow](browser-window.md#new-browserwindowoptions).
The string follows the same format as the features string in `window.open`.
A name by itself is given a `true` boolean value.
A preference can be set to another value by including an `=`, followed by the value.
Special values `yes` and `1` are interpreted as `true`, while `no` and `0` are interpreted as `false`.
### `blinkfeatures` ### `blinkfeatures`
```html ```html

View file

@ -48,6 +48,7 @@
'lib/common/api/native-image.js', 'lib/common/api/native-image.js',
'lib/common/api/shell.js', 'lib/common/api/shell.js',
'lib/common/init.js', 'lib/common/init.js',
'lib/common/parse-features-string.js',
'lib/common/reset-search-paths.js', 'lib/common/reset-search-paths.js',
'lib/renderer/chrome-api.js', 'lib/renderer/chrome-api.js',
'lib/renderer/content-scripts-injector.js', 'lib/renderer/content-scripts-injector.js',

View file

@ -2,6 +2,7 @@
const ipcMain = require('electron').ipcMain const ipcMain = require('electron').ipcMain
const webContents = require('electron').webContents const webContents = require('electron').webContents
const parseFeaturesString = require('../common/parse-features-string')
// Doesn't exist in early initialization. // Doesn't exist in early initialization.
let webViewManager = null let webViewManager = null
@ -184,6 +185,18 @@ const attachGuest = function (embedder, elementInstanceId, guestInstanceId, para
disableBlinkFeatures: params.disableblinkfeatures disableBlinkFeatures: params.disableblinkfeatures
} }
// parse the 'webpreferences' attribute string, if set
// this uses the same parsing rules as window.open uses for its features
if (typeof params.webpreferences === 'string') {
parseFeaturesString(params.webpreferences, function (key, value) {
if (value === undefined) {
// no value was specified, default it to true
value = true
}
webPreferences[key] = value
})
}
if (params.preload) { if (params.preload) {
webPreferences.preloadURL = params.preload webPreferences.preloadURL = params.preload
} }

View file

@ -0,0 +1,18 @@
// parses a feature string that has the format used in window.open()
// - `features` input string
// - `emit` function(key, value) - called for each parsed KV
module.exports = function parseFeaturesString (features, emit) {
// split the string by ','
features.split(/,\s*/).forEach((feature) => {
// expected form is either a key by itself or a key/value pair in the form of
// 'key=value'
let [key, value] = feature.split(/\s*=/)
if (!key) return
// interpret the value as a boolean, if possible
value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value
// emit the parsed pair
emit(key, value)
})
}

View file

@ -2,6 +2,7 @@
const ipcRenderer = require('electron').ipcRenderer const ipcRenderer = require('electron').ipcRenderer
const remote = require('electron').remote const remote = require('electron').remote
const parseFeaturesString = require('../common/parse-features-string')
// Helper function to resolve relative url. // Helper function to resolve relative url.
var a = window.top.document.createElement('a') var a = window.top.document.createElement('a')
@ -88,7 +89,7 @@ if (process.guestInstanceId == null) {
// Make the browser window or guest view emit "new-window" event. // Make the browser window or guest view emit "new-window" event.
window.open = function (url, frameName, features) { window.open = function (url, frameName, features) {
var feature, guestId, i, j, len, len1, name, options, ref1, ref2, value, additionalFeatures var guestId, j, len1, name, options, additionalFeatures
if (frameName == null) { if (frameName == null) {
frameName = '' frameName = ''
} }
@ -104,27 +105,21 @@ window.open = function (url, frameName, features) {
// Used to store additional features // Used to store additional features
additionalFeatures = [] additionalFeatures = []
// Make sure to get rid of excessive whitespace in the property name // Parse the features
ref1 = features.split(/,\s*/) parseFeaturesString(features, function (key, value) {
for (i = 0, len = ref1.length; i < len; i++) {
feature = ref1[i]
ref2 = feature.split(/\s*=/)
name = ref2[0]
value = ref2[1]
value = value === 'yes' || value === '1' ? true : value === 'no' || value === '0' ? false : value
if (value === undefined) { if (value === undefined) {
additionalFeatures.push(feature) additionalFeatures.push(key)
} else { } else {
if (webPreferences.includes(name)) { if (webPreferences.includes(key)) {
if (options.webPreferences == null) { if (options.webPreferences == null) {
options.webPreferences = {} options.webPreferences = {}
} }
options.webPreferences[name] = value options.webPreferences[key] = value
} else { } else {
options[name] = value options[key] = value
}
} }
} }
})
if (options.left) { if (options.left) {
if (options.x == null) { if (options.x == null) {
options.x = options.left options.x = options.left

View file

@ -312,6 +312,13 @@ class DisableBlinkFeaturesAttribute extends WebViewAttribute {
} }
} }
// Attribute that specifies the web preferences to be enabled.
class WebPreferencesAttribute extends WebViewAttribute {
constructor (webViewImpl) {
super(webViewConstants.ATTRIBUTE_WEBPREFERENCES, webViewImpl)
}
}
// Sets up all of the webview attributes. // Sets up all of the webview attributes.
WebViewImpl.prototype.setupWebViewAttributes = function () { WebViewImpl.prototype.setupWebViewAttributes = function () {
this.attributes = {} this.attributes = {}
@ -328,6 +335,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this) this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_GUESTINSTANCE] = new GuestInstanceAttribute(this) this.attributes[webViewConstants.ATTRIBUTE_GUESTINSTANCE] = new GuestInstanceAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this)
const autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH] const autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH]
autosizeAttributes.forEach((attribute) => { autosizeAttributes.forEach((attribute) => {

View file

@ -18,6 +18,7 @@ module.exports = {
ATTRIBUTE_BLINKFEATURES: 'blinkfeatures', ATTRIBUTE_BLINKFEATURES: 'blinkfeatures',
ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures', ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures',
ATTRIBUTE_GUESTINSTANCE: 'guestinstance', ATTRIBUTE_GUESTINSTANCE: 'guestinstance',
ATTRIBUTE_WEBPREFERENCES: 'webpreferences',
// Internal attribute. // Internal attribute.
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid', ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',

View file

@ -406,6 +406,33 @@ describe('<webview> tag', function () {
}) })
}) })
describe('webpreferences attribute', function () {
it('can enable nodeintegration', function (done) {
webview.addEventListener('console-message', function (e) {
assert.equal(e.message, 'function object object')
done()
})
webview.setAttribute('webpreferences', 'nodeIntegration')
webview.src = 'file://' + fixtures + '/pages/d.html'
document.body.appendChild(webview)
})
it('can disables web security and enable nodeintegration', function (done) {
var jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js')
var src = `<script src='file://${jqueryPath}'></script> <script>console.log('ok '+(typeof require));</script>`
var encoded = btoa(unescape(encodeURIComponent(src)))
var listener = function (e) {
assert.equal(e.message, 'ok function')
webview.removeEventListener('console-message', listener)
done()
}
webview.addEventListener('console-message', listener)
webview.setAttribute('webpreferences', 'webSecurity=no, nodeIntegration=yes')
webview.src = 'data:text/html;base64,' + encoded
document.body.appendChild(webview)
})
})
describe('new-window event', function () { describe('new-window event', function () {
if (process.env.TRAVIS === 'true' && process.platform === 'darwin') { if (process.env.TRAVIS === 'true' && process.platform === 'darwin') {
return return