Changes of Linux canary v0.0.71

This commit is contained in:
DisTok 2019-02-05 22:03:42 +01:00
parent 42e7e8d8e4
commit 1ecfb80379
37 changed files with 641 additions and 1132 deletions

View File

@ -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)

View File

@ -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) => {

View File

@ -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')

View File

@ -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) => {

View File

@ -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

View File

@ -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' }
)
}

View File

@ -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 () {

View File

@ -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)
}

View File

@ -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 = []
}

View File

@ -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)
}

View File

@ -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}`)

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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 <webview>
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

View File

@ -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('<h1>hello</h1>')
//
// The above code would not work if a navigation to "about:blank" is done
// here, since the window would be cleared of all changes in the next tick.
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.`)

View File

@ -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)

View File

@ -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))
})

View File

@ -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')

View File

@ -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)

View File

@ -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

View File

@ -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'
])

View File

@ -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)

View File

@ -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) {

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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)
})
}

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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 <webview> tag enabled by default. In ` +
`Electron 5.0.0, <webview> 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 `<webview>` tags
@ -261,6 +306,9 @@ const logSecurityWarnings = function (webPreferences, nodeIntegration) {
warnAboutEnableBlinkFeatures(webPreferences)
warnAboutInsecureCSP()
warnAboutAllowedPopups()
warnAboutNodeIntegrationDefault(webPreferences)
warnAboutContextIsolationDefault(webPreferences)
warnAboutDeprecatedWebviewTagDefault(webPreferences)
}
const getWebPreferences = function () {

View File

@ -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')

View File

@ -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 <webview> 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 <webview> 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 }

View File

@ -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 `<webview>`.
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)
}
}

View File

@ -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 <webview> 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 }

View File

@ -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')
}
})