Skip to content

Commit

Permalink
feat: enable NodeIntegrationInSubFrames for webview (5-0-x) (#17398)
Browse files Browse the repository at this point in the history
* feat: enable nodeIntegrationInSubFrames for webview

* test: add tests

* docs: document webview's nodeintegrationinsubframes

* lint: fix indent

* chore: roll node to make CI pass for now
  • Loading branch information
brenca authored and MarshallOfSound committed Mar 19, 2019
1 parent 361e5b2 commit 1b8a25b
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 16 deletions.
2 changes: 1 addition & 1 deletion DEPS
Expand Up @@ -12,7 +12,7 @@ vars = {
'chromium_version':
'73.0.3683.79',
'node_version':
'70a78f07b1c4d53f3da462b08cef42a4ff8f949f',
'f807d72614d4b8973d71548328be213532b46b1b',

'boto_version': 'f7574aa6cc2c819430c1f05e9a1a1a666ef8169b',
'pyyaml_version': '3.12',
Expand Down
11 changes: 11 additions & 0 deletions docs/api/webview-tag.md
Expand Up @@ -126,6 +126,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
<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`

```html
Expand Down
1 change: 1 addition & 0 deletions lib/browser/guest-view-manager.js
Expand Up @@ -210,6 +210,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(),
Expand Down
1 change: 1 addition & 0 deletions lib/renderer/web-view/web-view-attributes.js
Expand Up @@ -270,6 +270,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this)
this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this)
this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this)
this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this)
this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
Expand Down
1 change: 1 addition & 0 deletions lib/renderer/web-view/web-view-constants.js
Expand Up @@ -7,6 +7,7 @@ module.exports = {
ATTRIBUTE_SRC: 'src',
ATTRIBUTE_HTTPREFERRER: 'httpreferrer',
ATTRIBUTE_NODEINTEGRATION: 'nodeintegration',
ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES: 'nodeintegrationinsubframes',
ATTRIBUTE_ENABLEREMOTEMODULE: 'enableremotemodule',
ATTRIBUTE_PLUGINS: 'plugins',
ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity',
Expand Down
1 change: 1 addition & 0 deletions lib/renderer/web-view/web-view-element.js
Expand Up @@ -23,6 +23,7 @@ const defineWebViewElement = (v8Util, webViewImpl) => {
webViewConstants.ATTRIBUTE_HTTPREFERRER,
webViewConstants.ATTRIBUTE_USERAGENT,
webViewConstants.ATTRIBUTE_NODEINTEGRATION,
webViewConstants.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES,
webViewConstants.ATTRIBUTE_PLUGINS,
webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY,
webViewConstants.ATTRIBUTE_ALLOWPOPUPS,
Expand Down
69 changes: 54 additions & 15 deletions spec/api-subframe-spec.js
Expand Up @@ -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 () => {
Expand All @@ -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
})
})

Expand All @@ -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])
Expand All @@ -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)
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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) => {
Expand All @@ -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)
})
})
13 changes: 13 additions & 0 deletions spec/fixtures/sub-frames/frame-container-webview.html
@@ -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 changes: 13 additions & 0 deletions spec/fixtures/sub-frames/frame-with-frame-container-webview.html
@@ -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>

0 comments on commit 1b8a25b

Please sign in to comment.