Merge branch 'support-chromium-sandbox' of https://github.com/tarruda/electron into tarruda-support-chromium-sandbox
This commit is contained in:
commit
458c4dd129
35 changed files with 1055 additions and 100 deletions
|
@ -3,6 +3,7 @@
|
|||
const {ipcMain} = require('electron')
|
||||
const {EventEmitter} = require('events')
|
||||
const {BrowserWindow} = process.atomBinding('window')
|
||||
const v8Util = process.atomBinding('v8_util')
|
||||
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, EventEmitter.prototype)
|
||||
|
||||
|
@ -26,6 +27,34 @@ BrowserWindow.prototype._init = function () {
|
|||
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, disposition, options)
|
||||
})
|
||||
|
||||
this.webContents.on('-web-contents-created', (event, webContents, url,
|
||||
frameName) => {
|
||||
v8Util.setHiddenValue(webContents, 'url-framename', {url, frameName})
|
||||
})
|
||||
// Create a new browser window for the native implementation of
|
||||
// "window.open"(sandbox mode only)
|
||||
this.webContents.on('-add-new-contents', (event, webContents, disposition,
|
||||
userGesture, left, top, width,
|
||||
height) => {
|
||||
let urlFrameName = v8Util.getHiddenValue(webContents, 'url-framename')
|
||||
if ((disposition !== 'foreground-tab' && disposition !== 'new-window') ||
|
||||
!urlFrameName) {
|
||||
return
|
||||
}
|
||||
|
||||
let {url, frameName} = urlFrameName
|
||||
v8Util.deleteHiddenValue(webContents, 'url-framename')
|
||||
const options = {
|
||||
show: true,
|
||||
x: left,
|
||||
y: top,
|
||||
width: width || 800,
|
||||
height: height || 600,
|
||||
webContents: webContents
|
||||
}
|
||||
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, disposition, options)
|
||||
})
|
||||
|
||||
// window.resizeTo(...)
|
||||
// window.moveTo(...)
|
||||
this.webContents.on('move', (event, size) => {
|
||||
|
|
|
@ -123,7 +123,7 @@ const hookWebContentsEvents = function (webContents) {
|
|||
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
|
||||
frameId: 0,
|
||||
parentFrameId: -1,
|
||||
processId: webContents.getId(),
|
||||
processId: webContents.getProcessId(),
|
||||
tabId: tabId,
|
||||
timeStamp: Date.now(),
|
||||
url: url
|
||||
|
@ -134,7 +134,7 @@ const hookWebContentsEvents = function (webContents) {
|
|||
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', {
|
||||
frameId: 0,
|
||||
parentFrameId: -1,
|
||||
processId: webContents.getId(),
|
||||
processId: webContents.getProcessId(),
|
||||
tabId: tabId,
|
||||
timeStamp: Date.now(),
|
||||
url: url
|
||||
|
|
|
@ -57,7 +57,30 @@ const createGuest = function (embedder, url, frameName, options) {
|
|||
}
|
||||
options.webPreferences.openerId = embedder.id
|
||||
guest = new BrowserWindow(options)
|
||||
guest.loadURL(url)
|
||||
if (!options.webContents || url !== 'about:blank') {
|
||||
// We should not call `loadURL` if the window was constructed from an
|
||||
// existing webContents(window.open in a sandboxed renderer) and if the url
|
||||
// is not 'about:blank'.
|
||||
//
|
||||
// Navigating to the url when creating the window from an existing
|
||||
// webContents would not be necessary(it will navigate there anyway), but
|
||||
// apparently there's a bug that allows the child window to be scripted by
|
||||
// the opener, even when the child window is from another origin.
|
||||
//
|
||||
// That's why the second condition(url !== "about:blank") is required: to
|
||||
// force `OverrideSiteInstanceForNavigation` to be called and consequently
|
||||
// spawn a new renderer if the new window is targeting a different origin.
|
||||
//
|
||||
// If the URL is "about:blank", then it is very likely that the opener just
|
||||
// wants to synchronously script the popup, for example:
|
||||
//
|
||||
// let popup = window.open()
|
||||
// popup.document.body.write('<h1>hello</h1>')
|
||||
//
|
||||
// The above code would not work if a navigation to "about:blank" is done
|
||||
// here, since the window would be cleared of all changes in the next tick.
|
||||
guest.loadURL(url)
|
||||
}
|
||||
|
||||
// When |embedder| is destroyed we should also destroy attached guest, and if
|
||||
// guest is closed by user then we should prevent |embedder| from double
|
||||
|
@ -72,8 +95,19 @@ const createGuest = function (embedder, url, frameName, options) {
|
|||
embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
|
||||
embedder.removeListener('render-view-deleted', closedByEmbedder)
|
||||
}
|
||||
embedder.once('render-view-deleted', closedByEmbedder)
|
||||
guest.once('closed', closedByUser)
|
||||
if (!options.webPreferences.sandbox) {
|
||||
// These events should only be handled when the guest window is opened by a
|
||||
// non-sandboxed renderer for two reasons:
|
||||
//
|
||||
// - `render-view-deleted` is emitted when the popup is closed by the user,
|
||||
// and that will eventually result in NativeWindow::NotifyWindowClosed
|
||||
// using a dangling pointer since `destroy()` would have been called by
|
||||
// `closeByEmbedded`
|
||||
// - No need to emit `ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_` since
|
||||
// there's no renderer code listening to it.,
|
||||
embedder.once('render-view-deleted', closedByEmbedder)
|
||||
guest.once('closed', closedByUser)
|
||||
}
|
||||
|
||||
if (frameName) {
|
||||
frameToGuest[frameName] = guest
|
||||
|
|
|
@ -5,6 +5,8 @@ const electron = require('electron')
|
|||
const v8Util = process.atomBinding('v8_util')
|
||||
const {ipcMain, isPromise, webContents} = electron
|
||||
|
||||
const fs = require('fs')
|
||||
|
||||
const objectsRegistry = require('./objects-registry')
|
||||
|
||||
const hasProp = {}.hasOwnProperty
|
||||
|
@ -235,6 +237,13 @@ ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
|
|||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_READ_FILE', function (event, file) {
|
||||
fs.readFile(file, (err, data) => {
|
||||
if (err) event.returnValue = {err: err.message}
|
||||
else event.returnValue = {data: data.toString()}
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, electron[module])
|
||||
|
|
29
lib/renderer/api/ipc-renderer-setup.js
Normal file
29
lib/renderer/api/ipc-renderer-setup.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
module.exports = function (ipcRenderer, binding) {
|
||||
ipcRenderer.send = function (...args) {
|
||||
return binding.send('ipc-message', args)
|
||||
}
|
||||
|
||||
ipcRenderer.sendSync = function (...args) {
|
||||
return JSON.parse(binding.sendSync('ipc-message-sync', args))
|
||||
}
|
||||
|
||||
ipcRenderer.sendToHost = function (...args) {
|
||||
return binding.send('ipc-message-host', args)
|
||||
}
|
||||
|
||||
ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
|
||||
if (typeof webContentsId !== 'number') {
|
||||
throw new TypeError('First argument has to be webContentsId')
|
||||
}
|
||||
|
||||
ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', false, webContentsId, channel, ...args)
|
||||
}
|
||||
|
||||
ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
|
||||
if (typeof webContentsId !== 'number') {
|
||||
throw new TypeError('First argument has to be webContentsId')
|
||||
}
|
||||
|
||||
ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', true, webContentsId, channel, ...args)
|
||||
}
|
||||
}
|
|
@ -5,33 +5,6 @@ const v8Util = process.atomBinding('v8_util')
|
|||
|
||||
// Created by init.js.
|
||||
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
|
||||
|
||||
ipcRenderer.send = function (...args) {
|
||||
return binding.send('ipc-message', args)
|
||||
}
|
||||
|
||||
ipcRenderer.sendSync = function (...args) {
|
||||
return JSON.parse(binding.sendSync('ipc-message-sync', args))
|
||||
}
|
||||
|
||||
ipcRenderer.sendToHost = function (...args) {
|
||||
return binding.send('ipc-message-host', args)
|
||||
}
|
||||
|
||||
ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
|
||||
if (typeof webContentsId !== 'number') {
|
||||
throw new TypeError('First argument has to be webContentsId')
|
||||
}
|
||||
|
||||
ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', false, webContentsId, channel, ...args)
|
||||
}
|
||||
|
||||
ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
|
||||
if (typeof webContentsId !== 'number') {
|
||||
throw new TypeError('First argument has to be webContentsId')
|
||||
}
|
||||
|
||||
ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', true, webContentsId, channel, ...args)
|
||||
}
|
||||
require('./ipc-renderer-setup')(ipcRenderer, binding)
|
||||
|
||||
module.exports = ipcRenderer
|
||||
|
|
56
lib/sandboxed_renderer/init.js
Normal file
56
lib/sandboxed_renderer/init.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* eslint no-eval: "off" */
|
||||
/* global binding, preloadPath, process, Buffer */
|
||||
const events = require('events')
|
||||
|
||||
const ipcRenderer = new events.EventEmitter()
|
||||
const proc = new events.EventEmitter()
|
||||
// eval in window scope:
|
||||
// http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2
|
||||
const geval = eval
|
||||
|
||||
require('../renderer/api/ipc-renderer-setup')(ipcRenderer, binding)
|
||||
|
||||
binding.onMessage = function (channel, args) {
|
||||
ipcRenderer.emit.apply(ipcRenderer, [channel].concat(args))
|
||||
}
|
||||
|
||||
binding.onExit = function () {
|
||||
proc.emit('exit')
|
||||
}
|
||||
|
||||
const preloadModules = new Map([
|
||||
['electron', {
|
||||
ipcRenderer: ipcRenderer
|
||||
}]
|
||||
])
|
||||
|
||||
function preloadRequire (module) {
|
||||
if (preloadModules.has(module)) {
|
||||
return preloadModules.get(module)
|
||||
}
|
||||
throw new Error('module not found')
|
||||
}
|
||||
|
||||
// Fetch the source for the preload
|
||||
let preloadSrc = ipcRenderer.sendSync('ELECTRON_BROWSER_READ_FILE', preloadPath)
|
||||
if (preloadSrc.err) {
|
||||
throw new Error(preloadSrc.err)
|
||||
}
|
||||
|
||||
// Wrap the source into a function receives a `require` function as argument.
|
||||
// Browserify bundles can make use of this, as explained in:
|
||||
// https://github.com/substack/node-browserify#multiple-bundles
|
||||
//
|
||||
// For example, the user can create a browserify bundle with:
|
||||
//
|
||||
// $ browserify -x electron preload.js > renderer.js
|
||||
//
|
||||
// and any `require('electron')` calls in `preload.js` will work as expected
|
||||
// since browserify won't try to include `electron` in the bundle and will fall
|
||||
// back to the `preloadRequire` function above.
|
||||
let preloadWrapperSrc = `(function(require, process, Buffer, global) {
|
||||
${preloadSrc.data}
|
||||
})`
|
||||
|
||||
let preloadFn = geval(preloadWrapperSrc)
|
||||
preloadFn(preloadRequire, proc, Buffer, global)
|
Loading…
Add table
Add a link
Reference in a new issue