Skip to content

Commit

Permalink
fix: load window-setup in sandboxed renderer (#21416)
Browse files Browse the repository at this point in the history
  • Loading branch information
miniak authored and Milan Burda committed Jan 6, 2020
1 parent 64e48ad commit 95f82f9
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 62 deletions.
1 change: 1 addition & 0 deletions filenames.auto.gni
Expand Up @@ -165,6 +165,7 @@ auto_filenames = {
"lib/renderer/web-view/web-view-element.ts",
"lib/renderer/web-view/web-view-impl.ts",
"lib/renderer/web-view/web-view-init.ts",
"lib/renderer/window-setup.ts",
"lib/sandboxed_renderer/api/exports/electron.ts",
"lib/sandboxed_renderer/api/module-list.ts",
"lib/sandboxed_renderer/init.js",
Expand Down
6 changes: 4 additions & 2 deletions lib/browser/guest-window-manager.js
Expand Up @@ -75,8 +75,10 @@ const mergeBrowserWindowOptions = function (embedder, options) {
}
}

// Sets correct openerId here to give correct options to 'new-window' event handler
options.webPreferences.openerId = embedder.id
if (!webPreferences.nativeWindowOpen) {
// Sets correct openerId here to give correct options to 'new-window' event handler
options.webPreferences.openerId = embedder.id
}

return options
}
Expand Down
4 changes: 4 additions & 0 deletions lib/browser/rpc-server.js
Expand Up @@ -112,11 +112,15 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event)
contentScripts = getContentScripts()
}

const webPreferences = event.sender.getLastWebPreferences() || {}

return {
contentScripts,
preloadScripts: await Promise.all(preloadPaths.map(path => getPreloadScript(path))),
isRemoteModuleEnabled: isRemoteModuleEnabled(event.sender),
isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender),
guestInstanceId: webPreferences.guestInstanceId,
openerId: webPreferences.openerId,
process: {
arch: process.arch,
platform: process.platform,
Expand Down
74 changes: 39 additions & 35 deletions lib/renderer/window-setup.ts
Expand Up @@ -177,7 +177,7 @@ class BrowserWindowProxy {
export const windowSetup = (
guestInstanceId: number, openerId: number, isHiddenPage: boolean, usesNativeWindowOpen: boolean
) => {
if (guestInstanceId == null) {
if (!process.sandboxed && guestInstanceId == null) {
// Override default window.close.
window.close = function () {
ipcRendererInternal.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE')
Expand All @@ -197,53 +197,57 @@ export const windowSetup = (
return null
}
}
}

if (openerId != null) {
window.opener = getOrCreateProxy(openerId)
}
if (openerId != null) {
window.opener = getOrCreateProxy(openerId)
}

// But we do not support prompt().
window.prompt = function () {
throw new Error('prompt() is and will not be supported.')
}

ipcRendererInternal.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (
_event, sourceId: number, message: any, sourceOrigin: string
) {
// Manually dispatch event instead of using postMessage because we also need to
// set event.source.
//
// Why any? We can't construct a MessageEvent and we can't
// use `as MessageEvent` because you're not supposed to override
// data, origin, and source
const event: any = document.createEvent('Event')
event.initEvent('message', false, false)

event.data = message
event.origin = sourceOrigin
event.source = getOrCreateProxy(sourceId)

window.dispatchEvent(event as MessageEvent)
})

window.history.back = function () {
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK')
if (!usesNativeWindowOpen || openerId != null) {
ipcRendererInternal.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (
_event, sourceId: number, message: any, sourceOrigin: string
) {
// Manually dispatch event instead of using postMessage because we also need to
// set event.source.
//
// Why any? We can't construct a MessageEvent and we can't
// use `as MessageEvent` because you're not supposed to override
// data, origin, and source
const event: any = document.createEvent('Event')
event.initEvent('message', false, false)

event.data = message
event.origin = sourceOrigin
event.source = getOrCreateProxy(sourceId)

window.dispatchEvent(event as MessageEvent)
})
}

window.history.forward = function () {
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD')
}
if (!process.sandboxed) {
window.history.back = function () {
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK')
}

window.history.go = function (offset: number) {
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset)
}
window.history.forward = function () {
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD')
}

Object.defineProperty(window.history, 'length', {
get: function () {
return ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH')
window.history.go = function (offset: number) {
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset)
}
})

Object.defineProperty(window.history, 'length', {
get: function () {
return ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH')
}
})
}

if (guestInstanceId != null) {
// Webview `document.visibilityState` tracks window visibility (and ignores
Expand Down
24 changes: 21 additions & 3 deletions lib/sandboxed_renderer/init.js
Expand Up @@ -30,7 +30,13 @@ const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-rendere
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')

const {
contentScripts, preloadScripts, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps
contentScripts,
preloadScripts,
isRemoteModuleEnabled,
isWebViewTagEnabled,
guestInstanceId,
openerId,
process: processProps
} = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_SANDBOX_LOAD')

process.isRemoteModuleEnabled = isRemoteModuleEnabled
Expand Down Expand Up @@ -109,6 +115,11 @@ function preloadRequire (module) {
const { hasSwitch } = process.electronBinding('command_line')

const contextIsolation = hasSwitch('context-isolation')
const isHiddenPage = hasSwitch('hidden-page')
const usesNativeWindowOpen = true

// The arguments to be passed to isolated world.
const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen }

switch (window.location.protocol) {
case 'devtools:': {
Expand All @@ -127,21 +138,28 @@ switch (window.location.protocol) {
break
}
default: {
// Override default web functions.
const { windowSetup } = require('@electron/internal/renderer/window-setup')
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)

// Inject content scripts.
if (!process.electronBinding('features').isExtensionsEnabled()) {
require('@electron/internal/renderer/content-scripts-injector')(contentScripts)
}
}
}

const guestInstanceId = binding.guestInstanceId && parseInt(binding.guestInstanceId)

// Load webview tag implementation.
if (process.isMainFrame) {
const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init')
webViewInit(contextIsolation, isWebViewTagEnabled, guestInstanceId)
}

// Pass the arguments to isolatedWorld.
if (contextIsolation) {
v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs)
}

// Wrap the script into a function executed in global scope. It won't have
// access to the current scope, so we'll expose a few objects as arguments:
//
Expand Down
6 changes: 0 additions & 6 deletions shell/renderer/atom_sandboxed_renderer_client.cc
Expand Up @@ -146,12 +146,6 @@ void AtomSandboxedRendererClient::InitializeBindings(
process.SetReadOnly("sandboxed", true);
process.SetReadOnly("type", "renderer");
process.SetReadOnly("isMainFrame", is_main_frame);

// Pass in CLI flags needed to setup the renderer
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kGuestInstanceID))
b.Set(options::kGuestInstanceID,
command_line->GetSwitchValueASCII(switches::kGuestInstanceID));
}

void AtomSandboxedRendererClient::RenderFrameCreated(
Expand Down
37 changes: 21 additions & 16 deletions spec-main/chromium-spec.ts
Expand Up @@ -768,23 +768,28 @@ describe('chromium features', () => {
afterEach(closeAllWindows)

describe('when opened from main window', () => {
for (const {parent, child, nodeIntegration, nativeWindowOpen, openerAccessible} of table) {
const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen}, child should ${openerAccessible ? '' : 'not '}be able to access opener`
it(description, async () => {
const w = new BrowserWindow({show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen }})
await w.loadURL(parent)
const childOpenerLocation = await w.webContents.executeJavaScript(`new Promise(resolve => {
window.addEventListener('message', function f(e) {
resolve(e.data)
for (const { parent, child, nodeIntegration, nativeWindowOpen, openerAccessible } of table) {
for (const sandboxPopup of [false, true]) {
const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen} sandboxPopup=${sandboxPopup}, child should ${openerAccessible ? '' : 'not '}be able to access opener`
it(description, async () => {
const w = new BrowserWindow({show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen }})
w.webContents.once('new-window', (e, url, frameName, disposition, options) => {
options!.webPreferences!.sandbox = sandboxPopup
})
window.open(${JSON.stringify(child)}, "", "show=no,nodeIntegration=${nodeIntegration ? "yes" : "no"}")
})`)
if (openerAccessible) {
expect(childOpenerLocation).to.be.a('string')
} else {
expect(childOpenerLocation).to.be.null()
}
})
await w.loadURL(parent)
const childOpenerLocation = await w.webContents.executeJavaScript(`new Promise(resolve => {
window.addEventListener('message', function f(e) {
resolve(e.data)
})
window.open(${JSON.stringify(child)}, "", "show=no,nodeIntegration=${nodeIntegration ? "yes" : "no"}")
})`)
if (openerAccessible) {
expect(childOpenerLocation).to.be.a('string')
} else {
expect(childOpenerLocation).to.be.null()
}
})
}
}
})

Expand Down

0 comments on commit 95f82f9

Please sign in to comment.