From 1ecfb80379b3dac2c8578758b7c1ec5a5d2b7c30 Mon Sep 17 00:00:00 2001 From: DisTok Date: Tue, 5 Feb 2019 22:03:42 +0100 Subject: [PATCH] Changes of Linux canary v0.0.71 --- electronasar/canary/browser/api/app.js | 30 +-- .../canary/browser/api/browser-window.js | 65 +++++++ .../canary/browser/api/content-tracing.js | 8 +- .../canary/browser/api/menu-item-roles.js | 155 +++++++-------- electronasar/canary/browser/api/menu.js | 4 +- .../canary/browser/api/module-list.js | 6 +- .../{ => api}/navigation-controller.js | 75 +------ electronasar/canary/browser/api/session.js | 20 +- electronasar/canary/browser/api/touch-bar.js | 6 + .../canary/browser/api/web-contents.js | 181 ++++------------- .../canary/browser/chrome-extension.js | 15 +- electronasar/canary/browser/default-menu.js | 58 ------ .../canary/browser/desktop-capturer.js | 41 ++-- .../canary/browser/guest-view-manager.js | 87 +++------ .../canary/browser/guest-window-manager.js | 50 ++--- electronasar/canary/browser/init.js | 12 -- electronasar/canary/browser/rpc-server.js | 127 ++++-------- electronasar/canary/common/api/clipboard.js | 4 +- electronasar/canary/common/api/deprecate.js | 39 +--- .../canary/common/parse-features-string.js | 6 +- .../canary/common/web-view-methods.js | 70 ------- .../canary/renderer/api/desktop-capturer.js | 61 +++--- .../canary/renderer/api/ipc-renderer.js | 4 +- electronasar/canary/renderer/api/remote.js | 4 +- electronasar/canary/renderer/api/screen.js | 8 +- .../canary/renderer/extensions/i18n.js | 5 +- .../canary/renderer/extensions/storage.js | 8 +- electronasar/canary/renderer/init.js | 121 ++++++------ electronasar/canary/renderer/inspector.js | 14 +- electronasar/canary/renderer/override.js | 18 ++ electronasar/canary/renderer/remote.js | 21 +- .../canary/renderer/security-warnings.js | 48 +++++ .../renderer/web-view/web-view-attributes.js | 2 +- .../renderer/web-view/web-view-element.js | 110 ----------- .../canary/renderer/web-view/web-view-init.js | 35 ---- .../{web-view-impl.js => web-view.js} | 183 +++++++++++++----- electronasar/canary/renderer/window-setup.js | 72 ++----- 37 files changed, 641 insertions(+), 1132 deletions(-) rename electronasar/canary/browser/{ => api}/navigation-controller.js (61%) delete mode 100644 electronasar/canary/browser/default-menu.js delete mode 100644 electronasar/canary/common/web-view-methods.js create mode 100644 electronasar/canary/renderer/override.js delete mode 100644 electronasar/canary/renderer/web-view/web-view-element.js delete mode 100644 electronasar/canary/renderer/web-view/web-view-init.js rename electronasar/canary/renderer/web-view/{web-view-impl.js => web-view.js} (70%) diff --git a/electronasar/canary/browser/api/app.js b/electronasar/canary/browser/api/app.js index 8c51a02..4cef536 100644 --- a/electronasar/canary/browser/api/app.js +++ b/electronasar/canary/browser/api/app.js @@ -1,7 +1,6 @@ 'use strict' const bindings = process.atomBinding('app') -const commandLine = process.atomBinding('command_line') const path = require('path') const { app, App } = bindings @@ -26,21 +25,24 @@ Object.assign(app, { return Menu.getApplicationMenu() }, commandLine: { - hasSwitch: (...args) => commandLine.hasSwitch(...args.map(String)), - getSwitchValue: (...args) => commandLine.getSwitchValue(...args.map(String)), - appendSwitch: (...args) => commandLine.appendSwitch(...args.map(String)), - appendArgument: (...args) => commandLine.appendArgument(...args.map(String)) - }, - enableMixedSandbox () { - deprecate.log(`'enableMixedSandbox' is deprecated. Mixed-sandbox mode is now enabled by default. You can safely remove the call to enableMixedSandbox().`) + appendSwitch (...args) { + const castedArgs = args.map((arg) => { + return typeof arg !== 'string' ? `${arg}` : arg + }) + return bindings.appendSwitch(...castedArgs) + }, + appendArgument (...args) { + const castedArgs = args.map((arg) => { + return typeof arg !== 'string' ? `${arg}` : arg + }) + return bindings.appendArgument(...castedArgs) + } } }) -app.getFileIcon = deprecate.promisify(app.getFileIcon) - -const nativeAppMetrics = app.getAppMetrics +const nativeFn = app.getAppMetrics app.getAppMetrics = () => { - const metrics = nativeAppMetrics.call(app) + const metrics = nativeFn.call(app) for (const metric of metrics) { if ('memory' in metric) { deprecate.removeProperty(metric, 'memory') @@ -91,7 +93,9 @@ if (process.platform === 'linux') { } app.allowNTLMCredentialsForAllDomains = function (allow) { - deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains') + if (!process.noDeprecations) { + deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains') + } const domains = allow ? '*' : '' if (!this.isReady()) { this.commandLine.appendSwitch('auth-server-whitelist', domains) diff --git a/electronasar/canary/browser/api/browser-window.js b/electronasar/canary/browser/api/browser-window.js index 3694acd..7fdbf8e 100644 --- a/electronasar/canary/browser/api/browser-window.js +++ b/electronasar/canary/browser/api/browser-window.js @@ -3,6 +3,8 @@ const electron = require('electron') const { WebContentsView, TopLevelWindow } = electron const { BrowserWindow } = process.atomBinding('window') +const v8Util = process.atomBinding('v8_util') +const ipcMain = require('@electron/internal/browser/ipc-main-internal') Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype) @@ -25,6 +27,69 @@ BrowserWindow.prototype._init = function () { nativeSetBounds.call(this, bounds, ...opts) } + // Make new windows requested by links behave like "window.open" + this.webContents.on('-new-window', (event, url, frameName, disposition, + additionalFeatures, postData, + referrer) => { + const options = { + show: true, + width: 800, + height: 600 + } + ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', + event, url, referrer, frameName, disposition, + options, additionalFeatures, postData) + }) + + 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", used in sandbox and nativeWindowOpen mode + this.webContents.on('-add-new-contents', (event, webContents, disposition, + userGesture, left, top, width, + height) => { + const urlFrameName = v8Util.getHiddenValue(webContents, 'url-framename') + if ((disposition !== 'foreground-tab' && disposition !== 'new-window' && + disposition !== 'background-tab') || !urlFrameName) { + event.preventDefault() + return + } + + if (webContents.getLastWebPreferences().nodeIntegration === true) { + const message = + 'Enabling Node.js integration in child windows opened with the ' + + '"nativeWindowOpen" option will cause memory leaks, please turn off ' + + 'the "nodeIntegration" option.\\n' + + 'From 5.x child windows opened with the "nativeWindowOpen" option ' + + 'will always have Node.js integration disabled.\\n' + + 'See https://github.com/electron/electron/pull/15076 for more.' + // console is only available after DOM is created. + const printWarning = () => this.webContents.executeJavaScript(`console.warn('${message}')`) + if (this.webContents.isDomReady()) { + printWarning() + } else { + this.webContents.once('dom-ready', printWarning) + } + } + + const { url, frameName } = urlFrameName + v8Util.deleteHiddenValue(webContents, 'url-framename') + const options = { + show: true, + x: left, + y: top, + width: width || 800, + height: height || 600, + webContents: webContents + } + const referrer = { url: '', policy: 'default' } + ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', + event, url, referrer, frameName, disposition, options) + }) + // window.resizeTo(...) // window.moveTo(...) this.webContents.on('move', (event, size) => { diff --git a/electronasar/canary/browser/api/content-tracing.js b/electronasar/canary/browser/api/content-tracing.js index 9a4f480..81bd70f 100644 --- a/electronasar/canary/browser/api/content-tracing.js +++ b/electronasar/canary/browser/api/content-tracing.js @@ -1,9 +1,3 @@ 'use strict' -const { deprecate } = require('electron') -const contentTracing = process.atomBinding('content_tracing') -contentTracing.startRecording = deprecate.promisify(contentTracing.startRecording) -contentTracing.stopRecording = deprecate.promisify(contentTracing.stopRecording) -contentTracing.getCategories = deprecate.promisify(contentTracing.getCategories) - -module.exports = contentTracing +module.exports = process.atomBinding('content_tracing') diff --git a/electronasar/canary/browser/api/menu-item-roles.js b/electronasar/canary/browser/api/menu-item-roles.js index 22949b3..61c6226 100644 --- a/electronasar/canary/browser/api/menu-item-roles.js +++ b/electronasar/canary/browser/api/menu-item-roles.js @@ -2,18 +2,14 @@ const { app } = require('electron') -const isMac = process.platform === 'darwin' -const isWindows = process.platform === 'win32' -const isLinux = process.platform === 'linux' - const roles = { about: { get label () { - return isLinux ? 'About' : `About ${app.getName()}` + return process.platform === 'linux' ? 'About' : `About ${app.getName()}` } }, close: { - label: isMac ? 'Close Window' : 'Close', + label: process.platform === 'darwin' ? 'Close Window' : 'Close', accelerator: 'CommandOrControl+W', windowMethod: 'close' }, @@ -82,12 +78,12 @@ const roles = { default: return 'Quit' } }, - accelerator: isWindows ? null : 'CommandOrControl+Q', + accelerator: process.platform === 'win32' ? null : 'CommandOrControl+Q', appMethod: 'quit' }, redo: { label: 'Redo', - accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z', + accelerator: process.platform === 'win32' ? 'Control+Y' : 'Shift+CommandOrControl+Z', webContentsMethod: 'redo' }, reload: { @@ -126,13 +122,13 @@ const roles = { }, toggledevtools: { label: 'Toggle Developer Tools', - accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I', + accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', nonNativeMacOSRole: true, windowMethod: 'toggleDevTools' }, togglefullscreen: { label: 'Toggle Full Screen', - accelerator: isMac ? 'Control+Command+F' : 'F11', + accelerator: process.platform === 'darwin' ? 'Control+Command+F' : 'F11', windowMethod: (window) => { window.setFullScreen(!window.isFullScreen()) } @@ -156,8 +152,9 @@ const roles = { accelerator: 'CommandOrControl+Plus', nonNativeMacOSRole: true, webContentsMethod: (webContents) => { - const zoomLevel = webContents.getZoomLevel() - webContents.setZoomLevel(zoomLevel + 0.5) + webContents.getZoomLevel((zoomLevel) => { + webContents.setZoomLevel(zoomLevel + 0.5) + }) } }, zoomout: { @@ -165,97 +162,78 @@ const roles = { accelerator: 'CommandOrControl+-', nonNativeMacOSRole: true, webContentsMethod: (webContents) => { - const zoomLevel = webContents.getZoomLevel() - webContents.setZoomLevel(zoomLevel - 0.5) + webContents.getZoomLevel((zoomLevel) => { + webContents.setZoomLevel(zoomLevel - 0.5) + }) } }, - // App submenu should be used for Mac only - appmenu: { - get label () { - return app.getName() - }, - submenu: [ - { role: 'about' }, - { type: 'separator' }, - { role: 'services' }, - { type: 'separator' }, - { role: 'hide' }, - { role: 'hideothers' }, - { role: 'unhide' }, - { type: 'separator' }, - { role: 'quit' } - ] - }, - // File submenu - filemenu: { - label: 'File', - submenu: [ - isMac ? { role: 'close' } : { role: 'quit' } - ] - }, - // Edit submenu + // Edit submenu (should fit both Mac & Windows) editmenu: { label: 'Edit', submenu: [ - { role: 'undo' }, - { role: 'redo' }, - { type: 'separator' }, - { role: 'cut' }, - { role: 'copy' }, - { role: 'paste' }, - ...(isMac ? [ - { role: 'pasteAndMatchStyle' }, - { role: 'delete' }, - { role: 'selectAll' }, - { type: 'separator' }, - { - label: 'Speech', - submenu: [ - { role: 'startspeaking' }, - { role: 'stopspeaking' } - ] - } - ] : [ - { role: 'delete' }, - { type: 'separator' }, - { role: 'selectAll' } - ]) + { + role: 'undo' + }, + { + role: 'redo' + }, + { + type: 'separator' + }, + { + role: 'cut' + }, + { + role: 'copy' + }, + { + role: 'paste' + }, + + process.platform === 'darwin' ? { + role: 'pasteAndMatchStyle' + } : null, + + { + role: 'delete' + }, + + process.platform === 'win32' ? { + type: 'separator' + } : null, + + { + role: 'selectAll' + } ] }, - // View submenu - viewmenu: { - label: 'View', - submenu: [ - { role: 'reload' }, - { role: 'forcereload' }, - { role: 'toggledevtools' }, - { type: 'separator' }, - { role: 'resetzoom' }, - { role: 'zoomin' }, - { role: 'zoomout' }, - { type: 'separator' }, - { role: 'togglefullscreen' } - ] - }, - // Window submenu + + // Window submenu should be used for Mac only windowmenu: { label: 'Window', submenu: [ - { role: 'minimize' }, - { role: 'zoom' }, - ...(isMac ? [ - { type: 'separator' }, - { role: 'front' } - ] : [ - { role: 'close' } - ]) + { + role: 'minimize' + }, + { + role: 'close' + }, + + process.platform === 'darwin' ? { + type: 'separator' + } : null, + + process.platform === 'darwin' ? { + role: 'front' + } : null + ] } } const canExecuteRole = (role) => { if (!roles.hasOwnProperty(role)) return false - if (!isMac) return true + if (process.platform !== 'darwin') return true // macOS handles all roles natively except for a few return roles[role].nonNativeMacOSRole @@ -270,8 +248,7 @@ exports.getDefaultAccelerator = (role) => { } exports.shouldRegisterAccelerator = (role) => { - const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined - return hasRoleRegister ? roles[role].registerAccelerator : true + return roles.hasOwnProperty(role) ? roles[role].registerAccelerator : true } exports.getDefaultSubmenu = (role) => { diff --git a/electronasar/canary/browser/api/menu.js b/electronasar/canary/browser/api/menu.js index 2a97753..14fcaba 100644 --- a/electronasar/canary/browser/api/menu.js +++ b/electronasar/canary/browser/api/menu.js @@ -145,8 +145,6 @@ Menu.setApplicationMenu = function (menu) { } applicationMenu = menu - v8Util.setHiddenValue(global, 'applicationMenuSet', true) - if (process.platform === 'darwin') { if (!menu) return menu._callMenuWillShow() @@ -161,13 +159,13 @@ Menu.buildFromTemplate = function (template) { if (!Array.isArray(template)) { throw new TypeError('Invalid template for Menu: Menu template must be an array') } + const menu = new Menu() if (!areValidTemplateItems(template)) { throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type') } const filtered = removeExtraSeparators(template) const sorted = sortTemplate(filtered) - const menu = new Menu() sorted.forEach((item) => menu.append(new MenuItem(item))) return menu diff --git a/electronasar/canary/browser/api/module-list.js b/electronasar/canary/browser/api/module-list.js index 8547166..8ec7bda 100644 --- a/electronasar/canary/browser/api/module-list.js +++ b/electronasar/canary/browser/api/module-list.js @@ -30,7 +30,9 @@ module.exports = [ { name: 'Tray', file: 'tray' }, { name: 'View', file: 'view' }, { name: 'webContents', file: 'web-contents' }, - { name: 'WebContentsView', file: 'web-contents-view' } + { name: 'WebContentsView', file: 'web-contents-view' }, + // The internal modules, invisible unless you know their names. + { name: 'NavigationController', file: 'navigation-controller', private: true } ] if (features.isViewApiEnabled()) { @@ -39,8 +41,6 @@ if (features.isViewApiEnabled()) { { name: 'Button', file: 'views/button' }, { name: 'LabelButton', file: 'views/label-button' }, { name: 'LayoutManager', file: 'views/layout-manager' }, - { name: 'MdTextButton', file: 'views/md-text-button' }, - { name: 'ResizeArea', file: 'views/resize-area' }, { name: 'TextField', file: 'views/text-field' } ) } diff --git a/electronasar/canary/browser/navigation-controller.js b/electronasar/canary/browser/api/navigation-controller.js similarity index 61% rename from electronasar/canary/browser/navigation-controller.js rename to electronasar/canary/browser/api/navigation-controller.js index 33dc256..1863961 100644 --- a/electronasar/canary/browser/navigation-controller.js +++ b/electronasar/canary/browser/api/navigation-controller.js @@ -3,20 +3,12 @@ const ipcMain = require('@electron/internal/browser/ipc-main-internal') // The history operation in renderer is redirected to browser. -ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) { - event.sender.goBack() +ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER', function (event, method, ...args) { + event.sender[method](...args) }) -ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD', function (event) { - event.sender.goForward() -}) - -ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (event, offset) { - event.sender.goToOffset(offset) -}) - -ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) { - event.returnValue = event.sender.length() +ipcMain.on('ELECTRON_SYNC_NAVIGATION_CONTROLLER', function (event, method, ...args) { + event.returnValue = event.sender[method](...args) }) // JavaScript implementation of Chromium's NavigationController. @@ -63,66 +55,9 @@ const NavigationController = (function () { if (options == null) { options = {} } - const p = new Promise((resolve, reject) => { - const resolveAndCleanup = () => { - removeListeners() - resolve() - } - const rejectAndCleanup = (errorCode, errorDescription, url) => { - const err = new Error(`${errorDescription} (${errorCode}) loading '${url}'`) - Object.assign(err, { errno: errorCode, code: errorDescription, url }) - removeListeners() - reject(err) - } - const finishListener = () => { - resolveAndCleanup() - } - const failListener = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => { - if (isMainFrame) { - rejectAndCleanup(errorCode, errorDescription, validatedURL) - } - } - - let navigationStarted = false - const navigationListener = (event, url, isSameDocument, isMainFrame, frameProcessId, frameRoutingId, navigationId) => { - if (isMainFrame) { - if (navigationStarted) { - // the webcontents has started another unrelated navigation in the - // main frame (probably from the app calling `loadURL` again); reject - // the promise - return rejectAndCleanup(-3, 'ERR_ABORTED', url) - } - navigationStarted = true - } - } - const stopLoadingListener = () => { - // By the time we get here, either 'finish' or 'fail' should have fired - // if the navigation occurred. However, in some situations (e.g. when - // attempting to load a page with a bad scheme), loading will stop - // without emitting finish or fail. In this case, we reject the promise - // with a generic failure. - // TODO(jeremy): enumerate all the cases in which this can happen. If - // the only one is with a bad scheme, perhaps ERR_INVALID_ARGUMENT - // would be more appropriate. - rejectAndCleanup(-2, 'ERR_FAILED', url) - } - const removeListeners = () => { - this.webContents.removeListener('did-finish-load', finishListener) - this.webContents.removeListener('did-fail-load', failListener) - this.webContents.removeListener('did-start-navigation', navigationListener) - this.webContents.removeListener('did-stop-loading', stopLoadingListener) - } - this.webContents.on('did-finish-load', finishListener) - this.webContents.on('did-fail-load', failListener) - this.webContents.on('did-start-navigation', navigationListener) - this.webContents.on('did-stop-loading', stopLoadingListener) - }) - // Add a no-op rejection handler to silence the unhandled rejection error. - p.catch(() => {}) this.pendingIndex = -1 this.webContents._loadURL(url, options) - this.webContents.emit('load-url', url, options) - return p + return this.webContents.emit('load-url', url, options) } NavigationController.prototype.getURL = function () { diff --git a/electronasar/canary/browser/api/session.js b/electronasar/canary/browser/api/session.js index 96362cc..08c10ee 100644 --- a/electronasar/canary/browser/api/session.js +++ b/electronasar/canary/browser/api/session.js @@ -1,23 +1,8 @@ 'use strict' const { EventEmitter } = require('events') -const { app, deprecate } = require('electron') -const { Session, Cookies } = process.atomBinding('session') -const realFromPartition = process.atomBinding('session').fromPartition - -const wrappedSymbol = Symbol('wrapped-deprecate') -const fromPartition = (partition) => { - const session = realFromPartition(partition) - if (!session[wrappedSymbol]) { - session[wrappedSymbol] = true - const { cookies } = session - cookies.flushStore = deprecate.promisify(cookies.flushStore) - cookies.get = deprecate.promisify(cookies.get) - cookies.remove = deprecate.promisify(cookies.remove) - cookies.set = deprecate.promisify(cookies.set) - } - return session -} +const { app } = require('electron') +const { fromPartition, Session, Cookies } = process.atomBinding('session') // Public API. Object.defineProperties(exports, { @@ -35,6 +20,5 @@ Object.setPrototypeOf(Session.prototype, EventEmitter.prototype) Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype) Session.prototype._init = function () { - this.protocol.isProtocolHandled = deprecate.promisify(this.protocol.isProtocolHandled) app.emit('session-created', this) } diff --git a/electronasar/canary/browser/api/touch-bar.js b/electronasar/canary/browser/api/touch-bar.js index 183a1e0..ddacc73 100644 --- a/electronasar/canary/browser/api/touch-bar.js +++ b/electronasar/canary/browser/api/touch-bar.js @@ -31,6 +31,12 @@ class TouchBar extends EventEmitter { let { items, escapeItem } = options + // FIXME Support array as first argument, remove in 2.0 + if (Array.isArray(options)) { + items = options + escapeItem = null + } + if (!Array.isArray(items)) { items = [] } diff --git a/electronasar/canary/browser/api/web-contents.js b/electronasar/canary/browser/api/web-contents.js index 82b1629..c5d923e 100644 --- a/electronasar/canary/browser/api/web-contents.js +++ b/electronasar/canary/browser/api/web-contents.js @@ -5,9 +5,8 @@ const { EventEmitter } = require('events') const electron = require('electron') const path = require('path') const url = require('url') -const { app, ipcMain, session, deprecate } = electron +const { app, ipcMain, session, NavigationController, deprecate } = electron -const NavigationController = require('@electron/internal/browser/navigation-controller') const ipcMainInternal = require('@electron/internal/browser/ipc-main-internal') const errorUtils = require('@electron/internal/common/error-utils') @@ -111,7 +110,6 @@ WebContents.prototype.send = function (channel, ...args) { return this._send(internal, sendToAll, channel, args) } - WebContents.prototype.sendToAll = function (channel, ...args) { if (typeof channel !== 'string') { throw new Error('Missing required channel argument') @@ -143,30 +141,6 @@ WebContents.prototype._sendInternalToAll = function (channel, ...args) { return this._send(internal, sendToAll, channel, args) } -WebContents.prototype.sendToFrame = function (frameId, channel, ...args) { - if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') - } else if (typeof frameId !== 'number') { - throw new Error('Missing required frameId argument') - } - - const internal = false - const sendToAll = false - - return this._sendToFrame(internal, sendToAll, frameId, channel, args) -} -WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) { - if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') - } else if (typeof frameId !== 'number') { - throw new Error('Missing required frameId argument') - } - - const internal = true - const sendToAll = false - - return this._sendToFrame(internal, sendToAll, frameId, channel, args) -} // Following methods are mapped to webFrame. const webFrameMethods = [ @@ -222,34 +196,10 @@ WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callba } } -// TODO(codebytere): remove when promisifications is complete -const nativeZoomLevel = WebContents.prototype.getZoomLevel -WebContents.prototype.getZoomLevel = function (callback) { - if (callback == null) { - return nativeZoomLevel.call(this) - } else { - process.nextTick(() => { - callback(nativeZoomLevel.call(this)) - }) - } -} - -// TODO(codebytere): remove when promisifications is complete -const nativeZoomFactor = WebContents.prototype.getZoomFactor -WebContents.prototype.getZoomFactor = function (callback) { - if (callback == null) { - return nativeZoomFactor.call(this) - } else { - process.nextTick(() => { - callback(nativeZoomFactor.call(this)) - }) - } -} - WebContents.prototype.takeHeapSnapshot = function (filePath) { return new Promise((resolve, reject) => { const channel = `ELECTRON_TAKE_HEAP_SNAPSHOT_RESULT_${getNextId()}` - ipcMainInternal.once(channel, (event, success) => { + ipcMain.once(channel, (event, success) => { if (success) { resolve() } else { @@ -257,7 +207,7 @@ WebContents.prototype.takeHeapSnapshot = function (filePath) { } }) if (!this._takeHeapSnapshot(filePath, channel)) { - ipcMainInternal.emit(channel, false) + ipcMain.emit(channel, false) } }) } @@ -326,6 +276,16 @@ WebContents.prototype.getPrinters = function () { } } +WebContents.prototype.getZoomLevel = function (callback) { + if (typeof callback !== 'function') { + throw new Error('Must pass function as an argument') + } + process.nextTick(() => { + const zoomLevel = this._getZoomLevel() + callback(zoomLevel) + }) +} + WebContents.prototype.loadFile = function (filePath, options = {}) { if (typeof filePath !== 'string') { throw new Error('Must pass filePath as a string') @@ -342,31 +302,22 @@ WebContents.prototype.loadFile = function (filePath, options = {}) { })) } -const addReplyToEvent = (event) => { - event.reply = (...args) => { - event.sender.sendToFrame(event.frameId, ...args) +WebContents.prototype.getZoomFactor = function (callback) { + if (typeof callback !== 'function') { + throw new Error('Must pass function as an argument') } -} - -const addReplyInternalToEvent = (event) => { - Object.defineProperty(event, '_replyInternal', { - configurable: false, - enumerable: false, - value: (...args) => { - event.sender._sendToFrameInternal(event.frameId, ...args) - } + process.nextTick(() => { + const zoomFactor = this._getZoomFactor() + callback(zoomFactor) }) } -const safeProtocols = new Set([ - 'chrome-devtools:', - 'chrome-extension:' -]) - -const isWebContentsTrusted = function (contents) { - const pageURL = contents._getURL() - const { protocol } = url.parse(pageURL) - return safeProtocols.has(protocol) +WebContents.prototype.findInPage = function (text, options = {}) { + // TODO (nitsakh): Remove in 5.0 + if (options.wordStart != null || options.medialCapitalAtWordStart != null) { + deprecate.log('wordStart and medialCapitalAtWordStart options are deprecated') + } + return this._findInPage(text, options) } // Add JavaScript wrappers for WebContents class. @@ -374,38 +325,27 @@ WebContents.prototype._init = function () { // The navigation controller. NavigationController.call(this, this) - // Every remote callback from renderer process would add a listener to the - // render-view-deleted event, so ignore the listeners warning. + // Every remote callback from renderer process would add a listenter to the + // render-view-deleted event, so ignore the listenters warning. this.setMaxListeners(0) - this.capturePage = deprecate.promisify(this.capturePage) - this.hasServiceWorker = deprecate.function(this.hasServiceWorker) - this.unregisterServiceWorker = deprecate.function(this.unregisterServiceWorker) - // Dispatch IPC messages to the ipc module. - this.on('-ipc-message', function (event, [channel, ...args]) { - addReplyToEvent(event) - this.emit('ipc-message', event, channel, ...args) + this.on('ipc-message', function (event, [channel, ...args]) { ipcMain.emit(channel, event, ...args) }) - - this.on('-ipc-message-sync', function (event, [channel, ...args]) { + this.on('ipc-message-sync', function (event, [channel, ...args]) { Object.defineProperty(event, 'returnValue', { set: function (value) { return event.sendReply([value]) }, get: function () {} }) - addReplyToEvent(event) - this.emit('ipc-message-sync', event, channel, ...args) ipcMain.emit(channel, event, ...args) }) this.on('ipc-internal-message', function (event, [channel, ...args]) { - addReplyInternalToEvent(event) ipcMainInternal.emit(channel, event, ...args) }) - this.on('ipc-internal-message-sync', function (event, [channel, ...args]) { Object.defineProperty(event, 'returnValue', { set: function (value) { @@ -413,7 +353,6 @@ WebContents.prototype._init = function () { }, get: function () {} }) - addReplyInternalToEvent(event) ipcMainInternal.emit(channel, event, ...args) }) @@ -429,23 +368,13 @@ WebContents.prototype._init = function () { }) }) - const forwardedEvents = [ - 'desktop-capturer-get-sources', - 'remote-require', - 'remote-get-global', - 'remote-get-builtin', - 'remote-get-current-window', - 'remote-get-current-web-contents', - 'remote-get-guest-web-contents' - ] + this.on('remote-require', (event, ...args) => { + app.emit('remote-require', event, this, ...args) + }) - for (const eventName of forwardedEvents) { - this.on(eventName, (event, ...args) => { - if (!isWebContentsTrusted(event.sender)) { - app.emit(eventName, event, this, ...args) - } - }) - } + this.on('remote-get-global', (event, ...args) => { + app.emit('remote-get-global', event, this, ...args) + }) deprecate.event(this, 'did-get-response-details', '-did-get-response-details') deprecate.event(this, 'did-get-redirect-request', '-did-get-redirect-request') @@ -455,46 +384,6 @@ WebContents.prototype._init = function () { this.reload() }) - // Handle window.open for BrowserWindow and BrowserView. - if (['browserView', 'window'].includes(this.getType())) { - // Make new windows requested by links behave like "window.open". - this.on('-new-window', (event, url, frameName, disposition, - additionalFeatures, postData, - referrer) => { - const options = { - show: true, - width: 800, - height: 600 - } - ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', - event, url, referrer, frameName, disposition, - options, additionalFeatures, postData) - }) - - // Create a new browser window for the native implementation of - // "window.open", used in sandbox and nativeWindowOpen mode. - this.on('-add-new-contents', (event, webContents, disposition, - userGesture, left, top, width, height, url, frameName) => { - if ((disposition !== 'foreground-tab' && disposition !== 'new-window' && - disposition !== 'background-tab')) { - event.preventDefault() - return - } - - const options = { - show: true, - x: left, - y: top, - width: width || 800, - height: height || 600, - webContents - } - const referrer = { url: '', policy: 'default' } - ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', - event, url, referrer, frameName, disposition, options) - }) - } - app.emit('web-contents-created', {}, this) } diff --git a/electronasar/canary/browser/chrome-extension.js b/electronasar/canary/browser/chrome-extension.js index 5c4e04b..627487a 100644 --- a/electronasar/canary/browser/chrome-extension.js +++ b/electronasar/canary/browser/chrome-extension.js @@ -180,7 +180,7 @@ ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message, page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message, resultID) ipcMain.once(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, (event, result) => { - event._replyInternal(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, result) + event.sender._sendInternal(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, result) }) resultID++ }) @@ -196,23 +196,12 @@ ipcMain.on('CHROME_TABS_SEND_MESSAGE', function (event, tabId, extensionId, isBa contents._sendInternalToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message, resultID) ipcMain.once(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, (event, result) => { - event._replyInternal(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, result) + event.sender._sendInternal(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, result) }) resultID++ }) -const isChromeExtension = function (pageURL) { - const { protocol } = url.parse(pageURL) - return protocol === 'chrome-extension:' -} - ipcMain.on('CHROME_TABS_EXECUTESCRIPT', function (event, requestId, tabId, extensionId, details) { - const pageURL = event.sender._getURL() - if (!isChromeExtension(pageURL)) { - console.error(`Blocked ${pageURL} from calling chrome.tabs.executeScript()`) - return - } - const contents = webContents.fromId(tabId) if (!contents) { console.error(`Sending message to unknown tab ${tabId}`) diff --git a/electronasar/canary/browser/default-menu.js b/electronasar/canary/browser/default-menu.js deleted file mode 100644 index 09ed05b..0000000 --- a/electronasar/canary/browser/default-menu.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict' - -const { shell, Menu } = require('electron') -const v8Util = process.atomBinding('v8_util') - -const isMac = process.platform === 'darwin' - -const setDefaultApplicationMenu = () => { - if (v8Util.getHiddenValue(global, 'applicationMenuSet')) return - - const helpMenu = { - role: 'help', - submenu: [ - { - label: 'Learn More', - click () { - shell.openExternalSync('https://electronjs.org') - } - }, - { - label: 'Documentation', - click () { - shell.openExternalSync( - `https://github.com/electron/electron/tree/v${process.versions.electron}/docs#readme` - ) - } - }, - { - label: 'Community Discussions', - click () { - shell.openExternalSync('https://discuss.atom.io/c/electron') - } - }, - { - label: 'Search Issues', - click () { - shell.openExternalSync('https://github.com/electron/electron/issues') - } - } - ] - } - - const template = [ - ...(isMac ? [{ role: 'appMenu' }] : []), - { role: 'fileMenu' }, - { role: 'editMenu' }, - { role: 'viewMenu' }, - { role: 'windowMenu' }, - helpMenu - ] - - const menu = Menu.buildFromTemplate(template) - Menu.setApplicationMenu(menu) -} - -module.exports = { - setDefaultApplicationMenu -} diff --git a/electronasar/canary/browser/desktop-capturer.js b/electronasar/canary/browser/desktop-capturer.js index bf34199..62e68bb 100644 --- a/electronasar/canary/browser/desktop-capturer.js +++ b/electronasar/canary/browser/desktop-capturer.js @@ -1,9 +1,7 @@ 'use strict' const ipcMain = require('@electron/internal/browser/ipc-main-internal') - const { desktopCapturer } = process.atomBinding('desktop_capturer') -const eventBinding = process.atomBinding('event') const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b) @@ -13,40 +11,32 @@ let requestsQueue = [] const electronSources = 'ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES' const capturerResult = (id) => `ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}` -ipcMain.on(electronSources, (event, captureWindow, captureScreen, thumbnailSize, fetchWindowIcons, id) => { - const customEvent = eventBinding.createWithSender(event.sender) - event.sender.emit('desktop-capturer-get-sources', customEvent) - - if (customEvent.defaultPrevented) { - event._replyInternal(capturerResult(id), []) - return - } - +ipcMain.on(electronSources, (event, captureWindow, captureScreen, thumbnailSize, id) => { const request = { id, options: { captureWindow, captureScreen, - thumbnailSize, - fetchWindowIcons + thumbnailSize }, - event + webContents: event.sender } requestsQueue.push(request) if (requestsQueue.length === 1) { - desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons) + desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize) } // If the WebContents is destroyed before receiving result, just remove the // reference from requestsQueue to make the module not send the result to it. event.sender.once('destroyed', () => { - request.event = null + request.webContents = null }) }) -desktopCapturer.emit = (event, name, sources, fetchWindowIcons) => { +desktopCapturer.emit = (event, name, sources) => { // Receiving sources result from main process, now send them back to renderer. const handledRequest = requestsQueue.shift() + const handledWebContents = handledRequest.webContents const unhandledRequestsQueue = [] const result = sources.map(source => { @@ -54,21 +44,20 @@ desktopCapturer.emit = (event, name, sources, fetchWindowIcons) => { id: source.id, name: source.name, thumbnail: source.thumbnail.toDataURL(), - display_id: source.display_id, - appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null + display_id: source.display_id } }) - if (handledRequest.event) { - handledRequest.event._replyInternal(capturerResult(handledRequest.id), result) + if (handledWebContents) { + handledWebContents._sendInternal(capturerResult(handledRequest.id), result) } // Check the queue to see whether there is another identical request & handle requestsQueue.forEach(request => { - const event = request.event + const webContents = request.webContents if (deepEqual(handledRequest.options, request.options)) { - if (event) { - event._replyInternal(capturerResult(request.id), result) + if (webContents) { + webContents._sendInternal(capturerResult(request.id), result) } } else { unhandledRequestsQueue.push(request) @@ -78,7 +67,7 @@ desktopCapturer.emit = (event, name, sources, fetchWindowIcons) => { // If the requestsQueue is not empty, start a new request handling. if (requestsQueue.length > 0) { - const { captureWindow, captureScreen, thumbnailSize, fetchWindowIcons } = requestsQueue[0].options - return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons) + const { captureWindow, captureScreen, thumbnailSize } = requestsQueue[0].options + return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize) } } diff --git a/electronasar/canary/browser/guest-view-manager.js b/electronasar/canary/browser/guest-view-manager.js index 920b020..8693235 100644 --- a/electronasar/canary/browser/guest-view-manager.js +++ b/electronasar/canary/browser/guest-view-manager.js @@ -4,11 +4,6 @@ const { webContents } = require('electron') const ipcMain = require('@electron/internal/browser/ipc-main-internal') const parseFeaturesString = require('@electron/internal/common/parse-features-string') const errorUtils = require('@electron/internal/common/error-utils') -const { - syncMethods, - asyncCallbackMethods, - asyncPromiseMethods -} = require('@electron/internal/common/web-view-methods') // Doesn't exist in early initialization. let webViewManager = null @@ -163,6 +158,14 @@ const createGuest = function (embedder, params) { } } }) + guest.on('-web-contents-created', (...args) => { + if (guest.getLastWebPreferences().nativeWindowOpen === true) { + const embedder = getEmbedder(guestInstanceId) + if (embedder != null) { + embedder.emit('-web-contents-created', ...args) + } + } + }) return guestInstanceId } @@ -181,19 +184,16 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn const oldGuestInstance = guestInstances[oldGuestInstanceId] if (oldGuestInstance) { - oldGuestInstance.guest.detachFromOuterFrame() + oldGuestInstance.guest.destroy() } } const guestInstance = guestInstances[guestInstanceId] // If this isn't a valid guest instance then do nothing. if (!guestInstance) { - throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`) + return } const { guest } = guestInstance - if (guest.hostWebContents !== event.sender) { - throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`) - } // If this guest is already attached to an element then remove it if (guestInstance.elementInstanceId) { @@ -212,7 +212,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false, enableRemoteModule: params.enableremotemodule, plugins: params.plugins, - zoomFactor: embedder.getZoomFactor(), + zoomFactor: embedder._getZoomFactor(), webSecurity: !params.disablewebsecurity, enableBlinkFeatures: params.blinkfeatures, disableBlinkFeatures: params.disableblinkfeatures @@ -246,8 +246,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn ['nativeWindowOpen', true], ['nodeIntegration', false], ['enableRemoteModule', false], - ['sandbox', true], - ['nodeIntegrationInSubFrames', false] + ['sandbox', true] ]) // Inherit certain option values from embedder @@ -333,8 +332,8 @@ const isWebViewTagEnabledCache = new WeakMap() const isWebViewTagEnabled = function (contents) { if (!isWebViewTagEnabledCache.has(contents)) { - const webPreferences = contents.getLastWebPreferences() || {} - isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag) + const value = contents.getLastWebPreferences().webviewTag + isWebViewTagEnabledCache.set(contents, value) } return isWebViewTagEnabledCache.get(contents) @@ -351,7 +350,7 @@ const handleMessage = function (channel, handler) { } handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) { - event._replyInternal(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params)) + event.sender._sendInternal(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params)) }) handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) { @@ -359,37 +358,25 @@ handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, }) handleMessage('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) { - try { - const guest = getGuestForWebContents(guestInstanceId, event.sender) - guest.detachFromOuterFrame() - } catch (error) { - console.error(`Guest destroy failed: ${error}`) + const guest = getGuest(guestInstanceId) + if (guest) { + guest.destroy() } }) handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { - try { - attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params) - } catch (error) { - console.error(`Guest attach failed: ${error}`) - } + attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params) }) -// this message is sent by the actual -ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) { - const guest = getGuest(guestInstanceId) - if (guest === event.sender) { - event.sender.emit('focus-change', {}, focus, guestInstanceId) - } else { - console.error(`focus-change for guestInstanceId: ${guestInstanceId}`) - } +handleMessage('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) { + event.sender.emit('focus-change', {}, focus, guestInstanceId) }) handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, requestId, guestInstanceId, method, args, hasCallback) { new Promise(resolve => { - const guest = getGuestForWebContents(guestInstanceId, event.sender) - if (!asyncCallbackMethods.has(method) && !asyncPromiseMethods.has(method)) { - throw new Error(`Invalid method: ${method}`) + const guest = getGuest(guestInstanceId) + if (guest.hostWebContents !== event.sender) { + throw new Error('Access denied') } if (hasCallback) { guest[method](...args, resolve) @@ -401,34 +388,22 @@ handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, request }, error => { return [errorUtils.serialize(error)] }).then(responseArgs => { - event._replyInternal(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, ...responseArgs) + event.sender._sendInternal(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, ...responseArgs) }) }) handleMessage('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', function (event, guestInstanceId, method, args) { try { - const guest = getGuestForWebContents(guestInstanceId, event.sender) - if (!syncMethods.has(method)) { - throw new Error(`Invalid method: ${method}`) + const guest = getGuest(guestInstanceId) + if (guest.hostWebContents !== event.sender) { + throw new Error('Access denied') } - event.returnValue = [null, guest[method](...args)] + event.returnValue = [null, guest[method].apply(guest, args)] } catch (error) { event.returnValue = [errorUtils.serialize(error)] } }) -// Returns WebContents from its guest id hosted in given webContents. -const getGuestForWebContents = function (guestInstanceId, contents) { - const guest = getGuest(guestInstanceId) - if (!guest) { - throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`) - } - if (guest.hostWebContents !== contents) { - throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`) - } - return guest -} - // Returns WebContents from its guest id. const getGuest = function (guestInstanceId) { const guestInstance = guestInstances[guestInstanceId] @@ -441,5 +416,5 @@ const getEmbedder = function (guestInstanceId) { if (guestInstance != null) return guestInstance.embedder } -exports.getGuestForWebContents = getGuestForWebContents -exports.isWebViewTagEnabled = isWebViewTagEnabled +exports.getGuest = getGuest +exports.getEmbedder = getEmbedder diff --git a/electronasar/canary/browser/guest-window-manager.js b/electronasar/canary/browser/guest-window-manager.js index 84ac8ef..5bc5d26 100644 --- a/electronasar/canary/browser/guest-window-manager.js +++ b/electronasar/canary/browser/guest-window-manager.js @@ -16,8 +16,7 @@ const inheritedWebPreferences = new Map([ ['nodeIntegration', false], ['enableRemoteModule', false], ['sandbox', true], - ['webviewTag', false], - ['nodeIntegrationInSubFrames', false] + ['webviewTag', false] ]) // Copy attribute of |parent| to |child| if it is not defined in |child|. @@ -91,9 +90,9 @@ const setupGuest = function (embedder, frameName, guest, options) { } const closedByUser = function () { embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId) - embedder.removeListener('current-render-view-deleted', closedByEmbedder) + embedder.removeListener('render-view-deleted', closedByEmbedder) } - embedder.once('current-render-view-deleted', closedByEmbedder) + embedder.once('render-view-deleted', closedByEmbedder) guest.once('closed', closedByUser) if (frameName) { frameToGuest.set(frameName, guest) @@ -119,12 +118,28 @@ const createGuest = function (embedder, url, referrer, frameName, options, postD } guest = new BrowserWindow(options) - if (!options.webContents) { + 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). + // 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 is not necessary (it will navigate there anyway). + // 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('

hello

') + // + // 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. const loadOptions = { httpReferrer: referrer } @@ -273,11 +288,6 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestI if (guestWindow != null) guestWindow.destroy() }) -const windowMethods = new Set([ - 'focus', - 'blur' -]) - ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) { const guestContents = webContents.fromId(guestId) if (guestContents == null) { @@ -285,7 +295,7 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guest return } - if (!canAccessWindow(event.sender, guestContents) || !windowMethods.has(method)) { + if (!canAccessWindow(event.sender, guestContents)) { console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`) event.returnValue = null return @@ -316,27 +326,17 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, } }) -const webContentsMethods = new Set([ - 'print', - 'executeJavaScript' -]) - ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) { const guestContents = webContents.fromId(guestId) if (guestContents == null) return - if (canAccessWindow(event.sender, guestContents) && webContentsMethods.has(method)) { + if (canAccessWindow(event.sender, guestContents)) { guestContents[method](...args) } else { console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`) } }) -const webContentsSyncMethods = new Set([ - 'getURL', - 'loadURL' -]) - ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) { const guestContents = webContents.fromId(guestId) if (guestContents == null) { @@ -344,7 +344,7 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (e return } - if (canAccessWindow(event.sender, guestContents) && webContentsSyncMethods.has(method)) { + if (canAccessWindow(event.sender, guestContents)) { event.returnValue = guestContents[method](...args) } else { console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`) diff --git a/electronasar/canary/browser/init.js b/electronasar/canary/browser/init.js index c50c5f0..fc526e1 100644 --- a/electronasar/canary/browser/init.js +++ b/electronasar/canary/browser/init.js @@ -184,17 +184,5 @@ if (currentPlatformSupportsAppIndicator()) { process.env.XDG_CURRENT_DESKTOP = 'Unity' } -// Quit when all windows are closed and no other one is listening to this. -app.on('window-all-closed', () => { - if (app.listenerCount('window-all-closed') === 1) { - app.quit() - } -}) - -const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu') - -// Create default menu. -app.once('ready', setDefaultApplicationMenu) - // Finally load app's main.js and transfer control to C++. Module._load(path.join(packagePath, mainStartupScript), Module, true) diff --git a/electronasar/canary/browser/rpc-server.js b/electronasar/canary/browser/rpc-server.js index 962a4da..bf3d0a3 100644 --- a/electronasar/canary/browser/rpc-server.js +++ b/electronasar/canary/browser/rpc-server.js @@ -13,7 +13,6 @@ const { isPromise } = electron const ipcMain = require('@electron/internal/browser/ipc-main-internal') const objectsRegistry = require('@electron/internal/browser/objects-registry') -const guestViewManager = require('@electron/internal/browser/guest-view-manager') const bufferUtils = require('@electron/internal/common/buffer-utils') const errorUtils = require('@electron/internal/common/error-utils') @@ -139,7 +138,7 @@ const plainObjectToMeta = function (obj) { } // Convert Error into meta data. -const exceptionToMeta = function (error) { +const exceptionToMeta = function (sender, contextId, error) { return { type: 'exception', value: errorUtils.serialize(error) @@ -175,7 +174,7 @@ const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => { } // Convert array of meta data from renderer into array of real values. -const unwrapArgs = function (sender, frameId, contextId, args) { +const unwrapArgs = function (sender, contextId, args) { const metaToValue = function (meta) { switch (meta.type) { case 'value': @@ -183,7 +182,7 @@ const unwrapArgs = function (sender, frameId, contextId, args) { case 'remote-object': return objectsRegistry.get(meta.id) case 'array': - return unwrapArgs(sender, frameId, contextId, meta.value) + return unwrapArgs(sender, contextId, meta.value) case 'buffer': return bufferUtils.metaToBuffer(meta.value) case 'date': @@ -217,11 +216,9 @@ const unwrapArgs = function (sender, frameId, contextId, args) { } const callIntoRenderer = function (...args) { - let succeed = false if (!sender.isDestroyed()) { - succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args)) - } - if (!succeed) { + sender._sendInternal('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args)) + } else { removeRemoteListenersAndLogWarning(this, callIntoRenderer) } } @@ -276,7 +273,7 @@ const handleRemoteCommand = function (channel, handler) { try { returnValue = handler(event, contextId, ...args) } catch (error) { - returnValue = exceptionToMeta(error) + returnValue = exceptionToMeta(event.sender, contextId, error) } if (returnValue !== undefined) { @@ -298,79 +295,46 @@ handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, modu const customEvent = eventBinding.createWithSender(event.sender) event.sender.emit('remote-require', customEvent, moduleName) - if (customEvent.returnValue === undefined) { - if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.require('${moduleName}')`) - } else { - customEvent.returnValue = process.mainModule.require(moduleName) + if (customEvent.defaultPrevented) { + if (typeof customEvent.returnValue === 'undefined') { + throw new Error(`Invalid module: ${moduleName}`) } + } else { + customEvent.returnValue = process.mainModule.require(moduleName) } return valueToMeta(event.sender, contextId, customEvent.returnValue) }) -handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) { - const customEvent = eventBinding.createWithSender(event.sender) - event.sender.emit('remote-get-builtin', customEvent, moduleName) - - if (customEvent.returnValue === undefined) { - if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.getBuiltin('${moduleName}')`) - } else { - customEvent.returnValue = electron[moduleName] - } - } - - return valueToMeta(event.sender, contextId, customEvent.returnValue) +handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, module) { + return valueToMeta(event.sender, contextId, electron[module]) }) handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) { const customEvent = eventBinding.createWithSender(event.sender) event.sender.emit('remote-get-global', customEvent, globalName) - if (customEvent.returnValue === undefined) { - if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.getGlobal('${globalName}')`) - } else { - customEvent.returnValue = global[globalName] + if (customEvent.defaultPrevented) { + if (typeof customEvent.returnValue === 'undefined') { + throw new Error(`Invalid global: ${globalName}`) } + } else { + customEvent.returnValue = global[globalName] } return valueToMeta(event.sender, contextId, customEvent.returnValue) }) handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) { - const customEvent = eventBinding.createWithSender(event.sender) - event.sender.emit('remote-get-current-window', customEvent) - - if (customEvent.returnValue === undefined) { - if (customEvent.defaultPrevented) { - throw new Error('Blocked remote.getCurrentWindow()') - } else { - customEvent.returnValue = event.sender.getOwnerBrowserWindow() - } - } - - return valueToMeta(event.sender, contextId, customEvent.returnValue) + return valueToMeta(event.sender, contextId, event.sender.getOwnerBrowserWindow()) }) handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) { - const customEvent = eventBinding.createWithSender(event.sender) - event.sender.emit('remote-get-current-web-contents', customEvent) - - if (customEvent.returnValue === undefined) { - if (customEvent.defaultPrevented) { - throw new Error('Blocked remote.getCurrentWebContents()') - } else { - customEvent.returnValue = event.sender - } - } - - return valueToMeta(event.sender, contextId, customEvent.returnValue) + return valueToMeta(event.sender, contextId, event.sender) }) handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) + args = unwrapArgs(event.sender, contextId, args) const constructor = objectsRegistry.get(id) if (constructor == null) { @@ -381,7 +345,7 @@ handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, }) handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) + args = unwrapArgs(event.sender, contextId, args) const func = objectsRegistry.get(id) if (func == null) { @@ -392,7 +356,7 @@ handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId }) handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) + args = unwrapArgs(event.sender, contextId, args) const object = objectsRegistry.get(id) if (object == null) { @@ -403,7 +367,7 @@ handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, cont }) handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) + args = unwrapArgs(event.sender, contextId, args) const obj = objectsRegistry.get(id) if (obj == null) { @@ -414,7 +378,7 @@ handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, }) handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) + args = unwrapArgs(event.sender, contextId, args) const obj = objectsRegistry.get(id) if (obj == null) { @@ -445,20 +409,8 @@ handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => { }) handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) { - const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender) - - const customEvent = eventBinding.createWithSender(event.sender) - event.sender.emit('remote-get-guest-web-contents', customEvent, guest) - - if (customEvent.returnValue === undefined) { - if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.getGuestForWebContents()`) - } else { - customEvent.returnValue = guest - } - } - - return valueToMeta(event.sender, contextId, customEvent.returnValue) + const guestViewManager = require('@electron/internal/browser/guest-view-manager') + return valueToMeta(event.sender, contextId, guestViewManager.getGuest(guestInstanceId)) }) // Implements window.close() @@ -534,40 +486,27 @@ ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', function (event, text) setReturnValue(event, () => electron.clipboard.writeFindText(text)) }) -const getPreloadScript = function (preloadPath) { +ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) { + const preloadPath = event.sender._getPreloadPath() let preloadSrc = null let preloadError = null if (preloadPath) { try { preloadSrc = fs.readFileSync(preloadPath).toString() } catch (err) { - preloadError = errorUtils.serialize(err) + preloadError = { stack: err ? err.stack : (new Error(`Failed to load "${preloadPath}"`)).stack } } } - return { preloadPath, preloadSrc, preloadError } -} - -ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) { - const preloadPaths = [ - ...(event.sender.session ? event.sender.session.getPreloads() : []), - event.sender._getPreloadPath() - ] - event.returnValue = { - preloadScripts: preloadPaths.map(path => getPreloadScript(path)), + preloadSrc, + preloadError, isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(), - isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender), process: { arch: process.arch, platform: process.platform, env: process.env, version: process.version, - versions: process.versions, - execPath: process.helperExecPath + versions: process.versions } } }) - -ipcMain.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) { - event.sender.emit('preload-error', event, preloadPath, errorUtils.deserialize(error)) -}) diff --git a/electronasar/canary/common/api/clipboard.js b/electronasar/canary/common/api/clipboard.js index 530b08f..e0c9921 100644 --- a/electronasar/canary/common/api/clipboard.js +++ b/electronasar/canary/common/api/clipboard.js @@ -2,8 +2,8 @@ if (process.platform === 'linux' && process.type === 'renderer') { // On Linux we could not access clipboard in renderer process. - const { getRemote } = require('@electron/internal/renderer/remote') - module.exports = getRemote('clipboard') + const { getRemoteForUsage } = require('@electron/internal/renderer/remote') + module.exports = getRemoteForUsage('clipboard').clipboard } else { const clipboard = process.atomBinding('clipboard') diff --git a/electronasar/canary/common/api/deprecate.js b/electronasar/canary/common/api/deprecate.js index ef92a63..f72939b 100644 --- a/electronasar/canary/common/api/deprecate.js +++ b/electronasar/canary/common/api/deprecate.js @@ -19,9 +19,7 @@ const deprecate = { setHandler: (handler) => { deprecationHandler = handler }, getHandler: () => deprecationHandler, warn: (oldName, newName) => { - if (!process.noDeprecation) { - deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`) - } + return deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`) }, log: (message) => { if (typeof deprecationHandler === 'function') { @@ -69,41 +67,6 @@ const deprecate = { }) }, - function: (fn, newName) => { - // if newName is left blank, a removal warning will be displayed - const warn = warnOnce(fn.name, newName) - - return function () { - if (!process.noDeprecation) warn() - return fn.apply(this, arguments) - } - }, - - promisify: (fn) => { - const fnName = fn.name || 'function' - const oldName = `${fnName} with callbacks` - const newName = `${fnName} with Promises` - const warn = warnOnce(oldName, newName) - - return function (...params) { - let cb - if (params.length > 0 && typeof params[params.length - 1] === 'function') { - cb = params.pop() - } - const promise = fn.apply(this, params) - if (!cb) return promise - if (process.enablePromiseAPIs) warn() - return promise - .then(res => { - process.nextTick(() => { - cb.length === 2 ? cb(null, res) : cb(res) - }) - }, err => { - process.nextTick(() => cb(err)) - }) - } - }, - renameProperty: (o, oldName, newName) => { const warn = warnOnce(oldName, newName) diff --git a/electronasar/canary/common/parse-features-string.js b/electronasar/canary/common/parse-features-string.js index 1ef5109..955ab8f 100644 --- a/electronasar/canary/common/parse-features-string.js +++ b/electronasar/canary/common/parse-features-string.js @@ -4,12 +4,12 @@ // - `features` input string // - `emit` function(key, value) - called for each parsed KV module.exports = function parseFeaturesString (features, emit) { - features = `${features}`.trim() + features = `${features}` // split the string by ',' - features.split(/\s*,\s*/).forEach((feature) => { + features.split(/,\s*/).forEach((feature) => { // expected form is either a key by itself or a key/value pair in the form of // 'key=value' - let [key, value] = feature.split(/\s*=\s*/) + let [key, value] = feature.split(/\s*=/) if (!key) return // interpret the value as a boolean, if possible diff --git a/electronasar/canary/common/web-view-methods.js b/electronasar/canary/common/web-view-methods.js deleted file mode 100644 index a71c47c..0000000 --- a/electronasar/canary/common/web-view-methods.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict' - -// Public-facing API methods. -exports.syncMethods = new Set([ - 'getURL', - 'loadURL', - 'getTitle', - 'isLoading', - 'isLoadingMainFrame', - 'isWaitingForResponse', - 'stop', - 'reload', - 'reloadIgnoringCache', - 'canGoBack', - 'canGoForward', - 'canGoToOffset', - 'clearHistory', - 'goBack', - 'goForward', - 'goToIndex', - 'goToOffset', - 'isCrashed', - 'setUserAgent', - 'getUserAgent', - 'openDevTools', - 'closeDevTools', - 'isDevToolsOpened', - 'isDevToolsFocused', - 'inspectElement', - 'setAudioMuted', - 'isAudioMuted', - 'isCurrentlyAudible', - 'undo', - 'redo', - 'cut', - 'copy', - 'paste', - 'pasteAndMatchStyle', - 'delete', - 'selectAll', - 'unselect', - 'replace', - 'replaceMisspelling', - 'findInPage', - 'stopFindInPage', - 'downloadURL', - 'inspectServiceWorker', - 'showDefinitionForSelection', - 'getZoomFactor', - 'getZoomLevel', - 'setZoomFactor', - 'setZoomLevel', - 'sendImeEvent' -]) - -exports.asyncCallbackMethods = new Set([ - 'insertCSS', - 'insertText', - 'send', - 'sendInputEvent', - 'setLayoutZoomLevelLimits', - 'setVisualZoomLevelLimits', - 'print', - 'printToPDF' -]) - -exports.asyncPromiseMethods = new Set([ - 'capturePage', - 'executeJavaScript' -]) diff --git a/electronasar/canary/renderer/api/desktop-capturer.js b/electronasar/canary/renderer/api/desktop-capturer.js index 249a998..e4ece82 100644 --- a/electronasar/canary/renderer/api/desktop-capturer.js +++ b/electronasar/canary/renderer/api/desktop-capturer.js @@ -1,6 +1,6 @@ 'use strict' -const { nativeImage, deprecate } = require('electron') +const { nativeImage } = require('electron') const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') const includes = [].includes @@ -17,43 +17,32 @@ function isValid (options) { return Array.isArray(types) } -function mapSources (sources) { - return sources.map(source => ({ - id: source.id, - name: source.name, - thumbnail: nativeImage.createFromDataURL(source.thumbnail), - display_id: source.display_id, - appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null - })) -} +exports.getSources = function (options, callback) { + if (!isValid(options)) return callback(new Error('Invalid options')) + const captureWindow = includes.call(options.types, 'window') + const captureScreen = includes.call(options.types, 'screen') -const getSources = (options) => { - return new Promise((resolve, reject) => { - if (!isValid(options)) throw new Error('Invalid options') - - const captureWindow = includes.call(options.types, 'window') - const captureScreen = includes.call(options.types, 'screen') - - if (options.thumbnailSize == null) { - options.thumbnailSize = { - width: 150, - height: 150 - } - } - if (options.fetchWindowIcons == null) { - options.fetchWindowIcons = false + if (options.thumbnailSize == null) { + options.thumbnailSize = { + width: 150, + height: 150 } + } - const id = incrementId() - ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, options.fetchWindowIcons, id) - return ipcRenderer.once(`ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`, (event, sources) => { - try { - resolve(mapSources(sources)) - } catch (error) { - reject(error) - } - }) + const id = incrementId() + ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id) + return ipcRenderer.once(`ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`, (event, sources) => { + callback(null, (() => { + const results = [] + sources.forEach(source => { + results.push({ + id: source.id, + name: source.name, + thumbnail: nativeImage.createFromDataURL(source.thumbnail), + display_id: source.display_id + }) + }) + return results + })()) }) } - -exports.getSources = deprecate.promisify(getSources) diff --git a/electronasar/canary/renderer/api/ipc-renderer.js b/electronasar/canary/renderer/api/ipc-renderer.js index 1911543..7705f9f 100644 --- a/electronasar/canary/renderer/api/ipc-renderer.js +++ b/electronasar/canary/renderer/api/ipc-renderer.js @@ -8,11 +8,11 @@ const ipcRenderer = v8Util.getHiddenValue(global, 'ipc') const internal = false ipcRenderer.send = function (...args) { - return binding.send('-ipc-message', args) + return binding.send('ipc-message', args) } ipcRenderer.sendSync = function (...args) { - return binding.sendSync('-ipc-message-sync', args)[0] + return binding.sendSync('ipc-message-sync', args)[0] } ipcRenderer.sendToHost = function (...args) { diff --git a/electronasar/canary/renderer/api/remote.js b/electronasar/canary/renderer/api/remote.js index 98fc55c..036ebd8 100644 --- a/electronasar/canary/renderer/api/remote.js +++ b/electronasar/canary/renderer/api/remote.js @@ -311,9 +311,7 @@ exports.getCurrentWindow = () => { // Get current WebContents object. exports.getCurrentWebContents = () => { - const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS' - const meta = ipcRenderer.sendSync(command, contextId) - return metaToValue(meta) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', contextId)) } // Get a global object in browser. diff --git a/electronasar/canary/renderer/api/screen.js b/electronasar/canary/renderer/api/screen.js index a19604c..6cae10d 100644 --- a/electronasar/canary/renderer/api/screen.js +++ b/electronasar/canary/renderer/api/screen.js @@ -1,8 +1,4 @@ 'use strict' -const { deprecate } = require('electron') - -deprecate.warn(`electron.screen`, `electron.remote.screen`) - -const { getRemote } = require('@electron/internal/renderer/remote') -module.exports = getRemote('screen') +const { getRemoteForUsage } = require('@electron/internal/renderer/remote') +module.exports = getRemoteForUsage('screen').screen diff --git a/electronasar/canary/renderer/extensions/i18n.js b/electronasar/canary/renderer/extensions/i18n.js index 9554ff9..661b264 100644 --- a/electronasar/canary/renderer/extensions/i18n.js +++ b/electronasar/canary/renderer/extensions/i18n.js @@ -6,10 +6,9 @@ // Does not implement predefined messages: // https://developer.chrome.com/extensions/i18n#overview-predefined -const { potentiallyRemoteRequire } = require('@electron/internal/renderer/remote') const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') -const fs = potentiallyRemoteRequire('fs') -const path = potentiallyRemoteRequire('path') +const fs = require('fs') +const path = require('path') let metadata diff --git a/electronasar/canary/renderer/extensions/storage.js b/electronasar/canary/renderer/extensions/storage.js index d308881..8afaac7 100644 --- a/electronasar/canary/renderer/extensions/storage.js +++ b/electronasar/canary/renderer/extensions/storage.js @@ -1,9 +1,9 @@ 'use strict' -const { getRemote, potentiallyRemoteRequire } = require('@electron/internal/renderer/remote') -const fs = potentiallyRemoteRequire('fs') -const path = potentiallyRemoteRequire('path') -const app = getRemote('app') +const fs = require('fs') +const path = require('path') +const { remote } = require('electron') +const { app } = remote const getChromeStoragePath = (storageType, extensionId) => { return path.join( diff --git a/electronasar/canary/renderer/init.js b/electronasar/canary/renderer/init.js index cd2ffc5..cf1af5b 100644 --- a/electronasar/canary/renderer/init.js +++ b/electronasar/canary/renderer/init.js @@ -31,67 +31,68 @@ const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') require('@electron/internal/renderer/web-frame-init')() // Process command line arguments. -const { hasSwitch, getSwitchValue } = process.atomBinding('command_line') - -const parseOption = function (name, defaultValue, converter = value => value) { - return hasSwitch(name) ? converter(getSwitchValue(name)) : defaultValue +let nodeIntegration = false +let webviewTag = false +let preloadScript = null +let preloadScripts = [] +let isBackgroundPage = false +let appPath = null +for (const arg of process.argv) { + if (arg.indexOf('--guest-instance-id=') === 0) { + // This is a guest web view. + process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1)) + } else if (arg.indexOf('--opener-id=') === 0) { + // This is a guest BrowserWindow. + process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1)) + } else if (arg.indexOf('--node-integration=') === 0) { + nodeIntegration = arg.substr(arg.indexOf('=') + 1) === 'true' + } else if (arg.indexOf('--preload=') === 0) { + preloadScript = arg.substr(arg.indexOf('=') + 1) + } else if (arg === '--background-page') { + isBackgroundPage = true + } else if (arg.indexOf('--app-path=') === 0) { + appPath = arg.substr(arg.indexOf('=') + 1) + } else if (arg.indexOf('--webview-tag=') === 0) { + webviewTag = arg.substr(arg.indexOf('=') + 1) === 'true' + } else if (arg.indexOf('--preload-scripts') === 0) { + preloadScripts = arg.substr(arg.indexOf('=') + 1).split(path.delimiter) + } } -const contextIsolation = hasSwitch('context-isolation') -const nodeIntegration = hasSwitch('node-integration') -const webviewTag = hasSwitch('webview-tag') -const isHiddenPage = hasSwitch('hidden-page') -const isBackgroundPage = hasSwitch('background-page') -const usesNativeWindowOpen = hasSwitch('native-window-open') - -const preloadScript = parseOption('preload', null) -const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter)) -const appPath = parseOption('app-path', null) -const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value)) -const openerId = parseOption('opener-id', null, value => parseInt(value)) - -// The arguments to be passed to isolated world. -const isolatedWorldArgs = { ipcRenderer, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } - // The webContents preload script is loaded after the session preload scripts. if (preloadScript) { preloadScripts.push(preloadScript) } -switch (window.location.protocol) { - case 'chrome-devtools:': { - // Override some inspector APIs. - require('@electron/internal/renderer/inspector') - break - } - case 'chrome-extension:': { - // Inject the chrome.* APIs that chrome extensions require - require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, isBackgroundPage, window) - break - } - case 'chrome:': - break - default: { - // Override default web functions. - require('@electron/internal/renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen) +if (window.location.protocol === 'chrome-devtools:') { + // Override some inspector APIs. + require('@electron/internal/renderer/inspector') + nodeIntegration = false +} else if (window.location.protocol === 'chrome-extension:') { + // Add implementations of chrome API. + require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, isBackgroundPage, window) + nodeIntegration = false +} else if (window.location.protocol === 'chrome:') { + // Disable node integration for chrome UI scheme. + nodeIntegration = false +} else { + // Override default web functions. + require('@electron/internal/renderer/override') - // Inject content scripts. - if (process.isMainFrame) { - require('@electron/internal/renderer/content-scripts-injector') + // Inject content scripts. + require('@electron/internal/renderer/content-scripts-injector') + + // Load webview tag implementation. + if (webviewTag && process.guestInstanceId == null) { + const { setupWebView } = require('@electron/internal/renderer/web-view/web-view') + if (process.argv.includes('--context-isolation')) { + v8Util.setHiddenValue(window, 'setup-webview', setupWebView) + } else { + setupWebView(window) } } } -// Load webview tag implementation. -if (process.isMainFrame) { - require('@electron/internal/renderer/web-view/web-view-init')(contextIsolation, webviewTag, guestInstanceId) -} - -// Pass the arguments to isolatedWorld. -if (contextIsolation) { - v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs) -} - if (nodeIntegration) { // Export node bindings to global. global.require = require @@ -149,21 +150,27 @@ if (nodeIntegration) { }) } -const errorUtils = require('@electron/internal/common/error-utils') - // Load the preload scripts. for (const preloadScript of preloadScripts) { try { require(preloadScript) } catch (error) { - console.error(`Unable to load preload script: ${preloadScript}`) - console.error(`${error}`) - - ipcRenderer.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, errorUtils.serialize(error)) + console.error('Unable to load preload script: ' + preloadScript) + console.error(error.stack || error.message) } } // Warn about security issues -if (process.isMainFrame) { - require('@electron/internal/renderer/security-warnings')(nodeIntegration) +require('@electron/internal/renderer/security-warnings')(nodeIntegration) + +// Report focus/blur events of webview to browser. +// Note that while Chromium content APIs have observer for focus/blur, they +// unfortunately do not work for webview. +if (process.guestInstanceId) { + window.addEventListener('focus', () => { + ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, process.guestInstanceId) + }) + window.addEventListener('blur', () => { + ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, process.guestInstanceId) + }) } diff --git a/electronasar/canary/renderer/inspector.js b/electronasar/canary/renderer/inspector.js index 5ed4e9d..b358fa6 100644 --- a/electronasar/canary/renderer/inspector.js +++ b/electronasar/canary/renderer/inspector.js @@ -1,7 +1,5 @@ 'use strict' -const { getRemote, potentiallyRemoteRequire } = require('@electron/internal/renderer/remote') - window.onload = function () { // Use menu API to show context menu. window.InspectorFrontendHost.showContextMenuAtPoint = createMenu @@ -20,7 +18,7 @@ function completeURL (project, path) { } window.confirm = function (message, title) { - const dialog = getRemote('dialog') + const { dialog } = require('electron').remote if (title == null) { title = '' } @@ -64,7 +62,8 @@ const convertToMenuTemplate = function (items) { } const createMenu = function (x, y, items) { - const Menu = getRemote('Menu') + const { remote } = require('electron') + const { Menu } = remote let template = convertToMenuTemplate(items) if (useEditMenuItems(x, y, template)) { @@ -74,8 +73,7 @@ const createMenu = function (x, y, items) { // The menu is expected to show asynchronously. setTimeout(function () { - const getCurrentWindow = getRemote('getCurrentWindow') - menu.popup({ window: getCurrentWindow() }) + menu.popup({ window: remote.getCurrentWindow() }) }) } @@ -118,7 +116,7 @@ const getEditMenuItems = function () { } const showFileChooserDialog = function (callback) { - const dialog = getRemote('dialog') + const { dialog } = require('electron').remote const files = dialog.showOpenDialog({}) if (files != null) { callback(pathToHtml5FileObject(files[0])) @@ -126,7 +124,7 @@ const showFileChooserDialog = function (callback) { } const pathToHtml5FileObject = function (path) { - const fs = potentiallyRemoteRequire('fs') + const fs = require('fs') const blob = new Blob([fs.readFileSync(path)]) blob.name = path return blob diff --git a/electronasar/canary/renderer/override.js b/electronasar/canary/renderer/override.js new file mode 100644 index 0000000..72a4a5a --- /dev/null +++ b/electronasar/canary/renderer/override.js @@ -0,0 +1,18 @@ +'use strict' + +const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') + +const v8Util = process.atomBinding('v8_util') + +const { guestInstanceId, openerId } = process +const hiddenPage = process.argv.includes('--hidden-page') +const usesNativeWindowOpen = process.argv.includes('--native-window-open') +const contextIsolation = process.argv.includes('--context-isolation') + +// Pass the arguments to isolatedWorld. +if (contextIsolation) { + const isolatedWorldArgs = { ipcRenderer, guestInstanceId, hiddenPage, openerId, usesNativeWindowOpen } + v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs) +} + +require('@electron/internal/renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen) diff --git a/electronasar/canary/renderer/remote.js b/electronasar/canary/renderer/remote.js index 0ef98ca..b807723 100644 --- a/electronasar/canary/renderer/remote.js +++ b/electronasar/canary/renderer/remote.js @@ -2,24 +2,9 @@ const { remote } = require('electron') -exports.getRemote = function (name) { +exports.getRemoteForUsage = function (usage) { if (!remote) { - throw new Error(`${name} requires remote, which is not enabled`) - } - return remote[name] -} - -exports.remoteRequire = function (name) { - if (!remote) { - throw new Error(`${name} requires remote, which is not enabled`) - } - return remote.require(name) -} - -exports.potentiallyRemoteRequire = function (name) { - if (process.sandboxed) { - return exports.remoteRequire(name) - } else { - return require(name) + throw new Error(`${usage} requires remote, which is not enabled`) } + return remote } diff --git a/electronasar/canary/renderer/security-warnings.js b/electronasar/canary/renderer/security-warnings.js index 9d68ee0..9d41190 100644 --- a/electronasar/canary/renderer/security-warnings.js +++ b/electronasar/canary/renderer/security-warnings.js @@ -249,6 +249,51 @@ const warnAboutAllowedPopups = function () { } } +const warnAboutNodeIntegrationDefault = function (webPreferences) { + if (webPreferences.nodeIntegration && !webPreferences.nodeIntegrationWasExplicitlyEnabled) { + const warning = `This window has node integration enabled by default. In ` + + `Electron 5.0.0, node integration will be disabled by default. To prepare ` + + `for this change, set {nodeIntegration: true} in the webPreferences for ` + + `this window, or ensure that this window does not rely on node integration ` + + `and set {nodeIntegration: false}.` + console.warn('%cElectron Deprecation Warning (nodeIntegration default change)', 'font-weight: bold;', warning) + } +} + +const warnAboutContextIsolationDefault = function (webPreferences) { + if (webPreferences.preload && !webPreferences.contextIsolation && !webPreferences.contextIsolationWasExplicitlyDisabled) { + const url = 'https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content' + const warning = `This window has context isolation disabled by default. In ` + + `Electron 5.0.0, context isolation will be enabled by default. To prepare ` + + `for this change, set {contextIsolation: false} in the webPreferences for ` + + `this window, or ensure that this window does not rely on context ` + + `isolation being disabled, and set {contextIsolation: true}.\n\n` + + `For more information, see ${url}` + console.warn('%cElectron Deprecation Warning (contextIsolation default change)', 'font-weight: bold;', warning) + } +} + +const warnAboutDeprecatedWebviewTagDefault = function (webPreferences) { + if (webPreferences.webviewTagWasExplicitlyEnabled) { + return + } + if (!document || !document.getElementsByTagName) { + return + } + const webviews = document.getElementsByTagName('webview') + if (webviews && webviews.length > 0) { + const url = 'https://github.com/electron/electron/blob/master/docs/api/breaking-changes.md#new-browserwindow-webpreferences-' + const warning = `This window has the tag enabled by default. In ` + + `Electron 5.0.0, tags will be disabled by default. To prepare ` + + `for this change, set {webviewTag: true} in the webPreferences for ` + + `this window.\n\n` + + `For more information, see ${url}` + + console.warn('%cElectron Deprecation Warning (webviewTag default change)', + 'font-weight: bold;', warning) + } +} + // Currently missing since we can't easily programmatically check for it: // #12WebViews: Verify the options and params of all `` tags @@ -261,6 +306,9 @@ const logSecurityWarnings = function (webPreferences, nodeIntegration) { warnAboutEnableBlinkFeatures(webPreferences) warnAboutInsecureCSP() warnAboutAllowedPopups() + warnAboutNodeIntegrationDefault(webPreferences) + warnAboutContextIsolationDefault(webPreferences) + warnAboutDeprecatedWebviewTagDefault(webPreferences) } const getWebPreferences = function () { diff --git a/electronasar/canary/renderer/web-view/web-view-attributes.js b/electronasar/canary/renderer/web-view/web-view-attributes.js index 2db7736..39d986e 100644 --- a/electronasar/canary/renderer/web-view/web-view-attributes.js +++ b/electronasar/canary/renderer/web-view/web-view-attributes.js @@ -1,7 +1,7 @@ 'use strict' const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') -const { WebViewImpl } = require('@electron/internal/renderer/web-view/web-view-impl') +const { WebViewImpl } = require('@electron/internal/renderer/web-view/web-view') const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants') const errorUtils = require('@electron/internal/common/error-utils') diff --git a/electronasar/canary/renderer/web-view/web-view-element.js b/electronasar/canary/renderer/web-view/web-view-element.js deleted file mode 100644 index beb0a34..0000000 --- a/electronasar/canary/renderer/web-view/web-view-element.js +++ /dev/null @@ -1,110 +0,0 @@ -'use strict' - -// When using context isolation, the WebViewElement and the custom element -// methods have to be defined in the main world to be able to be registered. -// -// Note: The hidden values can only be read/set inside the same context, all -// methods that access the "internal" hidden value must be put in this file. -// -// Note: This file could be loaded in the main world of contextIsolation page, -// which runs in browserify environment instead of Node environment, all native -// modules must be passed from outside, all included files must be plain JS. - -const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants') - -// Return a WebViewElement class that is defined in this context. -const defineWebViewElement = (v8Util, webViewImpl) => { - const { guestViewInternal, WebViewImpl } = webViewImpl - return class WebViewElement extends HTMLElement { - static get observedAttributes () { - return [ - webViewConstants.ATTRIBUTE_PARTITION, - webViewConstants.ATTRIBUTE_SRC, - webViewConstants.ATTRIBUTE_HTTPREFERRER, - webViewConstants.ATTRIBUTE_USERAGENT, - webViewConstants.ATTRIBUTE_NODEINTEGRATION, - webViewConstants.ATTRIBUTE_PLUGINS, - webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, - webViewConstants.ATTRIBUTE_ALLOWPOPUPS, - webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE, - webViewConstants.ATTRIBUTE_PRELOAD, - webViewConstants.ATTRIBUTE_BLINKFEATURES, - webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, - webViewConstants.ATTRIBUTE_WEBPREFERENCES - ] - } - - constructor () { - super() - v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this)) - } - - connectedCallback () { - const internal = v8Util.getHiddenValue(this, 'internal') - if (!internal) { - return - } - if (!internal.elementAttached) { - guestViewInternal.registerEvents(internal, internal.viewInstanceId) - internal.elementAttached = true - internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse() - } - } - - attributeChangedCallback (name, oldValue, newValue) { - const internal = v8Util.getHiddenValue(this, 'internal') - if (internal) { - internal.handleWebviewAttributeMutation(name, oldValue, newValue) - } - } - - disconnectedCallback () { - const internal = v8Util.getHiddenValue(this, 'internal') - if (!internal) { - return - } - guestViewInternal.deregisterEvents(internal.viewInstanceId) - internal.elementAttached = false - this.internalInstanceId = 0 - internal.reset() - } - } -} - -// Register custom element. -const registerWebViewElement = (v8Util, webViewImpl) => { - const WebViewElement = defineWebViewElement(v8Util, webViewImpl) - - webViewImpl.setupMethods(WebViewElement) - - // The customElements.define has to be called in a special scope. - webViewImpl.webFrame.allowGuestViewElementDefinition(window, () => { - window.customElements.define('webview', WebViewElement) - window.WebView = WebViewElement - - // Delete the callbacks so developers cannot call them and produce unexpected - // behavior. - delete WebViewElement.prototype.connectedCallback - delete WebViewElement.prototype.disconnectedCallback - delete WebViewElement.prototype.attributeChangedCallback - - // Now that |observedAttributes| has been retrieved, we can hide it from - // user code as well. - delete WebViewElement.observedAttributes - }) -} - -// Prepare to register the element. -const setupWebView = (v8Util, webViewImpl) => { - const useCapture = true - window.addEventListener('readystatechange', function listener (event) { - if (document.readyState === 'loading') { - return - } - webViewImpl.setupAttributes() - registerWebViewElement(v8Util, webViewImpl) - window.removeEventListener(event.type, listener, useCapture) - }, useCapture) -} - -module.exports = { setupWebView } diff --git a/electronasar/canary/renderer/web-view/web-view-init.js b/electronasar/canary/renderer/web-view/web-view-init.js deleted file mode 100644 index da302c3..0000000 --- a/electronasar/canary/renderer/web-view/web-view-init.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict' - -const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') -const v8Util = process.atomBinding('v8_util') - -function handleFocusBlur (guestInstanceId) { - // Note that while Chromium content APIs have observer for focus/blur, they - // unfortunately do not work for webview. - - window.addEventListener('focus', () => { - ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, guestInstanceId) - }) - - window.addEventListener('blur', () => { - ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, guestInstanceId) - }) -} - -module.exports = function (contextIsolation, webviewTag, guestInstanceId) { - // Don't allow recursive ``. - if (webviewTag && guestInstanceId == null) { - const webViewImpl = require('@electron/internal/renderer/web-view/web-view-impl') - if (contextIsolation) { - v8Util.setHiddenValue(window, 'web-view-impl', webViewImpl) - } else { - const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') - setupWebView(v8Util, webViewImpl) - } - } - - if (guestInstanceId) { - // Report focus/blur events of webview to browser. - handleFocusBlur(guestInstanceId) - } -} diff --git a/electronasar/canary/renderer/web-view/web-view-impl.js b/electronasar/canary/renderer/web-view/web-view.js similarity index 70% rename from electronasar/canary/renderer/web-view/web-view-impl.js rename to electronasar/canary/renderer/web-view/web-view.js index 6579c45..1413f4c 100644 --- a/electronasar/canary/renderer/web-view/web-view-impl.js +++ b/electronasar/canary/renderer/web-view/web-view.js @@ -7,11 +7,6 @@ const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal') const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants') const errorUtils = require('@electron/internal/common/error-utils') -const { - syncMethods, - asyncCallbackMethods, - asyncPromiseMethods -} = require('@electron/internal/common/web-view-methods') // ID generator. let nextId = 0 @@ -24,6 +19,7 @@ const getNextId = function () { class WebViewImpl { constructor (webviewNode) { this.webviewNode = webviewNode + v8Util.setHiddenValue(this.webviewNode, 'internal', this) this.elementAttached = false this.beforeFirstNavigation = true this.hasFocus = false @@ -200,26 +196,105 @@ class WebViewImpl { } } -const setupAttributes = () => { - require('@electron/internal/renderer/web-view/web-view-attributes') -} - -const setupMethods = (WebViewElement) => { - // WebContents associated with this webview. - WebViewElement.prototype.getWebContents = function () { - const { getRemote } = require('@electron/internal/renderer/remote') - const getGuestWebContents = getRemote('getGuestWebContents') +// Registers custom element. +const registerWebViewElement = (window) => { + const proto = Object.create(window.HTMLObjectElement.prototype) + proto.createdCallback = function () { + return new WebViewImpl(this) + } + proto.attributeChangedCallback = function (name, oldValue, newValue) { const internal = v8Util.getHiddenValue(this, 'internal') - if (!internal.guestInstanceId) { - internal.createGuestSync() + if (internal) { + internal.handleWebviewAttributeMutation(name, oldValue, newValue) + } + } + proto.detachedCallback = function () { + const internal = v8Util.getHiddenValue(this, 'internal') + if (!internal) { + return + } + guestViewInternal.deregisterEvents(internal.viewInstanceId) + internal.elementAttached = false + this.internalInstanceId = 0 + internal.reset() + } + proto.attachedCallback = function () { + const internal = v8Util.getHiddenValue(this, 'internal') + if (!internal) { + return + } + if (!internal.elementAttached) { + guestViewInternal.registerEvents(internal, internal.viewInstanceId) + internal.elementAttached = true + internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse() } - return getGuestWebContents(internal.guestInstanceId) } - // Focusing the webview should move page focus to the underlying iframe. - WebViewElement.prototype.focus = function () { - this.contentWindow.focus() - } + // Public-facing API methods. + const methods = [ + 'getURL', + 'loadURL', + 'getTitle', + 'isLoading', + 'isLoadingMainFrame', + 'isWaitingForResponse', + 'stop', + 'reload', + 'reloadIgnoringCache', + 'canGoBack', + 'canGoForward', + 'canGoToOffset', + 'clearHistory', + 'goBack', + 'goForward', + 'goToIndex', + 'goToOffset', + 'isCrashed', + 'setUserAgent', + 'getUserAgent', + 'openDevTools', + 'closeDevTools', + 'isDevToolsOpened', + 'isDevToolsFocused', + 'inspectElement', + 'setAudioMuted', + 'isAudioMuted', + 'isCurrentlyAudible', + 'undo', + 'redo', + 'cut', + 'copy', + 'paste', + 'pasteAndMatchStyle', + 'delete', + 'selectAll', + 'unselect', + 'replace', + 'replaceMisspelling', + 'findInPage', + 'stopFindInPage', + 'downloadURL', + 'inspectServiceWorker', + 'showDefinitionForSelection', + 'setZoomFactor', + 'setZoomLevel', + 'sendImeEvent' + ] + const nonblockMethods = [ + 'insertCSS', + 'insertText', + 'send', + 'sendInputEvent', + 'setLayoutZoomLevelLimits', + 'setVisualZoomLevelLimits', + // with callback + 'capturePage', + 'executeJavaScript', + 'getZoomFactor', + 'getZoomLevel', + 'print', + 'printToPDF' + ] const getGuestInstanceId = function (self) { const internal = v8Util.getHiddenValue(self, 'internal') @@ -240,8 +315,8 @@ const setupMethods = (WebViewElement) => { } } } - for (const method of syncMethods) { - WebViewElement.prototype[method] = createBlockHandler(method) + for (const method of methods) { + proto[method] = createBlockHandler(method) } const createNonBlockHandler = function (method) { @@ -258,35 +333,49 @@ const setupMethods = (WebViewElement) => { ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null) } } - - for (const method of asyncCallbackMethods) { - WebViewElement.prototype[method] = createNonBlockHandler(method) + for (const method of nonblockMethods) { + proto[method] = createNonBlockHandler(method) } - const createPromiseHandler = function (method) { - return function (...args) { - return new Promise((resolve, reject) => { - const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null - const requestId = getNextId() - - ipcRenderer.once(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, function (event, error, result) { - if (error == null) { - if (callback) { - callback(result) - } - resolve(result) - } else { - reject(errorUtils.deserialize(error)) - } - }) - ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null) - }) + // WebContents associated with this webview. + proto.getWebContents = function () { + const { getRemoteForUsage } = require('@electron/internal/renderer/remote') + const remote = getRemoteForUsage('getWebContents()') + const internal = v8Util.getHiddenValue(this, 'internal') + if (!internal.guestInstanceId) { + internal.createGuestSync() } + return remote.getGuestWebContents(internal.guestInstanceId) } - for (const method of asyncPromiseMethods) { - WebViewElement.prototype[method] = createPromiseHandler(method) + // Focusing the webview should move page focus to the underlying iframe. + proto.focus = function () { + this.contentWindow.focus() } + + window.WebView = webFrame.registerEmbedderCustomElement(window, 'webview', { + prototype: proto + }) + + // Delete the callbacks so developers cannot call them and produce unexpected + // behavior. + delete proto.createdCallback + delete proto.attachedCallback + delete proto.detachedCallback + delete proto.attributeChangedCallback } -module.exports = { setupAttributes, setupMethods, guestViewInternal, webFrame, WebViewImpl } +const setupWebView = (window) => { + require('@electron/internal/renderer/web-view/web-view-attributes') + + const useCapture = true + window.addEventListener('readystatechange', function listener (event) { + if (document.readyState === 'loading') { + return + } + registerWebViewElement(window) + window.removeEventListener(event.type, listener, useCapture) + }, useCapture) +} + +module.exports = { setupWebView, WebViewImpl } diff --git a/electronasar/canary/renderer/window-setup.js b/electronasar/canary/renderer/window-setup.js index b4801a8..f4ab44a 100644 --- a/electronasar/canary/renderer/window-setup.js +++ b/electronasar/canary/renderer/window-setup.js @@ -23,10 +23,10 @@ // - document.hidden // - document.visibilityState -const { defineProperty, defineProperties } = Object +const { defineProperty } = Object // Helper function to resolve relative url. -const a = window.document.createElement('a') +const a = window.top.document.createElement('a') const resolveURL = function (url) { a.href = url return a.href @@ -54,61 +54,12 @@ const removeProxy = (guestId) => { delete windowProxies[guestId] } -function LocationProxy (ipcRenderer, guestId) { - const getGuestURL = function () { - const urlString = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'getURL') - try { - return new URL(urlString) - } catch (e) { - console.error('LocationProxy: failed to parse string', urlString, e) - } - - return null - } - - const propertyProxyFor = function (property) { - return { - get: function () { - const guestURL = getGuestURL() - const value = guestURL ? guestURL[property] : '' - return value === undefined ? '' : value - }, - set: function (newVal) { - const guestURL = getGuestURL() - if (guestURL) { - guestURL[property] = newVal - return ipcRenderer.sendSync( - 'ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', - guestId, 'loadURL', guestURL.toString()) - } - } - } - } - - defineProperties(this, { - hash: propertyProxyFor('hash'), - href: propertyProxyFor('href'), - host: propertyProxyFor('host'), - hostname: propertyProxyFor('hostname'), - origin: propertyProxyFor('origin'), - pathname: propertyProxyFor('pathname'), - port: propertyProxyFor('port'), - protocol: propertyProxyFor('protocol'), - search: propertyProxyFor('search') - }) - - this.toString = function () { - return this.href - } -} - function BrowserWindowProxy (ipcRenderer, guestId) { this.closed = false - const location = new LocationProxy(ipcRenderer, guestId) defineProperty(this, 'location', { get: function () { - return location + return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'getURL') }, set: function (url) { url = resolveURL(url) @@ -146,6 +97,15 @@ function BrowserWindowProxy (ipcRenderer, guestId) { } } +// Forward history operations to browser. +const sendHistoryOperation = function (ipcRenderer, ...args) { + ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER', ...args) +} + +const getHistoryOperation = function (ipcRenderer, ...args) { + return ipcRenderer.sendSync('ELECTRON_SYNC_NAVIGATION_CONTROLLER', ...args) +} + module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen) => { if (guestInstanceId == null) { // Override default window.close. @@ -190,20 +150,20 @@ module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNative }) window.history.back = function () { - ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK') + sendHistoryOperation(ipcRenderer, 'goBack') } window.history.forward = function () { - ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD') + sendHistoryOperation(ipcRenderer, 'goForward') } window.history.go = function (offset) { - ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset) + sendHistoryOperation(ipcRenderer, 'goToOffset', +offset) } defineProperty(window.history, 'length', { get: function () { - return ipcRenderer.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH') + return getHistoryOperation(ipcRenderer, 'length') } })