feat: enable NodeIntegrationInSubFrames for webview (#17226)
* feat: enable nodeIntegrationInSubFrames for webview * test: add tests * docs: document webview's nodeintegrationinsubframes * lint: fix indent * fix: resolve some merge bloopers
This commit is contained in:
parent
961c9a88a8
commit
43ef561d48
8 changed files with 95 additions and 15 deletions
|
@ -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
|
level system resources. Node integration is disabled by default in the guest
|
||||||
page.
|
page.
|
||||||
|
|
||||||
|
### `nodeintegrationinsubframes`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<webview src="http://www.google.com/" nodeintegrationinsubframes></webview>
|
||||||
|
```
|
||||||
|
|
||||||
|
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`
|
### `enableremotemodule`
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
|
|
@ -209,6 +209,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
|
||||||
const webPreferences = {
|
const webPreferences = {
|
||||||
guestInstanceId: guestInstanceId,
|
guestInstanceId: guestInstanceId,
|
||||||
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
|
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
|
||||||
|
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
|
||||||
enableRemoteModule: params.enableremotemodule,
|
enableRemoteModule: params.enableremotemodule,
|
||||||
plugins: params.plugins,
|
plugins: params.plugins,
|
||||||
zoomFactor: embedder.getZoomFactor(),
|
zoomFactor: embedder.getZoomFactor(),
|
||||||
|
|
|
@ -280,6 +280,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
|
||||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
|
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_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_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_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_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this)
|
||||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this)
|
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this)
|
||||||
|
|
|
@ -5,6 +5,7 @@ export const enum WEB_VIEW_CONSTANTS {
|
||||||
ATTRIBUTE_SRC = 'src',
|
ATTRIBUTE_SRC = 'src',
|
||||||
ATTRIBUTE_HTTPREFERRER = 'httpreferrer',
|
ATTRIBUTE_HTTPREFERRER = 'httpreferrer',
|
||||||
ATTRIBUTE_NODEINTEGRATION = 'nodeintegration',
|
ATTRIBUTE_NODEINTEGRATION = 'nodeintegration',
|
||||||
|
ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES = 'nodeintegrationinsubframes',
|
||||||
ATTRIBUTE_ENABLEREMOTEMODULE = 'enableremotemodule',
|
ATTRIBUTE_ENABLEREMOTEMODULE = 'enableremotemodule',
|
||||||
ATTRIBUTE_PLUGINS = 'plugins',
|
ATTRIBUTE_PLUGINS = 'plugins',
|
||||||
ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity',
|
ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity',
|
||||||
|
|
|
@ -24,6 +24,7 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION,
|
||||||
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS,
|
||||||
|
|
|
@ -10,6 +10,7 @@ const { BrowserWindow } = remote
|
||||||
describe('renderer nodeIntegrationInSubFrames', () => {
|
describe('renderer nodeIntegrationInSubFrames', () => {
|
||||||
const generateTests = (description, webPreferences) => {
|
const generateTests = (description, webPreferences) => {
|
||||||
describe(description, () => {
|
describe(description, () => {
|
||||||
|
const fixtureSuffix = webPreferences.webviewTag ? '-webview' : ''
|
||||||
let w
|
let w
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
@ -18,11 +19,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
|
||||||
show: false,
|
show: false,
|
||||||
width: 400,
|
width: 400,
|
||||||
height: 400,
|
height: 400,
|
||||||
webPreferences: {
|
webPreferences
|
||||||
preload: path.resolve(__dirname, 'fixtures/sub-frames/preload.js'),
|
|
||||||
nodeIntegrationInSubFrames: true,
|
|
||||||
...webPreferences
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -34,7 +31,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
|
||||||
|
|
||||||
it('should load preload scripts in top level iframes', async () => {
|
it('should load preload scripts in top level iframes', async () => {
|
||||||
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
|
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
|
const [event1, event2] = await detailsPromise
|
||||||
expect(event1[0].frameId).to.not.equal(event2[0].frameId)
|
expect(event1[0].frameId).to.not.equal(event2[0].frameId)
|
||||||
expect(event1[0].frameId).to.equal(event1[2])
|
expect(event1[0].frameId).to.equal(event1[2])
|
||||||
|
@ -43,7 +40,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
|
||||||
|
|
||||||
it('should load preload scripts in nested iframes', async () => {
|
it('should load preload scripts in nested iframes', async () => {
|
||||||
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 3)
|
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
|
const [event1, event2, event3] = await detailsPromise
|
||||||
expect(event1[0].frameId).to.not.equal(event2[0].frameId)
|
expect(event1[0].frameId).to.not.equal(event2[0].frameId)
|
||||||
expect(event1[0].frameId).to.not.equal(event3[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 () => {
|
it('should correctly reply to the main frame with using event.reply', async () => {
|
||||||
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
|
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 [event1] = await detailsPromise
|
||||||
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
|
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
|
||||||
event1[0].reply('preload-ping')
|
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 () => {
|
it('should correctly reply to the sub-frames with using event.reply', async () => {
|
||||||
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
|
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 [, event2] = await detailsPromise
|
||||||
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
|
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
|
||||||
event2[0].reply('preload-ping')
|
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 () => {
|
it('should correctly reply to the nested sub-frames with using event.reply', async () => {
|
||||||
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 3)
|
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 [, , event3] = await detailsPromise
|
||||||
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
|
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
|
||||||
event3[0].reply('preload-ping')
|
event3[0].reply('preload-ping')
|
||||||
|
@ -85,7 +82,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
|
||||||
|
|
||||||
it('should not expose globals in main world', async () => {
|
it('should not expose globals in main world', async () => {
|
||||||
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
|
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 details = await detailsPromise
|
||||||
const senders = details.map(event => event[0].sender)
|
const senders = details.map(event => event[0].sender)
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
|
@ -108,8 +105,50 @@ describe('renderer nodeIntegrationInSubFrames', () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
generateTests('without sandbox', {})
|
const generateConfigs = (webPreferences, ...permutations) => {
|
||||||
generateTests('with sandbox', { sandbox: true })
|
const configs = [{ webPreferences, names: [] }]
|
||||||
generateTests('with contextIsolation', { contextIsolation: true })
|
for (let i = 0; i < permutations.length; i++) {
|
||||||
generateTests('with contextIsolation + sandbox', { contextIsolation: true, sandbox: true })
|
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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
13
spec/fixtures/sub-frames/frame-container-webview.html
vendored
Normal file
13
spec/fixtures/sub-frames/frame-container-webview.html
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
This is the root page with a webview
|
||||||
|
<webview src="./frame-container.html" sandbox nodeIntegrationInSubFrames preload="./preload.js"></webview>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
spec/fixtures/sub-frames/frame-with-frame-container-webview.html
vendored
Normal file
13
spec/fixtures/sub-frames/frame-with-frame-container-webview.html
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
This is the root page with a webview
|
||||||
|
<webview src="./frame-with-frame-container.html" sandbox nodeIntegrationInSubFrames preload="./preload.js"></webview>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue