From 194b14100e08a9043364e308f83d53433b52fb05 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Fri, 14 Oct 2016 17:16:39 -0500 Subject: [PATCH 1/7] add the 'webpreferences' attribute to webviews --- docs/api/web-view-tag.md | 14 +++++++++++ lib/browser/guest-view-manager.js | 25 ++++++++++++++++++++ lib/renderer/web-view/web-view-attributes.js | 8 +++++++ lib/renderer/web-view/web-view-constants.js | 1 + 4 files changed, 48 insertions(+) diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 512300c9f67c..c5d7c7b958c0 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/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index df2589a9bcf5..b62e7cf2bf40 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -184,6 +184,31 @@ 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') { + // split the attribute's value by ',' + let webpreferencesTokens = params.webpreferences.split(/,\s*/) + for (i = 0, len = webpreferencesTokens.length; i < len; i++) { + // expected form is either a name by itself (true boolean flag) + // or a key/value, in the form of 'name=value' + // split the tokens by '=' + let pref = webpreferencesTokens[i] + let prefTokens = pref.split(/\s*=/) + name = prefTokens[0] + value = prefTokens[1] + if (!name) continue + + // interpret the value as a boolean, if possible + value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value + if (value === undefined) { + // no value was specified, default it to true + value = true + } + webPreferences[name] = value + } + } + if (params.preload) { webPreferences.preloadURL = params.preload } diff --git a/lib/renderer/web-view/web-view-attributes.js b/lib/renderer/web-view/web-view-attributes.js index 3bbc83311124..7c5cf81e4b65 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', From e7962c7ba2421c809134f38fd82d0e5ca2e2c2a9 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Fri, 14 Oct 2016 18:04:33 -0500 Subject: [PATCH 2/7] fixes and tests for webview 'webpreferences' attr --- lib/browser/guest-view-manager.js | 5 ++-- lib/renderer/web-view/web-view-attributes.js | 2 +- spec/webview-spec.js | 27 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index b62e7cf2bf40..c4b0022f67af 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -188,6 +188,7 @@ const attachGuest = function (embedder, elementInstanceId, guestInstanceId, para // this uses the same parsing rules as window.open uses for its features if (typeof params.webpreferences === 'string') { // split the attribute's value by ',' + let i, len let webpreferencesTokens = params.webpreferences.split(/,\s*/) for (i = 0, len = webpreferencesTokens.length; i < len; i++) { // expected form is either a name by itself (true boolean flag) @@ -195,8 +196,8 @@ const attachGuest = function (embedder, elementInstanceId, guestInstanceId, para // split the tokens by '=' let pref = webpreferencesTokens[i] let prefTokens = pref.split(/\s*=/) - name = prefTokens[0] - value = prefTokens[1] + let name = prefTokens[0] + let value = prefTokens[1] if (!name) continue // interpret the value as a boolean, if possible diff --git a/lib/renderer/web-view/web-view-attributes.js b/lib/renderer/web-view/web-view-attributes.js index 7c5cf81e4b65..f87fca312238 100644 --- a/lib/renderer/web-view/web-view-attributes.js +++ b/lib/renderer/web-view/web-view-attributes.js @@ -335,7 +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) + 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/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 From f35536bdc5a8ee49d7dfa6705038d7364bf385e2 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Mon, 24 Oct 2016 20:21:42 -0500 Subject: [PATCH 3/7] factor out parse-features-string.js --- lib/browser/guest-view-manager.js | 19 +++---------------- lib/common/parse-features-string.js | 24 ++++++++++++++++++++++++ lib/renderer/override.js | 21 ++++++++------------- 3 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 lib/common/parse-features-string.js diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index c4b0022f67af..de11b03206bb 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 @@ -187,27 +188,13 @@ const attachGuest = function (embedder, elementInstanceId, guestInstanceId, para // 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') { - // split the attribute's value by ',' - let i, len - let webpreferencesTokens = params.webpreferences.split(/,\s*/) - for (i = 0, len = webpreferencesTokens.length; i < len; i++) { - // expected form is either a name by itself (true boolean flag) - // or a key/value, in the form of 'name=value' - // split the tokens by '=' - let pref = webpreferencesTokens[i] - let prefTokens = pref.split(/\s*=/) - let name = prefTokens[0] - let value = prefTokens[1] - if (!name) continue - - // interpret the value as a boolean, if possible - value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value + parseFeaturesString(params.webpreferences, function (key, value) { if (value === undefined) { // no value was specified, default it to true value = true } webPreferences[name] = value - } + }) } if (params.preload) { diff --git a/lib/common/parse-features-string.js b/lib/common/parse-features-string.js new file mode 100644 index 000000000000..da0381bbcf12 --- /dev/null +++ b/lib/common/parse-features-string.js @@ -0,0 +1,24 @@ +// parses a feature string that has the format used in window.open() +// - `str` input string +// - `emit` function(key, value) - called for each parsed KV +module.exports = function parseFeaturesString (str, emit) { + // split the string by ',' + let i, len + let strTokens = params.str.split(/,\s*/) + for (i = 0, len = strTokens.length; i < len; i++) { + // expected form is either a key by itself (true boolean flag) + // or a key/value, in the form of 'key=value' + // split the tokens by '=' + let kv = strTokens[i] + let kvTokens = kv.split(/\s*=/) + let key = kvTokens[0] + let value = kvTokens[1] + if (!key) continue + + // 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..8c00661ddbe7 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') @@ -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 From 2556ed87449257d58a19a2a26631a06cd9e2bbd1 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 25 Oct 2016 10:28:36 +0900 Subject: [PATCH 4/7] Add parse-features-strings.js --- filenames.gypi | 1 + 1 file changed, 1 insertion(+) diff --git a/filenames.gypi b/filenames.gypi index 9c4b42f622d5..b410f96bd73d 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', From 3aad6a0c993fa4617bbe16c85d8b13651a0137ca Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 25 Oct 2016 10:31:50 +0900 Subject: [PATCH 5/7] Use forEach and destructuring for parsing features --- lib/browser/guest-view-manager.js | 2 +- lib/common/parse-features-string.js | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index de11b03206bb..2f8ffefebc1c 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -193,7 +193,7 @@ const attachGuest = function (embedder, elementInstanceId, guestInstanceId, para // no value was specified, default it to true value = true } - webPreferences[name] = value + webPreferences[key] = value }) } diff --git a/lib/common/parse-features-string.js b/lib/common/parse-features-string.js index da0381bbcf12..2c74c3463dc9 100644 --- a/lib/common/parse-features-string.js +++ b/lib/common/parse-features-string.js @@ -1,24 +1,19 @@ // parses a feature string that has the format used in window.open() // - `str` input string // - `emit` function(key, value) - called for each parsed KV -module.exports = function parseFeaturesString (str, emit) { +module.exports = function parseFeaturesString (features, emit) { // split the string by ',' - let i, len - let strTokens = params.str.split(/,\s*/) - for (i = 0, len = strTokens.length; i < len; i++) { + features.split(/,\s*/).forEach((feature) => { // expected form is either a key by itself (true boolean flag) // or a key/value, in the form of 'key=value' // split the tokens by '=' - let kv = strTokens[i] - let kvTokens = kv.split(/\s*=/) - let key = kvTokens[0] - let value = kvTokens[1] - if (!key) continue + let [key, value] = feature.split(/\s*=/) + if (!key) return - // interpret the value as a boolean, if possible + // 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) - } + }) } From 68c2c9825fab05f5a6e674b49a0672de542890eb Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 25 Oct 2016 10:36:43 +0900 Subject: [PATCH 6/7] Remove lint warnings --- lib/common/parse-features-string.js | 7 +++---- lib/renderer/override.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/common/parse-features-string.js b/lib/common/parse-features-string.js index 2c74c3463dc9..a700617e6c9e 100644 --- a/lib/common/parse-features-string.js +++ b/lib/common/parse-features-string.js @@ -1,12 +1,11 @@ // parses a feature string that has the format used in window.open() -// - `str` input string +// - `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 (true boolean flag) - // or a key/value, in the form of 'key=value' - // split the tokens by '=' + // 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 diff --git a/lib/renderer/override.js b/lib/renderer/override.js index 8c00661ddbe7..6b5e0e1615dd 100644 --- a/lib/renderer/override.js +++ b/lib/renderer/override.js @@ -89,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 = '' } From b4cc3541a8cb103136ccdb8484f6bbef41976a3b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 25 Oct 2016 12:08:19 +0900 Subject: [PATCH 7/7] Minor tweaks to webpreferences docs --- docs/api/web-view-tag.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index c5d7c7b958c0..88f042f65ed5 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -201,12 +201,12 @@ If "on", the guest page will be allowed to open new windows. ``` 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 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. +Special values `yes` and `1` are interpreted as `true`, while `no` and `0` are interpreted as `false`. ### `blinkfeatures`