Changes of Linux development v0.0.64
This commit is contained in:
parent
aea9939f13
commit
efd2dc31ab
46 changed files with 985 additions and 26282 deletions
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,7 @@
|
|||
'use strict'
|
||||
|
||||
const bindings = process.atomBinding('app')
|
||||
const commandLine = process.atomBinding('command_line')
|
||||
const path = require('path')
|
||||
const { app, App } = bindings
|
||||
|
||||
|
@ -25,24 +26,21 @@ Object.assign(app, {
|
|||
return Menu.getApplicationMenu()
|
||||
},
|
||||
commandLine: {
|
||||
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)
|
||||
}
|
||||
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().`)
|
||||
}
|
||||
})
|
||||
|
||||
const nativeFn = app.getAppMetrics
|
||||
app.getFileIcon = deprecate.promisify(app.getFileIcon)
|
||||
|
||||
const nativeAppMetrics = app.getAppMetrics
|
||||
app.getAppMetrics = () => {
|
||||
const metrics = nativeFn.call(app)
|
||||
const metrics = nativeAppMetrics.call(app)
|
||||
for (const metric of metrics) {
|
||||
if ('memory' in metric) {
|
||||
deprecate.removeProperty(metric, 'memory')
|
||||
|
@ -93,9 +91,7 @@ if (process.platform === 'linux') {
|
|||
}
|
||||
|
||||
app.allowNTLMCredentialsForAllDomains = function (allow) {
|
||||
if (!process.noDeprecations) {
|
||||
deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains')
|
||||
}
|
||||
deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains')
|
||||
const domains = allow ? '*' : ''
|
||||
if (!this.isReady()) {
|
||||
this.commandLine.appendSwitch('auth-server-whitelist', domains)
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
'use strict'
|
||||
const { deprecate } = require('electron')
|
||||
const contentTracing = process.atomBinding('content_tracing')
|
||||
|
||||
module.exports = 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
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
'use strict'
|
||||
|
||||
const CrashReporter = require('@electron/internal/common/crash-reporter')
|
||||
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
|
||||
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
|
||||
|
||||
class CrashReporterMain extends CrashReporter {
|
||||
sendSync (channel, ...args) {
|
||||
const event = {}
|
||||
ipcMain.emit(channel, event, ...args)
|
||||
return event.returnValue
|
||||
init (options) {
|
||||
return crashReporterInit(options)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
|
||||
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 process.platform === 'linux' ? 'About' : `About ${app.getName()}`
|
||||
return isLinux ? 'About' : `About ${app.getName()}`
|
||||
}
|
||||
},
|
||||
close: {
|
||||
label: process.platform === 'darwin' ? 'Close Window' : 'Close',
|
||||
label: isMac ? 'Close Window' : 'Close',
|
||||
accelerator: 'CommandOrControl+W',
|
||||
windowMethod: 'close'
|
||||
},
|
||||
|
@ -78,12 +82,12 @@ const roles = {
|
|||
default: return 'Quit'
|
||||
}
|
||||
},
|
||||
accelerator: process.platform === 'win32' ? null : 'CommandOrControl+Q',
|
||||
accelerator: isWindows ? null : 'CommandOrControl+Q',
|
||||
appMethod: 'quit'
|
||||
},
|
||||
redo: {
|
||||
label: 'Redo',
|
||||
accelerator: process.platform === 'win32' ? 'Control+Y' : 'Shift+CommandOrControl+Z',
|
||||
accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z',
|
||||
webContentsMethod: 'redo'
|
||||
},
|
||||
reload: {
|
||||
|
@ -122,13 +126,13 @@ const roles = {
|
|||
},
|
||||
toggledevtools: {
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: 'toggleDevTools'
|
||||
},
|
||||
togglefullscreen: {
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: process.platform === 'darwin' ? 'Control+Command+F' : 'F11',
|
||||
accelerator: isMac ? 'Control+Command+F' : 'F11',
|
||||
windowMethod: (window) => {
|
||||
window.setFullScreen(!window.isFullScreen())
|
||||
}
|
||||
|
@ -152,9 +156,8 @@ const roles = {
|
|||
accelerator: 'CommandOrControl+Plus',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.getZoomLevel((zoomLevel) => {
|
||||
webContents.setZoomLevel(zoomLevel + 0.5)
|
||||
})
|
||||
const zoomLevel = webContents.getZoomLevel()
|
||||
webContents.setZoomLevel(zoomLevel + 0.5)
|
||||
}
|
||||
},
|
||||
zoomout: {
|
||||
|
@ -162,78 +165,97 @@ const roles = {
|
|||
accelerator: 'CommandOrControl+-',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.getZoomLevel((zoomLevel) => {
|
||||
webContents.setZoomLevel(zoomLevel - 0.5)
|
||||
})
|
||||
const zoomLevel = webContents.getZoomLevel()
|
||||
webContents.setZoomLevel(zoomLevel - 0.5)
|
||||
}
|
||||
},
|
||||
// Edit submenu (should fit both Mac & Windows)
|
||||
// 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
|
||||
editmenu: {
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{
|
||||
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'
|
||||
}
|
||||
{ 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' }
|
||||
])
|
||||
]
|
||||
},
|
||||
|
||||
// Window submenu should be used for Mac only
|
||||
// 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
|
||||
windowmenu: {
|
||||
label: 'Window',
|
||||
submenu: [
|
||||
{
|
||||
role: 'minimize'
|
||||
},
|
||||
{
|
||||
role: 'close'
|
||||
},
|
||||
|
||||
process.platform === 'darwin' ? {
|
||||
type: 'separator'
|
||||
} : null,
|
||||
|
||||
process.platform === 'darwin' ? {
|
||||
role: 'front'
|
||||
} : null
|
||||
|
||||
{ role: 'minimize' },
|
||||
{ role: 'zoom' },
|
||||
...(isMac ? [
|
||||
{ type: 'separator' },
|
||||
{ role: 'front' }
|
||||
] : [
|
||||
{ role: 'close' }
|
||||
])
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const canExecuteRole = (role) => {
|
||||
if (!roles.hasOwnProperty(role)) return false
|
||||
if (process.platform !== 'darwin') return true
|
||||
if (!isMac) return true
|
||||
|
||||
// macOS handles all roles natively except for a few
|
||||
return roles[role].nonNativeMacOSRole
|
||||
|
|
|
@ -109,6 +109,12 @@ Menu.prototype.insert = function (pos, item) {
|
|||
throw new TypeError('Invalid item')
|
||||
}
|
||||
|
||||
if (pos < 0) {
|
||||
throw new RangeError(`Position ${pos} cannot be less than 0`)
|
||||
} else if (pos > this.getItemCount()) {
|
||||
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`)
|
||||
}
|
||||
|
||||
// insert item depending on its type
|
||||
insertItemByType.call(this, item, pos)
|
||||
|
||||
|
@ -145,6 +151,8 @@ Menu.setApplicationMenu = function (menu) {
|
|||
}
|
||||
|
||||
applicationMenu = menu
|
||||
v8Util.setHiddenValue(global, 'applicationMenuSet', true)
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
if (!menu) return
|
||||
menu._callMenuWillShow()
|
||||
|
@ -159,14 +167,20 @@ 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)
|
||||
|
||||
sorted.forEach((item) => menu.append(new MenuItem(item)))
|
||||
const menu = new Menu()
|
||||
sorted.forEach(item => {
|
||||
if (item instanceof MenuItem) {
|
||||
menu.append(item)
|
||||
} else {
|
||||
menu.append(new MenuItem(item))
|
||||
}
|
||||
})
|
||||
|
||||
return menu
|
||||
}
|
||||
|
@ -176,7 +190,11 @@ Menu.buildFromTemplate = function (template) {
|
|||
// validate the template against having the wrong attribute
|
||||
function areValidTemplateItems (template) {
|
||||
return template.every(item =>
|
||||
item != null && typeof item === 'object' && (item.hasOwnProperty('label') || item.hasOwnProperty('role') || item.type === 'separator'))
|
||||
item != null &&
|
||||
typeof item === 'object' &&
|
||||
(item.hasOwnProperty('label') ||
|
||||
item.hasOwnProperty('role') ||
|
||||
item.type === 'separator'))
|
||||
}
|
||||
|
||||
function sortTemplate (template) {
|
||||
|
|
|
@ -30,9 +30,7 @@ module.exports = [
|
|||
{ name: 'Tray', file: 'tray' },
|
||||
{ name: 'View', file: 'view' },
|
||||
{ name: 'webContents', file: 'web-contents' },
|
||||
{ name: 'WebContentsView', file: 'web-contents-view' },
|
||||
// The internal modules, invisible unless you know their names.
|
||||
{ name: 'NavigationController', file: 'navigation-controller', private: true }
|
||||
{ name: 'WebContentsView', file: 'web-contents-view' }
|
||||
]
|
||||
|
||||
if (features.isViewApiEnabled()) {
|
||||
|
@ -41,6 +39,8 @@ 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' }
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict'
|
||||
|
||||
// TODO(deepak1556): Deprecate and remove standalone netLog module,
|
||||
// it is now a property of sessio module.
|
||||
// it is now a property of session module.
|
||||
const { app, session } = require('electron')
|
||||
|
||||
// Fallback to default session.
|
||||
|
@ -10,8 +10,12 @@ Object.setPrototypeOf(module.exports, new Proxy({}, {
|
|||
if (!app.isReady()) return
|
||||
|
||||
const netLog = session.defaultSession.netLog
|
||||
|
||||
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return
|
||||
|
||||
// check for properties on the prototype chain that aren't functions
|
||||
if (typeof netLog[property] !== 'function') return netLog[property]
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args) => netLog[property](...args)
|
||||
},
|
||||
|
|
|
@ -22,4 +22,30 @@ if (process.platform === 'linux') {
|
|||
})
|
||||
}
|
||||
|
||||
// TODO(deepak1556): Deprecate async api in favor of sync version in 5.0
|
||||
powerMonitor.querySystemIdleState = function (threshold, callback) {
|
||||
if (typeof threshold !== 'number') {
|
||||
throw new Error('Must pass threshold as a number')
|
||||
}
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Must pass callback as a function argument')
|
||||
}
|
||||
|
||||
const idleState = this._querySystemIdleState(threshold)
|
||||
|
||||
process.nextTick(() => callback(idleState))
|
||||
}
|
||||
|
||||
// TODO(deepak1556): Deprecate async api in favor of sync version in 5.0
|
||||
powerMonitor.querySystemIdleTime = function (callback) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Must pass function as an argument')
|
||||
}
|
||||
|
||||
const idleTime = this._querySystemIdleTime()
|
||||
|
||||
process.nextTick(() => callback(idleTime))
|
||||
}
|
||||
|
||||
module.exports = powerMonitor
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
'use strict'
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { app } = require('electron')
|
||||
const { fromPartition, Session, Cookies } = process.atomBinding('session')
|
||||
const { app, deprecate } = require('electron')
|
||||
const { fromPartition, Session, Cookies, Protocol } = process.atomBinding('session')
|
||||
|
||||
// Public API.
|
||||
Object.defineProperties(exports, {
|
||||
|
@ -22,3 +22,10 @@ Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
|
|||
Session.prototype._init = function () {
|
||||
app.emit('session-created', this)
|
||||
}
|
||||
|
||||
Cookies.prototype.flushStore = deprecate.promisify(Cookies.prototype.flushStore)
|
||||
Cookies.prototype.get = deprecate.promisify(Cookies.prototype.get)
|
||||
Cookies.prototype.remove = deprecate.promisify(Cookies.prototype.remove)
|
||||
Cookies.prototype.set = deprecate.promisify(Cookies.prototype.set)
|
||||
|
||||
Protocol.prototype.isProtocolHandled = deprecate.promisify(Protocol.prototype.isProtocolHandled)
|
||||
|
|
|
@ -31,12 +31,6 @@ 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 = []
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ const { EventEmitter } = require('events')
|
|||
const electron = require('electron')
|
||||
const path = require('path')
|
||||
const url = require('url')
|
||||
const v8Util = process.atomBinding('v8_util')
|
||||
const { app, ipcMain, session, NavigationController, deprecate } = electron
|
||||
const { app, ipcMain, session, 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')
|
||||
|
||||
|
@ -78,6 +78,7 @@ const defaultPrintingSetting = {
|
|||
printWithCloudPrint: false,
|
||||
printWithPrivet: false,
|
||||
printWithExtension: false,
|
||||
pagesPerSheet: 1,
|
||||
deviceName: 'Save as PDF',
|
||||
generateDraftData: true,
|
||||
fitToPageEnabled: false,
|
||||
|
@ -111,6 +112,7 @@ 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')
|
||||
|
@ -142,6 +144,30 @@ 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 = [
|
||||
|
@ -197,6 +223,30 @@ 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()}`
|
||||
|
@ -277,16 +327,6 @@ 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')
|
||||
|
@ -303,22 +343,20 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
|
|||
}))
|
||||
}
|
||||
|
||||
WebContents.prototype.getZoomFactor = function (callback) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Must pass function as an argument')
|
||||
const addReplyToEvent = (event) => {
|
||||
event.reply = (...args) => {
|
||||
event.sender.sendToFrame(event.frameId, ...args)
|
||||
}
|
||||
process.nextTick(() => {
|
||||
const zoomFactor = this._getZoomFactor()
|
||||
callback(zoomFactor)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
const addReplyInternalToEvent = (event) => {
|
||||
Object.defineProperty(event, '_replyInternal', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: (...args) => {
|
||||
event.sender._sendToFrameInternal(event.frameId, ...args)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const safeProtocols = new Set([
|
||||
|
@ -337,27 +375,38 @@ WebContents.prototype._init = function () {
|
|||
// The navigation controller.
|
||||
NavigationController.call(this, this)
|
||||
|
||||
// Every remote callback from renderer process would add a listenter to the
|
||||
// render-view-deleted event, so ignore the listenters warning.
|
||||
// Every remote callback from renderer process would add a listener to the
|
||||
// render-view-deleted event, so ignore the listeners 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]) {
|
||||
this.on('-ipc-message', function (event, [channel, ...args]) {
|
||||
addReplyToEvent(event)
|
||||
this.emit('ipc-message', 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) {
|
||||
|
@ -365,6 +414,7 @@ WebContents.prototype._init = function () {
|
|||
},
|
||||
get: function () {}
|
||||
})
|
||||
addReplyInternalToEvent(event)
|
||||
ipcMainInternal.emit(channel, event, ...args)
|
||||
})
|
||||
|
||||
|
@ -381,6 +431,7 @@ WebContents.prototype._init = function () {
|
|||
})
|
||||
|
||||
const forwardedEvents = [
|
||||
'desktop-capturer-get-sources',
|
||||
'remote-require',
|
||||
'remote-get-global',
|
||||
'remote-get-builtin',
|
||||
|
@ -407,8 +458,8 @@ WebContents.prototype._init = function () {
|
|||
|
||||
// Handle window.open for BrowserWindow and BrowserView.
|
||||
if (['browserView', 'window'].includes(this.getType())) {
|
||||
// Make new windows requested by links behave like "window.open"
|
||||
this.webContents.on('-new-window', (event, url, frameName, disposition,
|
||||
// Make new windows requested by links behave like "window.open".
|
||||
this.on('-new-window', (event, url, frameName, disposition,
|
||||
additionalFeatures, postData,
|
||||
referrer) => {
|
||||
const options = {
|
||||
|
@ -421,42 +472,16 @@ WebContents.prototype._init = function () {
|
|||
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')
|
||||
// "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') || !urlFrameName) {
|
||||
disposition !== 'background-tab')) {
|
||||
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,
|
||||
|
@ -477,6 +502,8 @@ WebContents.prototype._init = function () {
|
|||
// JavaScript wrapper of Debugger.
|
||||
const { Debugger } = process.atomBinding('debugger')
|
||||
|
||||
Debugger.prototype.sendCommand = deprecate.promisify(Debugger.prototype.sendCommand)
|
||||
|
||||
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)
|
||||
|
||||
// Public APIs.
|
||||
|
|
|
@ -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.sender._sendInternal(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, result)
|
||||
event._replyInternal(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, result)
|
||||
})
|
||||
resultID++
|
||||
})
|
||||
|
@ -196,7 +196,7 @@ 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.sender._sendInternal(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, result)
|
||||
event._replyInternal(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, result)
|
||||
})
|
||||
resultID++
|
||||
})
|
||||
|
@ -298,7 +298,7 @@ const loadDevToolsExtensions = function (win, manifests) {
|
|||
extensionInfoArray.forEach((extension) => {
|
||||
win.devToolsWebContents._grantOriginAccess(extension.startPage)
|
||||
})
|
||||
win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
|
||||
win.devToolsWebContents.executeJavaScript(`InspectorFrontendAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
|
||||
}
|
||||
|
||||
app.on('web-contents-created', function (event, webContents) {
|
||||
|
|
46
electronasar/development/browser/crash-reporter-init.js
Normal file
46
electronasar/development/browser/crash-reporter-init.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
'use strict'
|
||||
|
||||
const { app } = require('electron')
|
||||
const cp = require('child_process')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
|
||||
const getTempDirectory = function () {
|
||||
try {
|
||||
return app.getPath('temp')
|
||||
} catch (error) {
|
||||
return os.tmpdir()
|
||||
}
|
||||
}
|
||||
|
||||
exports.crashReporterInit = function (options) {
|
||||
const productName = options.productName || app.getName()
|
||||
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`)
|
||||
let crashServicePid
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const env = {
|
||||
ELECTRON_INTERNAL_CRASH_SERVICE: 1
|
||||
}
|
||||
const args = [
|
||||
'--reporter-url=' + options.submitURL,
|
||||
'--application-name=' + productName,
|
||||
'--crashes-directory=' + crashesDirectory,
|
||||
'--v=1'
|
||||
]
|
||||
|
||||
const crashServiceProcess = cp.spawn(process.helperExecPath, args, {
|
||||
env,
|
||||
detached: true
|
||||
})
|
||||
|
||||
crashServicePid = crashServiceProcess.pid
|
||||
}
|
||||
|
||||
return {
|
||||
productName,
|
||||
crashesDirectory,
|
||||
crashServicePid,
|
||||
appVersion: app.getVersion()
|
||||
}
|
||||
}
|
58
electronasar/development/browser/default-menu.js
Normal file
58
electronasar/development/browser/default-menu.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
'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
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
'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)
|
||||
|
||||
|
@ -11,32 +13,40 @@ 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, 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
|
||||
}
|
||||
|
||||
const request = {
|
||||
id,
|
||||
options: {
|
||||
captureWindow,
|
||||
captureScreen,
|
||||
thumbnailSize
|
||||
thumbnailSize,
|
||||
fetchWindowIcons
|
||||
},
|
||||
webContents: event.sender
|
||||
event
|
||||
}
|
||||
requestsQueue.push(request)
|
||||
if (requestsQueue.length === 1) {
|
||||
desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize)
|
||||
desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons)
|
||||
}
|
||||
|
||||
// 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.webContents = null
|
||||
request.event = null
|
||||
})
|
||||
})
|
||||
|
||||
desktopCapturer.emit = (event, name, sources) => {
|
||||
desktopCapturer.emit = (event, name, sources, fetchWindowIcons) => {
|
||||
// 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 => {
|
||||
|
@ -44,20 +54,21 @@ desktopCapturer.emit = (event, name, sources) => {
|
|||
id: source.id,
|
||||
name: source.name,
|
||||
thumbnail: source.thumbnail.toDataURL(),
|
||||
display_id: source.display_id
|
||||
display_id: source.display_id,
|
||||
appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null
|
||||
}
|
||||
})
|
||||
|
||||
if (handledWebContents) {
|
||||
handledWebContents._sendInternal(capturerResult(handledRequest.id), result)
|
||||
if (handledRequest.event) {
|
||||
handledRequest.event._replyInternal(capturerResult(handledRequest.id), result)
|
||||
}
|
||||
|
||||
// Check the queue to see whether there is another identical request & handle
|
||||
requestsQueue.forEach(request => {
|
||||
const webContents = request.webContents
|
||||
const event = request.event
|
||||
if (deepEqual(handledRequest.options, request.options)) {
|
||||
if (webContents) {
|
||||
webContents._sendInternal(capturerResult(request.id), result)
|
||||
if (event) {
|
||||
event._replyInternal(capturerResult(request.id), result)
|
||||
}
|
||||
} else {
|
||||
unhandledRequestsQueue.push(request)
|
||||
|
@ -67,7 +78,7 @@ desktopCapturer.emit = (event, name, sources) => {
|
|||
|
||||
// If the requestsQueue is not empty, start a new request handling.
|
||||
if (requestsQueue.length > 0) {
|
||||
const { captureWindow, captureScreen, thumbnailSize } = requestsQueue[0].options
|
||||
return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize)
|
||||
const { captureWindow, captureScreen, thumbnailSize, fetchWindowIcons } = requestsQueue[0].options
|
||||
return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,14 +163,6 @@ 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
|
||||
}
|
||||
|
@ -189,7 +181,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
|
|||
|
||||
const oldGuestInstance = guestInstances[oldGuestInstanceId]
|
||||
if (oldGuestInstance) {
|
||||
oldGuestInstance.guest.destroy()
|
||||
oldGuestInstance.guest.detachFromOuterFrame()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,9 +210,10 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
|
|||
const webPreferences = {
|
||||
guestInstanceId: guestInstanceId,
|
||||
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
|
||||
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
|
||||
enableRemoteModule: params.enableremotemodule,
|
||||
plugins: params.plugins,
|
||||
zoomFactor: embedder._getZoomFactor(),
|
||||
zoomFactor: embedder.getZoomFactor(),
|
||||
webSecurity: !params.disablewebsecurity,
|
||||
enableBlinkFeatures: params.blinkfeatures,
|
||||
disableBlinkFeatures: params.disableblinkfeatures
|
||||
|
@ -254,7 +247,8 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
|
|||
['nativeWindowOpen', true],
|
||||
['nodeIntegration', false],
|
||||
['enableRemoteModule', false],
|
||||
['sandbox', true]
|
||||
['sandbox', true],
|
||||
['nodeIntegrationInSubFrames', false]
|
||||
])
|
||||
|
||||
// Inherit certain option values from embedder
|
||||
|
@ -340,8 +334,8 @@ const isWebViewTagEnabledCache = new WeakMap()
|
|||
|
||||
const isWebViewTagEnabled = function (contents) {
|
||||
if (!isWebViewTagEnabledCache.has(contents)) {
|
||||
const value = contents.getLastWebPreferences().webviewTag
|
||||
isWebViewTagEnabledCache.set(contents, value)
|
||||
const webPreferences = contents.getLastWebPreferences() || {}
|
||||
isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag)
|
||||
}
|
||||
|
||||
return isWebViewTagEnabledCache.get(contents)
|
||||
|
@ -358,7 +352,7 @@ const handleMessage = function (channel, handler) {
|
|||
}
|
||||
|
||||
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
|
||||
event.sender._sendInternal(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
|
||||
event._replyInternal(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
|
||||
})
|
||||
|
||||
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) {
|
||||
|
@ -368,7 +362,7 @@ 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.destroy()
|
||||
guest.detachFromOuterFrame()
|
||||
} catch (error) {
|
||||
console.error(`Guest destroy failed: ${error}`)
|
||||
}
|
||||
|
@ -408,7 +402,7 @@ handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, request
|
|||
}, error => {
|
||||
return [errorUtils.serialize(error)]
|
||||
}).then(responseArgs => {
|
||||
event.sender._sendInternal(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, ...responseArgs)
|
||||
event._replyInternal(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, ...responseArgs)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -449,3 +443,4 @@ const getEmbedder = function (guestInstanceId) {
|
|||
}
|
||||
|
||||
exports.getGuestForWebContents = getGuestForWebContents
|
||||
exports.isWebViewTagEnabled = isWebViewTagEnabled
|
||||
|
|
|
@ -16,7 +16,8 @@ const inheritedWebPreferences = new Map([
|
|||
['nodeIntegration', false],
|
||||
['enableRemoteModule', false],
|
||||
['sandbox', true],
|
||||
['webviewTag', false]
|
||||
['webviewTag', false],
|
||||
['nodeIntegrationInSubFrames', false]
|
||||
])
|
||||
|
||||
// Copy attribute of |parent| to |child| if it is not defined in |child|.
|
||||
|
@ -90,9 +91,9 @@ const setupGuest = function (embedder, frameName, guest, options) {
|
|||
}
|
||||
const closedByUser = function () {
|
||||
embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
|
||||
embedder.removeListener('render-view-deleted', closedByEmbedder)
|
||||
embedder.removeListener('current-render-view-deleted', closedByEmbedder)
|
||||
}
|
||||
embedder.once('render-view-deleted', closedByEmbedder)
|
||||
embedder.once('current-render-view-deleted', closedByEmbedder)
|
||||
guest.once('closed', closedByUser)
|
||||
if (frameName) {
|
||||
frameToGuest.set(frameName, guest)
|
||||
|
@ -118,28 +119,12 @@ const createGuest = function (embedder, url, referrer, frameName, options, postD
|
|||
}
|
||||
|
||||
guest = new BrowserWindow(options)
|
||||
if (!options.webContents || url !== 'about:blank') {
|
||||
if (!options.webContents) {
|
||||
// We should not call `loadURL` if the window was constructed from an
|
||||
// existing webContents(window.open in a sandboxed renderer) and if the url
|
||||
// is not 'about:blank'.
|
||||
// existing webContents (window.open in a sandboxed renderer).
|
||||
//
|
||||
// Navigating to the url when creating the window from an existing
|
||||
// webContents would not be necessary(it will navigate there anyway), but
|
||||
// apparently there's a bug that allows the child window to be scripted by
|
||||
// the opener, even when the child window is from another origin.
|
||||
//
|
||||
// That's why the second condition(url !== "about:blank") is required: to
|
||||
// force `OverrideSiteInstanceForNavigation` to be called and consequently
|
||||
// spawn a new renderer if the new window is targeting a different origin.
|
||||
//
|
||||
// If the URL is "about:blank", then it is very likely that the opener just
|
||||
// wants to synchronously script the popup, for example:
|
||||
//
|
||||
// let popup = window.open()
|
||||
// popup.document.body.write('<h1>hello</h1>')
|
||||
//
|
||||
// The above code would not work if a navigation to "about:blank" is done
|
||||
// here, since the window would be cleared of all changes in the next tick.
|
||||
// webContents is not necessary (it will navigate there anyway).
|
||||
const loadOptions = {
|
||||
httpReferrer: referrer
|
||||
}
|
||||
|
|
|
@ -184,5 +184,17 @@ 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)
|
||||
|
|
|
@ -63,9 +63,66 @@ 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 '${typeof url === 'string' ? url.substr(0, 2048) : 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)
|
||||
return this.webContents.emit('load-url', url, options)
|
||||
this.webContents.emit('load-url', url, options)
|
||||
return p
|
||||
}
|
||||
|
||||
NavigationController.prototype.getURL = function () {
|
|
@ -1,16 +1,14 @@
|
|||
'use strict'
|
||||
|
||||
const { spawn } = require('child_process')
|
||||
const electron = require('electron')
|
||||
const { EventEmitter } = require('events')
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const v8Util = process.atomBinding('v8_util')
|
||||
const eventBinding = process.atomBinding('event')
|
||||
|
||||
const { isPromise } = electron
|
||||
|
||||
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
|
||||
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')
|
||||
|
@ -139,7 +137,7 @@ const plainObjectToMeta = function (obj) {
|
|||
}
|
||||
|
||||
// Convert Error into meta data.
|
||||
const exceptionToMeta = function (sender, contextId, error) {
|
||||
const exceptionToMeta = function (error) {
|
||||
return {
|
||||
type: 'exception',
|
||||
value: errorUtils.serialize(error)
|
||||
|
@ -175,7 +173,7 @@ const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => {
|
|||
}
|
||||
|
||||
// Convert array of meta data from renderer into array of real values.
|
||||
const unwrapArgs = function (sender, contextId, args) {
|
||||
const unwrapArgs = function (sender, frameId, contextId, args) {
|
||||
const metaToValue = function (meta) {
|
||||
switch (meta.type) {
|
||||
case 'value':
|
||||
|
@ -183,7 +181,7 @@ const unwrapArgs = function (sender, contextId, args) {
|
|||
case 'remote-object':
|
||||
return objectsRegistry.get(meta.id)
|
||||
case 'array':
|
||||
return unwrapArgs(sender, contextId, meta.value)
|
||||
return unwrapArgs(sender, frameId, contextId, meta.value)
|
||||
case 'buffer':
|
||||
return bufferUtils.metaToBuffer(meta.value)
|
||||
case 'date':
|
||||
|
@ -217,9 +215,11 @@ const unwrapArgs = function (sender, contextId, args) {
|
|||
}
|
||||
|
||||
const callIntoRenderer = function (...args) {
|
||||
let succeed = false
|
||||
if (!sender.isDestroyed()) {
|
||||
sender._sendInternal('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
|
||||
} else {
|
||||
succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
|
||||
}
|
||||
if (!succeed) {
|
||||
removeRemoteListenersAndLogWarning(this, callIntoRenderer)
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ const handleRemoteCommand = function (channel, handler) {
|
|||
try {
|
||||
returnValue = handler(event, contextId, ...args)
|
||||
} catch (error) {
|
||||
returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
returnValue = exceptionToMeta(error)
|
||||
}
|
||||
|
||||
if (returnValue !== undefined) {
|
||||
|
@ -368,7 +368,7 @@ handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, co
|
|||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const constructor = objectsRegistry.get(id)
|
||||
|
||||
if (constructor == null) {
|
||||
|
@ -379,7 +379,7 @@ handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId,
|
|||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const func = objectsRegistry.get(id)
|
||||
|
||||
if (func == null) {
|
||||
|
@ -390,7 +390,7 @@ handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId
|
|||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const object = objectsRegistry.get(id)
|
||||
|
||||
if (object == null) {
|
||||
|
@ -401,7 +401,7 @@ handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, cont
|
|||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
|
@ -412,7 +412,7 @@ handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId,
|
|||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
|
@ -468,46 +468,6 @@ ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
|
|||
event.returnValue = null
|
||||
})
|
||||
|
||||
const getTempDirectory = function () {
|
||||
try {
|
||||
return electron.app.getPath('temp')
|
||||
} catch (error) {
|
||||
return os.tmpdir()
|
||||
}
|
||||
}
|
||||
|
||||
const crashReporterInit = function (options) {
|
||||
const productName = options.productName || electron.app.getName()
|
||||
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`)
|
||||
let crashServicePid
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const env = {
|
||||
ELECTRON_INTERNAL_CRASH_SERVICE: 1
|
||||
}
|
||||
const args = [
|
||||
'--reporter-url=' + options.submitURL,
|
||||
'--application-name=' + productName,
|
||||
'--crashes-directory=' + crashesDirectory,
|
||||
'--v=1'
|
||||
]
|
||||
|
||||
const crashServiceProcess = spawn(process.helperExecPath, args, {
|
||||
env,
|
||||
detached: true
|
||||
})
|
||||
|
||||
crashServicePid = crashServiceProcess.pid
|
||||
}
|
||||
|
||||
return {
|
||||
productName,
|
||||
crashesDirectory,
|
||||
crashServicePid,
|
||||
appVersion: electron.app.getVersion()
|
||||
}
|
||||
}
|
||||
|
||||
const setReturnValue = function (event, getValue) {
|
||||
try {
|
||||
event.returnValue = [null, getValue()]
|
||||
|
@ -532,27 +492,40 @@ ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', function (event, text)
|
|||
setReturnValue(event, () => electron.clipboard.writeFindText(text))
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
|
||||
const preloadPath = event.sender._getPreloadPath()
|
||||
const getPreloadScript = function (preloadPath) {
|
||||
let preloadSrc = null
|
||||
let preloadError = null
|
||||
if (preloadPath) {
|
||||
try {
|
||||
preloadSrc = fs.readFileSync(preloadPath).toString()
|
||||
} catch (err) {
|
||||
preloadError = { stack: err ? err.stack : (new Error(`Failed to load "${preloadPath}"`)).stack }
|
||||
preloadError = errorUtils.serialize(err)
|
||||
}
|
||||
}
|
||||
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 = {
|
||||
preloadSrc,
|
||||
preloadError,
|
||||
preloadScripts: preloadPaths.map(path => getPreloadScript(path)),
|
||||
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
|
||||
versions: process.versions,
|
||||
execPath: process.helperExecPath
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) {
|
||||
event.sender.emit('preload-error', event, preloadPath, errorUtils.deserialize(error))
|
||||
})
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
if (process.platform === 'linux' && process.type === 'renderer') {
|
||||
// On Linux we could not access clipboard in renderer process.
|
||||
const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
|
||||
module.exports = getRemoteForUsage('clipboard').clipboard
|
||||
const { getRemote } = require('@electron/internal/renderer/remote')
|
||||
module.exports = getRemote('clipboard')
|
||||
} else {
|
||||
const clipboard = process.atomBinding('clipboard')
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ const deprecate = {
|
|||
setHandler: (handler) => { deprecationHandler = handler },
|
||||
getHandler: () => deprecationHandler,
|
||||
warn: (oldName, newName) => {
|
||||
return deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`)
|
||||
if (!process.noDeprecation) {
|
||||
deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`)
|
||||
}
|
||||
},
|
||||
log: (message) => {
|
||||
if (typeof deprecationHandler === 'function') {
|
||||
|
@ -33,6 +35,14 @@ const deprecate = {
|
|||
}
|
||||
},
|
||||
|
||||
function: (fn, newName) => {
|
||||
const warn = warnOnce(fn.name, newName)
|
||||
return function () {
|
||||
warn()
|
||||
fn.apply(this, arguments)
|
||||
}
|
||||
},
|
||||
|
||||
event: (emitter, oldName, newName) => {
|
||||
const warn = newName.startsWith('-') /* internal event */
|
||||
? warnOnce(`${oldName} event`)
|
||||
|
@ -67,6 +77,43 @@ 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.length === 2 ? cb(err) : cb()
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
renameProperty: (o, oldName, newName) => {
|
||||
const warn = warnOnce(oldName, newName)
|
||||
|
||||
|
|
|
@ -2,28 +2,16 @@
|
|||
|
||||
const binding = process.atomBinding('crash_reporter')
|
||||
|
||||
const errorUtils = require('@electron/internal/common/error-utils')
|
||||
|
||||
class CrashReporter {
|
||||
contructor () {
|
||||
this.productName = null
|
||||
this.crashesDirectory = null
|
||||
}
|
||||
|
||||
sendSync (channel, ...args) {
|
||||
init (options) {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
invoke (command, ...args) {
|
||||
const [ error, result ] = this.sendSync(command, ...args)
|
||||
|
||||
if (error) {
|
||||
throw errorUtils.deserialize(error)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
start (options) {
|
||||
if (options == null) options = {}
|
||||
|
||||
|
@ -51,7 +39,7 @@ class CrashReporter {
|
|||
throw new Error('submitURL is a required option to crashReporter.start')
|
||||
}
|
||||
|
||||
const ret = this.invoke('ELECTRON_CRASH_REPORTER_INIT', {
|
||||
const ret = this.init({
|
||||
submitURL,
|
||||
productName
|
||||
})
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
// - `features` input string
|
||||
// - `emit` function(key, value) - called for each parsed KV
|
||||
module.exports = function parseFeaturesString (features, emit) {
|
||||
features = `${features}`
|
||||
features = `${features}`.trim()
|
||||
// split the string by ','
|
||||
features.split(/,\s*/).forEach((feature) => {
|
||||
features.split(/\s*,\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*=/)
|
||||
let [key, value] = feature.split(/\s*=\s*/)
|
||||
if (!key) return
|
||||
|
||||
// interpret the value as a boolean, if possible
|
||||
|
|
|
@ -46,6 +46,8 @@ exports.syncMethods = new Set([
|
|||
'downloadURL',
|
||||
'inspectServiceWorker',
|
||||
'showDefinitionForSelection',
|
||||
'getZoomFactor',
|
||||
'getZoomLevel',
|
||||
'setZoomFactor',
|
||||
'setZoomLevel',
|
||||
'sendImeEvent'
|
||||
|
@ -58,8 +60,6 @@ exports.asyncCallbackMethods = new Set([
|
|||
'sendInputEvent',
|
||||
'setLayoutZoomLevelLimits',
|
||||
'setVisualZoomLevelLimits',
|
||||
'getZoomFactor',
|
||||
'getZoomLevel',
|
||||
'print',
|
||||
'printToPDF'
|
||||
])
|
||||
|
|
|
@ -2,10 +2,21 @@
|
|||
|
||||
const CrashReporter = require('@electron/internal/common/crash-reporter')
|
||||
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
|
||||
const errorUtils = require('@electron/internal/common/error-utils')
|
||||
|
||||
const invoke = function (command, ...args) {
|
||||
const [ error, result ] = ipcRenderer.sendSync(command, ...args)
|
||||
|
||||
if (error) {
|
||||
throw errorUtils.deserialize(error)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
class CrashReporterRenderer extends CrashReporter {
|
||||
sendSync (channel, ...args) {
|
||||
return ipcRenderer.sendSync(channel, ...args)
|
||||
init (options) {
|
||||
return invoke('ELECTRON_CRASH_REPORTER_INIT', options)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const { nativeImage } = require('electron')
|
||||
const { nativeImage, deprecate } = require('electron')
|
||||
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
|
||||
|
||||
const includes = [].includes
|
||||
|
@ -17,32 +17,43 @@ function isValid (options) {
|
|||
return Array.isArray(types)
|
||||
}
|
||||
|
||||
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')
|
||||
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
|
||||
}))
|
||||
}
|
||||
|
||||
if (options.thumbnailSize == null) {
|
||||
options.thumbnailSize = {
|
||||
width: 150,
|
||||
height: 150
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
})())
|
||||
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)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
exports.getSources = deprecate.promisify(getSources)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -311,7 +311,9 @@ exports.getCurrentWindow = () => {
|
|||
|
||||
// Get current WebContents object.
|
||||
exports.getCurrentWebContents = () => {
|
||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', contextId))
|
||||
const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'
|
||||
const meta = ipcRenderer.sendSync(command, contextId)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
// Get a global object in browser.
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
'use strict'
|
||||
|
||||
const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
|
||||
module.exports = getRemoteForUsage('screen').screen
|
||||
const { deprecate } = require('electron')
|
||||
|
||||
deprecate.warn(`electron.screen`, `electron.remote.screen`)
|
||||
|
||||
const { getRemote } = require('@electron/internal/renderer/remote')
|
||||
module.exports = getRemote('screen')
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
const { EventEmitter } = require('events')
|
||||
const binding = process.atomBinding('web_frame')
|
||||
const { deprecate } = require('electron')
|
||||
|
||||
class WebFrame extends EventEmitter {
|
||||
constructor (context) {
|
||||
|
@ -47,6 +48,26 @@ class WebFrame extends EventEmitter {
|
|||
get routingId () {
|
||||
return binding._getRoutingId(this.context)
|
||||
}
|
||||
|
||||
// Deprecations
|
||||
// TODO(nitsakh): Remove in 6.0
|
||||
setIsolatedWorldSecurityOrigin (worldId, securityOrigin) {
|
||||
deprecate.warn('webFrame.setIsolatedWorldSecurityOrigin', 'webFrame.setIsolatedWorldInfo')
|
||||
binding.setIsolatedWorldInfo(this.context, worldId, { securityOrigin })
|
||||
}
|
||||
|
||||
setIsolatedWorldContentSecurityPolicy (worldId, csp) {
|
||||
deprecate.warn('webFrame.setIsolatedWorldContentSecurityPolicy', 'webFrame.setIsolatedWorldInfo')
|
||||
binding.setIsolatedWorldInfo(this.context, worldId, {
|
||||
securityOrigin: window.location.origin,
|
||||
csp
|
||||
})
|
||||
}
|
||||
|
||||
setIsolatedWorldHumanReadableName (worldId, name) {
|
||||
deprecate.warn('webFrame.setIsolatedWorldHumanReadableName', 'webFrame.setIsolatedWorldInfo')
|
||||
binding.setIsolatedWorldInfo(this.context, worldId, { name })
|
||||
}
|
||||
}
|
||||
|
||||
// Populate the methods.
|
||||
|
@ -59,7 +80,7 @@ for (const name in binding) {
|
|||
}
|
||||
|
||||
// Helper to return WebFrame or null depending on context.
|
||||
// TODO(zcbenz): Consider returning same WebFrame for the same context.
|
||||
// TODO(zcbenz): Consider returning same WebFrame for the same frame.
|
||||
function getWebFrame (context) {
|
||||
return context ? new WebFrame(context) : null
|
||||
}
|
||||
|
|
|
@ -3,11 +3,15 @@
|
|||
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
|
||||
const { runInThisContext } = require('vm')
|
||||
|
||||
const escapePattern = function (pattern) {
|
||||
return pattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&')
|
||||
}
|
||||
|
||||
// Check whether pattern matches.
|
||||
// https://developer.chrome.com/extensions/match_patterns
|
||||
const matchesPattern = function (pattern) {
|
||||
if (pattern === '<all_urls>') return true
|
||||
const regexp = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`)
|
||||
const regexp = new RegExp(`^${pattern.split('*').map(escapePattern).join('.*')}$`)
|
||||
const url = `${location.protocol}//${location.host}${location.pathname}`
|
||||
return url.match(regexp)
|
||||
}
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
// 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 = require('fs')
|
||||
const path = require('path')
|
||||
const fs = potentiallyRemoteRequire('fs')
|
||||
const path = potentiallyRemoteRequire('path')
|
||||
|
||||
let metadata
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { remote } = require('electron')
|
||||
const { app } = remote
|
||||
const { getRemote, potentiallyRemoteRequire } = require('@electron/internal/renderer/remote')
|
||||
const fs = potentiallyRemoteRequire('fs')
|
||||
const path = potentiallyRemoteRequire('path')
|
||||
const app = getRemote('app')
|
||||
|
||||
const getChromeStoragePath = (storageType, extensionId) => {
|
||||
return path.join(
|
||||
|
|
|
@ -31,68 +31,67 @@ const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
|
|||
require('@electron/internal/renderer/web-frame-init')()
|
||||
|
||||
// Process command line arguments.
|
||||
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 { hasSwitch, getSwitchValue } = process.atomBinding('command_line')
|
||||
|
||||
const parseOption = function (name, defaultValue, converter = value => value) {
|
||||
return hasSwitch(name) ? converter(getSwitchValue(name)) : defaultValue
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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')
|
||||
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)
|
||||
|
||||
// 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)
|
||||
// Inject content scripts.
|
||||
if (process.isMainFrame) {
|
||||
require('@electron/internal/renderer/content-scripts-injector')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -150,27 +149,21 @@ 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.stack || error.message)
|
||||
console.error(`Unable to load preload script: ${preloadScript}`)
|
||||
console.error(`${error}`)
|
||||
|
||||
ipcRenderer.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, errorUtils.serialize(error))
|
||||
}
|
||||
}
|
||||
|
||||
// Warn about security issues
|
||||
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)
|
||||
})
|
||||
if (process.isMainFrame) {
|
||||
require('@electron/internal/renderer/security-warnings')(nodeIntegration)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
'use strict'
|
||||
|
||||
const { getRemote, potentiallyRemoteRequire } = require('@electron/internal/renderer/remote')
|
||||
|
||||
window.onload = function () {
|
||||
// Use menu API to show context menu.
|
||||
window.InspectorFrontendHost.showContextMenuAtPoint = createMenu
|
||||
|
@ -18,7 +20,7 @@ function completeURL (project, path) {
|
|||
}
|
||||
|
||||
window.confirm = function (message, title) {
|
||||
const { dialog } = require('electron').remote
|
||||
const dialog = getRemote('dialog')
|
||||
if (title == null) {
|
||||
title = ''
|
||||
}
|
||||
|
@ -62,8 +64,7 @@ const convertToMenuTemplate = function (items) {
|
|||
}
|
||||
|
||||
const createMenu = function (x, y, items) {
|
||||
const { remote } = require('electron')
|
||||
const { Menu } = remote
|
||||
const Menu = getRemote('Menu')
|
||||
|
||||
let template = convertToMenuTemplate(items)
|
||||
if (useEditMenuItems(x, y, template)) {
|
||||
|
@ -73,7 +74,8 @@ const createMenu = function (x, y, items) {
|
|||
|
||||
// The menu is expected to show asynchronously.
|
||||
setTimeout(function () {
|
||||
menu.popup({ window: remote.getCurrentWindow() })
|
||||
const getCurrentWindow = getRemote('getCurrentWindow')
|
||||
menu.popup({ window: getCurrentWindow() })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -116,7 +118,7 @@ const getEditMenuItems = function () {
|
|||
}
|
||||
|
||||
const showFileChooserDialog = function (callback) {
|
||||
const { dialog } = require('electron').remote
|
||||
const dialog = getRemote('dialog')
|
||||
const files = dialog.showOpenDialog({})
|
||||
if (files != null) {
|
||||
callback(pathToHtml5FileObject(files[0]))
|
||||
|
@ -124,7 +126,7 @@ const showFileChooserDialog = function (callback) {
|
|||
}
|
||||
|
||||
const pathToHtml5FileObject = function (path) {
|
||||
const fs = require('fs')
|
||||
const fs = potentiallyRemoteRequire('fs')
|
||||
const blob = new Blob([fs.readFileSync(path)])
|
||||
blob.name = path
|
||||
return blob
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
'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)
|
|
@ -2,9 +2,24 @@
|
|||
|
||||
const { remote } = require('electron')
|
||||
|
||||
exports.getRemoteForUsage = function (usage) {
|
||||
exports.getRemote = function (name) {
|
||||
if (!remote) {
|
||||
throw new Error(`${usage} requires remote, which is not enabled`)
|
||||
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)
|
||||
}
|
||||
return remote
|
||||
}
|
||||
|
|
|
@ -249,54 +249,6 @@ const warnAboutAllowedPopups = function () {
|
|||
}
|
||||
}
|
||||
|
||||
const warnAboutNodeIntegrationDefault = function (webPreferences) {
|
||||
if (webPreferences && 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 && 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) {
|
||||
return
|
||||
}
|
||||
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
|
||||
|
||||
|
@ -309,9 +261,6 @@ const logSecurityWarnings = function (webPreferences, nodeIntegration) {
|
|||
warnAboutEnableBlinkFeatures(webPreferences)
|
||||
warnAboutInsecureCSP()
|
||||
warnAboutAllowedPopups()
|
||||
warnAboutNodeIntegrationDefault(webPreferences)
|
||||
warnAboutContextIsolationDefault(webPreferences)
|
||||
warnAboutDeprecatedWebviewTagDefault(webPreferences)
|
||||
}
|
||||
|
||||
const getWebPreferences = function () {
|
||||
|
|
|
@ -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')
|
||||
const { WebViewImpl } = require('@electron/internal/renderer/web-view/web-view-impl')
|
||||
const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
|
||||
const errorUtils = require('@electron/internal/common/error-utils')
|
||||
|
||||
|
@ -270,6 +270,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
|
|||
this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
|
||||
|
|
|
@ -7,6 +7,7 @@ module.exports = {
|
|||
ATTRIBUTE_SRC: 'src',
|
||||
ATTRIBUTE_HTTPREFERRER: 'httpreferrer',
|
||||
ATTRIBUTE_NODEINTEGRATION: 'nodeintegration',
|
||||
ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES: 'nodeintegrationinsubframes',
|
||||
ATTRIBUTE_ENABLEREMOTEMODULE: 'enableremotemodule',
|
||||
ATTRIBUTE_PLUGINS: 'plugins',
|
||||
ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity',
|
||||
|
|
111
electronasar/development/renderer/web-view/web-view-element.js
Normal file
111
electronasar/development/renderer/web-view/web-view-element.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
'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_NODEINTEGRATIONINSUBFRAMES,
|
||||
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 }
|
|
@ -24,7 +24,6 @@ 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
|
||||
|
@ -201,38 +200,25 @@ class WebViewImpl {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 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')
|
||||
const internal = v8Util.getHiddenValue(this, 'internal')
|
||||
if (internal) {
|
||||
internal.handleWebviewAttributeMutation(name, oldValue, newValue)
|
||||
if (!internal.guestInstanceId) {
|
||||
internal.createGuestSync()
|
||||
}
|
||||
return getGuestWebContents(internal.guestInstanceId)
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
// Focusing the webview should move page focus to the underlying iframe.
|
||||
WebViewElement.prototype.focus = function () {
|
||||
this.contentWindow.focus()
|
||||
}
|
||||
|
||||
const getGuestInstanceId = function (self) {
|
||||
|
@ -255,7 +241,7 @@ const registerWebViewElement = (window) => {
|
|||
}
|
||||
}
|
||||
for (const method of syncMethods) {
|
||||
proto[method] = createBlockHandler(method)
|
||||
WebViewElement.prototype[method] = createBlockHandler(method)
|
||||
}
|
||||
|
||||
const createNonBlockHandler = function (method) {
|
||||
|
@ -274,7 +260,7 @@ const registerWebViewElement = (window) => {
|
|||
}
|
||||
|
||||
for (const method of asyncCallbackMethods) {
|
||||
proto[method] = createNonBlockHandler(method)
|
||||
WebViewElement.prototype[method] = createNonBlockHandler(method)
|
||||
}
|
||||
|
||||
const createPromiseHandler = function (method) {
|
||||
|
@ -299,48 +285,8 @@ const registerWebViewElement = (window) => {
|
|||
}
|
||||
|
||||
for (const method of asyncPromiseMethods) {
|
||||
proto[method] = createPromiseHandler(method)
|
||||
WebViewElement.prototype[method] = createPromiseHandler(method)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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 }
|
||||
module.exports = { setupAttributes, setupMethods, guestViewInternal, webFrame, WebViewImpl }
|
35
electronasar/development/renderer/web-view/web-view-init.js
Normal file
35
electronasar/development/renderer/web-view/web-view-init.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
'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)
|
||||
}
|
||||
}
|
|
@ -23,10 +23,10 @@
|
|||
// - document.hidden
|
||||
// - document.visibilityState
|
||||
|
||||
const { defineProperty } = Object
|
||||
const { defineProperty, defineProperties } = Object
|
||||
|
||||
// Helper function to resolve relative url.
|
||||
const a = window.top.document.createElement('a')
|
||||
const a = window.document.createElement('a')
|
||||
const resolveURL = function (url) {
|
||||
a.href = url
|
||||
return a.href
|
||||
|
@ -54,12 +54,61 @@ 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 ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'getURL')
|
||||
return location
|
||||
},
|
||||
set: function (url) {
|
||||
url = resolveURL(url)
|
||||
|
|
Loading…
Reference in a new issue