diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 595e32353e2d..0c8c1bb94cb0 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -194,6 +194,20 @@ value will fail with a DOM exception. If "on", the guest page will be allowed to open new windows. +### `webpreferences` + +```html + +``` + +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` ```html diff --git a/filenames.gypi b/filenames.gypi index f1ee8d069c34..ed1329b9bf98 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -48,6 +48,7 @@ 'lib/common/api/native-image.js', 'lib/common/api/shell.js', 'lib/common/init.js', + 'lib/common/parse-features-string.js', 'lib/common/reset-search-paths.js', 'lib/renderer/chrome-api.js', 'lib/renderer/content-scripts-injector.js', diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index df2589a9bcf5..2f8ffefebc1c 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -2,6 +2,7 @@ const ipcMain = require('electron').ipcMain const webContents = require('electron').webContents +const parseFeaturesString = require('../common/parse-features-string') // Doesn't exist in early initialization. let webViewManager = null @@ -184,6 +185,18 @@ const attachGuest = function (embedder, elementInstanceId, guestInstanceId, para 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) { webPreferences.preloadURL = params.preload } diff --git a/lib/common/parse-features-string.js b/lib/common/parse-features-string.js new file mode 100644 index 000000000000..a700617e6c9e --- /dev/null +++ b/lib/common/parse-features-string.js @@ -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) + }) +} diff --git a/lib/renderer/override.js b/lib/renderer/override.js index 9358a06a09d6..6b5e0e1615dd 100644 --- a/lib/renderer/override.js +++ b/lib/renderer/override.js @@ -2,6 +2,7 @@ const ipcRenderer = require('electron').ipcRenderer const remote = require('electron').remote +const parseFeaturesString = require('../common/parse-features-string') // Helper function to resolve relative url. 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. 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) { frameName = '' } @@ -104,27 +105,21 @@ window.open = function (url, frameName, features) { // Used to store additional features additionalFeatures = [] - // Make sure to get rid of excessive whitespace in the property name - ref1 = features.split(/,\s*/) - 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 + // Parse the features + parseFeaturesString(features, function (key, value) { if (value === undefined) { - additionalFeatures.push(feature) + additionalFeatures.push(key) } else { - if (webPreferences.includes(name)) { + if (webPreferences.includes(key)) { if (options.webPreferences == null) { options.webPreferences = {} } - options.webPreferences[name] = value + options.webPreferences[key] = value } else { - options[name] = value + options[key] = value } } - } + }) if (options.left) { if (options.x == null) { options.x = options.left diff --git a/lib/renderer/web-view/web-view-attributes.js b/lib/renderer/web-view/web-view-attributes.js index 3bbc83311124..f87fca312238 100644 --- a/lib/renderer/web-view/web-view-attributes.js +++ b/lib/renderer/web-view/web-view-attributes.js @@ -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. WebViewImpl.prototype.setupWebViewAttributes = function () { this.attributes = {} @@ -328,6 +335,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () { this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(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] autosizeAttributes.forEach((attribute) => { diff --git a/lib/renderer/web-view/web-view-constants.js b/lib/renderer/web-view/web-view-constants.js index 5aed6d5ea9e4..80fcaf91f24b 100644 --- a/lib/renderer/web-view/web-view-constants.js +++ b/lib/renderer/web-view/web-view-constants.js @@ -18,6 +18,7 @@ module.exports = { ATTRIBUTE_BLINKFEATURES: 'blinkfeatures', ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures', ATTRIBUTE_GUESTINSTANCE: 'guestinstance', + ATTRIBUTE_WEBPREFERENCES: 'webpreferences', // Internal attribute. ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid', diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 1c49688adeb8..059fc7228889 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -406,6 +406,33 @@ describe(' 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 = ` ` + 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 () { if (process.env.TRAVIS === 'true' && process.platform === 'darwin') { return