diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md
index 22963112a0..7f14fed95d 100644
--- a/docs/api/webview-tag.md
+++ b/docs/api/webview-tag.md
@@ -119,6 +119,17 @@ integration and can use node APIs like `require` and `process` to access low
level system resources. Node integration is disabled by default in the guest
page.
+### `nodeintegrationinsubframes`
+
+```html
+
+```
+
+Experimental option for enabling NodeJS support in sub-frames such as iframes
+inside the `webview`. All your preloads will load for every iframe, you can
+use `process.isMainFrame` to determine if you are in the main frame or not.
+This option is disabled by default in the guest page.
+
### `enableremotemodule`
```html
diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js
index d0da665f75..9f232026f3 100644
--- a/lib/browser/guest-view-manager.js
+++ b/lib/browser/guest-view-manager.js
@@ -209,6 +209,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
const webPreferences = {
guestInstanceId: guestInstanceId,
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
+ nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
enableRemoteModule: params.enableremotemodule,
plugins: params.plugins,
zoomFactor: embedder.getZoomFactor(),
diff --git a/lib/renderer/web-view/web-view-attributes.ts b/lib/renderer/web-view/web-view-attributes.ts
index 68425a3ecb..9d094bfbd5 100644
--- a/lib/renderer/web-view/web-view-attributes.ts
+++ b/lib/renderer/web-view/web-view-attributes.ts
@@ -280,6 +280,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this)
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, this)
+ this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this)
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this)
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this)
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this)
diff --git a/lib/renderer/web-view/web-view-constants.ts b/lib/renderer/web-view/web-view-constants.ts
index 6a31dbc0d0..433d12ae3b 100644
--- a/lib/renderer/web-view/web-view-constants.ts
+++ b/lib/renderer/web-view/web-view-constants.ts
@@ -5,6 +5,7 @@ export const enum WEB_VIEW_CONSTANTS {
ATTRIBUTE_SRC = 'src',
ATTRIBUTE_HTTPREFERRER = 'httpreferrer',
ATTRIBUTE_NODEINTEGRATION = 'nodeintegration',
+ ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES = 'nodeintegrationinsubframes',
ATTRIBUTE_ENABLEREMOTEMODULE = 'enableremotemodule',
ATTRIBUTE_PLUGINS = 'plugins',
ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity',
diff --git a/lib/renderer/web-view/web-view-element.ts b/lib/renderer/web-view/web-view-element.ts
index 098dca0ff8..cdb81308e2 100644
--- a/lib/renderer/web-view/web-view-element.ts
+++ b/lib/renderer/web-view/web-view-element.ts
@@ -24,6 +24,7 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof
WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER,
WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT,
WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION,
+ WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES,
WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS,
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY,
WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS,
diff --git a/spec/api-subframe-spec.js b/spec/api-subframe-spec.js
index df56873490..568fbf8067 100644
--- a/spec/api-subframe-spec.js
+++ b/spec/api-subframe-spec.js
@@ -10,6 +10,7 @@ const { BrowserWindow } = remote
describe('renderer nodeIntegrationInSubFrames', () => {
const generateTests = (description, webPreferences) => {
describe(description, () => {
+ const fixtureSuffix = webPreferences.webviewTag ? '-webview' : ''
let w
beforeEach(async () => {
@@ -18,11 +19,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
show: false,
width: 400,
height: 400,
- webPreferences: {
- preload: path.resolve(__dirname, 'fixtures/sub-frames/preload.js'),
- nodeIntegrationInSubFrames: true,
- ...webPreferences
- }
+ webPreferences
})
})
@@ -34,7 +31,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
it('should load preload scripts in top level iframes', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
- w.loadFile(path.resolve(__dirname, 'fixtures/sub-frames/frame-container.html'))
+ w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
const [event1, event2] = await detailsPromise
expect(event1[0].frameId).to.not.equal(event2[0].frameId)
expect(event1[0].frameId).to.equal(event1[2])
@@ -43,7 +40,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
it('should load preload scripts in nested iframes', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 3)
- w.loadFile(path.resolve(__dirname, 'fixtures/sub-frames/frame-with-frame-container.html'))
+ w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`))
const [event1, event2, event3] = await detailsPromise
expect(event1[0].frameId).to.not.equal(event2[0].frameId)
expect(event1[0].frameId).to.not.equal(event3[0].frameId)
@@ -55,7 +52,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
it('should correctly reply to the main frame with using event.reply', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
- w.loadFile(path.resolve(__dirname, 'fixtures/sub-frames/frame-container.html'))
+ w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
const [event1] = await detailsPromise
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
event1[0].reply('preload-ping')
@@ -65,7 +62,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
it('should correctly reply to the sub-frames with using event.reply', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
- w.loadFile(path.resolve(__dirname, 'fixtures/sub-frames/frame-container.html'))
+ w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
const [, event2] = await detailsPromise
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
event2[0].reply('preload-ping')
@@ -75,7 +72,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
it('should correctly reply to the nested sub-frames with using event.reply', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 3)
- w.loadFile(path.resolve(__dirname, 'fixtures/sub-frames/frame-with-frame-container.html'))
+ w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`))
const [, , event3] = await detailsPromise
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
event3[0].reply('preload-ping')
@@ -85,7 +82,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
it('should not expose globals in main world', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
- w.loadFile(path.resolve(__dirname, 'fixtures/sub-frames/frame-container.html'))
+ w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
const details = await detailsPromise
const senders = details.map(event => event[0].sender)
await new Promise((resolve) => {
@@ -108,8 +105,50 @@ describe('renderer nodeIntegrationInSubFrames', () => {
})
}
- generateTests('without sandbox', {})
- generateTests('with sandbox', { sandbox: true })
- generateTests('with contextIsolation', { contextIsolation: true })
- generateTests('with contextIsolation + sandbox', { contextIsolation: true, sandbox: true })
+ const generateConfigs = (webPreferences, ...permutations) => {
+ const configs = [{ webPreferences, names: [] }]
+ for (let i = 0; i < permutations.length; i++) {
+ const length = configs.length
+ for (let j = 0; j < length; j++) {
+ const newConfig = Object.assign({}, configs[j])
+ newConfig.webPreferences = Object.assign({},
+ newConfig.webPreferences, permutations[i].webPreferences)
+ newConfig.names = newConfig.names.slice(0)
+ newConfig.names.push(permutations[i].name)
+ configs.push(newConfig)
+ }
+ }
+
+ return configs.map(config => {
+ if (config.names.length > 0) {
+ config.title = `with ${config.names.join(', ')} on`
+ } else {
+ config.title = `without anything special turned on`
+ }
+ delete config.names
+
+ return config
+ })
+ }
+
+ generateConfigs(
+ {
+ preload: path.resolve(__dirname, 'fixtures/sub-frames/preload.js'),
+ nodeIntegrationInSubFrames: true
+ },
+ {
+ name: 'sandbox',
+ webPreferences: { sandbox: true }
+ },
+ {
+ name: 'context isolation',
+ webPreferences: { contextIsolation: true }
+ },
+ {
+ name: 'webview',
+ webPreferences: { webviewTag: true, preload: false }
+ }
+ ).forEach(config => {
+ generateTests(config.title, config.webPreferences)
+ })
})
diff --git a/spec/fixtures/sub-frames/frame-container-webview.html b/spec/fixtures/sub-frames/frame-container-webview.html
new file mode 100644
index 0000000000..aabc9e87e1
--- /dev/null
+++ b/spec/fixtures/sub-frames/frame-container-webview.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Document
+
+
+ This is the root page with a webview
+
+
+
diff --git a/spec/fixtures/sub-frames/frame-with-frame-container-webview.html b/spec/fixtures/sub-frames/frame-with-frame-container-webview.html
new file mode 100644
index 0000000000..880862f967
--- /dev/null
+++ b/spec/fixtures/sub-frames/frame-with-frame-container-webview.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Document
+
+
+ This is the root page with a webview
+
+
+