Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable NodeIntegrationInSubFrames for webview (5-0-x) #17398

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>