Changes of Linux canary v0.0.84

This commit is contained in:
root 2019-06-20 23:19:08 +02:00
parent 96ce99bd55
commit 32675a586f
98 changed files with 8286 additions and 7901 deletions

View File

@ -1,88 +1,117 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path"); const bindings = process.atomBinding('app')
const electron = require("electron"); const path = require('path')
const events_1 = require("events"); const { app, App } = bindings
const bindings = process.electronBinding('app');
const commandLine = process.electronBinding('command_line');
const { app, App } = bindings;
// Only one app object permitted. // Only one app object permitted.
exports.default = app; module.exports = app
const { deprecate, Menu } = electron;
let dockMenu = null; const electron = require('electron')
const { deprecate, Menu } = electron
const { EventEmitter } = require('events')
let dockMenu = null
// App is an EventEmitter. // App is an EventEmitter.
Object.setPrototypeOf(App.prototype, events_1.EventEmitter.prototype); Object.setPrototypeOf(App.prototype, EventEmitter.prototype)
events_1.EventEmitter.call(app); EventEmitter.call(app)
Object.assign(app, { Object.assign(app, {
// TODO(codebytere): remove in 7.0 setApplicationMenu (menu) {
setApplicationMenu(menu) { return Menu.setApplicationMenu(menu)
return Menu.setApplicationMenu(menu); },
getApplicationMenu () {
return Menu.getApplicationMenu()
},
commandLine: {
appendSwitch (...args) {
const castedArgs = args.map((arg) => {
return typeof arg !== 'string' ? `${arg}` : arg
})
return bindings.appendSwitch(...castedArgs)
}, },
// TODO(codebytere): remove in 7.0 appendArgument (...args) {
getApplicationMenu() { const castedArgs = args.map((arg) => {
return Menu.getApplicationMenu(); return typeof arg !== 'string' ? `${arg}` : arg
}, })
commandLine: { return bindings.appendArgument(...castedArgs)
hasSwitch: (theSwitch) => commandLine.hasSwitch(String(theSwitch)),
getSwitchValue: (theSwitch) => commandLine.getSwitchValue(String(theSwitch)),
appendSwitch: (theSwitch, value) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)),
appendArgument: (arg) => commandLine.appendArgument(String(arg))
},
enableMixedSandbox() {
deprecate.log(`'enableMixedSandbox' is deprecated. Mixed-sandbox mode is now enabled by default. You can safely remove the call to enableMixedSandbox().`);
} }
}); }
// we define this here because it'd be overly complicated to })
// do in native land
Object.defineProperty(app, 'applicationMenu', { const nativeFn = app.getAppMetrics
get() { app.getAppMetrics = () => {
return Menu.getApplicationMenu(); const metrics = nativeFn.call(app)
}, for (const metric of metrics) {
set(menu) { if ('memory' in metric) {
return Menu.setApplicationMenu(menu); deprecate.removeProperty(metric, 'memory')
} }
}); }
return metrics
}
app.isPackaged = (() => { app.isPackaged = (() => {
const execFile = path.basename(process.execPath).toLowerCase(); const execFile = path.basename(process.execPath).toLowerCase()
if (process.platform === 'win32') { if (process.platform === 'win32') {
return execFile !== 'electron.exe'; return execFile !== 'electron.exe'
} }
return execFile !== 'electron'; return execFile !== 'electron'
})(); })()
app._setDefaultAppPaths = (packagePath) => {
// Set the user path according to application's name.
app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
app.setPath('userCache', path.join(app.getPath('cache'), app.getName()));
app.setAppPath(packagePath);
// Add support for --user-data-dir=
const userDataDirFlag = '--user-data-dir=';
const userDataArg = process.argv.find(arg => arg.startsWith(userDataDirFlag));
if (userDataArg) {
const userDataDir = userDataArg.substr(userDataDirFlag.length);
if (path.isAbsolute(userDataDir))
app.setPath('userData', userDataDir);
}
};
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
const setDockMenu = app.dock.setMenu; app.dock = {
app.dock.setMenu = (menu) => { bounce (type = 'informational') {
dockMenu = menu; return bindings.dockBounce(type)
setDockMenu(menu); },
}; cancelBounce: bindings.dockCancelBounce,
app.dock.getMenu = () => dockMenu; downloadFinished: bindings.dockDownloadFinished,
setBadge: bindings.dockSetBadgeText,
getBadge: bindings.dockGetBadgeText,
hide: bindings.dockHide,
show: bindings.dockShow,
isVisible: bindings.dockIsVisible,
setMenu (menu) {
dockMenu = menu
bindings.dockSetMenu(menu)
},
getMenu () {
return dockMenu
},
setIcon: bindings.dockSetIcon
}
} }
if (process.platform === 'linux') {
app.launcher = {
setBadgeCount: bindings.unityLauncherSetBadgeCount,
getBadgeCount: bindings.unityLauncherGetBadgeCount,
isCounterBadgeAvailable: bindings.unityLauncherAvailable,
isUnityRunning: bindings.unityLauncherAvailable
}
}
app.allowNTLMCredentialsForAllDomains = function (allow) {
if (!process.noDeprecations) {
deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains')
}
const domains = allow ? '*' : ''
if (!this.isReady()) {
this.commandLine.appendSwitch('auth-server-whitelist', domains)
} else {
electron.session.defaultSession.allowNTLMCredentialsForDomains(domains)
}
}
// Routes the events to webContents. // Routes the events to webContents.
const events = ['login', 'certificate-error', 'select-client-certificate']; const events = ['login', 'certificate-error', 'select-client-certificate']
for (const name of events) { for (const name of events) {
app.on(name, (event, webContents, ...args) => { app.on(name, (event, webContents, ...args) => {
webContents.emit(name, event, ...args); webContents.emit(name, event, ...args)
}); })
} }
// Function Deprecations
app.getFileIcon = deprecate.promisify(app.getFileIcon);
// Property Deprecations
deprecate.fnToProperty(app, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled');
// Wrappers for native classes. // Wrappers for native classes.
const { DownloadItem } = process.electronBinding('download_item'); const { DownloadItem } = process.atomBinding('download_item')
Object.setPrototypeOf(DownloadItem.prototype, events_1.EventEmitter.prototype); Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype)
//# sourceMappingURL=app.js.map

View File

@ -1,8 +1,7 @@
'use strict'; 'use strict'
if (process.platform === 'win32') { if (process.platform === 'win32') {
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win'); module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win')
} else {
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native')
} }
else {
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native');
}
//# sourceMappingURL=auto-updater.js.map

View File

@ -1,8 +1,10 @@
'use strict'; 'use strict'
const EventEmitter = require('events').EventEmitter;
const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater'); const EventEmitter = require('events').EventEmitter
const { autoUpdater, AutoUpdater } = process.atomBinding('auto_updater')
// AutoUpdater is an EventEmitter. // AutoUpdater is an EventEmitter.
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype); Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype)
EventEmitter.call(autoUpdater); EventEmitter.call(autoUpdater)
module.exports = autoUpdater;
//# sourceMappingURL=auto-updater-native.js.map module.exports = autoUpdater

View File

@ -1,71 +1,74 @@
'use strict'; 'use strict'
const { app } = require('electron');
const { EventEmitter } = require('events'); const { app } = require('electron')
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win'); const { EventEmitter } = require('events')
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win')
class AutoUpdater extends EventEmitter { class AutoUpdater extends EventEmitter {
quitAndInstall() { quitAndInstall () {
if (!this.updateAvailable) { if (!this.updateAvailable) {
return this.emitError('No update available, can\'t quit and install'); return this.emitError('No update available, can\'t quit and install')
}
squirrelUpdate.processStart();
app.quit();
} }
getFeedURL() { squirrelUpdate.processStart()
return this.updateURL; app.quit()
}
getFeedURL () {
return this.updateURL
}
setFeedURL (options) {
let updateURL
if (typeof options === 'object') {
if (typeof options.url === 'string') {
updateURL = options.url
} else {
throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call')
}
} else if (typeof options === 'string') {
updateURL = options
} else {
throw new Error('Expected an options object with a \'url\' property to be provided')
} }
setFeedURL(options) { this.updateURL = updateURL
let updateURL; }
if (typeof options === 'object') {
if (typeof options.url === 'string') { checkForUpdates () {
updateURL = options.url; if (!this.updateURL) {
} return this.emitError('Update URL is not set')
else {
throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call');
}
}
else if (typeof options === 'string') {
updateURL = options;
}
else {
throw new Error('Expected an options object with a \'url\' property to be provided');
}
this.updateURL = updateURL;
} }
checkForUpdates() { if (!squirrelUpdate.supported()) {
if (!this.updateURL) { return this.emitError('Can not find Squirrel')
return this.emitError('Update URL is not set');
}
if (!squirrelUpdate.supported()) {
return this.emitError('Can not find Squirrel');
}
this.emit('checking-for-update');
squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => {
if (error != null) {
return this.emitError(error);
}
if (update == null) {
return this.emit('update-not-available');
}
this.updateAvailable = true;
this.emit('update-available');
squirrelUpdate.update(this.updateURL, (error) => {
if (error != null) {
return this.emitError(error);
}
const { releaseNotes, version } = update;
// Date is not available on Windows, so fake it.
const date = new Date();
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
this.quitAndInstall();
});
});
});
}
// Private: Emit both error object and message, this is to keep compatibility
// with Old APIs.
emitError(message) {
this.emit('error', new Error(message), message);
} }
this.emit('checking-for-update')
squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => {
if (error != null) {
return this.emitError(error)
}
if (update == null) {
return this.emit('update-not-available')
}
this.updateAvailable = true
this.emit('update-available')
squirrelUpdate.update(this.updateURL, (error) => {
if (error != null) {
return this.emitError(error)
}
const { releaseNotes, version } = update
// Date is not available on Windows, so fake it.
const date = new Date()
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
this.quitAndInstall()
})
})
})
}
// Private: Emit both error object and message, this is to keep compatibility
// with Old APIs.
emitError (message) {
this.emit('error', new Error(message), message)
}
} }
module.exports = new AutoUpdater();
//# sourceMappingURL=auto-updater-win.js.map module.exports = new AutoUpdater()

View File

@ -1,107 +1,119 @@
'use strict'; 'use strict'
const fs = require('fs');
const path = require('path'); const fs = require('fs')
const spawn = require('child_process').spawn; const path = require('path')
const spawn = require('child_process').spawn
// i.e. my-app/app-0.1.13/ // i.e. my-app/app-0.1.13/
const appFolder = path.dirname(process.execPath); const appFolder = path.dirname(process.execPath)
// i.e. my-app/Update.exe // i.e. my-app/Update.exe
const updateExe = path.resolve(appFolder, '..', 'Update.exe'); const updateExe = path.resolve(appFolder, '..', 'Update.exe')
const exeName = path.basename(process.execPath); const exeName = path.basename(process.execPath)
let spawnedArgs = []; let spawnedArgs = []
let spawnedProcess; let spawnedProcess
const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]);
const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i])
// Spawn a command and invoke the callback when it completes with an error // Spawn a command and invoke the callback when it completes with an error
// and the output from standard out. // and the output from standard out.
const spawnUpdate = function (args, detached, callback) { const spawnUpdate = function (args, detached, callback) {
let error, errorEmitted, stderr, stdout; let error, errorEmitted, stderr, stdout
try {
// Ensure we don't spawn multiple squirrel processes try {
// Process spawned, same args: Attach events to alread running process // Ensure we don't spawn multiple squirrel processes
// Process spawned, different args: Return with error // Process spawned, same args: Attach events to alread running process
// No process spawned: Spawn new process // Process spawned, different args: Return with error
if (spawnedProcess && !isSameArgs(args)) { // No process spawned: Spawn new process
// Disabled for backwards compatibility: if (spawnedProcess && !isSameArgs(args)) {
// eslint-disable-next-line standard/no-callback-literal // Disabled for backwards compatibility:
return callback(`AutoUpdater process with arguments ${args} is already running`); // eslint-disable-next-line standard/no-callback-literal
} return callback(`AutoUpdater process with arguments ${args} is already running`)
else if (!spawnedProcess) { } else if (!spawnedProcess) {
spawnedProcess = spawn(updateExe, args, { spawnedProcess = spawn(updateExe, args, {
detached: detached, detached: detached,
windowsHide: true windowsHide: true
}); })
spawnedArgs = args || []; spawnedArgs = args || []
}
} }
catch (error1) { } catch (error1) {
error = error1; error = error1
// Shouldn't happen, but still guard it.
process.nextTick(function () { // Shouldn't happen, but still guard it.
return callback(error); process.nextTick(function () {
}); return callback(error)
return; })
return
}
stdout = ''
stderr = ''
spawnedProcess.stdout.on('data', (data) => { stdout += data })
spawnedProcess.stderr.on('data', (data) => { stderr += data })
errorEmitted = false
spawnedProcess.on('error', (error) => {
errorEmitted = true
callback(error)
})
return spawnedProcess.on('exit', function (code, signal) {
spawnedProcess = undefined
spawnedArgs = []
// We may have already emitted an error.
if (errorEmitted) {
return
} }
stdout = '';
stderr = ''; // Process terminated with error.
spawnedProcess.stdout.on('data', (data) => { stdout += data; }); if (code !== 0) {
spawnedProcess.stderr.on('data', (data) => { stderr += data; }); // Disabled for backwards compatibility:
errorEmitted = false; // eslint-disable-next-line standard/no-callback-literal
spawnedProcess.on('error', (error) => { return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`)
errorEmitted = true; }
callback(error);
}); // Success.
return spawnedProcess.on('exit', function (code, signal) { callback(null, stdout)
spawnedProcess = undefined; })
spawnedArgs = []; }
// We may have already emitted an error.
if (errorEmitted) {
return;
}
// Process terminated with error.
if (code !== 0) {
// Disabled for backwards compatibility:
// eslint-disable-next-line standard/no-callback-literal
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`);
}
// Success.
callback(null, stdout);
});
};
// Start an instance of the installed app. // Start an instance of the installed app.
exports.processStart = function () { exports.processStart = function () {
return spawnUpdate(['--processStartAndWait', exeName], true, function () { }); return spawnUpdate(['--processStartAndWait', exeName], true, function () {})
}; }
// Download the releases specified by the URL and write new results to stdout. // Download the releases specified by the URL and write new results to stdout.
exports.checkForUpdate = function (updateURL, callback) { exports.checkForUpdate = function (updateURL, callback) {
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) { return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) {
let ref, ref1, update; let ref, ref1, update
if (error != null) { if (error != null) {
return callback(error); return callback(error)
} }
try { try {
// Last line of output is the JSON details about the releases // Last line of output is the JSON details about the releases
const json = stdout.trim().split('\n').pop(); const json = stdout.trim().split('\n').pop()
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0; update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0
} } catch (jsonError) {
catch (_a) { // Disabled for backwards compatibility:
// Disabled for backwards compatibility: // eslint-disable-next-line standard/no-callback-literal
// eslint-disable-next-line standard/no-callback-literal return callback(`Invalid result:\n${stdout}`)
return callback(`Invalid result:\n${stdout}`); }
} return callback(null, update)
return callback(null, update); })
}); }
};
// Update the application to the latest remote version specified by URL. // Update the application to the latest remote version specified by URL.
exports.update = function (updateURL, callback) { exports.update = function (updateURL, callback) {
return spawnUpdate(['--update', updateURL], false, callback); return spawnUpdate(['--update', updateURL], false, callback)
}; }
// Is the Update.exe installed with the current application? // Is the Update.exe installed with the current application?
exports.supported = function () { exports.supported = function () {
try { try {
fs.accessSync(updateExe, fs.R_OK); fs.accessSync(updateExe, fs.R_OK)
return true; return true
} } catch (error) {
catch (_a) { return false
return false; }
} }
};
//# sourceMappingURL=squirrel-update-win.js.map

View File

@ -1,13 +1,16 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
const { BrowserView } = process.electronBinding('browser_view'); const { EventEmitter } = require('events')
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype); const { BrowserView } = process.atomBinding('browser_view')
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype)
BrowserView.fromWebContents = (webContents) => { BrowserView.fromWebContents = (webContents) => {
for (const view of BrowserView.getAllViews()) { for (const view of BrowserView.getAllViews()) {
if (view.webContents.equal(webContents)) if (view.webContents.equal(webContents)) return view
return view; }
}
return null; return null
}; }
module.exports = BrowserView;
//# sourceMappingURL=browser-view.js.map module.exports = BrowserView

View File

@ -1,171 +1,191 @@
'use strict'; 'use strict'
const electron = require('electron');
const { WebContentsView, TopLevelWindow } = electron; const electron = require('electron')
const { BrowserWindow } = process.electronBinding('window'); const { WebContentsView, TopLevelWindow } = electron
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype); const { BrowserWindow } = process.atomBinding('window')
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype)
BrowserWindow.prototype._init = function () { BrowserWindow.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
TopLevelWindow.prototype._init.call(this); TopLevelWindow.prototype._init.call(this)
// Avoid recursive require.
const { app } = electron; // Avoid recursive require.
// Create WebContentsView. const { app } = electron
this.setContentView(new WebContentsView(this.webContents));
const nativeSetBounds = this.setBounds; // Create WebContentsView.
this.setBounds = (bounds, ...opts) => { this.setContentView(new WebContentsView(this.webContents))
bounds = Object.assign({}, this.getBounds(), bounds);
nativeSetBounds.call(this, bounds, ...opts); const nativeSetBounds = this.setBounds
}; this.setBounds = (bounds, ...opts) => {
// window.resizeTo(...) bounds = {
// window.moveTo(...) ...this.getBounds(),
this.webContents.on('move', (event, size) => { ...bounds
this.setBounds(size);
});
// Hide the auto-hide menu when webContents is focused.
this.webContents.on('activate', () => {
if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) {
this.setMenuBarVisibility(false);
}
});
// Change window title to page title.
this.webContents.on('page-title-updated', (event, title, ...args) => {
// Route the event to BrowserWindow.
this.emit('page-title-updated', event, title, ...args);
if (!this.isDestroyed() && !event.defaultPrevented)
this.setTitle(title);
});
// Sometimes the webContents doesn't get focus when window is shown, so we
// have to force focusing on webContents in this case. The safest way is to
// focus it when we first start to load URL, if we do it earlier it won't
// have effect, if we do it later we might move focus in the page.
//
// Though this hack is only needed on macOS when the app is launched from
// Finder, we still do it on all platforms in case of other bugs we don't
// know.
this.webContents.once('load-url', function () {
this.focus();
});
// Redirect focus/blur event to app instance too.
this.on('blur', (event) => {
app.emit('browser-window-blur', event, this);
});
this.on('focus', (event) => {
app.emit('browser-window-focus', event, this);
});
// Subscribe to visibilityState changes and pass to renderer process.
let isVisible = this.isVisible() && !this.isMinimized();
const visibilityChanged = () => {
const newState = this.isVisible() && !this.isMinimized();
if (isVisible !== newState) {
isVisible = newState;
const visibilityState = isVisible ? 'visible' : 'hidden';
this.webContents.emit('-window-visibility-change', visibilityState);
}
};
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
for (const event of visibilityEvents) {
this.on(event, visibilityChanged);
} }
// Notify the creation of the window. nativeSetBounds.call(this, bounds, ...opts)
app.emit('browser-window-created', {}, this); }
Object.defineProperty(this, 'devToolsWebContents', {
enumerable: true, // window.resizeTo(...)
configurable: false, // window.moveTo(...)
get() { this.webContents.on('move', (event, size) => {
return this.webContents.devToolsWebContents; this.setBounds(size)
} })
});
}; // Hide the auto-hide menu when webContents is focused.
this.webContents.on('activate', () => {
if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) {
this.setMenuBarVisibility(false)
}
})
// Change window title to page title.
this.webContents.on('page-title-updated', (event, title) => {
// Route the event to BrowserWindow.
this.emit('page-title-updated', event, title)
if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title)
})
// Sometimes the webContents doesn't get focus when window is shown, so we
// have to force focusing on webContents in this case. The safest way is to
// focus it when we first start to load URL, if we do it earlier it won't
// have effect, if we do it later we might move focus in the page.
//
// Though this hack is only needed on macOS when the app is launched from
// Finder, we still do it on all platforms in case of other bugs we don't
// know.
this.webContents.once('load-url', function () {
this.focus()
})
// Redirect focus/blur event to app instance too.
this.on('blur', (event) => {
app.emit('browser-window-blur', event, this)
})
this.on('focus', (event) => {
app.emit('browser-window-focus', event, this)
})
// Subscribe to visibilityState changes and pass to renderer process.
let isVisible = this.isVisible() && !this.isMinimized()
const visibilityChanged = () => {
const newState = this.isVisible() && !this.isMinimized()
if (isVisible !== newState) {
isVisible = newState
const visibilityState = isVisible ? 'visible' : 'hidden'
this.webContents.emit('-window-visibility-change', visibilityState)
}
}
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']
for (const event of visibilityEvents) {
this.on(event, visibilityChanged)
}
// Notify the creation of the window.
app.emit('browser-window-created', {}, this)
Object.defineProperty(this, 'devToolsWebContents', {
enumerable: true,
configurable: false,
get () {
return this.webContents.devToolsWebContents
}
})
}
const isBrowserWindow = (win) => { const isBrowserWindow = (win) => {
return win && win.constructor.name === 'BrowserWindow'; return win && win.constructor.name === 'BrowserWindow'
}; }
BrowserWindow.fromId = (id) => { BrowserWindow.fromId = (id) => {
const win = TopLevelWindow.fromId(id); const win = TopLevelWindow.fromId(id)
return isBrowserWindow(win) ? win : null; return isBrowserWindow(win) ? win : null
}; }
BrowserWindow.getAllWindows = () => { BrowserWindow.getAllWindows = () => {
return TopLevelWindow.getAllWindows().filter(isBrowserWindow); return TopLevelWindow.getAllWindows().filter(isBrowserWindow)
}; }
BrowserWindow.getFocusedWindow = () => { BrowserWindow.getFocusedWindow = () => {
for (const window of BrowserWindow.getAllWindows()) { for (const window of BrowserWindow.getAllWindows()) {
if (window.isFocused() || window.isDevToolsFocused()) if (window.isFocused() || window.isDevToolsFocused()) return window
return window; }
} return null
return null; }
};
BrowserWindow.fromWebContents = (webContents) => { BrowserWindow.fromWebContents = (webContents) => {
for (const window of BrowserWindow.getAllWindows()) { for (const window of BrowserWindow.getAllWindows()) {
if (window.webContents.equal(webContents)) if (window.webContents.equal(webContents)) return window
return window; }
} }
};
BrowserWindow.fromBrowserView = (browserView) => { BrowserWindow.fromBrowserView = (browserView) => {
for (const window of BrowserWindow.getAllWindows()) { for (const window of BrowserWindow.getAllWindows()) {
if (window.getBrowserView() === browserView) if (window.getBrowserView() === browserView) return window
return window; }
}
return null; return null
}; }
BrowserWindow.fromDevToolsWebContents = (webContents) => { BrowserWindow.fromDevToolsWebContents = (webContents) => {
for (const window of BrowserWindow.getAllWindows()) { for (const window of BrowserWindow.getAllWindows()) {
const { devToolsWebContents } = window; const { devToolsWebContents } = window
if (devToolsWebContents != null && devToolsWebContents.equal(webContents)) { if (devToolsWebContents != null && devToolsWebContents.equal(webContents)) {
return window; return window
}
} }
}; }
}
// Helpers. // Helpers.
Object.assign(BrowserWindow.prototype, { Object.assign(BrowserWindow.prototype, {
loadURL(...args) { loadURL (...args) {
return this.webContents.loadURL(...args); return this.webContents.loadURL(...args)
}, },
getURL(...args) { getURL (...args) {
return this.webContents.getURL(); return this.webContents.getURL()
}, },
loadFile(...args) { loadFile (...args) {
return this.webContents.loadFile(...args); return this.webContents.loadFile(...args)
}, },
reload(...args) { reload (...args) {
return this.webContents.reload(...args); return this.webContents.reload(...args)
}, },
send(...args) { send (...args) {
return this.webContents.send(...args); return this.webContents.send(...args)
}, },
openDevTools(...args) { openDevTools (...args) {
return this.webContents.openDevTools(...args); return this.webContents.openDevTools(...args)
}, },
closeDevTools() { closeDevTools () {
return this.webContents.closeDevTools(); return this.webContents.closeDevTools()
}, },
isDevToolsOpened() { isDevToolsOpened () {
return this.webContents.isDevToolsOpened(); return this.webContents.isDevToolsOpened()
}, },
isDevToolsFocused() { isDevToolsFocused () {
return this.webContents.isDevToolsFocused(); return this.webContents.isDevToolsFocused()
}, },
toggleDevTools() { toggleDevTools () {
return this.webContents.toggleDevTools(); return this.webContents.toggleDevTools()
}, },
inspectElement(...args) { inspectElement (...args) {
return this.webContents.inspectElement(...args); return this.webContents.inspectElement(...args)
}, },
inspectSharedWorker() { inspectServiceWorker () {
return this.webContents.inspectSharedWorker(); return this.webContents.inspectServiceWorker()
}, },
inspectServiceWorker() { showDefinitionForSelection () {
return this.webContents.inspectServiceWorker(); return this.webContents.showDefinitionForSelection()
}, },
showDefinitionForSelection() { capturePage (...args) {
return this.webContents.showDefinitionForSelection(); return this.webContents.capturePage(...args)
}, },
capturePage(...args) { setTouchBar (touchBar) {
return this.webContents.capturePage(...args); electron.TouchBar._setOnWindow(touchBar, this)
}, },
setTouchBar(touchBar) { setBackgroundThrottling (allowed) {
electron.TouchBar._setOnWindow(touchBar, this); this.webContents.setBackgroundThrottling(allowed)
}, }
setBackgroundThrottling(allowed) { })
this.webContents.setBackgroundThrottling(allowed);
} module.exports = BrowserWindow
});
module.exports = BrowserWindow;
//# sourceMappingURL=browser-window.js.map

View File

@ -1,12 +1,3 @@
'use strict'; 'use strict'
const { deprecate } = require('electron');
const contentTracing = process.electronBinding('content_tracing'); module.exports = process.atomBinding('content_tracing')
contentTracing.getCategories = deprecate.promisify(contentTracing.getCategories);
contentTracing.startRecording = deprecate.promisify(contentTracing.startRecording);
contentTracing.stopRecording = deprecate.promisify(contentTracing.stopRecording);
contentTracing.getTraceBufferUsage = deprecate.promisifyMultiArg(contentTracing.getTraceBufferUsage
// convertPromiseValue: Temporarily disabled until it's used
/* (value) => [value.paths, value.bookmarks] */
);
module.exports = contentTracing;
//# sourceMappingURL=content-tracing.js.map

View File

@ -1,10 +1,14 @@
'use strict'; 'use strict'
const CrashReporter = require('@electron/internal/common/crash-reporter');
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init'); const CrashReporter = require('@electron/internal/common/crash-reporter')
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
class CrashReporterMain extends CrashReporter { class CrashReporterMain extends CrashReporter {
init(options) { sendSync (channel, ...args) {
return crashReporterInit(options); const event = {}
} ipcMain.emit(channel, event, ...args)
return event.returnValue
}
} }
module.exports = new CrashReporterMain();
//# sourceMappingURL=crash-reporter.js.map module.exports = new CrashReporterMain()

View File

@ -1,184 +1,312 @@
'use strict'; 'use strict'
const { app, BrowserWindow, deprecate } = require('electron');
const binding = process.electronBinding('dialog'); const { app, BrowserWindow } = require('electron')
const v8Util = process.electronBinding('v8_util'); const binding = process.atomBinding('dialog')
const v8Util = process.atomBinding('v8_util')
const fileDialogProperties = { const fileDialogProperties = {
openFile: 1 << 0, openFile: 1 << 0,
openDirectory: 1 << 1, openDirectory: 1 << 1,
multiSelections: 1 << 2, multiSelections: 1 << 2,
createDirectory: 1 << 3, createDirectory: 1 << 3,
showHiddenFiles: 1 << 4, showHiddenFiles: 1 << 4,
promptToCreate: 1 << 5, promptToCreate: 1 << 5,
noResolveAliases: 1 << 6, noResolveAliases: 1 << 6,
treatPackageAsDirectory: 1 << 7 treatPackageAsDirectory: 1 << 7
}; }
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
const messageBoxOptions = {
noLink: 1 << 0
}
const parseArgs = function (window, options, callback, ...args) {
if (window != null && window.constructor !== BrowserWindow) {
// Shift.
[callback, options, window] = [options, window, null]
}
if ((callback == null) && typeof options === 'function') {
// Shift.
[callback, options] = [options, null]
}
// Fallback to using very last argument as the callback function
const lastArgument = args[args.length - 1]
if ((callback == null) && typeof lastArgument === 'function') {
callback = lastArgument
}
return [window, options, callback]
}
const normalizeAccessKey = (text) => { const normalizeAccessKey = (text) => {
if (typeof text !== 'string') if (typeof text !== 'string') return text
return text;
// macOS does not have access keys so remove single ampersands // macOS does not have access keys so remove single ampersands
// and replace double ampersands with a single ampersand // and replace double ampersands with a single ampersand
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
return text.replace(/&(&?)/g, '$1'); return text.replace(/&(&?)/g, '$1')
} }
// Linux uses a single underscore as an access key prefix so escape
// existing single underscores with a second underscore, replace double // Linux uses a single underscore as an access key prefix so escape
// ampersands with a single ampersand, and replace a single ampersand with // existing single underscores with a second underscore, replace double
// a single underscore // ampersands with a single ampersand, and replace a single ampersand with
if (process.platform === 'linux') { // a single underscore
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => { if (process.platform === 'linux') {
if (after === '&') return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
return after; if (after === '&') return after
return `_${after}`; return `_${after}`
}); })
} }
return text;
}; return text
}
const checkAppInitialized = function () { const checkAppInitialized = function () {
if (!app.isReady()) { if (!app.isReady()) {
throw new Error('dialog module can only be used after app is ready'); throw new Error('dialog module can only be used after app is ready')
} }
}; }
const saveDialog = (sync, window, options) => {
checkAppInitialized(); module.exports = {
if (window && window.constructor !== BrowserWindow) { showOpenDialog: function (...args) {
options = window; checkAppInitialized()
window = null;
} let [window, options, callback] = parseArgs(...args)
if (options == null)
options = { title: 'Save' };
const { buttonLabel = '', defaultPath = '', filters = [], title = '', message = '', securityScopedBookmarks = false, nameFieldLabel = '', showsTagField = true } = options;
if (typeof title !== 'string')
throw new TypeError('Title must be a string');
if (typeof buttonLabel !== 'string')
throw new TypeError('Button label must be a string');
if (typeof defaultPath !== 'string')
throw new TypeError('Default path must be a string');
if (typeof message !== 'string')
throw new TypeError('Message must be a string');
if (typeof nameFieldLabel !== 'string')
throw new TypeError('Name field label must be a string');
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window };
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings);
};
const openDialog = (sync, window, options) => {
checkAppInitialized();
if (window && window.constructor !== BrowserWindow) {
options = window;
window = null;
}
if (options == null) { if (options == null) {
options = { options = {
title: 'Open', title: 'Open',
properties: ['openFile'] properties: ['openFile']
}; }
} }
const { buttonLabel = '', defaultPath = '', filters = [], properties = ['openFile'], title = '', message = '', securityScopedBookmarks = false } = options;
if (!Array.isArray(properties)) let { buttonLabel, defaultPath, filters, properties, title, message, securityScopedBookmarks = false } = options
throw new TypeError('Properties must be an array');
let dialogProperties = 0; if (properties == null) {
properties = ['openFile']
} else if (!Array.isArray(properties)) {
throw new TypeError('Properties must be an array')
}
let dialogProperties = 0
for (const prop in fileDialogProperties) { for (const prop in fileDialogProperties) {
if (properties.includes(prop)) { if (properties.includes(prop)) {
dialogProperties |= fileDialogProperties[prop]; dialogProperties |= fileDialogProperties[prop]
} }
} }
if (typeof title !== 'string')
throw new TypeError('Title must be a string'); if (title == null) {
if (typeof buttonLabel !== 'string') title = ''
throw new TypeError('Button label must be a string'); } else if (typeof title !== 'string') {
if (typeof defaultPath !== 'string') throw new TypeError('Title must be a string')
throw new TypeError('Default path must be a string');
if (typeof message !== 'string')
throw new TypeError('Message must be a string');
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window };
settings.properties = dialogProperties;
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings);
};
const messageBox = (sync, window, options) => {
checkAppInitialized();
if (window && window.constructor !== BrowserWindow) {
options = window;
window = null;
} }
if (options == null)
options = { type: 'none' }; if (buttonLabel == null) {
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']; buttonLabel = ''
const messageBoxOptions = { noLink: 1 << 0 }; } else if (typeof buttonLabel !== 'string') {
let { buttons = [], cancelId, checkboxLabel = '', checkboxChecked, defaultId = -1, detail = '', icon = null, message = '', title = '', type = 'none' } = options; throw new TypeError('Button label must be a string')
const messageBoxType = messageBoxTypes.indexOf(type); }
if (messageBoxType === -1)
throw new TypeError('Invalid message box type'); if (defaultPath == null) {
if (!Array.isArray(buttons)) defaultPath = ''
throw new TypeError('Buttons must be an array'); } else if (typeof defaultPath !== 'string') {
if (options.normalizeAccessKeys) throw new TypeError('Default path must be a string')
buttons = buttons.map(normalizeAccessKey); }
if (typeof title !== 'string')
throw new TypeError('Title must be a string'); if (filters == null) {
if (typeof message !== 'string') filters = []
throw new TypeError('Message must be a string'); }
if (typeof detail !== 'string')
throw new TypeError('Detail must be a string'); if (message == null) {
if (typeof checkboxLabel !== 'string') message = ''
throw new TypeError('checkboxLabel must be a string'); } else if (typeof message !== 'string') {
checkboxChecked = !!checkboxChecked; throw new TypeError('Message must be a string')
}
const wrappedCallback = typeof callback === 'function' ? function (success, result, bookmarkData) {
return success ? callback(result, bookmarkData) : callback()
} : null
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }
settings.properties = dialogProperties
return binding.showOpenDialog(settings, wrappedCallback)
},
showSaveDialog: function (...args) {
checkAppInitialized()
let [window, options, callback] = parseArgs(...args)
if (options == null) {
options = {
title: 'Save'
}
}
let { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks = false, nameFieldLabel, showsTagField } = options
if (title == null) {
title = ''
} else if (typeof title !== 'string') {
throw new TypeError('Title must be a string')
}
if (buttonLabel == null) {
buttonLabel = ''
} else if (typeof buttonLabel !== 'string') {
throw new TypeError('Button label must be a string')
}
if (defaultPath == null) {
defaultPath = ''
} else if (typeof defaultPath !== 'string') {
throw new TypeError('Default path must be a string')
}
if (filters == null) {
filters = []
}
if (message == null) {
message = ''
} else if (typeof message !== 'string') {
throw new TypeError('Message must be a string')
}
if (nameFieldLabel == null) {
nameFieldLabel = ''
} else if (typeof nameFieldLabel !== 'string') {
throw new TypeError('Name field label must be a string')
}
if (showsTagField == null) {
showsTagField = true
}
const wrappedCallback = typeof callback === 'function' ? function (success, result, bookmarkData) {
return success ? callback(result, bookmarkData) : callback()
} : null
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window }
return binding.showSaveDialog(settings, wrappedCallback)
},
showMessageBox: function (...args) {
checkAppInitialized()
let [window, options, callback] = parseArgs(...args)
if (options == null) {
options = {
type: 'none'
}
}
let {
buttons, cancelId, checkboxLabel, checkboxChecked, defaultId, detail,
icon, message, title, type
} = options
if (type == null) {
type = 'none'
}
const messageBoxType = messageBoxTypes.indexOf(type)
if (messageBoxType === -1) {
throw new TypeError('Invalid message box type')
}
if (buttons == null) {
buttons = []
} else if (!Array.isArray(buttons)) {
throw new TypeError('Buttons must be an array')
}
if (options.normalizeAccessKeys) {
buttons = buttons.map(normalizeAccessKey)
}
if (title == null) {
title = ''
} else if (typeof title !== 'string') {
throw new TypeError('Title must be a string')
}
if (message == null) {
message = ''
} else if (typeof message !== 'string') {
throw new TypeError('Message must be a string')
}
if (detail == null) {
detail = ''
} else if (typeof detail !== 'string') {
throw new TypeError('Detail must be a string')
}
checkboxChecked = !!checkboxChecked
if (checkboxLabel == null) {
checkboxLabel = ''
} else if (typeof checkboxLabel !== 'string') {
throw new TypeError('checkboxLabel must be a string')
}
if (icon == null) {
icon = null
}
if (defaultId == null) {
defaultId = -1
}
// Choose a default button to get selected when dialog is cancelled. // Choose a default button to get selected when dialog is cancelled.
if (cancelId == null) { if (cancelId == null) {
// If the defaultId is set to 0, ensure the cancel button is a different index (1) // If the defaultId is set to 0, ensure the cancel button is a different index (1)
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0; cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0
for (let i = 0; i < buttons.length; i++) { for (let i = 0; i < buttons.length; i++) {
const text = buttons[i].toLowerCase(); const text = buttons[i].toLowerCase()
if (text === 'cancel' || text === 'no') { if (text === 'cancel' || text === 'no') {
cancelId = i; cancelId = i
break; break
}
} }
}
} }
const flags = options.noLink ? messageBoxOptions.noLink : 0;
if (sync) { const flags = options.noLink ? messageBoxOptions.noLink : 0
return binding.showMessageBoxSync(messageBoxType, buttons, defaultId, cancelId, flags, title, message, detail, checkboxLabel, checkboxChecked, icon, window); return binding.showMessageBox(messageBoxType, buttons, defaultId, cancelId,
flags, title, message, detail, checkboxLabel,
checkboxChecked, icon, window, callback)
},
showErrorBox: function (...args) {
return binding.showErrorBox(...args)
},
showCertificateTrustDialog: function (...args) {
const [window, options, callback] = parseArgs(...args)
if (options == null || typeof options !== 'object') {
throw new TypeError('options must be an object')
} }
else {
return binding.showMessageBox(messageBoxType, buttons, defaultId, cancelId, flags, title, message, detail, checkboxLabel, checkboxChecked, icon, window); let { certificate, message } = options
if (certificate == null || typeof certificate !== 'object') {
throw new TypeError('certificate must be an object')
} }
};
module.exports = { if (message == null) {
showOpenDialog: function (window, options) { message = ''
return openDialog(false, window, options); } else if (typeof message !== 'string') {
}, throw new TypeError('message must be a string')
showOpenDialogSync: function (window, options) {
return openDialog(true, window, options);
},
showSaveDialog: function (window, options) {
return saveDialog(false, window, options);
},
showSaveDialogSync: function (window, options) {
return saveDialog(true, window, options);
},
showMessageBox: function (window, options) {
return messageBox(false, window, options);
},
showMessageBoxSync: function (window, options) {
return messageBox(true, window, options);
},
showErrorBox: function (...args) {
return binding.showErrorBox(...args);
},
showCertificateTrustDialog: function (window, options) {
if (window && window.constructor !== BrowserWindow)
options = window;
if (options == null || typeof options !== 'object') {
throw new TypeError('options must be an object');
}
const { certificate, message = '' } = options;
if (certificate == null || typeof certificate !== 'object') {
throw new TypeError('certificate must be an object');
}
if (typeof message !== 'string')
throw new TypeError('message must be a string');
return binding.showCertificateTrustDialog(window, certificate, message);
} }
};
module.exports.showMessageBox = deprecate.promisifyMultiArg(module.exports.showMessageBox, ({ response, checkboxChecked }) => [response, checkboxChecked]); return binding.showCertificateTrustDialog(window, certificate, message, callback)
module.exports.showOpenDialog = deprecate.promisifyMultiArg(module.exports.showOpenDialog, ({ filePaths, bookmarks }) => [filePaths, bookmarks]); }
module.exports.showSaveDialog = deprecate.promisifyMultiArg(module.exports.showSaveDialog, ({ filePath, bookmarks }) => [filePath, bookmarks]); }
module.exports.showCertificateTrustDialog = deprecate.promisify(module.exports.showCertificateTrustDialog);
//# sourceMappingURL=dialog.js.map // Mark standard asynchronous functions.
v8Util.setHiddenValue(module.exports.showMessageBox, 'asynchronous', true)
v8Util.setHiddenValue(module.exports.showOpenDialog, 'asynchronous', true)
v8Util.setHiddenValue(module.exports.showSaveDialog, 'asynchronous', true)

View File

@ -1,19 +1,15 @@
'use strict'; 'use strict'
const common = require('@electron/internal/common/api/exports/electron');
const common = require('@electron/internal/common/api/exports/electron')
// since browser module list is also used in renderer, keep it separate. // since browser module list is also used in renderer, keep it separate.
const moduleList = require('@electron/internal/browser/api/module-list'); const moduleList = require('@electron/internal/browser/api/module-list')
// Import common modules. // Import common modules.
common.defineProperties(exports); common.defineProperties(exports)
for (const module of moduleList) { for (const module of moduleList) {
Object.defineProperty(exports, module.name, { Object.defineProperty(exports, module.name, {
enumerable: !module.private, enumerable: !module.private,
get: common.memoizedGetter(() => { get: common.memoizedGetter(() => require(`@electron/internal/browser/api/${module.file}.js`))
const value = require(`@electron/internal/browser/api/${module.file}.js`); })
// Handle Typescript modules with an "export default X" statement
if (value.__esModule)
return value.default;
return value;
})
});
} }
//# sourceMappingURL=electron.js.map

View File

@ -1,3 +1,3 @@
'use strict'; 'use strict'
module.exports = process.electronBinding('global_shortcut').globalShortcut;
//# sourceMappingURL=global-shortcut.js.map module.exports = process.atomBinding('global_shortcut').globalShortcut

View File

@ -1,22 +1,20 @@
'use strict'; 'use strict'
const { deprecate } = require('electron');
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
const { EventEmitter } = require('events'); const { EventEmitter } = require('events')
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase'); const { inAppPurchase, InAppPurchase } = process.atomBinding('in_app_purchase')
// inAppPurchase is an EventEmitter.
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype); // inAppPurchase is an EventEmitter.
EventEmitter.call(inAppPurchase); Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype)
module.exports = inAppPurchase; EventEmitter.call(inAppPurchase)
module.exports = inAppPurchase
} else {
module.exports = {
purchaseProduct: (productID, quantity, callback) => {
throw new Error('The inAppPurchase module can only be used on macOS')
},
canMakePayments: () => false,
getReceiptURL: () => ''
}
} }
else {
module.exports = {
purchaseProduct: (productID, quantity, callback) => {
throw new Error('The inAppPurchase module can only be used on macOS');
},
canMakePayments: () => false,
getReceiptURL: () => ''
};
}
module.exports.purchaseProduct = deprecate.promisify(module.exports.purchaseProduct);
module.exports.getProducts = deprecate.promisify(module.exports.getProducts);
//# sourceMappingURL=in-app-purchase.js.map

View File

@ -1,8 +1,10 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events"); const { EventEmitter } = require('events')
const emitter = new events_1.EventEmitter();
const emitter = new EventEmitter()
// Do not throw exception when channel name is "error". // Do not throw exception when channel name is "error".
emitter.on('error', () => { }); emitter.on('error', () => {})
exports.default = emitter;
//# sourceMappingURL=ipc-main.js.map module.exports = emitter

View File

@ -1,309 +1,297 @@
'use strict'; 'use strict'
const { app } = require('electron');
const isMac = process.platform === 'darwin'; const { app } = require('electron')
const isWindows = process.platform === 'win32';
const isLinux = process.platform === 'linux';
const roles = { const roles = {
about: { about: {
get label() { get label () {
return isLinux ? 'About' : `About ${app.getName()}`; return process.platform === 'linux' ? 'About' : `About ${app.getName()}`
}
},
close: {
label: isMac ? 'Close Window' : 'Close',
accelerator: 'CommandOrControl+W',
windowMethod: 'close'
},
copy: {
label: 'Copy',
accelerator: 'CommandOrControl+C',
webContentsMethod: 'copy',
registerAccelerator: false
},
cut: {
label: 'Cut',
accelerator: 'CommandOrControl+X',
webContentsMethod: 'cut',
registerAccelerator: false
},
delete: {
label: 'Delete',
webContentsMethod: 'delete'
},
forcereload: {
label: 'Force Reload',
accelerator: 'Shift+CmdOrCtrl+R',
nonNativeMacOSRole: true,
windowMethod: (window) => {
window.webContents.reloadIgnoringCache();
}
},
front: {
label: 'Bring All to Front'
},
help: {
label: 'Help'
},
hide: {
get label() {
return `Hide ${app.getName()}`;
},
accelerator: 'Command+H'
},
hideothers: {
label: 'Hide Others',
accelerator: 'Command+Alt+H'
},
minimize: {
label: 'Minimize',
accelerator: 'CommandOrControl+M',
windowMethod: 'minimize'
},
paste: {
label: 'Paste',
accelerator: 'CommandOrControl+V',
webContentsMethod: 'paste',
registerAccelerator: false
},
pasteandmatchstyle: {
label: 'Paste and Match Style',
accelerator: 'Shift+CommandOrControl+V',
webContentsMethod: 'pasteAndMatchStyle',
registerAccelerator: false
},
quit: {
get label() {
switch (process.platform) {
case 'darwin': return `Quit ${app.getName()}`;
case 'win32': return 'Exit';
default: return 'Quit';
}
},
accelerator: isWindows ? null : 'CommandOrControl+Q',
appMethod: 'quit'
},
redo: {
label: 'Redo',
accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z',
webContentsMethod: 'redo'
},
reload: {
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
nonNativeMacOSRole: true,
windowMethod: 'reload'
},
resetzoom: {
label: 'Actual Size',
accelerator: 'CommandOrControl+0',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.setZoomLevel(0);
}
},
selectall: {
label: 'Select All',
accelerator: 'CommandOrControl+A',
webContentsMethod: 'selectAll'
},
services: {
label: 'Services'
},
recentdocuments: {
label: 'Open Recent'
},
clearrecentdocuments: {
label: 'Clear Menu'
},
startspeaking: {
label: 'Start Speaking'
},
stopspeaking: {
label: 'Stop Speaking'
},
toggledevtools: {
label: 'Toggle Developer Tools',
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
nonNativeMacOSRole: true,
windowMethod: 'toggleDevTools'
},
togglefullscreen: {
label: 'Toggle Full Screen',
accelerator: isMac ? 'Control+Command+F' : 'F11',
windowMethod: (window) => {
window.setFullScreen(!window.isFullScreen());
}
},
undo: {
label: 'Undo',
accelerator: 'CommandOrControl+Z',
webContentsMethod: 'undo'
},
unhide: {
label: 'Show All'
},
window: {
label: 'Window'
},
zoom: {
label: 'Zoom'
},
zoomin: {
label: 'Zoom In',
accelerator: 'CommandOrControl+Plus',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
const zoomLevel = webContents.getZoomLevel();
webContents.setZoomLevel(zoomLevel + 0.5);
}
},
zoomout: {
label: 'Zoom Out',
accelerator: 'CommandOrControl+-',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
const zoomLevel = webContents.getZoomLevel();
webContents.setZoomLevel(zoomLevel - 0.5);
}
},
// App submenu should be used for Mac only
appmenu: {
get label() {
return app.getName();
},
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideothers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
},
// File submenu
filemenu: {
label: 'File',
submenu: [
isMac ? { role: 'close' } : { role: 'quit' }
]
},
// Edit submenu
editmenu: {
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
...(isMac ? [
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [
{ role: 'startspeaking' },
{ role: 'stopspeaking' }
]
}
] : [
{ role: 'delete' },
{ type: 'separator' },
{ role: 'selectAll' }
])
]
},
// 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: 'zoom' },
...(isMac ? [
{ type: 'separator' },
{ role: 'front' }
] : [
{ role: 'close' }
])
]
} }
}; },
close: {
label: process.platform === 'darwin' ? 'Close Window' : 'Close',
accelerator: 'CommandOrControl+W',
windowMethod: 'close'
},
copy: {
label: 'Copy',
accelerator: 'CommandOrControl+C',
webContentsMethod: 'copy',
registerAccelerator: false
},
cut: {
label: 'Cut',
accelerator: 'CommandOrControl+X',
webContentsMethod: 'cut',
registerAccelerator: false
},
delete: {
label: 'Delete',
webContentsMethod: 'delete'
},
forcereload: {
label: 'Force Reload',
accelerator: 'Shift+CmdOrCtrl+R',
nonNativeMacOSRole: true,
windowMethod: (window) => {
window.webContents.reloadIgnoringCache()
}
},
front: {
label: 'Bring All to Front'
},
help: {
label: 'Help'
},
hide: {
get label () {
return `Hide ${app.getName()}`
},
accelerator: 'Command+H'
},
hideothers: {
label: 'Hide Others',
accelerator: 'Command+Alt+H'
},
minimize: {
label: 'Minimize',
accelerator: 'CommandOrControl+M',
windowMethod: 'minimize'
},
paste: {
label: 'Paste',
accelerator: 'CommandOrControl+V',
webContentsMethod: 'paste',
registerAccelerator: false
},
pasteandmatchstyle: {
label: 'Paste and Match Style',
accelerator: 'Shift+CommandOrControl+V',
webContentsMethod: 'pasteAndMatchStyle',
registerAccelerator: false
},
quit: {
get label () {
switch (process.platform) {
case 'darwin': return `Quit ${app.getName()}`
case 'win32': return 'Exit'
default: return 'Quit'
}
},
accelerator: process.platform === 'win32' ? null : 'CommandOrControl+Q',
appMethod: 'quit'
},
redo: {
label: 'Redo',
accelerator: process.platform === 'win32' ? 'Control+Y' : 'Shift+CommandOrControl+Z',
webContentsMethod: 'redo'
},
reload: {
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
nonNativeMacOSRole: true,
windowMethod: 'reload'
},
resetzoom: {
label: 'Actual Size',
accelerator: 'CommandOrControl+0',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.setZoomLevel(0)
}
},
selectall: {
label: 'Select All',
accelerator: 'CommandOrControl+A',
webContentsMethod: 'selectAll'
},
services: {
label: 'Services'
},
recentdocuments: {
label: 'Open Recent'
},
clearrecentdocuments: {
label: 'Clear Menu'
},
startspeaking: {
label: 'Start Speaking'
},
stopspeaking: {
label: 'Stop Speaking'
},
toggledevtools: {
label: 'Toggle Developer Tools',
accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
nonNativeMacOSRole: true,
windowMethod: 'toggleDevTools'
},
togglefullscreen: {
label: 'Toggle Full Screen',
accelerator: process.platform === 'darwin' ? 'Control+Command+F' : 'F11',
windowMethod: (window) => {
window.setFullScreen(!window.isFullScreen())
}
},
undo: {
label: 'Undo',
accelerator: 'CommandOrControl+Z',
webContentsMethod: 'undo'
},
unhide: {
label: 'Show All'
},
window: {
label: 'Window'
},
zoom: {
label: 'Zoom'
},
zoomin: {
label: 'Zoom In',
accelerator: 'CommandOrControl+Plus',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.getZoomLevel((zoomLevel) => {
webContents.setZoomLevel(zoomLevel + 0.5)
})
}
},
zoomout: {
label: 'Zoom Out',
accelerator: 'CommandOrControl+-',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.getZoomLevel((zoomLevel) => {
webContents.setZoomLevel(zoomLevel - 0.5)
})
}
},
// Edit submenu (should fit both Mac & Windows)
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'
}
]
},
// Window submenu should be used for Mac only
windowmenu: {
label: 'Window',
submenu: [
{
role: 'minimize'
},
{
role: 'close'
},
process.platform === 'darwin' ? {
type: 'separator'
} : null,
process.platform === 'darwin' ? {
role: 'front'
} : null
]
}
}
const canExecuteRole = (role) => { const canExecuteRole = (role) => {
if (!roles.hasOwnProperty(role)) if (!roles.hasOwnProperty(role)) return false
return false; if (process.platform !== 'darwin') return true
if (!isMac)
return true; // macOS handles all roles natively except for a few
// macOS handles all roles natively except for a few return roles[role].nonNativeMacOSRole
return roles[role].nonNativeMacOSRole; }
};
exports.getDefaultLabel = (role) => { exports.getDefaultLabel = (role) => {
return roles.hasOwnProperty(role) ? roles[role].label : ''; return roles.hasOwnProperty(role) ? roles[role].label : ''
}; }
exports.getDefaultAccelerator = (role) => { exports.getDefaultAccelerator = (role) => {
if (roles.hasOwnProperty(role)) if (roles.hasOwnProperty(role)) return roles[role].accelerator
return roles[role].accelerator; }
};
exports.shouldRegisterAccelerator = (role) => { exports.shouldRegisterAccelerator = (role) => {
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined; const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined
return hasRoleRegister ? roles[role].registerAccelerator : true; return hasRoleRegister ? roles[role].registerAccelerator : true
}; }
exports.getDefaultSubmenu = (role) => { exports.getDefaultSubmenu = (role) => {
if (!roles.hasOwnProperty(role)) if (!roles.hasOwnProperty(role)) return
return;
let { submenu } = roles[role]; let { submenu } = roles[role]
// remove null items from within the submenu
if (Array.isArray(submenu)) { // remove null items from within the submenu
submenu = submenu.filter((item) => item != null); if (Array.isArray(submenu)) {
} submenu = submenu.filter((item) => item != null)
return submenu; }
};
return submenu
}
exports.execute = (role, focusedWindow, focusedWebContents) => { exports.execute = (role, focusedWindow, focusedWebContents) => {
if (!canExecuteRole(role)) if (!canExecuteRole(role)) return false
return false;
const { appMethod, webContentsMethod, windowMethod } = roles[role]; const { appMethod, webContentsMethod, windowMethod } = roles[role]
if (appMethod) {
app[appMethod](); if (appMethod) {
return true; app[appMethod]()
return true
}
if (windowMethod && focusedWindow != null) {
if (typeof windowMethod === 'function') {
windowMethod(focusedWindow)
} else {
focusedWindow[windowMethod]()
} }
if (windowMethod && focusedWindow != null) { return true
if (typeof windowMethod === 'function') { }
windowMethod(focusedWindow);
} if (webContentsMethod && focusedWebContents != null) {
else { if (typeof webContentsMethod === 'function') {
focusedWindow[windowMethod](); webContentsMethod(focusedWebContents)
} } else {
return true; focusedWebContents[webContentsMethod]()
} }
if (webContentsMethod && focusedWebContents != null) { return true
if (typeof webContentsMethod === 'function') { }
webContentsMethod(focusedWebContents);
} return false
else { }
focusedWebContents[webContentsMethod]();
}
return true;
}
return false;
};
//# sourceMappingURL=menu-item-roles.js.map

View File

@ -1,74 +1,85 @@
'use strict'; 'use strict'
const roles = require('@electron/internal/browser/api/menu-item-roles');
let nextCommandId = 0; const roles = require('@electron/internal/browser/api/menu-item-roles')
let nextCommandId = 0
const MenuItem = function (options) { const MenuItem = function (options) {
const { Menu } = require('electron'); const { Menu } = require('electron')
// Preserve extra fields specified by user
for (const key in options) { // Preserve extra fields specified by user
if (!(key in this)) for (const key in options) {
this[key] = options[key]; if (!(key in this)) this[key] = options[key]
}
if (typeof this.role === 'string' || this.role instanceof String) {
this.role = this.role.toLowerCase()
}
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role)
if (this.submenu != null && this.submenu.constructor !== Menu) {
this.submenu = Menu.buildFromTemplate(this.submenu)
}
if (this.type == null && this.submenu != null) {
this.type = 'submenu'
}
if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) {
throw new Error('Invalid submenu')
}
this.overrideReadOnlyProperty('type', 'normal')
this.overrideReadOnlyProperty('role')
this.overrideReadOnlyProperty('accelerator')
this.overrideReadOnlyProperty('icon')
this.overrideReadOnlyProperty('submenu')
this.overrideProperty('label', roles.getDefaultLabel(this.role))
this.overrideProperty('sublabel', '')
this.overrideProperty('enabled', true)
this.overrideProperty('visible', true)
this.overrideProperty('checked', false)
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role))
if (!MenuItem.types.includes(this.type)) {
throw new Error(`Unknown menu item type: ${this.type}`)
}
this.overrideReadOnlyProperty('commandId', ++nextCommandId)
const click = options.click
this.click = (event, focusedWindow, focusedWebContents) => {
// Manually flip the checked flags when clicked.
if (this.type === 'checkbox' || this.type === 'radio') {
this.checked = !this.checked
} }
if (typeof this.role === 'string' || this.role instanceof String) {
this.role = this.role.toLowerCase(); if (!roles.execute(this.role, focusedWindow, focusedWebContents)) {
if (typeof click === 'function') {
click(this, focusedWindow, event)
} else if (typeof this.selector === 'string' && process.platform === 'darwin') {
Menu.sendActionToFirstResponder(this.selector)
}
} }
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role); }
if (this.submenu != null && this.submenu.constructor !== Menu) { }
this.submenu = Menu.buildFromTemplate(this.submenu);
} MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
if (this.type == null && this.submenu != null) {
this.type = 'submenu';
}
if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) {
throw new Error('Invalid submenu');
}
this.overrideReadOnlyProperty('type', 'normal');
this.overrideReadOnlyProperty('role');
this.overrideReadOnlyProperty('accelerator');
this.overrideReadOnlyProperty('icon');
this.overrideReadOnlyProperty('submenu');
this.overrideProperty('label', roles.getDefaultLabel(this.role));
this.overrideProperty('sublabel', '');
this.overrideProperty('enabled', true);
this.overrideProperty('visible', true);
this.overrideProperty('checked', false);
this.overrideProperty('acceleratorWorksWhenHidden', true);
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role));
if (!MenuItem.types.includes(this.type)) {
throw new Error(`Unknown menu item type: ${this.type}`);
}
this.overrideReadOnlyProperty('commandId', ++nextCommandId);
const click = options.click;
this.click = (event, focusedWindow, focusedWebContents) => {
// Manually flip the checked flags when clicked.
if (this.type === 'checkbox' || this.type === 'radio') {
this.checked = !this.checked;
}
if (!roles.execute(this.role, focusedWindow, focusedWebContents)) {
if (typeof click === 'function') {
click(this, focusedWindow, event);
}
else if (typeof this.selector === 'string' && process.platform === 'darwin') {
Menu.sendActionToFirstResponder(this.selector);
}
}
};
};
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'];
MenuItem.prototype.getDefaultRoleAccelerator = function () { MenuItem.prototype.getDefaultRoleAccelerator = function () {
return roles.getDefaultAccelerator(this.role); return roles.getDefaultAccelerator(this.role)
}; }
MenuItem.prototype.overrideProperty = function (name, defaultValue = null) { MenuItem.prototype.overrideProperty = function (name, defaultValue = null) {
if (this[name] == null) { if (this[name] == null) {
this[name] = defaultValue; this[name] = defaultValue
} }
}; }
MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) { MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
this.overrideProperty(name, defaultValue); this.overrideProperty(name, defaultValue)
Object.defineProperty(this, name, { Object.defineProperty(this, name, {
enumerable: true, enumerable: true,
writable: false, writable: false,
value: this[name] value: this[name]
}); })
}; }
module.exports = MenuItem;
//# sourceMappingURL=menu-item.js.map module.exports = MenuItem

View File

@ -1,155 +1,176 @@
'use strict'; 'use strict'
function splitArray(arr, predicate) {
const result = arr.reduce((multi, item) => { function splitArray (arr, predicate) {
const current = multi[multi.length - 1]; const result = arr.reduce((multi, item) => {
if (predicate(item)) { const current = multi[multi.length - 1]
if (current.length > 0) if (predicate(item)) {
multi.push([]); if (current.length > 0) multi.push([])
} } else {
else { current.push(item)
current.push(item);
}
return multi;
}, [[]]);
if (result[result.length - 1].length === 0) {
return result.slice(0, result.length - 1);
} }
return result; return multi
}, [[]])
if (result[result.length - 1].length === 0) {
return result.slice(0, result.length - 1)
}
return result
} }
function joinArrays(arrays, joinIDs) {
return arrays.reduce((joined, arr, i) => { function joinArrays (arrays, joinIDs) {
if (i > 0 && arr.length) { return arrays.reduce((joined, arr, i) => {
if (joinIDs.length > 0) { if (i > 0 && arr.length) {
joined.push(joinIDs[0]); if (joinIDs.length > 0) {
joinIDs.splice(0, 1); joined.push(joinIDs[0])
} joinIDs.splice(0, 1)
else { } else {
joined.push({ type: 'separator' }); joined.push({ type: 'separator' })
} }
}
return joined.concat(arr);
}, []);
}
function pushOntoMultiMap(map, key, value) {
if (!map.has(key)) {
map.set(key, []);
} }
map.get(key).push(value); return joined.concat(arr)
}, [])
} }
function indexOfGroupContainingID(groups, id, ignoreGroup) {
return groups.findIndex(candidateGroup => candidateGroup !== ignoreGroup && function pushOntoMultiMap (map, key, value) {
candidateGroup.some(candidateItem => candidateItem.id === id)); if (!map.has(key)) {
map.set(key, [])
}
map.get(key).push(value)
} }
function indexOfGroupContainingID (groups, id, ignoreGroup) {
return groups.findIndex(
candidateGroup =>
candidateGroup !== ignoreGroup &&
candidateGroup.some(
candidateItem => candidateItem.id === id
)
)
}
// Sort nodes topologically using a depth-first approach. Encountered cycles // Sort nodes topologically using a depth-first approach. Encountered cycles
// are broken. // are broken.
function sortTopologically(originalOrder, edgesById) { function sortTopologically (originalOrder, edgesById) {
const sorted = []; const sorted = []
const marked = new Set(); const marked = new Set()
const visit = (mark) => {
if (marked.has(mark)) const visit = (mark) => {
return; if (marked.has(mark)) return
marked.add(mark); marked.add(mark)
const edges = edgesById.get(mark); const edges = edgesById.get(mark)
if (edges != null) { if (edges != null) {
edges.forEach(visit); edges.forEach(visit)
}
sorted.push(mark);
};
originalOrder.forEach(visit);
return sorted;
}
function attemptToMergeAGroup(groups) {
for (let i = 0; i < groups.length; i++) {
const group = groups[i];
for (const item of group) {
const toIDs = [...(item.before || []), ...(item.after || [])];
for (const id of toIDs) {
const index = indexOfGroupContainingID(groups, id, group);
if (index === -1)
continue;
const mergeTarget = groups[index];
mergeTarget.push(...group);
groups.splice(i, 1);
return true;
}
}
} }
return false; sorted.push(mark)
}
originalOrder.forEach(visit)
return sorted
} }
function mergeGroups(groups) {
let merged = true; function attemptToMergeAGroup (groups) {
while (merged) { for (let i = 0; i < groups.length; i++) {
merged = attemptToMergeAGroup(groups); const group = groups[i]
}
return groups;
}
function sortItemsInGroup(group) {
const originalOrder = group.map((node, i) => i);
const edges = new Map();
const idToIndex = new Map(group.map((item, i) => [item.id, i]));
group.forEach((item, i) => {
if (item.before) {
item.before.forEach(toID => {
const to = idToIndex.get(toID);
if (to != null) {
pushOntoMultiMap(edges, to, i);
}
});
}
if (item.after) {
item.after.forEach(toID => {
const to = idToIndex.get(toID);
if (to != null) {
pushOntoMultiMap(edges, i, to);
}
});
}
});
const sortedNodes = sortTopologically(originalOrder, edges);
return sortedNodes.map(i => group[i]);
}
function findEdgesInGroup(groups, i, edges) {
const group = groups[i];
for (const item of group) { for (const item of group) {
if (item.beforeGroupContaining) { const toIDs = [...(item.before || []), ...(item.after || [])]
for (const id of item.beforeGroupContaining) { for (const id of toIDs) {
const to = indexOfGroupContainingID(groups, id, group); const index = indexOfGroupContainingID(groups, id, group)
if (to !== -1) { if (index === -1) continue
pushOntoMultiMap(edges, to, i); const mergeTarget = groups[index]
return;
} mergeTarget.push(...group)
} groups.splice(i, 1)
} return true
if (item.afterGroupContaining) { }
for (const id of item.afterGroupContaining) {
const to = indexOfGroupContainingID(groups, id, group);
if (to !== -1) {
pushOntoMultiMap(edges, i, to);
return;
}
}
}
} }
}
return false
} }
function sortGroups(groups) {
const originalOrder = groups.map((item, i) => i); function mergeGroups (groups) {
const edges = new Map(); let merged = true
for (let i = 0; i < groups.length; i++) { while (merged) {
findEdgesInGroup(groups, i, edges); merged = attemptToMergeAGroup(groups)
}
return groups
}
function sortItemsInGroup (group) {
const originalOrder = group.map((node, i) => i)
const edges = new Map()
const idToIndex = new Map(group.map((item, i) => [item.id, i]))
group.forEach((item, i) => {
if (item.before) {
item.before.forEach(toID => {
const to = idToIndex.get(toID)
if (to != null) {
pushOntoMultiMap(edges, to, i)
}
})
} }
const sortedGroupIndexes = sortTopologically(originalOrder, edges); if (item.after) {
return sortedGroupIndexes.map(i => groups[i]); item.after.forEach(toID => {
const to = idToIndex.get(toID)
if (to != null) {
pushOntoMultiMap(edges, i, to)
}
})
}
})
const sortedNodes = sortTopologically(originalOrder, edges)
return sortedNodes.map(i => group[i])
} }
function sortMenuItems(menuItems) {
const isSeparator = (item) => item.type === 'separator'; function findEdgesInGroup (groups, i, edges) {
const separators = menuItems.filter(i => i.type === 'separator'); const group = groups[i]
// Split the items into their implicit groups based upon separators. for (const item of group) {
const groups = splitArray(menuItems, isSeparator); if (item.beforeGroupContaining) {
const mergedGroups = mergeGroups(groups); for (const id of item.beforeGroupContaining) {
const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup); const to = indexOfGroupContainingID(groups, id, group)
const sortedGroups = sortGroups(mergedGroupsWithSortedItems); if (to !== -1) {
const joined = joinArrays(sortedGroups, separators); pushOntoMultiMap(edges, to, i)
return joined; return
}
}
}
if (item.afterGroupContaining) {
for (const id of item.afterGroupContaining) {
const to = indexOfGroupContainingID(groups, id, group)
if (to !== -1) {
pushOntoMultiMap(edges, i, to)
return
}
}
}
}
} }
module.exports = { sortMenuItems };
//# sourceMappingURL=menu-utils.js.map function sortGroups (groups) {
const originalOrder = groups.map((item, i) => i)
const edges = new Map()
for (let i = 0; i < groups.length; i++) {
findEdgesInGroup(groups, i, edges)
}
const sortedGroupIndexes = sortTopologically(originalOrder, edges)
return sortedGroupIndexes.map(i => groups[i])
}
function sortMenuItems (menuItems) {
const isSeparator = (item) => item.type === 'separator'
const separators = menuItems.filter(i => i.type === 'separator')
// Split the items into their implicit groups based upon separators.
const groups = splitArray(menuItems, isSeparator)
const mergedGroups = mergeGroups(groups)
const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup)
const sortedGroups = sortGroups(mergedGroupsWithSortedItems)
const joined = joinArrays(sortedGroups, separators)
return joined
}
module.exports = { sortMenuItems }

View File

@ -1,263 +1,258 @@
'use strict'; 'use strict'
const { TopLevelWindow, MenuItem, webContents } = require('electron');
const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils'); const { TopLevelWindow, MenuItem, webContents } = require('electron')
const EventEmitter = require('events').EventEmitter; const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils')
const v8Util = process.electronBinding('v8_util'); const EventEmitter = require('events').EventEmitter
const bindings = process.electronBinding('menu'); const v8Util = process.atomBinding('v8_util')
const { Menu } = bindings; const bindings = process.atomBinding('menu')
let applicationMenu = null;
let groupIdIndex = 0; const { Menu } = bindings
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype); let applicationMenu = null
let groupIdIndex = 0
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)
// Menu Delegate. // Menu Delegate.
// This object should hold no reference to |Menu| to avoid cyclic reference. // This object should hold no reference to |Menu| to avoid cyclic reference.
const delegate = { const delegate = {
isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : undefined, isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : undefined,
isCommandIdEnabled: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].enabled : undefined, isCommandIdEnabled: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].enabled : undefined,
shouldCommandIdWorkWhenHidden: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].acceleratorWorksWhenHidden : undefined, isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined, getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => { const command = menu.commandsMap[id]
const command = menu.commandsMap[id]; if (!command) return
if (!command) if (command.accelerator != null) return command.accelerator
return; if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
if (command.accelerator != null) },
return command.accelerator; shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined,
if (useDefaultAccelerator) executeCommand: (menu, event, id) => {
return command.getDefaultRoleAccelerator(); const command = menu.commandsMap[id]
}, if (!command) return
shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined, command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents())
executeCommand: (menu, event, id) => { },
const command = menu.commandsMap[id]; menuWillShow: (menu) => {
if (!command) // Ensure radio groups have at least one menu item seleted
return; for (const id in menu.groupsMap) {
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents()); const found = menu.groupsMap[id].find(item => item.checked) || null
}, if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true)
menuWillShow: (menu) => {
// Ensure radio groups have at least one menu item selected
for (const id in menu.groupsMap) {
const found = menu.groupsMap[id].find(item => item.checked) || null;
if (!found)
v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true);
}
} }
}; }
}
/* Instance Methods */ /* Instance Methods */
Menu.prototype._init = function () { Menu.prototype._init = function () {
this.commandsMap = {}; this.commandsMap = {}
this.groupsMap = {}; this.groupsMap = {}
this.items = []; this.items = []
this.delegate = delegate; this.delegate = delegate
}; }
Menu.prototype.popup = function (options = {}) { Menu.prototype.popup = function (options = {}) {
if (options == null || typeof options !== 'object') { if (options == null || typeof options !== 'object') {
throw new TypeError('Options must be an object'); throw new TypeError('Options must be an object')
}
let { window, x, y, positioningItem, callback } = options
// no callback passed
if (!callback || typeof callback !== 'function') callback = () => {}
// set defaults
if (typeof x !== 'number') x = -1
if (typeof y !== 'number') y = -1
if (typeof positioningItem !== 'number') positioningItem = -1
// find which window to use
const wins = TopLevelWindow.getAllWindows()
if (!wins || wins.indexOf(window) === -1) {
window = TopLevelWindow.getFocusedWindow()
if (!window && wins && wins.length > 0) {
window = wins[0]
} }
let { window, x, y, positioningItem, callback } = options; if (!window) {
// no callback passed throw new Error(`Cannot open Menu without a TopLevelWindow present`)
if (!callback || typeof callback !== 'function')
callback = () => { };
// set defaults
if (typeof x !== 'number')
x = -1;
if (typeof y !== 'number')
y = -1;
if (typeof positioningItem !== 'number')
positioningItem = -1;
// find which window to use
const wins = TopLevelWindow.getAllWindows();
if (!wins || wins.indexOf(window) === -1) {
window = TopLevelWindow.getFocusedWindow();
if (!window && wins && wins.length > 0) {
window = wins[0];
}
if (!window) {
throw new Error(`Cannot open Menu without a TopLevelWindow present`);
}
} }
this.popupAt(window, x, y, positioningItem, callback); }
return { browserWindow: window, x, y, position: positioningItem };
}; this.popupAt(window, x, y, positioningItem, callback)
return { browserWindow: window, x, y, position: positioningItem }
}
Menu.prototype.closePopup = function (window) { Menu.prototype.closePopup = function (window) {
if (window instanceof TopLevelWindow) { if (window instanceof TopLevelWindow) {
this.closePopupAt(window.id); this.closePopupAt(window.id)
} } else {
else { // Passing -1 (invalid) would make closePopupAt close the all menu runners
// Passing -1 (invalid) would make closePopupAt close the all menu runners // belong to this menu.
// belong to this menu. this.closePopupAt(-1)
this.closePopupAt(-1); }
} }
};
Menu.prototype.getMenuItemById = function (id) { Menu.prototype.getMenuItemById = function (id) {
const items = this.items; const items = this.items
let found = items.find(item => item.id === id) || null;
for (let i = 0; !found && i < items.length; i++) { let found = items.find(item => item.id === id) || null
if (items[i].submenu) { for (let i = 0; !found && i < items.length; i++) {
found = items[i].submenu.getMenuItemById(id); if (items[i].submenu) {
} found = items[i].submenu.getMenuItemById(id)
} }
return found; }
}; return found
}
Menu.prototype.append = function (item) { Menu.prototype.append = function (item) {
return this.insert(this.getItemCount(), item); return this.insert(this.getItemCount(), item)
}; }
Menu.prototype.insert = function (pos, item) { Menu.prototype.insert = function (pos, item) {
if ((item ? item.constructor : void 0) !== MenuItem) { if ((item ? item.constructor : void 0) !== MenuItem) {
throw new TypeError('Invalid item'); throw new TypeError('Invalid item')
} }
if (pos < 0) {
throw new RangeError(`Position ${pos} cannot be less than 0`); // insert item depending on its type
} insertItemByType.call(this, item, pos)
else if (pos > this.getItemCount()) {
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`); // set item properties
} if (item.sublabel) this.setSublabel(pos, item.sublabel)
// insert item depending on its type if (item.icon) this.setIcon(pos, item.icon)
insertItemByType.call(this, item, pos); if (item.role) this.setRole(pos, item.role)
// set item properties
if (item.sublabel) // Make menu accessable to items.
this.setSublabel(pos, item.sublabel); item.overrideReadOnlyProperty('menu', this)
if (item.icon)
this.setIcon(pos, item.icon); // Remember the items.
if (item.role) this.items.splice(pos, 0, item)
this.setRole(pos, item.role); this.commandsMap[item.commandId] = item
// Make menu accessable to items. }
item.overrideReadOnlyProperty('menu', this);
// Remember the items.
this.items.splice(pos, 0, item);
this.commandsMap[item.commandId] = item;
};
Menu.prototype._callMenuWillShow = function () { Menu.prototype._callMenuWillShow = function () {
if (this.delegate) if (this.delegate) this.delegate.menuWillShow(this)
this.delegate.menuWillShow(this); this.items.forEach(item => {
this.items.forEach(item => { if (item.submenu) item.submenu._callMenuWillShow()
if (item.submenu) })
item.submenu._callMenuWillShow(); }
});
};
/* Static Methods */ /* Static Methods */
Menu.getApplicationMenu = () => applicationMenu;
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder; Menu.getApplicationMenu = () => applicationMenu
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
// set application menu with a preexisting menu // set application menu with a preexisting menu
Menu.setApplicationMenu = function (menu) { Menu.setApplicationMenu = function (menu) {
if (menu && menu.constructor !== Menu) { if (menu && menu.constructor !== Menu) {
throw new TypeError('Invalid menu'); throw new TypeError('Invalid menu')
} }
applicationMenu = menu;
v8Util.setHiddenValue(global, 'applicationMenuSet', true); applicationMenu = menu
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
if (!menu) if (!menu) return
return; menu._callMenuWillShow()
menu._callMenuWillShow(); bindings.setApplicationMenu(menu)
bindings.setApplicationMenu(menu); } else {
} const windows = TopLevelWindow.getAllWindows()
else { return windows.map(w => w.setMenu(menu))
const windows = TopLevelWindow.getAllWindows(); }
return windows.map(w => w.setMenu(menu)); }
}
};
Menu.buildFromTemplate = function (template) { Menu.buildFromTemplate = function (template) {
if (!Array.isArray(template)) { if (!Array.isArray(template)) {
throw new TypeError('Invalid template for Menu: Menu template must be an array'); throw new TypeError('Invalid template for Menu: Menu template must be an array')
} }
if (!areValidTemplateItems(template)) { const menu = new Menu()
throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type'); if (!areValidTemplateItems(template)) {
} throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type')
const filtered = removeExtraSeparators(template); }
const sorted = sortTemplate(filtered); const filtered = removeExtraSeparators(template)
const menu = new Menu(); const sorted = sortTemplate(filtered)
sorted.forEach(item => {
if (item instanceof MenuItem) { sorted.forEach((item) => menu.append(new MenuItem(item)))
menu.append(item);
} return menu
else { }
menu.append(new MenuItem(item));
}
});
return menu;
};
/* Helper Functions */ /* Helper Functions */
// validate the template against having the wrong attribute // validate the template against having the wrong attribute
function areValidTemplateItems(template) { function areValidTemplateItems (template) {
return template.every(item => item != null && return template.every(item =>
typeof item === 'object' && item != null && typeof item === 'object' && (item.hasOwnProperty('label') || item.hasOwnProperty('role') || item.type === 'separator'))
(item.hasOwnProperty('label') ||
item.hasOwnProperty('role') ||
item.type === 'separator'));
} }
function sortTemplate(template) {
const sorted = sortMenuItems(template); function sortTemplate (template) {
for (const id in sorted) { const sorted = sortMenuItems(template)
const item = sorted[id]; for (const id in sorted) {
if (Array.isArray(item.submenu)) { const item = sorted[id]
item.submenu = sortTemplate(item.submenu); if (Array.isArray(item.submenu)) {
} item.submenu = sortTemplate(item.submenu)
} }
return sorted; }
return sorted
} }
// Search between separators to find a radio menu item and return its group id // Search between separators to find a radio menu item and return its group id
function generateGroupId(items, pos) { function generateGroupId (items, pos) {
if (pos > 0) { if (pos > 0) {
for (let idx = pos - 1; idx >= 0; idx--) { for (let idx = pos - 1; idx >= 0; idx--) {
if (items[idx].type === 'radio') if (items[idx].type === 'radio') return items[idx].groupId
return items[idx].groupId; if (items[idx].type === 'separator') break
if (items[idx].type === 'separator')
break;
}
} }
else if (pos < items.length) { } else if (pos < items.length) {
for (let idx = pos; idx <= items.length - 1; idx++) { for (let idx = pos; idx <= items.length - 1; idx++) {
if (items[idx].type === 'radio') if (items[idx].type === 'radio') return items[idx].groupId
return items[idx].groupId; if (items[idx].type === 'separator') break
if (items[idx].type === 'separator')
break;
}
} }
groupIdIndex += 1; }
return groupIdIndex; groupIdIndex += 1
return groupIdIndex
} }
function removeExtraSeparators(items) {
// fold adjacent separators together function removeExtraSeparators (items) {
let ret = items.filter((e, idx, arr) => { // fold adjacent separators together
if (e.visible === false) let ret = items.filter((e, idx, arr) => {
return true; if (e.visible === false) return true
return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator'; return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator'
}); })
// remove edge separators
ret = ret.filter((e, idx, arr) => { // remove edge separators
if (e.visible === false) ret = ret.filter((e, idx, arr) => {
return true; if (e.visible === false) return true
return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1); return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1)
}); })
return ret;
return ret
} }
function insertItemByType(item, pos) {
const types = { function insertItemByType (item, pos) {
normal: () => this.insertItem(pos, item.commandId, item.label), const types = {
checkbox: () => this.insertCheckItem(pos, item.commandId, item.label), normal: () => this.insertItem(pos, item.commandId, item.label),
separator: () => this.insertSeparator(pos), checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),
submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu), separator: () => this.insertSeparator(pos),
radio: () => { submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
// Grouping radio menu items radio: () => {
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos)); // Grouping radio menu items
if (this.groupsMap[item.groupId] == null) { item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
this.groupsMap[item.groupId] = []; if (this.groupsMap[item.groupId] == null) {
} this.groupsMap[item.groupId] = []
this.groupsMap[item.groupId].push(item); }
// Setting a radio menu item should flip other items in the group. this.groupsMap[item.groupId].push(item)
v8Util.setHiddenValue(item, 'checked', item.checked);
Object.defineProperty(item, 'checked', { // Setting a radio menu item should flip other items in the group.
enumerable: true, v8Util.setHiddenValue(item, 'checked', item.checked)
get: () => v8Util.getHiddenValue(item, 'checked'), Object.defineProperty(item, 'checked', {
set: () => { enumerable: true,
this.groupsMap[item.groupId].forEach(other => { get: () => v8Util.getHiddenValue(item, 'checked'),
if (other !== item) set: () => {
v8Util.setHiddenValue(other, 'checked', false); this.groupsMap[item.groupId].forEach(other => {
}); if (other !== item) v8Util.setHiddenValue(other, 'checked', false)
v8Util.setHiddenValue(item, 'checked', true); })
} v8Util.setHiddenValue(item, 'checked', true)
});
this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
} }
}; })
types[item.type](); this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
}
}
types[item.type]()
} }
module.exports = Menu;
//# sourceMappingURL=menu.js.map module.exports = Menu

View File

@ -1,36 +1,46 @@
'use strict'; 'use strict'
const features = process.electronBinding('features');
const features = process.atomBinding('features')
// Browser side modules, please sort alphabetically. // Browser side modules, please sort alphabetically.
module.exports = [ module.exports = [
{ name: 'app', file: 'app' }, { name: 'app', file: 'app' },
{ name: 'autoUpdater', file: 'auto-updater' }, { name: 'autoUpdater', file: 'auto-updater' },
{ name: 'BrowserView', file: 'browser-view' }, { name: 'BrowserView', file: 'browser-view' },
{ name: 'BrowserWindow', file: 'browser-window' }, { name: 'BrowserWindow', file: 'browser-window' },
{ name: 'contentTracing', file: 'content-tracing' }, { name: 'contentTracing', file: 'content-tracing' },
{ name: 'crashReporter', file: 'crash-reporter' }, { name: 'crashReporter', file: 'crash-reporter' },
{ name: 'dialog', file: 'dialog' }, { name: 'dialog', file: 'dialog' },
{ name: 'globalShortcut', file: 'global-shortcut' }, { name: 'globalShortcut', file: 'global-shortcut' },
{ name: 'ipcMain', file: 'ipc-main' }, { name: 'ipcMain', file: 'ipc-main' },
{ name: 'inAppPurchase', file: 'in-app-purchase' }, { name: 'inAppPurchase', file: 'in-app-purchase' },
{ name: 'Menu', file: 'menu' }, { name: 'Menu', file: 'menu' },
{ name: 'MenuItem', file: 'menu-item' }, { name: 'MenuItem', file: 'menu-item' },
{ name: 'net', file: 'net' }, { name: 'net', file: 'net' },
{ name: 'netLog', file: 'net-log' }, { name: 'netLog', file: 'net-log' },
{ name: 'Notification', file: 'notification' }, { name: 'Notification', file: 'notification' },
{ name: 'powerMonitor', file: 'power-monitor' }, { name: 'powerMonitor', file: 'power-monitor' },
{ name: 'powerSaveBlocker', file: 'power-save-blocker' }, { name: 'powerSaveBlocker', file: 'power-save-blocker' },
{ name: 'protocol', file: 'protocol' }, { name: 'protocol', file: 'protocol' },
{ name: 'screen', file: 'screen' }, { name: 'screen', file: 'screen' },
{ name: 'session', file: 'session' }, { name: 'session', file: 'session' },
{ name: 'systemPreferences', file: 'system-preferences' }, { name: 'systemPreferences', file: 'system-preferences' },
{ name: 'TopLevelWindow', file: 'top-level-window' }, { name: 'TopLevelWindow', file: 'top-level-window' },
{ name: 'TouchBar', file: 'touch-bar' }, { name: 'TouchBar', file: 'touch-bar' },
{ name: 'Tray', file: 'tray' }, { name: 'Tray', file: 'tray' },
{ name: 'View', file: 'view' }, { name: 'View', file: 'view' },
{ name: 'webContents', file: 'web-contents' }, { name: 'webContents', file: 'web-contents' },
{ name: 'WebContentsView', file: 'web-contents-view' } { name: 'WebContentsView', file: 'web-contents-view' },
]; // The internal modules, invisible unless you know their names.
{ name: 'NavigationController', file: 'navigation-controller', private: true }
]
if (features.isViewApiEnabled()) { if (features.isViewApiEnabled()) {
module.exports.push({ name: 'BoxLayout', file: 'views/box-layout' }, { 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' }); module.exports.push(
{ name: 'BoxLayout', file: 'views/box-layout' },
{ name: 'Button', file: 'views/button' },
{ name: 'LabelButton', file: 'views/label-button' },
{ name: 'LayoutManager', file: 'views/layout-manager' },
{ name: 'TextField', file: 'views/text-field' }
)
} }
//# sourceMappingURL=module-list.js.map

View File

@ -0,0 +1,179 @@
'use strict'
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
// The history operation in renderer is redirected to browser.
ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) {
event.sender.goBack()
})
ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD', function (event) {
event.sender.goForward()
})
ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (event, offset) {
event.sender.goToOffset(offset)
})
ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
event.returnValue = event.sender.length()
})
// JavaScript implementation of Chromium's NavigationController.
// Instead of relying on Chromium for history control, we compeletely do history
// control on user land, and only rely on WebContents.loadURL for navigation.
// This helps us avoid Chromium's various optimizations so we can ensure renderer
// process is restarted everytime.
const NavigationController = (function () {
function NavigationController (webContents) {
this.webContents = webContents
this.clearHistory()
// webContents may have already navigated to a page.
if (this.webContents._getURL()) {
this.currentIndex++
this.history.push(this.webContents._getURL())
}
this.webContents.on('navigation-entry-commited', (event, url, inPage, replaceEntry) => {
if (this.inPageIndex > -1 && !inPage) {
// Navigated to a new page, clear in-page mark.
this.inPageIndex = -1
} else if (this.inPageIndex === -1 && inPage && !replaceEntry) {
// Started in-page navigations.
this.inPageIndex = this.currentIndex
}
if (this.pendingIndex >= 0) {
// Go to index.
this.currentIndex = this.pendingIndex
this.pendingIndex = -1
this.history[this.currentIndex] = url
} else if (replaceEntry) {
// Non-user initialized navigation.
this.history[this.currentIndex] = url
} else {
// Normal navigation. Clear history.
this.history = this.history.slice(0, this.currentIndex + 1)
this.currentIndex++
this.history.push(url)
}
})
}
NavigationController.prototype.loadURL = function (url, options) {
if (options == null) {
options = {}
}
this.pendingIndex = -1
this.webContents._loadURL(url, options)
return this.webContents.emit('load-url', url, options)
}
NavigationController.prototype.getURL = function () {
if (this.currentIndex === -1) {
return ''
} else {
return this.history[this.currentIndex]
}
}
NavigationController.prototype.stop = function () {
this.pendingIndex = -1
return this.webContents._stop()
}
NavigationController.prototype.reload = function () {
this.pendingIndex = this.currentIndex
return this.webContents._loadURL(this.getURL(), {})
}
NavigationController.prototype.reloadIgnoringCache = function () {
this.pendingIndex = this.currentIndex
return this.webContents._loadURL(this.getURL(), {
extraHeaders: 'pragma: no-cache\n'
})
}
NavigationController.prototype.canGoBack = function () {
return this.getActiveIndex() > 0
}
NavigationController.prototype.canGoForward = function () {
return this.getActiveIndex() < this.history.length - 1
}
NavigationController.prototype.canGoToIndex = function (index) {
return index >= 0 && index < this.history.length
}
NavigationController.prototype.canGoToOffset = function (offset) {
return this.canGoToIndex(this.currentIndex + offset)
}
NavigationController.prototype.clearHistory = function () {
this.history = []
this.currentIndex = -1
this.pendingIndex = -1
this.inPageIndex = -1
}
NavigationController.prototype.goBack = function () {
if (!this.canGoBack()) {
return
}
this.pendingIndex = this.getActiveIndex() - 1
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goBack()
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {})
}
}
NavigationController.prototype.goForward = function () {
if (!this.canGoForward()) {
return
}
this.pendingIndex = this.getActiveIndex() + 1
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goForward()
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {})
}
}
NavigationController.prototype.goToIndex = function (index) {
if (!this.canGoToIndex(index)) {
return
}
this.pendingIndex = index
return this.webContents._loadURL(this.history[this.pendingIndex], {})
}
NavigationController.prototype.goToOffset = function (offset) {
if (!this.canGoToOffset(offset)) {
return
}
const pendingIndex = this.currentIndex + offset
if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
this.pendingIndex = pendingIndex
return this.webContents._goToOffset(offset)
} else {
return this.goToIndex(pendingIndex)
}
}
NavigationController.prototype.getActiveIndex = function () {
if (this.pendingIndex === -1) {
return this.currentIndex
} else {
return this.pendingIndex
}
}
NavigationController.prototype.length = function () {
return this.history.length
}
return NavigationController
})()
module.exports = NavigationController

View File

@ -1,28 +1,28 @@
'use strict'; 'use strict'
// TODO(deepak1556): Deprecate and remove standalone netLog module, // TODO(deepak1556): Deprecate and remove standalone netLog module,
// it is now a property of session module. // it is now a property of sessio module.
const { app, session } = require('electron'); const { app, session } = require('electron')
// Fallback to default session. // Fallback to default session.
Object.setPrototypeOf(module.exports, new Proxy({}, { Object.setPrototypeOf(module.exports, new Proxy({}, {
get(target, property) { get (target, property) {
if (!app.isReady()) if (!app.isReady()) return
return;
const netLog = session.defaultSession.netLog; const netLog = session.defaultSession.netLog
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return
return;
// check for properties on the prototype chain that aren't functions // Returning a native function directly would throw error.
if (typeof netLog[property] !== 'function') return (...args) => netLog[property](...args)
return netLog[property]; },
// Returning a native function directly would throw error.
return (...args) => netLog[property](...args); ownKeys () {
}, if (!app.isReady()) return []
ownKeys() {
if (!app.isReady()) return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog))
return []; },
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog));
}, getOwnPropertyDescriptor (target) {
getOwnPropertyDescriptor(target) { return { configurable: true, enumerable: true }
return { configurable: true, enumerable: true }; }
} }))
}));
//# sourceMappingURL=net-log.js.map

View File

@ -1,358 +1,372 @@
'use strict'; 'use strict'
const url = require('url');
const { EventEmitter } = require('events'); const url = require('url')
const { Readable } = require('stream'); const { EventEmitter } = require('events')
const { app } = require('electron'); const { Readable } = require('stream')
const { Session } = process.electronBinding('session'); const { app } = require('electron')
const { net, Net } = process.electronBinding('net'); const { Session } = process.atomBinding('session')
const { URLRequest } = net; const { net, Net } = process.atomBinding('net')
const { URLRequest } = net
// Net is an EventEmitter. // Net is an EventEmitter.
Object.setPrototypeOf(Net.prototype, EventEmitter.prototype); Object.setPrototypeOf(Net.prototype, EventEmitter.prototype)
EventEmitter.call(net); EventEmitter.call(net)
Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype);
const kSupportedProtocols = new Set(['http:', 'https:']); Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype)
// set of headers that Node.js discards duplicates for
// see https://nodejs.org/api/http.html#http_message_headers const kSupportedProtocols = new Set(['http:', 'https:'])
const discardableDuplicateHeaders = new Set([
'content-type',
'content-length',
'user-agent',
'referer',
'host',
'authorization',
'proxy-authorization',
'if-modified-since',
'if-unmodified-since',
'from',
'location',
'max-forwards',
'retry-after',
'etag',
'last-modified',
'server',
'age',
'expires'
]);
class IncomingMessage extends Readable { class IncomingMessage extends Readable {
constructor(urlRequest) { constructor (urlRequest) {
super(); super()
this.urlRequest = urlRequest; this.urlRequest = urlRequest
this.shouldPush = false; this.shouldPush = false
this.data = []; this.data = []
this.urlRequest.on('data', (event, chunk) => { this.urlRequest.on('data', (event, chunk) => {
this._storeInternalData(chunk); this._storeInternalData(chunk)
this._pushInternalData(); this._pushInternalData()
}); })
this.urlRequest.on('end', () => { this.urlRequest.on('end', () => {
this._storeInternalData(null); this._storeInternalData(null)
this._pushInternalData(); this._pushInternalData()
}); })
} }
get statusCode() {
return this.urlRequest.statusCode; get statusCode () {
} return this.urlRequest.statusCode
get statusMessage() { }
return this.urlRequest.statusMessage;
} get statusMessage () {
get headers() { return this.urlRequest.statusMessage
const filteredHeaders = {}; }
const rawHeaders = this.urlRequest.rawResponseHeaders;
Object.keys(rawHeaders).forEach(header => { get headers () {
if (header in filteredHeaders && discardableDuplicateHeaders.has(header)) { return this.urlRequest.rawResponseHeaders
// do nothing with discardable duplicate headers }
}
else { get httpVersion () {
if (header === 'set-cookie') { return `${this.httpVersionMajor}.${this.httpVersionMinor}`
// keep set-cookie as an array per Node.js rules }
// see https://nodejs.org/api/http.html#http_message_headers
filteredHeaders[header] = rawHeaders[header]; get httpVersionMajor () {
} return this.urlRequest.httpVersionMajor
else { }
// for non-cookie headers, the values are joined together with ', '
filteredHeaders[header] = rawHeaders[header].join(', '); get httpVersionMinor () {
} return this.urlRequest.httpVersionMinor
} }
});
return filteredHeaders; get rawTrailers () {
} throw new Error('HTTP trailers are not supported.')
get httpVersion() { }
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
} get trailers () {
get httpVersionMajor() { throw new Error('HTTP trailers are not supported.')
return this.urlRequest.httpVersionMajor; }
}
get httpVersionMinor() { _storeInternalData (chunk) {
return this.urlRequest.httpVersionMinor; this.data.push(chunk)
} }
get rawTrailers() {
throw new Error('HTTP trailers are not supported.'); _pushInternalData () {
} while (this.shouldPush && this.data.length > 0) {
get trailers() { const chunk = this.data.shift()
throw new Error('HTTP trailers are not supported.'); this.shouldPush = this.push(chunk)
}
_storeInternalData(chunk) {
this.data.push(chunk);
}
_pushInternalData() {
while (this.shouldPush && this.data.length > 0) {
const chunk = this.data.shift();
this.shouldPush = this.push(chunk);
}
}
_read() {
this.shouldPush = true;
this._pushInternalData();
} }
}
_read () {
this.shouldPush = true
this._pushInternalData()
}
} }
URLRequest.prototype._emitRequestEvent = function (isAsync, ...rest) { URLRequest.prototype._emitRequestEvent = function (isAsync, ...rest) {
if (isAsync) { if (isAsync) {
process.nextTick(() => { process.nextTick(() => {
this.clientRequest.emit(...rest); this.clientRequest.emit(...rest)
}); })
} } else {
else { this.clientRequest.emit(...rest)
this.clientRequest.emit(...rest); }
} }
};
URLRequest.prototype._emitResponseEvent = function (isAsync, ...rest) { URLRequest.prototype._emitResponseEvent = function (isAsync, ...rest) {
if (isAsync) { if (isAsync) {
process.nextTick(() => { process.nextTick(() => {
this._response.emit(...rest); this._response.emit(...rest)
}); })
} } else {
else { this._response.emit(...rest)
this._response.emit(...rest); }
} }
};
class ClientRequest extends EventEmitter { class ClientRequest extends EventEmitter {
constructor(options, callback) { constructor (options, callback) {
super(); super()
if (!app.isReady()) {
throw new Error('net module can only be used after app is ready'); if (!app.isReady()) {
} throw new Error('net module can only be used after app is ready')
if (typeof options === 'string') {
options = url.parse(options);
}
else {
options = Object.assign({}, options);
}
const method = (options.method || 'GET').toUpperCase();
let urlStr = options.url;
if (!urlStr) {
const urlObj = {};
const protocol = options.protocol || 'http:';
if (!kSupportedProtocols.has(protocol)) {
throw new Error('Protocol "' + protocol + '" not supported. ');
}
urlObj.protocol = protocol;
if (options.host) {
urlObj.host = options.host;
}
else {
if (options.hostname) {
urlObj.hostname = options.hostname;
}
else {
urlObj.hostname = 'localhost';
}
if (options.port) {
urlObj.port = options.port;
}
}
if (options.path && / /.test(options.path)) {
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
// with an additional rule for ignoring percentage-escaped characters
// but that's a) hard to capture in a regular expression that performs
// well, and b) possibly too restrictive for real-world usage. That's
// why it only scans for spaces because those are guaranteed to create
// an invalid request.
throw new TypeError('Request path contains unescaped characters.');
}
const pathObj = url.parse(options.path || '/');
urlObj.pathname = pathObj.pathname;
urlObj.search = pathObj.search;
urlObj.hash = pathObj.hash;
urlStr = url.format(urlObj);
}
const redirectPolicy = options.redirect || 'follow';
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
throw new Error('redirect mode should be one of follow, error or manual');
}
const urlRequestOptions = {
method: method,
url: urlStr,
redirect: redirectPolicy
};
if (options.session) {
if (options.session instanceof Session) {
urlRequestOptions.session = options.session;
}
else {
throw new TypeError('`session` should be an instance of the Session class.');
}
}
else if (options.partition) {
if (typeof options.partition === 'string') {
urlRequestOptions.partition = options.partition;
}
else {
throw new TypeError('`partition` should be an a string.');
}
}
const urlRequest = new URLRequest(urlRequestOptions);
// Set back and forward links.
this.urlRequest = urlRequest;
urlRequest.clientRequest = this;
// This is a copy of the extra headers structure held by the native
// net::URLRequest. The main reason is to keep the getHeader API synchronous
// after the request starts.
this.extraHeaders = {};
if (options.headers) {
for (const key in options.headers) {
this.setHeader(key, options.headers[key]);
}
}
// Set when the request uses chunked encoding. Can be switched
// to true only once and never set back to false.
this.chunkedEncodingEnabled = false;
urlRequest.on('response', () => {
const response = new IncomingMessage(urlRequest);
urlRequest._response = response;
this.emit('response', response);
});
urlRequest.on('login', (event, authInfo, callback) => {
this.emit('login', authInfo, (username, password) => {
// If null or undefined username/password, force to empty string.
if (username === null || username === undefined) {
username = '';
}
if (typeof username !== 'string') {
throw new Error('username must be a string');
}
if (password === null || password === undefined) {
password = '';
}
if (typeof password !== 'string') {
throw new Error('password must be a string');
}
callback(username, password);
});
});
if (callback) {
this.once('response', callback);
}
} }
get chunkedEncoding() {
return this.chunkedEncodingEnabled; if (typeof options === 'string') {
options = url.parse(options)
} else {
options = Object.assign({}, options)
} }
set chunkedEncoding(value) {
if (!this.urlRequest.notStarted) { const method = (options.method || 'GET').toUpperCase()
throw new Error('Can\'t set the transfer encoding, headers have been sent.'); let urlStr = options.url
if (!urlStr) {
const urlObj = {}
const protocol = options.protocol || 'http:'
if (!kSupportedProtocols.has(protocol)) {
throw new Error('Protocol "' + protocol + '" not supported. ')
}
urlObj.protocol = protocol
if (options.host) {
urlObj.host = options.host
} else {
if (options.hostname) {
urlObj.hostname = options.hostname
} else {
urlObj.hostname = 'localhost'
} }
this.chunkedEncodingEnabled = value;
if (options.port) {
urlObj.port = options.port
}
}
if (options.path && / /.test(options.path)) {
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
// with an additional rule for ignoring percentage-escaped characters
// but that's a) hard to capture in a regular expression that performs
// well, and b) possibly too restrictive for real-world usage. That's
// why it only scans for spaces because those are guaranteed to create
// an invalid request.
throw new TypeError('Request path contains unescaped characters.')
}
const pathObj = url.parse(options.path || '/')
urlObj.pathname = pathObj.pathname
urlObj.search = pathObj.search
urlObj.hash = pathObj.hash
urlStr = url.format(urlObj)
} }
setHeader(name, value) {
if (typeof name !== 'string') { const redirectPolicy = options.redirect || 'follow'
throw new TypeError('`name` should be a string in setHeader(name, value).'); if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
} throw new Error('redirect mode should be one of follow, error or manual')
if (value == null) {
throw new Error('`value` required in setHeader("' + name + '", value).');
}
if (!this.urlRequest.notStarted) {
throw new Error('Can\'t set headers after they are sent.');
}
const key = name.toLowerCase();
this.extraHeaders[key] = value;
this.urlRequest.setExtraHeader(name, value.toString());
} }
getHeader(name) {
if (name == null) { const urlRequestOptions = {
throw new Error('`name` is required for getHeader(name).'); method: method,
} url: urlStr,
if (!this.extraHeaders) { redirect: redirectPolicy
return;
}
const key = name.toLowerCase();
return this.extraHeaders[key];
} }
removeHeader(name) { if (options.session) {
if (name == null) { if (options.session instanceof Session) {
throw new Error('`name` is required for removeHeader(name).'); urlRequestOptions.session = options.session
} } else {
if (!this.urlRequest.notStarted) { throw new TypeError('`session` should be an instance of the Session class.')
throw new Error('Can\'t remove headers after they are sent.'); }
} } else if (options.partition) {
const key = name.toLowerCase(); if (typeof options.partition === 'string') {
delete this.extraHeaders[key]; urlRequestOptions.partition = options.partition
this.urlRequest.removeExtraHeader(name); } else {
throw new TypeError('`partition` should be an a string.')
}
} }
_write(chunk, encoding, callback, isLast) {
const chunkIsString = typeof chunk === 'string'; const urlRequest = new URLRequest(urlRequestOptions)
const chunkIsBuffer = chunk instanceof Buffer;
if (!chunkIsString && !chunkIsBuffer) { // Set back and forward links.
throw new TypeError('First argument must be a string or Buffer.'); this.urlRequest = urlRequest
} urlRequest.clientRequest = this
if (chunkIsString) {
// We convert all strings into binary buffers. // This is a copy of the extra headers structure held by the native
chunk = Buffer.from(chunk, encoding); // net::URLRequest. The main reason is to keep the getHeader API synchronous
} // after the request starts.
// Since writing to the network is asynchronous, we conservatively this.extraHeaders = {}
// assume that request headers are written after delivering the first
// buffer to the network IO thread. if (options.headers) {
if (this.urlRequest.notStarted) { for (const key in options.headers) {
this.urlRequest.setChunkedUpload(this.chunkedEncoding); this.setHeader(key, options.headers[key])
} }
// Headers are assumed to be sent on first call to _writeBuffer,
// i.e. after the first call to write or end.
const result = this.urlRequest.write(chunk, isLast);
// The write callback is fired asynchronously to mimic Node.js.
if (callback) {
process.nextTick(callback);
}
return result;
} }
write(data, encoding, callback) {
if (this.urlRequest.finished) { // Set when the request uses chunked encoding. Can be switched
const error = new Error('Write after end.'); // to true only once and never set back to false.
process.nextTick(writeAfterEndNT, this, error, callback); this.chunkedEncodingEnabled = false
return true;
urlRequest.on('response', () => {
const response = new IncomingMessage(urlRequest)
urlRequest._response = response
this.emit('response', response)
})
urlRequest.on('login', (event, authInfo, callback) => {
this.emit('login', authInfo, (username, password) => {
// If null or undefined username/password, force to empty string.
if (username === null || username === undefined) {
username = ''
} }
return this._write(data, encoding, callback, false); if (typeof username !== 'string') {
} throw new Error('username must be a string')
end(data, encoding, callback) {
if (this.urlRequest.finished) {
return false;
} }
if (typeof data === 'function') { if (password === null || password === undefined) {
callback = data; password = ''
encoding = null;
data = null;
} }
else if (typeof encoding === 'function') { if (typeof password !== 'string') {
callback = encoding; throw new Error('password must be a string')
encoding = null;
} }
data = data || ''; callback(username, password)
return this._write(data, encoding, callback, true); })
})
if (callback) {
this.once('response', callback)
} }
followRedirect() { }
this.urlRequest.followRedirect();
get chunkedEncoding () {
return this.chunkedEncodingEnabled
}
set chunkedEncoding (value) {
if (!this.urlRequest.notStarted) {
throw new Error('Can\'t set the transfer encoding, headers have been sent.')
} }
abort() { this.chunkedEncodingEnabled = value
this.urlRequest.cancel(); }
setHeader (name, value) {
if (typeof name !== 'string') {
throw new TypeError('`name` should be a string in setHeader(name, value).')
} }
getUploadProgress() { if (value == null) {
return this.urlRequest.getUploadProgress(); throw new Error('`value` required in setHeader("' + name + '", value).')
} }
if (!this.urlRequest.notStarted) {
throw new Error('Can\'t set headers after they are sent.')
}
const key = name.toLowerCase()
this.extraHeaders[key] = value
this.urlRequest.setExtraHeader(name, value.toString())
}
getHeader (name) {
if (name == null) {
throw new Error('`name` is required for getHeader(name).')
}
if (!this.extraHeaders) {
return
}
const key = name.toLowerCase()
return this.extraHeaders[key]
}
removeHeader (name) {
if (name == null) {
throw new Error('`name` is required for removeHeader(name).')
}
if (!this.urlRequest.notStarted) {
throw new Error('Can\'t remove headers after they are sent.')
}
const key = name.toLowerCase()
delete this.extraHeaders[key]
this.urlRequest.removeExtraHeader(name)
}
_write (chunk, encoding, callback, isLast) {
const chunkIsString = typeof chunk === 'string'
const chunkIsBuffer = chunk instanceof Buffer
if (!chunkIsString && !chunkIsBuffer) {
throw new TypeError('First argument must be a string or Buffer.')
}
if (chunkIsString) {
// We convert all strings into binary buffers.
chunk = Buffer.from(chunk, encoding)
}
// Since writing to the network is asynchronous, we conservatively
// assume that request headers are written after delivering the first
// buffer to the network IO thread.
if (this.urlRequest.notStarted) {
this.urlRequest.setChunkedUpload(this.chunkedEncoding)
}
// Headers are assumed to be sent on first call to _writeBuffer,
// i.e. after the first call to write or end.
const result = this.urlRequest.write(chunk, isLast)
// The write callback is fired asynchronously to mimic Node.js.
if (callback) {
process.nextTick(callback)
}
return result
}
write (data, encoding, callback) {
if (this.urlRequest.finished) {
const error = new Error('Write after end.')
process.nextTick(writeAfterEndNT, this, error, callback)
return true
}
return this._write(data, encoding, callback, false)
}
end (data, encoding, callback) {
if (this.urlRequest.finished) {
return false
}
if (typeof data === 'function') {
callback = data
encoding = null
data = null
} else if (typeof encoding === 'function') {
callback = encoding
encoding = null
}
data = data || ''
return this._write(data, encoding, callback, true)
}
followRedirect () {
this.urlRequest.followRedirect()
}
abort () {
this.urlRequest.cancel()
}
getUploadProgress () {
return this.urlRequest.getUploadProgress()
}
} }
function writeAfterEndNT(self, error, callback) {
self.emit('error', error); function writeAfterEndNT (self, error, callback) {
if (callback) self.emit('error', error)
callback(error); if (callback) callback(error)
} }
Net.prototype.request = function (options, callback) { Net.prototype.request = function (options, callback) {
return new ClientRequest(options, callback); return new ClientRequest(options, callback)
}; }
net.ClientRequest = ClientRequest;
module.exports = net; net.ClientRequest = ClientRequest
//# sourceMappingURL=net.js.map
module.exports = net

View File

@ -1,7 +1,10 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
const { Notification, isSupported } = process.electronBinding('notification'); const { EventEmitter } = require('events')
Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype); const { Notification, isSupported } = process.atomBinding('notification')
Notification.isSupported = isSupported;
module.exports = Notification; Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype)
//# sourceMappingURL=notification.js.map
Notification.isSupported = isSupported
module.exports = Notification

View File

@ -1,43 +1,25 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
const { powerMonitor, PowerMonitor } = process.electronBinding('power_monitor'); const { EventEmitter } = require('events')
const { deprecate } = require('electron'); const { powerMonitor, PowerMonitor } = process.atomBinding('power_monitor')
// PowerMonitor is an EventEmitter. // PowerMonitor is an EventEmitter.
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype); Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)
EventEmitter.call(powerMonitor); EventEmitter.call(powerMonitor)
// On Linux we need to call blockShutdown() to subscribe to shutdown event. // On Linux we need to call blockShutdown() to subscribe to shutdown event.
if (process.platform === 'linux') { if (process.platform === 'linux') {
powerMonitor.on('newListener', (event) => { powerMonitor.on('newListener', (event) => {
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
powerMonitor.blockShutdown(); powerMonitor.blockShutdown()
} }
}); })
powerMonitor.on('removeListener', (event) => {
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { powerMonitor.on('removeListener', (event) => {
powerMonitor.unblockShutdown(); if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
} powerMonitor.unblockShutdown()
}); }
})
} }
// TODO(nitsakh): Remove in 7.0
powerMonitor.querySystemIdleState = function (threshold, callback) { module.exports = powerMonitor
deprecate.warn('powerMonitor.querySystemIdleState', 'powerMonitor.getSystemIdleState');
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.getSystemIdleState(threshold);
process.nextTick(() => callback(idleState));
};
// TODO(nitsakh): Remove in 7.0
powerMonitor.querySystemIdleTime = function (callback) {
deprecate.warn('powerMonitor.querySystemIdleTime', 'powerMonitor.getSystemIdleTime');
if (typeof callback !== 'function') {
throw new Error('Must pass function as an argument');
}
const idleTime = this.getSystemIdleTime();
process.nextTick(() => callback(idleTime));
};
module.exports = powerMonitor;
//# sourceMappingURL=power-monitor.js.map

View File

@ -1,3 +1,3 @@
'use strict'; 'use strict'
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker;
//# sourceMappingURL=power-save-blocker.js.map module.exports = process.atomBinding('power_save_blocker').powerSaveBlocker

View File

@ -1,27 +1,29 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron"); const { app, session } = require('electron')
// Global protocol APIs. // Global protocol APIs.
const protocol = process.electronBinding('protocol'); module.exports = process.atomBinding('protocol')
// Fallback protocol APIs of default session. // Fallback protocol APIs of default session.
Object.setPrototypeOf(protocol, new Proxy({}, { Object.setPrototypeOf(module.exports, new Proxy({}, {
get(_target, property) { get (target, property) {
if (!electron_1.app.isReady()) if (!app.isReady()) return
return;
const protocol = electron_1.session.defaultSession.protocol; const protocol = session.defaultSession.protocol
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return
return;
// Returning a native function directly would throw error. // Returning a native function directly would throw error.
return (...args) => protocol[property](...args); return (...args) => protocol[property](...args)
}, },
ownKeys() {
if (!electron_1.app.isReady()) ownKeys () {
return []; if (!app.isReady()) return []
return Object.getOwnPropertyNames(Object.getPrototypeOf(electron_1.session.defaultSession.protocol));
}, return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.protocol))
getOwnPropertyDescriptor() { },
return { configurable: true, enumerable: true };
} getOwnPropertyDescriptor (target) {
})); return { configurable: true, enumerable: true }
exports.default = protocol; }
//# sourceMappingURL=protocol.js.map }))

View File

@ -1,8 +1,10 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
const { screen, Screen } = process.electronBinding('screen'); const { EventEmitter } = require('events')
const { screen, Screen } = process.atomBinding('screen')
// Screen is an EventEmitter. // Screen is an EventEmitter.
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype); Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype)
EventEmitter.call(screen); EventEmitter.call(screen)
module.exports = screen;
//# sourceMappingURL=screen.js.map module.exports = screen

View File

@ -1,41 +1,24 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
const { app, deprecate } = require('electron'); const { EventEmitter } = require('events')
const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session'); const { app } = require('electron')
const { fromPartition, Session, Cookies } = process.atomBinding('session')
// Public API. // Public API.
Object.defineProperties(exports, { Object.defineProperties(exports, {
defaultSession: { defaultSession: {
enumerable: true, enumerable: true,
get() { return fromPartition(''); } get () { return fromPartition('') }
}, },
fromPartition: { fromPartition: {
enumerable: true, enumerable: true,
value: fromPartition value: fromPartition
} }
}); })
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype);
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype); Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
Session.prototype._init = function () { Session.prototype._init = function () {
app.emit('session-created', this); app.emit('session-created', this)
}; }
Session.prototype.clearStorageData = deprecate.promisify(Session.prototype.clearStorageData);
Session.prototype.clearHostResolverCache = deprecate.promisify(Session.prototype.clearHostResolverCache);
Session.prototype.resolveProxy = deprecate.promisify(Session.prototype.resolveProxy);
Session.prototype.setProxy = deprecate.promisify(Session.prototype.setProxy);
Session.prototype.getCacheSize = deprecate.promisify(Session.prototype.getCacheSize);
Session.prototype.clearCache = deprecate.promisify(Session.prototype.clearCache);
Session.prototype.clearAuthCache = deprecate.promisify(Session.prototype.clearAuthCache);
Session.prototype.getBlobData = deprecate.promisifyMultiArg(Session.prototype.getBlobData);
Session.prototype.clearAuthCache = ((clearAuthCache) => function (...args) {
if (args.length > 0) {
deprecate.log(`The 'options' argument to 'clearAuthCache' is deprecated. Beginning with Electron 7, clearAuthCache will clear the entire auth cache unconditionally.`);
}
return clearAuthCache.apply(this, args);
})(Session.prototype.clearAuthCache);
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);
NetLog.prototype.stopLogging = deprecate.promisify(NetLog.prototype.stopLogging);
Protocol.prototype.isProtocolHandled = deprecate.promisify(Protocol.prototype.isProtocolHandled);
//# sourceMappingURL=session.js.map

View File

@ -1,8 +1,10 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences'); const { EventEmitter } = require('events')
const { systemPreferences, SystemPreferences } = process.atomBinding('system_preferences')
// SystemPreferences is an EventEmitter. // SystemPreferences is an EventEmitter.
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype); Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
EventEmitter.call(systemPreferences); EventEmitter.call(systemPreferences)
module.exports = systemPreferences;
//# sourceMappingURL=system-preferences.js.map module.exports = systemPreferences

View File

@ -1,20 +1,24 @@
'use strict'; 'use strict'
const electron = require('electron');
const { EventEmitter } = require('events'); const electron = require('electron')
const { TopLevelWindow } = process.electronBinding('top_level_window'); const { EventEmitter } = require('events')
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype); const { TopLevelWindow } = process.atomBinding('top_level_window')
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype)
TopLevelWindow.prototype._init = function () { TopLevelWindow.prototype._init = function () {
// Avoid recursive require. // Avoid recursive require.
const { app } = electron; const { app } = electron
// Simulate the application menu on platforms other than macOS.
if (process.platform !== 'darwin') { // Simulate the application menu on platforms other than macOS.
const menu = app.applicationMenu; if (process.platform !== 'darwin') {
if (menu) const menu = app.getApplicationMenu()
this.setMenu(menu); if (menu) this.setMenu(menu)
} }
}; }
TopLevelWindow.getFocusedWindow = () => { TopLevelWindow.getFocusedWindow = () => {
return TopLevelWindow.getAllWindows().find((win) => win.isFocused()); return TopLevelWindow.getAllWindows().find((win) => win.isFocused())
}; }
module.exports = TopLevelWindow;
//# sourceMappingURL=top-level-window.js.map module.exports = TopLevelWindow

View File

@ -1,311 +1,343 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
let nextItemID = 1; const { EventEmitter } = require('events')
let nextItemID = 1
class TouchBar extends EventEmitter { class TouchBar extends EventEmitter {
// Bind a touch bar to a window // Bind a touch bar to a window
static _setOnWindow(touchBar, window) { static _setOnWindow (touchBar, window) {
if (window._touchBar != null) { if (window._touchBar != null) {
window._touchBar._removeFromWindow(window); window._touchBar._removeFromWindow(window)
}
if (touchBar == null) {
window._setTouchBarItems([]);
return;
}
if (Array.isArray(touchBar)) {
touchBar = new TouchBar(touchBar);
}
touchBar._addToWindow(window);
} }
constructor(options) {
super(); if (touchBar == null) {
if (options == null) { window._setTouchBarItems([])
throw new Error('Must specify options object as first argument'); return
}
let { items, escapeItem } = options;
if (!Array.isArray(items)) {
items = [];
}
this.changeListener = (item) => {
this.emit('change', item.id, item.type);
};
this.windowListeners = {};
this.items = {};
this.ordereredItems = [];
this.escapeItem = escapeItem;
const registerItem = (item) => {
this.items[item.id] = item;
item.on('change', this.changeListener);
if (item.child instanceof TouchBar) {
item.child.ordereredItems.forEach(registerItem);
}
};
items.forEach((item) => {
if (!(item instanceof TouchBarItem)) {
throw new Error('Each item must be an instance of TouchBarItem');
}
this.ordereredItems.push(item);
registerItem(item);
});
} }
set escapeItem(item) {
if (item != null && !(item instanceof TouchBarItem)) { if (Array.isArray(touchBar)) {
throw new Error('Escape item must be an instance of TouchBarItem'); touchBar = new TouchBar(touchBar)
}
touchBar._addToWindow(window)
}
constructor (options) {
super()
if (options == null) {
throw new Error('Must specify options object as first argument')
}
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 = []
}
this.changeListener = (item) => {
this.emit('change', item.id, item.type)
}
this.windowListeners = {}
this.items = {}
this.ordereredItems = []
this.escapeItem = escapeItem
const registerItem = (item) => {
this.items[item.id] = item
item.on('change', this.changeListener)
if (item.child instanceof TouchBar) {
item.child.ordereredItems.forEach(registerItem)
}
}
items.forEach((item) => {
if (!(item instanceof TouchBarItem)) {
throw new Error('Each item must be an instance of TouchBarItem')
}
this.ordereredItems.push(item)
registerItem(item)
})
}
set escapeItem (item) {
if (item != null && !(item instanceof TouchBarItem)) {
throw new Error('Escape item must be an instance of TouchBarItem')
}
if (this.escapeItem != null) {
this.escapeItem.removeListener('change', this.changeListener)
}
this._escapeItem = item
if (this.escapeItem != null) {
this.escapeItem.on('change', this.changeListener)
}
this.emit('escape-item-change', item)
}
get escapeItem () {
return this._escapeItem
}
_addToWindow (window) {
const { id } = window
// Already added to window
if (this.windowListeners.hasOwnProperty(id)) return
window._touchBar = this
const changeListener = (itemID) => {
window._refreshTouchBarItem(itemID)
}
this.on('change', changeListener)
const escapeItemListener = (item) => {
window._setEscapeTouchBarItem(item != null ? item : {})
}
this.on('escape-item-change', escapeItemListener)
const interactionListener = (event, itemID, details) => {
let item = this.items[itemID]
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
item = this.escapeItem
}
if (item != null && item.onInteraction != null) {
item.onInteraction(details)
}
}
window.on('-touch-bar-interaction', interactionListener)
const removeListeners = () => {
this.removeListener('change', changeListener)
this.removeListener('escape-item-change', escapeItemListener)
window.removeListener('-touch-bar-interaction', interactionListener)
window.removeListener('closed', removeListeners)
window._touchBar = null
delete this.windowListeners[id]
const unregisterItems = (items) => {
for (const item of items) {
item.removeListener('change', this.changeListener)
if (item.child instanceof TouchBar) {
unregisterItems(item.child.ordereredItems)
}
} }
if (this.escapeItem != null) { }
this.escapeItem.removeListener('change', this.changeListener); unregisterItems(this.ordereredItems)
} if (this.escapeItem) {
this._escapeItem = item; this.escapeItem.removeListener('change', this.changeListener)
if (this.escapeItem != null) { }
this.escapeItem.on('change', this.changeListener);
}
this.emit('escape-item-change', item);
}
get escapeItem() {
return this._escapeItem;
}
_addToWindow(window) {
const { id } = window;
// Already added to window
if (this.windowListeners.hasOwnProperty(id))
return;
window._touchBar = this;
const changeListener = (itemID) => {
window._refreshTouchBarItem(itemID);
};
this.on('change', changeListener);
const escapeItemListener = (item) => {
window._setEscapeTouchBarItem(item != null ? item : {});
};
this.on('escape-item-change', escapeItemListener);
const interactionListener = (event, itemID, details) => {
let item = this.items[itemID];
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
item = this.escapeItem;
}
if (item != null && item.onInteraction != null) {
item.onInteraction(details);
}
};
window.on('-touch-bar-interaction', interactionListener);
const removeListeners = () => {
this.removeListener('change', changeListener);
this.removeListener('escape-item-change', escapeItemListener);
window.removeListener('-touch-bar-interaction', interactionListener);
window.removeListener('closed', removeListeners);
window._touchBar = null;
delete this.windowListeners[id];
const unregisterItems = (items) => {
for (const item of items) {
item.removeListener('change', this.changeListener);
if (item.child instanceof TouchBar) {
unregisterItems(item.child.ordereredItems);
}
}
};
unregisterItems(this.ordereredItems);
if (this.escapeItem) {
this.escapeItem.removeListener('change', this.changeListener);
}
};
window.once('closed', removeListeners);
this.windowListeners[id] = removeListeners;
window._setTouchBarItems(this.ordereredItems);
escapeItemListener(this.escapeItem);
}
_removeFromWindow(window) {
const removeListeners = this.windowListeners[window.id];
if (removeListeners != null)
removeListeners();
} }
window.once('closed', removeListeners)
this.windowListeners[id] = removeListeners
window._setTouchBarItems(this.ordereredItems)
escapeItemListener(this.escapeItem)
}
_removeFromWindow (window) {
const removeListeners = this.windowListeners[window.id]
if (removeListeners != null) removeListeners()
}
} }
class TouchBarItem extends EventEmitter { class TouchBarItem extends EventEmitter {
constructor() { constructor () {
super(); super()
this._addImmutableProperty('id', `${nextItemID++}`); this._addImmutableProperty('id', `${nextItemID++}`)
this._parents = []; this._parents = []
} }
_addImmutableProperty(name, value) {
Object.defineProperty(this, name, { _addImmutableProperty (name, value) {
get: function () { Object.defineProperty(this, name, {
return value; get: function () {
}, return value
set: function () { },
throw new Error(`Cannot override property ${name}`); set: function () {
}, throw new Error(`Cannot override property ${name}`)
enumerable: true, },
configurable: false enumerable: true,
}); configurable: false
} })
_addLiveProperty(name, initialValue) { }
const privateName = `_${name}`;
this[privateName] = initialValue; _addLiveProperty (name, initialValue) {
Object.defineProperty(this, name, { const privateName = `_${name}`
get: function () { this[privateName] = initialValue
return this[privateName]; Object.defineProperty(this, name, {
}, get: function () {
set: function (value) { return this[privateName]
this[privateName] = value; },
this.emit('change', this); set: function (value) {
}, this[privateName] = value
enumerable: true this.emit('change', this)
}); },
} enumerable: true
_addParent(item) { })
const existing = this._parents.some(test => test.id === item.id); }
if (!existing) {
this._parents.push({ _addParent (item) {
id: item.id, const existing = this._parents.some(test => test.id === item.id)
type: item.type if (!existing) {
}); this._parents.push({
} id: item.id,
type: item.type
})
} }
}
} }
TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem { TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; this._addImmutableProperty('type', 'button')
this._addImmutableProperty('type', 'button'); const { click, icon, iconPosition, label, backgroundColor } = config
const { click, icon, iconPosition, label, backgroundColor } = config; this._addLiveProperty('label', label)
this._addLiveProperty('label', label); this._addLiveProperty('backgroundColor', backgroundColor)
this._addLiveProperty('backgroundColor', backgroundColor); this._addLiveProperty('icon', icon)
this._addLiveProperty('icon', icon); this._addLiveProperty('iconPosition', iconPosition)
this._addLiveProperty('iconPosition', iconPosition); if (typeof click === 'function') {
if (typeof click === 'function') { this._addImmutableProperty('onInteraction', () => {
this._addImmutableProperty('onInteraction', () => { config.click()
config.click(); })
});
}
} }
}; }
}
TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem { TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; this._addImmutableProperty('type', 'colorpicker')
this._addImmutableProperty('type', 'colorpicker'); const { availableColors, change, selectedColor } = config
const { availableColors, change, selectedColor } = config; this._addLiveProperty('availableColors', availableColors)
this._addLiveProperty('availableColors', availableColors); this._addLiveProperty('selectedColor', selectedColor)
this._addLiveProperty('selectedColor', selectedColor);
if (typeof change === 'function') { if (typeof change === 'function') {
this._addImmutableProperty('onInteraction', (details) => { this._addImmutableProperty('onInteraction', (details) => {
this._selectedColor = details.color; this._selectedColor = details.color
change(details.color); change(details.color)
}); })
}
} }
}; }
}
TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem { TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; this._addImmutableProperty('type', 'group')
this._addImmutableProperty('type', 'group'); const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items); this._addLiveProperty('child', defaultChild)
this._addLiveProperty('child', defaultChild); this.child.ordereredItems.forEach((item) => item._addParent(this))
this.child.ordereredItems.forEach((item) => item._addParent(this)); }
} }
};
TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem { TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; this._addImmutableProperty('type', 'label')
this._addImmutableProperty('type', 'label'); this._addLiveProperty('label', config.label)
this._addLiveProperty('label', config.label); this._addLiveProperty('textColor', config.textColor)
this._addLiveProperty('textColor', config.textColor); }
} }
};
TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem { TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; this._addImmutableProperty('type', 'popover')
this._addImmutableProperty('type', 'popover'); this._addLiveProperty('label', config.label)
this._addLiveProperty('label', config.label); this._addLiveProperty('icon', config.icon)
this._addLiveProperty('icon', config.icon); this._addLiveProperty('showCloseButton', config.showCloseButton)
this._addLiveProperty('showCloseButton', config.showCloseButton); const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items); this._addLiveProperty('child', defaultChild)
this._addLiveProperty('child', defaultChild); this.child.ordereredItems.forEach((item) => item._addParent(this))
this.child.ordereredItems.forEach((item) => item._addParent(this)); }
} }
};
TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem { TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; this._addImmutableProperty('type', 'slider')
this._addImmutableProperty('type', 'slider'); const { change, label, minValue, maxValue, value } = config
const { change, label, minValue, maxValue, value } = config; this._addLiveProperty('label', label)
this._addLiveProperty('label', label); this._addLiveProperty('minValue', minValue)
this._addLiveProperty('minValue', minValue); this._addLiveProperty('maxValue', maxValue)
this._addLiveProperty('maxValue', maxValue); this._addLiveProperty('value', value)
this._addLiveProperty('value', value);
if (typeof change === 'function') { if (typeof change === 'function') {
this._addImmutableProperty('onInteraction', (details) => { this._addImmutableProperty('onInteraction', (details) => {
this._value = details.value; this._value = details.value
change(details.value); change(details.value)
}); })
}
} }
}; }
}
TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem { TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; this._addImmutableProperty('type', 'spacer')
this._addImmutableProperty('type', 'spacer'); this._addImmutableProperty('size', config.size)
this._addImmutableProperty('size', config.size); }
} }
};
TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem { TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; const { segmentStyle, segments, selectedIndex, change, mode } = config
const { segmentStyle, segments, selectedIndex, change, mode } = config; this._addImmutableProperty('type', 'segmented_control')
this._addImmutableProperty('type', 'segmented_control'); this._addLiveProperty('segmentStyle', segmentStyle)
this._addLiveProperty('segmentStyle', segmentStyle); this._addLiveProperty('segments', segments || [])
this._addLiveProperty('segments', segments || []); this._addLiveProperty('selectedIndex', selectedIndex)
this._addLiveProperty('selectedIndex', selectedIndex); this._addLiveProperty('mode', mode)
this._addLiveProperty('mode', mode);
if (typeof change === 'function') { if (typeof change === 'function') {
this._addImmutableProperty('onInteraction', (details) => { this._addImmutableProperty('onInteraction', (details) => {
this._selectedIndex = details.selectedIndex; this._selectedIndex = details.selectedIndex
change(details.selectedIndex, details.isSelected); change(details.selectedIndex, details.isSelected)
}); })
}
} }
}; }
}
TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem { TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem {
constructor(config) { constructor (config) {
super(); super()
if (config == null) if (config == null) config = {}
config = {}; const { items, selectedStyle, overlayStyle, showArrowButtons, continuous, mode } = config
const { items, selectedStyle, overlayStyle, showArrowButtons, continuous, mode } = config; let { select, highlight } = config
let { select, highlight } = config; this._addImmutableProperty('type', 'scrubber')
this._addImmutableProperty('type', 'scrubber'); this._addLiveProperty('items', items)
this._addLiveProperty('items', items); this._addLiveProperty('selectedStyle', selectedStyle || null)
this._addLiveProperty('selectedStyle', selectedStyle || null); this._addLiveProperty('overlayStyle', overlayStyle || null)
this._addLiveProperty('overlayStyle', overlayStyle || null); this._addLiveProperty('showArrowButtons', showArrowButtons || false)
this._addLiveProperty('showArrowButtons', showArrowButtons || false); this._addLiveProperty('mode', mode || 'free')
this._addLiveProperty('mode', mode || 'free'); this._addLiveProperty('continuous', typeof continuous === 'undefined' ? true : continuous)
this._addLiveProperty('continuous', typeof continuous === 'undefined' ? true : continuous);
if (typeof select === 'function' || typeof highlight === 'function') { if (typeof select === 'function' || typeof highlight === 'function') {
if (select == null) if (select == null) select = () => {}
select = () => { }; if (highlight == null) highlight = () => {}
if (highlight == null) this._addImmutableProperty('onInteraction', (details) => {
highlight = () => { }; if (details.type === 'select' && typeof select === 'function') {
this._addImmutableProperty('onInteraction', (details) => { select(details.selectedIndex)
if (details.type === 'select' && typeof select === 'function') { } else if (details.type === 'highlight' && typeof highlight === 'function') {
select(details.selectedIndex); highlight(details.highlightedIndex)
}
else if (details.type === 'highlight' && typeof highlight === 'function') {
highlight(details.highlightedIndex);
}
});
} }
})
} }
}; }
module.exports = TouchBar; }
//# sourceMappingURL=touch-bar.js.map
module.exports = TouchBar

View File

@ -1,6 +1,8 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
const { Tray } = process.electronBinding('tray'); const { EventEmitter } = require('events')
Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype); const { Tray } = process.atomBinding('tray')
module.exports = Tray;
//# sourceMappingURL=tray.js.map Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype)
module.exports = Tray

View File

@ -1,8 +1,11 @@
'use strict'; 'use strict'
const { EventEmitter } = require('events');
const { View } = process.electronBinding('view'); const { EventEmitter } = require('events')
Object.setPrototypeOf(View.prototype, EventEmitter.prototype); const { View } = process.atomBinding('view')
Object.setPrototypeOf(View.prototype, EventEmitter.prototype)
View.prototype._init = function () { View.prototype._init = function () {
}; }
module.exports = View;
//# sourceMappingURL=view.js.map module.exports = View

View File

@ -1,11 +1,15 @@
'use strict'; 'use strict'
const electron = require('electron');
const { View } = electron; const electron = require('electron')
const { WebContentsView } = process.electronBinding('web_contents_view');
Object.setPrototypeOf(WebContentsView.prototype, View.prototype); const { View } = electron
const { WebContentsView } = process.atomBinding('web_contents_view')
Object.setPrototypeOf(WebContentsView.prototype, View.prototype)
WebContentsView.prototype._init = function () { WebContentsView.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
View.prototype._init.call(this); View.prototype._init.call(this)
}; }
module.exports = WebContentsView;
//# sourceMappingURL=web-contents-view.js.map module.exports = WebContentsView

View File

@ -1,447 +1,507 @@
'use strict'; 'use strict'
const features = process.electronBinding('features');
const { EventEmitter } = require('events'); const features = process.atomBinding('features')
const electron = require('electron'); const { EventEmitter } = require('events')
const path = require('path'); const electron = require('electron')
const url = require('url'); const path = require('path')
const { app, ipcMain, session, deprecate } = electron; const url = require('url')
const NavigationController = require('@electron/internal/browser/navigation-controller'); const v8Util = process.atomBinding('v8_util')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); const { app, ipcMain, session, NavigationController, deprecate } = electron
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const errorUtils = require('@electron/internal/common/error-utils'); const ipcMainInternal = require('@electron/internal/browser/ipc-main-internal')
const errorUtils = require('@electron/internal/common/error-utils')
// session is not used here, the purpose is to make sure session is initalized // session is not used here, the purpose is to make sure session is initalized
// before the webContents module. // before the webContents module.
// eslint-disable-next-line // eslint-disable-next-line
session; session
let nextId = 0;
let nextId = 0
const getNextId = function () { const getNextId = function () {
return ++nextId; return ++nextId
}; }
// Stock page sizes // Stock page sizes
const PDFPageSizes = { const PDFPageSizes = {
A5: { A5: {
custom_display_name: 'A5', custom_display_name: 'A5',
height_microns: 210000, height_microns: 210000,
name: 'ISO_A5', name: 'ISO_A5',
width_microns: 148000 width_microns: 148000
}, },
A4: { A4: {
custom_display_name: 'A4', custom_display_name: 'A4',
height_microns: 297000, height_microns: 297000,
name: 'ISO_A4', name: 'ISO_A4',
is_default: 'true', is_default: 'true',
width_microns: 210000 width_microns: 210000
}, },
A3: { A3: {
custom_display_name: 'A3', custom_display_name: 'A3',
height_microns: 420000, height_microns: 420000,
name: 'ISO_A3', name: 'ISO_A3',
width_microns: 297000 width_microns: 297000
}, },
Legal: { Legal: {
custom_display_name: 'Legal', custom_display_name: 'Legal',
height_microns: 355600, height_microns: 355600,
name: 'NA_LEGAL', name: 'NA_LEGAL',
width_microns: 215900 width_microns: 215900
}, },
Letter: { Letter: {
custom_display_name: 'Letter', custom_display_name: 'Letter',
height_microns: 279400, height_microns: 279400,
name: 'NA_LETTER', name: 'NA_LETTER',
width_microns: 215900 width_microns: 215900
}, },
Tabloid: { Tabloid: {
height_microns: 431800, height_microns: 431800,
name: 'NA_LEDGER', name: 'NA_LEDGER',
width_microns: 279400, width_microns: 279400,
custom_display_name: 'Tabloid' custom_display_name: 'Tabloid'
} }
}; }
// Default printing setting // Default printing setting
const defaultPrintingSetting = { const defaultPrintingSetting = {
pageRage: [], pageRage: [],
mediaSize: {}, mediaSize: {},
landscape: false, landscape: false,
color: 2, color: 2,
headerFooterEnabled: false, headerFooterEnabled: false,
marginsType: 0, marginsType: 0,
isFirstRequest: false, isFirstRequest: false,
requestID: getNextId(), requestID: getNextId(),
previewUIID: 0, previewUIID: 0,
previewModifiable: true, previewModifiable: true,
printToPDF: true, printToPDF: true,
printWithCloudPrint: false, printWithCloudPrint: false,
printWithPrivet: false, printWithPrivet: false,
printWithExtension: false, printWithExtension: false,
pagesPerSheet: 1, deviceName: 'Save as PDF',
deviceName: 'Save as PDF', generateDraftData: true,
generateDraftData: true, fitToPageEnabled: false,
fitToPageEnabled: false, scaleFactor: 1,
scaleFactor: 1, dpiHorizontal: 72,
dpiHorizontal: 72, dpiVertical: 72,
dpiVertical: 72, rasterizePDF: false,
rasterizePDF: false, duplex: 0,
duplex: 0, copies: 1,
copies: 1, collate: true,
collate: true, shouldPrintBackgrounds: false,
shouldPrintBackgrounds: false, shouldPrintSelectionOnly: false
shouldPrintSelectionOnly: false }
};
// JavaScript implementations of WebContents. // JavaScript implementations of WebContents.
const binding = process.electronBinding('web_contents'); const binding = process.atomBinding('web_contents')
const { WebContents } = binding; const { WebContents } = binding
Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype);
Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype); Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype)
Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype)
// WebContents::send(channel, args..) // WebContents::send(channel, args..)
// WebContents::sendToAll(channel, args..) // WebContents::sendToAll(channel, args..)
WebContents.prototype.send = function (channel, ...args) { WebContents.prototype.send = function (channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument'); throw new Error('Missing required channel argument')
} }
const internal = false;
const sendToAll = false; const internal = false
return this._send(internal, sendToAll, channel, args); const sendToAll = false
};
return this._send(internal, sendToAll, channel, args)
}
WebContents.prototype.sendToAll = function (channel, ...args) { WebContents.prototype.sendToAll = function (channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument'); throw new Error('Missing required channel argument')
} }
const internal = false;
const sendToAll = true; const internal = false
return this._send(internal, sendToAll, channel, args); const sendToAll = true
};
return this._send(internal, sendToAll, channel, args)
}
WebContents.prototype._sendInternal = function (channel, ...args) { WebContents.prototype._sendInternal = function (channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument'); throw new Error('Missing required channel argument')
} }
const internal = true;
const sendToAll = false; const internal = true
return this._send(internal, sendToAll, channel, args); const sendToAll = false
};
return this._send(internal, sendToAll, channel, args)
}
WebContents.prototype._sendInternalToAll = function (channel, ...args) { WebContents.prototype._sendInternalToAll = function (channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument'); throw new Error('Missing required channel argument')
} }
const internal = true;
const sendToAll = true; const internal = true
return this._send(internal, sendToAll, channel, args); const sendToAll = true
};
WebContents.prototype.sendToFrame = function (frameId, channel, ...args) { return this._send(internal, sendToAll, 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. // Following methods are mapped to webFrame.
const webFrameMethods = [ const webFrameMethods = [
'insertCSS', 'insertCSS',
'insertText', 'insertText',
'setLayoutZoomLevelLimits', 'setLayoutZoomLevelLimits',
'setVisualZoomLevelLimits' 'setVisualZoomLevelLimits'
]; ]
for (const method of webFrameMethods) {
WebContents.prototype[method] = function (...args) { const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args); return new Promise((resolve, reject) => {
}; ipcMainInternal.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, error, result) {
if (error == null) {
if (typeof callback === 'function') callback(result)
resolve(result)
} else {
reject(errorUtils.deserialize(error))
}
})
this._sendInternal('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
})
} }
const executeJavaScript = (contents, code, hasUserGesture) => {
return ipcMainUtils.invokeInWebContents(contents, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture); for (const method of webFrameMethods) {
}; WebContents.prototype[method] = function (...args) {
this._sendInternal('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
}
}
// Make sure WebContents::executeJavaScript would run the code only when the // Make sure WebContents::executeJavaScript would run the code only when the
// WebContents has been loaded. // WebContents has been loaded.
WebContents.prototype.executeJavaScript = function (code, hasUserGesture) { WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callback) {
if (this.getURL() && !this.isLoadingMainFrame()) { const requestId = getNextId()
return executeJavaScript(this, code, hasUserGesture);
} if (typeof hasUserGesture === 'function') {
else { // Shift.
return new Promise((resolve, reject) => { callback = hasUserGesture
this.once('did-stop-loading', () => { hasUserGesture = null
executeJavaScript(this, code, hasUserGesture).then(resolve, reject); }
});
}); if (hasUserGesture == null) {
} hasUserGesture = false
}; }
// TODO(codebytere): remove when promisifications is complete
const nativeZoomLevel = WebContents.prototype.getZoomLevel; if (this.getURL() && !this.isLoadingMainFrame()) {
WebContents.prototype.getZoomLevel = function (callback) { return asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
if (callback == null) { } else {
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) => { return new Promise((resolve, reject) => {
this._takeHeapSnapshot(filePath, (success) => { this.once('did-stop-loading', () => {
if (success) { asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture).then(resolve).catch(reject)
resolve(); })
} })
else { }
reject(new Error('takeHeapSnapshot failed')); }
}
}); WebContents.prototype.takeHeapSnapshot = function (filePath) {
}); return new Promise((resolve, reject) => {
}; const channel = `ELECTRON_TAKE_HEAP_SNAPSHOT_RESULT_${getNextId()}`
ipcMainInternal.once(channel, (event, success) => {
if (success) {
resolve()
} else {
reject(new Error('takeHeapSnapshot failed'))
}
})
if (!this._takeHeapSnapshot(filePath, channel)) {
ipcMainInternal.emit(channel, false)
}
})
}
// Translate the options of printToPDF. // Translate the options of printToPDF.
WebContents.prototype.printToPDF = function (options) { WebContents.prototype.printToPDF = function (options, callback) {
const printingSetting = Object.assign({}, defaultPrintingSetting); const printingSetting = Object.assign({}, defaultPrintingSetting)
if (options.landscape) { if (options.landscape) {
printingSetting.landscape = options.landscape; printingSetting.landscape = options.landscape
}
if (options.marginsType) {
printingSetting.marginsType = options.marginsType
}
if (options.printSelectionOnly) {
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
}
if (options.printBackground) {
printingSetting.shouldPrintBackgrounds = options.printBackground
}
if (options.pageSize) {
const pageSize = options.pageSize
if (typeof pageSize === 'object') {
if (!pageSize.height || !pageSize.width) {
return callback(new Error('Must define height and width for pageSize'))
}
// Dimensions in Microns
// 1 meter = 10^6 microns
printingSetting.mediaSize = {
name: 'CUSTOM',
custom_display_name: 'Custom',
height_microns: Math.ceil(pageSize.height),
width_microns: Math.ceil(pageSize.width)
}
} else if (PDFPageSizes[pageSize]) {
printingSetting.mediaSize = PDFPageSizes[pageSize]
} else {
return callback(new Error(`Does not support pageSize with ${pageSize}`))
} }
if (options.marginsType) { } else {
printingSetting.marginsType = options.marginsType; printingSetting.mediaSize = PDFPageSizes['A4']
} }
if (options.printSelectionOnly) {
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly; // Chromium expects this in a 0-100 range number, not as float
} printingSetting.scaleFactor *= 100
if (options.printBackground) { if (features.isPrintingEnabled()) {
printingSetting.shouldPrintBackgrounds = options.printBackground; this._printToPDF(printingSetting, callback)
} } else {
if (options.pageSize) { console.error('Error: Printing feature is disabled.')
const pageSize = options.pageSize; }
if (typeof pageSize === 'object') { }
if (!pageSize.height || !pageSize.width) {
return Promise.reject(new Error('Must define height and width for pageSize'));
}
// Dimensions in Microns
// 1 meter = 10^6 microns
printingSetting.mediaSize = {
name: 'CUSTOM',
custom_display_name: 'Custom',
height_microns: Math.ceil(pageSize.height),
width_microns: Math.ceil(pageSize.width)
};
}
else if (PDFPageSizes[pageSize]) {
printingSetting.mediaSize = PDFPageSizes[pageSize];
}
else {
return Promise.reject(new Error(`Does not support pageSize with ${pageSize}`));
}
}
else {
printingSetting.mediaSize = PDFPageSizes['A4'];
}
// Chromium expects this in a 0-100 range number, not as float
printingSetting.scaleFactor *= 100;
if (features.isPrintingEnabled()) {
return this._printToPDF(printingSetting);
}
else {
return Promise.reject(new Error('Printing feature is disabled'));
}
};
WebContents.prototype.print = function (...args) { WebContents.prototype.print = function (...args) {
if (features.isPrintingEnabled()) { if (features.isPrintingEnabled()) {
this._print(...args); this._print(...args)
} } else {
else { console.error('Error: Printing feature is disabled.')
console.error('Error: Printing feature is disabled.'); }
} }
};
WebContents.prototype.getPrinters = function () { WebContents.prototype.getPrinters = function () {
if (features.isPrintingEnabled()) { if (features.isPrintingEnabled()) {
return this._getPrinters(); return this._getPrinters()
} } else {
else { console.error('Error: Printing feature is disabled.')
console.error('Error: Printing feature is disabled.'); }
} }
};
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 = {}) { WebContents.prototype.loadFile = function (filePath, options = {}) {
if (typeof filePath !== 'string') { if (typeof filePath !== 'string') {
throw new Error('Must pass filePath as a string'); throw new Error('Must pass filePath as a string')
} }
const { query, search, hash } = options; const { query, search, hash } = options
return this.loadURL(url.format({
protocol: 'file', return this.loadURL(url.format({
slashes: true, protocol: 'file',
pathname: path.resolve(app.getAppPath(), filePath), slashes: true,
query, pathname: path.resolve(app.getAppPath(), filePath),
search, query,
hash search,
})); hash
}; }))
WebContents.prototype.capturePage = deprecate.promisify(WebContents.prototype.capturePage); }
WebContents.prototype.executeJavaScript = deprecate.promisify(WebContents.prototype.executeJavaScript);
WebContents.prototype.printToPDF = deprecate.promisify(WebContents.prototype.printToPDF); WebContents.prototype.getZoomFactor = function (callback) {
WebContents.prototype.savePage = deprecate.promisify(WebContents.prototype.savePage); if (typeof callback !== 'function') {
const addReplyToEvent = (event) => { throw new Error('Must pass function as an argument')
event.reply = (...args) => { }
event.sender.sendToFrame(event.frameId, ...args); process.nextTick(() => {
}; const zoomFactor = this._getZoomFactor()
}; callback(zoomFactor)
const addReplyInternalToEvent = (event) => { })
Object.defineProperty(event, '_replyInternal', { }
configurable: false,
enumerable: false, WebContents.prototype.findInPage = function (text, options = {}) {
value: (...args) => { // TODO (nitsakh): Remove in 5.0
event.sender._sendToFrameInternal(event.frameId, ...args); if (options.wordStart != null || options.medialCapitalAtWordStart != null) {
} deprecate.log('wordStart and medialCapitalAtWordStart options are deprecated')
}); }
}; return this._findInPage(text, options)
const addReturnValueToEvent = (event) => { }
Object.defineProperty(event, 'returnValue', {
set: (value) => event.sendReply([value]), const safeProtocols = new Set([
get: () => { } 'chrome-devtools:',
}); 'chrome-extension:'
}; ])
const isWebContentsTrusted = function (contents) {
const pageURL = contents._getURL()
const { protocol } = url.parse(pageURL)
return safeProtocols.has(protocol)
}
// Add JavaScript wrappers for WebContents class. // Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () { WebContents.prototype._init = function () {
// The navigation controller. // The navigation controller.
NavigationController.call(this, this); NavigationController.call(this, this)
// Every remote callback from renderer process would add a listener to the
// render-view-deleted event, so ignore the listeners warning. // Every remote callback from renderer process would add a listenter to the
this.setMaxListeners(0); // render-view-deleted event, so ignore the listenters warning.
// Dispatch IPC messages to the ipc module. this.setMaxListeners(0)
this.on('-ipc-message', function (event, internal, channel, args) {
if (internal) { // Dispatch IPC messages to the ipc module.
addReplyInternalToEvent(event); this.on('ipc-message', function (event, [channel, ...args]) {
ipcMainInternal.emit(channel, event, ...args); ipcMain.emit(channel, event, ...args)
})
this.on('ipc-message-sync', function (event, [channel, ...args]) {
Object.defineProperty(event, 'returnValue', {
set: function (value) {
return event.sendReply([value])
},
get: function () {}
})
ipcMain.emit(channel, event, ...args)
})
this.on('ipc-internal-message', function (event, [channel, ...args]) {
ipcMainInternal.emit(channel, event, ...args)
})
this.on('ipc-internal-message-sync', function (event, [channel, ...args]) {
Object.defineProperty(event, 'returnValue', {
set: function (value) {
return event.sendReply([value])
},
get: function () {}
})
ipcMainInternal.emit(channel, event, ...args)
})
// Handle context menu action request from pepper plugin.
this.on('pepper-context-menu', function (event, params, callback) {
// Access Menu via electron.Menu to prevent circular require.
const menu = electron.Menu.buildFromTemplate(params.menu)
menu.popup({
window: event.sender.getOwnerBrowserWindow(),
x: params.x,
y: params.y,
callback
})
})
const forwardedEvents = [
'remote-require',
'remote-get-global',
'remote-get-builtin',
'remote-get-current-window',
'remote-get-current-web-contents',
'remote-get-guest-web-contents'
]
for (const eventName of forwardedEvents) {
this.on(eventName, (event, ...args) => {
if (!isWebContentsTrusted(event.sender)) {
app.emit(eventName, event, this, ...args)
}
})
}
deprecate.event(this, 'did-get-response-details', '-did-get-response-details')
deprecate.event(this, 'did-get-redirect-request', '-did-get-redirect-request')
// The devtools requests the webContents to reload.
this.on('devtools-reload-page', function () {
this.reload()
})
// Handle window.open for BrowserWindow and BrowserView.
if (['browserView', 'window'].includes(this.getType())) {
// Make new windows requested by links behave like "window.open"
this.webContents.on('-new-window', (event, url, frameName, disposition,
additionalFeatures, postData,
referrer) => {
const options = {
show: true,
width: 800,
height: 600
}
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
event, url, referrer, frameName, disposition,
options, additionalFeatures, postData)
})
this.webContents.on('-web-contents-created', (event, webContents, url,
frameName) => {
v8Util.setHiddenValue(webContents, 'url-framename', { url, frameName })
})
// Create a new browser window for the native implementation of
// "window.open", used in sandbox and nativeWindowOpen mode
this.webContents.on('-add-new-contents', (event, webContents, disposition,
userGesture, left, top, width,
height) => {
const urlFrameName = v8Util.getHiddenValue(webContents, 'url-framename')
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
disposition !== 'background-tab') || !urlFrameName) {
event.preventDefault()
return
}
if (webContents.getLastWebPreferences().nodeIntegration === true) {
const message =
'Enabling Node.js integration in child windows opened with the ' +
'"nativeWindowOpen" option will cause memory leaks, please turn off ' +
'the "nodeIntegration" option.\\n' +
'From 5.x child windows opened with the "nativeWindowOpen" option ' +
'will always have Node.js integration disabled.\\n' +
'See https://github.com/electron/electron/pull/15076 for more.'
// console is only available after DOM is created.
const printWarning = () => this.webContents.executeJavaScript(`console.warn('${message}')`)
if (this.webContents.isDomReady()) {
printWarning()
} else {
this.webContents.once('dom-ready', printWarning)
} }
else { }
addReplyToEvent(event);
this.emit('ipc-message', event, channel, ...args); const { url, frameName } = urlFrameName
ipcMain.emit(channel, event, ...args); v8Util.deleteHiddenValue(webContents, 'url-framename')
} const options = {
}); show: true,
this.on('-ipc-message-sync', function (event, internal, channel, args) { x: left,
addReturnValueToEvent(event); y: top,
if (internal) { width: width || 800,
addReplyInternalToEvent(event); height: height || 600,
ipcMainInternal.emit(channel, event, ...args); webContents
} }
else { const referrer = { url: '', policy: 'default' }
addReplyToEvent(event); ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
this.emit('ipc-message-sync', event, channel, ...args); event, url, referrer, frameName, disposition, options)
ipcMain.emit(channel, event, ...args); })
} }
});
// Handle context menu action request from pepper plugin. app.emit('web-contents-created', {}, this)
this.on('pepper-context-menu', function (event, params, callback) { }
// Access Menu via electron.Menu to prevent circular require.
const menu = electron.Menu.buildFromTemplate(params.menu);
menu.popup({
window: event.sender.getOwnerBrowserWindow(),
x: params.x,
y: params.y,
callback
});
});
const forwardedEvents = [
'desktop-capturer-get-sources',
'remote-require',
'remote-get-global',
'remote-get-builtin',
'remote-get-current-window',
'remote-get-current-web-contents',
'remote-get-guest-web-contents'
];
for (const eventName of forwardedEvents) {
this.on(eventName, (event, ...args) => {
app.emit(eventName, event, this, ...args);
});
}
this.on('crashed', (event, ...args) => {
app.emit('renderer-process-crashed', event, this, ...args);
});
deprecate.event(this, 'did-get-response-details', '-did-get-response-details');
deprecate.event(this, 'did-get-redirect-request', '-did-get-redirect-request');
// The devtools requests the webContents to reload.
this.on('devtools-reload-page', function () {
this.reload();
});
// Handle window.open for BrowserWindow and BrowserView.
if (['browserView', 'window'].includes(this.getType())) {
// Make new windows requested by links behave like "window.open".
this.on('-new-window', (event, url, frameName, disposition, additionalFeatures, postData, referrer) => {
const options = {
show: true,
width: 800,
height: 600
};
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event, url, referrer, frameName, disposition, options, additionalFeatures, postData);
});
// Create a new browser window for the native implementation of
// "window.open", used in sandbox and nativeWindowOpen mode.
this.on('-add-new-contents', (event, webContents, disposition, userGesture, left, top, width, height, url, frameName) => {
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
disposition !== 'background-tab')) {
event.preventDefault();
return;
}
const options = {
show: true,
x: left,
y: top,
width: width || 800,
height: height || 600,
webContents
};
const referrer = { url: '', policy: 'default' };
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event, url, referrer, frameName, disposition, options);
});
}
app.emit('web-contents-created', {}, this);
};
// JavaScript wrapper of Debugger. // JavaScript wrapper of Debugger.
const { Debugger } = process.electronBinding('debugger'); const { Debugger } = process.atomBinding('debugger')
Debugger.prototype.sendCommand = deprecate.promisify(Debugger.prototype.sendCommand);
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype); Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)
// Public APIs. // Public APIs.
module.exports = { module.exports = {
create(options = {}) { create (options = {}) {
return binding.create(options); return binding.create(options)
}, },
fromId(id) {
return binding.fromId(id); fromId (id) {
}, return binding.fromId(id)
getFocusedWebContents() { },
let focused = null;
for (const contents of binding.getAllWebContents()) { getFocusedWebContents () {
if (!contents.isFocused()) let focused = null
continue; for (const contents of binding.getAllWebContents()) {
if (focused == null) if (!contents.isFocused()) continue
focused = contents; if (focused == null) focused = contents
// Return webview web contents which may be embedded inside another // Return webview web contents which may be embedded inside another
// web contents that is also reporting as focused // web contents that is also reporting as focused
if (contents.getType() === 'webview') if (contents.getType() === 'webview') return contents
return contents;
}
return focused;
},
getAllWebContents() {
return binding.getAllWebContents();
} }
}; return focused
//# sourceMappingURL=web-contents.js.map },
getAllWebContents () {
return binding.getAllWebContents()
}
}

View File

@ -1,490 +1,446 @@
'use strict'; 'use strict'
const { app, webContents, BrowserWindow } = require('electron');
const { getAllWebContents } = process.electronBinding('web_contents'); const { app, webContents, BrowserWindow } = require('electron')
const renderProcessPreferences = process.electronBinding('render_process_preferences').forAllWebContents(); const { getAllWebContents } = process.atomBinding('web_contents')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllWebContents()
const { Buffer } = require('buffer'); const ipcMain = require('@electron/internal/browser/ipc-main-internal')
const fs = require('fs');
const path = require('path'); const { Buffer } = require('buffer')
const url = require('url'); const fs = require('fs')
const util = require('util'); const path = require('path')
const readFile = util.promisify(fs.readFile); const url = require('url')
const writeFile = util.promisify(fs.writeFile);
// Mapping between extensionId(hostname) and manifest. // Mapping between extensionId(hostname) and manifest.
const manifestMap = {}; // extensionId => manifest const manifestMap = {} // extensionId => manifest
const manifestNameMap = {}; // name => manifest const manifestNameMap = {} // name => manifest
const devToolsExtensionNames = new Set(); const devToolsExtensionNames = new Set()
const generateExtensionIdFromName = function (name) { const generateExtensionIdFromName = function (name) {
return name.replace(/[\W_]+/g, '-').toLowerCase(); return name.replace(/[\W_]+/g, '-').toLowerCase()
}; }
const isWindowOrWebView = function (webContents) { const isWindowOrWebView = function (webContents) {
const type = webContents.getType(); const type = webContents.getType()
return type === 'window' || type === 'webview'; return type === 'window' || type === 'webview'
}; }
const isBackgroundPage = function (webContents) {
return webContents.getType() === 'backgroundPage';
};
// Create or get manifest object from |srcDirectory|. // Create or get manifest object from |srcDirectory|.
const getManifestFromPath = function (srcDirectory) { const getManifestFromPath = function (srcDirectory) {
let manifest; let manifest
let manifestContent; let manifestContent
try {
manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json')); try {
} manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
catch (readError) { } catch (readError) {
console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`); console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`)
console.warn(readError.stack || readError); console.warn(readError.stack || readError)
throw readError; throw readError
} }
try {
manifest = JSON.parse(manifestContent); try {
} manifest = JSON.parse(manifestContent)
catch (parseError) { } catch (parseError) {
console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`); console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`)
console.warn(parseError.stack || parseError); console.warn(parseError.stack || parseError)
throw parseError; throw parseError
} }
if (!manifestNameMap[manifest.name]) {
const extensionId = generateExtensionIdFromName(manifest.name); if (!manifestNameMap[manifest.name]) {
manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest; const extensionId = generateExtensionIdFromName(manifest.name)
Object.assign(manifest, { manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest
srcDirectory: srcDirectory, Object.assign(manifest, {
extensionId: extensionId, srcDirectory: srcDirectory,
// We can not use 'file://' directly because all resources in the extension extensionId: extensionId,
// will be treated as relative to the root in Chrome. // We can not use 'file://' directly because all resources in the extension
startPage: url.format({ // will be treated as relative to the root in Chrome.
protocol: 'chrome-extension', startPage: url.format({
slashes: true,
hostname: extensionId,
pathname: manifest.devtools_page
})
});
return manifest;
}
else if (manifest && manifest.name) {
console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`);
return manifest;
}
};
// Manage the background pages.
const backgroundPages = {};
const startBackgroundPages = function (manifest) {
if (backgroundPages[manifest.extensionId] || !manifest.background)
return;
let html;
let name;
if (manifest.background.page) {
name = manifest.background.page;
html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page));
}
else {
name = '_generated_background_page.html';
const scripts = manifest.background.scripts.map((name) => {
return `<script src="${name}"></script>`;
}).join('');
html = Buffer.from(`<html><body>${scripts}</body></html>`);
}
const contents = webContents.create({
partition: 'persist:__chrome_extension',
isBackgroundPage: true,
sandbox: true,
enableRemoteModule: false
});
backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name };
contents.loadURL(url.format({
protocol: 'chrome-extension', protocol: 'chrome-extension',
slashes: true, slashes: true,
hostname: manifest.extensionId, hostname: extensionId,
pathname: name pathname: manifest.devtools_page
})); })
}; })
return manifest
} else if (manifest && manifest.name) {
console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`)
return manifest
}
}
// Manage the background pages.
const backgroundPages = {}
const startBackgroundPages = function (manifest) {
if (backgroundPages[manifest.extensionId] || !manifest.background) return
let html
let name
if (manifest.background.page) {
name = manifest.background.page
html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page))
} else {
name = '_generated_background_page.html'
const scripts = manifest.background.scripts.map((name) => {
return `<script src="${name}"></script>`
}).join('')
html = Buffer.from(`<html><body>${scripts}</body></html>`)
}
const contents = webContents.create({
partition: 'persist:__chrome_extension',
isBackgroundPage: true,
commandLineSwitches: ['--background-page']
})
backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name }
contents.loadURL(url.format({
protocol: 'chrome-extension',
slashes: true,
hostname: manifest.extensionId,
pathname: name
}))
}
const removeBackgroundPages = function (manifest) { const removeBackgroundPages = function (manifest) {
if (!backgroundPages[manifest.extensionId]) if (!backgroundPages[manifest.extensionId]) return
return;
backgroundPages[manifest.extensionId].webContents.destroy(); backgroundPages[manifest.extensionId].webContents.destroy()
delete backgroundPages[manifest.extensionId]; delete backgroundPages[manifest.extensionId]
}; }
const sendToBackgroundPages = function (...args) { const sendToBackgroundPages = function (...args) {
for (const page of Object.values(backgroundPages)) { for (const page of Object.values(backgroundPages)) {
if (!page.webContents.isDestroyed()) { page.webContents._sendInternalToAll(...args)
page.webContents._sendInternalToAll(...args); }
} }
}
};
// Dispatch web contents events to Chrome APIs // Dispatch web contents events to Chrome APIs
const hookWebContentsEvents = function (webContents) { const hookWebContentsEvents = function (webContents) {
const tabId = webContents.id; const tabId = webContents.id
sendToBackgroundPages('CHROME_TABS_ONCREATED');
webContents.on('will-navigate', (event, url) => { sendToBackgroundPages('CHROME_TABS_ONCREATED')
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
frameId: 0, webContents.on('will-navigate', (event, url) => {
parentFrameId: -1, sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
processId: webContents.getProcessId(), frameId: 0,
tabId: tabId, parentFrameId: -1,
timeStamp: Date.now(), processId: webContents.getProcessId(),
url: url tabId: tabId,
}); timeStamp: Date.now(),
}); url: url
webContents.on('did-navigate', (event, url) => { })
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', { })
frameId: 0,
parentFrameId: -1, webContents.on('did-navigate', (event, url) => {
processId: webContents.getProcessId(), sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', {
tabId: tabId, frameId: 0,
timeStamp: Date.now(), parentFrameId: -1,
url: url processId: webContents.getProcessId(),
}); tabId: tabId,
}); timeStamp: Date.now(),
webContents.once('destroyed', () => { url: url
sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId); })
}); })
};
webContents.once('destroyed', () => {
sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId)
})
}
// Handle the chrome.* API messages. // Handle the chrome.* API messages.
let nextId = 0; let nextId = 0
ipcMainUtils.handle('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
if (isBackgroundPage(event.sender)) { ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
throw new Error('chrome.runtime.connect is not supported in background page'); const page = backgroundPages[extensionId]
} if (!page) {
const page = backgroundPages[extensionId]; console.error(`Connect to unknown extension ${extensionId}`)
if (!page || page.webContents.isDestroyed()) { return
throw new Error(`Connect to unknown extension ${extensionId}`); }
}
const tabId = page.webContents.id; const portId = ++nextId
const portId = ++nextId; event.returnValue = { tabId: page.webContents.id, portId: portId }
event.sender.once('render-view-deleted', () => {
if (page.webContents.isDestroyed()) event.sender.once('render-view-deleted', () => {
return; if (page.webContents.isDestroyed()) return
page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`); page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`)
}); })
page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo); page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo)
return { tabId, portId }; })
});
ipcMainUtils.handle('CHROME_EXTENSION_MANIFEST', function (event, extensionId) { ipcMain.on('CHROME_I18N_MANIFEST', function (event, extensionId) {
const manifest = manifestMap[extensionId]; event.returnValue = manifestMap[extensionId]
if (!manifest) { })
throw new Error(`Invalid extensionId: ${extensionId}`);
} let resultID = 1
return manifest; ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message, originResultID) {
}); const page = backgroundPages[extensionId]
ipcMainUtils.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) { if (!page) {
if (isBackgroundPage(event.sender)) { console.error(`Connect to unknown extension ${extensionId}`)
throw new Error('chrome.runtime.sendMessage is not supported in background page'); return
} }
const page = backgroundPages[extensionId];
if (!page || page.webContents.isDestroyed()) { page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message, resultID)
throw new Error(`Connect to unknown extension ${extensionId}`); ipcMain.once(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, (event, result) => {
} event.sender._sendInternal(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, result)
return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message); })
}); resultID++
ipcMainUtils.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) { })
const contents = webContents.fromId(tabId);
if (!contents) { ipcMain.on('CHROME_TABS_SEND_MESSAGE', function (event, tabId, extensionId, isBackgroundPage, message, originResultID) {
throw new Error(`Sending message to unknown tab ${tabId}`); const contents = webContents.fromId(tabId)
} if (!contents) {
const senderTabId = isBackgroundPage(event.sender) ? null : event.sender.id; console.error(`Sending message to unknown tab ${tabId}`)
return ipcMainUtils.invokeInWebContents(contents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message); return
}); }
const getLanguage = () => {
return app.getLocale().replace(/-.*$/, '').toLowerCase(); const senderTabId = isBackgroundPage ? null : event.sender.id
};
const getMessagesPath = (extensionId) => { contents._sendInternalToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message, resultID)
const metadata = manifestMap[extensionId]; ipcMain.once(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, (event, result) => {
if (!metadata) { event.sender._sendInternal(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, result)
throw new Error(`Invalid extensionId: ${extensionId}`); })
} resultID++
const localesDirectory = path.join(metadata.srcDirectory, '_locales'); })
const language = getLanguage();
try {
const filename = path.join(localesDirectory, language, 'messages.json');
fs.accessSync(filename, fs.constants.R_OK);
return filename;
}
catch (_a) {
const defaultLocale = metadata.default_locale || 'en';
return path.join(localesDirectory, defaultLocale, 'messages.json');
}
};
ipcMainUtils.handle('CHROME_GET_MESSAGES', async function (event, extensionId) {
const messagesPath = getMessagesPath(extensionId);
return readFile(messagesPath);
});
const validStorageTypes = new Set(['sync', 'local']);
const getChromeStoragePath = (storageType, extensionId) => {
if (!validStorageTypes.has(storageType)) {
throw new Error(`Invalid storageType: ${storageType}`);
}
if (!manifestMap[extensionId]) {
throw new Error(`Invalid extensionId: ${extensionId}`);
}
return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`);
};
const mkdirp = util.promisify((dir, callback) => {
fs.mkdir(dir, (error) => {
if (error && error.code === 'ENOENT') {
mkdirp(path.dirname(dir), (error) => {
if (!error) {
mkdirp(dir, callback);
}
});
}
else if (error && error.code === 'EEXIST') {
callback(null);
}
else {
callback(error);
}
});
});
ipcMainUtils.handle('CHROME_STORAGE_READ', async function (event, storageType, extensionId) {
const filePath = getChromeStoragePath(storageType, extensionId);
try {
return await readFile(filePath, 'utf8');
}
catch (error) {
if (error.code === 'ENOENT') {
return null;
}
else {
throw error;
}
}
});
ipcMainUtils.handle('CHROME_STORAGE_WRITE', async function (event, storageType, extensionId, data) {
const filePath = getChromeStoragePath(storageType, extensionId);
try {
await mkdirp(path.dirname(filePath));
}
catch (_a) {
// we just ignore the errors of mkdir or mkdirp
}
return writeFile(filePath, data, 'utf8');
});
const isChromeExtension = function (pageURL) { const isChromeExtension = function (pageURL) {
const { protocol } = url.parse(pageURL); const { protocol } = url.parse(pageURL)
return protocol === 'chrome-extension:'; return protocol === 'chrome-extension:'
}; }
const assertChromeExtension = function (contents, api) {
const pageURL = contents._getURL(); ipcMain.on('CHROME_TABS_EXECUTESCRIPT', function (event, requestId, tabId, extensionId, details) {
if (!isChromeExtension(pageURL)) { const pageURL = event.sender._getURL()
console.error(`Blocked ${pageURL} from calling ${api}`); if (!isChromeExtension(pageURL)) {
throw new Error(`Blocked ${api}`); console.error(`Blocked ${pageURL} from calling chrome.tabs.executeScript()`)
} return
}; }
ipcMainUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) {
assertChromeExtension(event.sender, 'chrome.tabs.executeScript()'); const contents = webContents.fromId(tabId)
const contents = webContents.fromId(tabId); if (!contents) {
if (!contents) { console.error(`Sending message to unknown tab ${tabId}`)
throw new Error(`Sending message to unknown tab ${tabId}`); return
} }
let code, url;
if (details.file) { let code, url
const manifest = manifestMap[extensionId]; if (details.file) {
code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file))); const manifest = manifestMap[extensionId]
url = `chrome-extension://${extensionId}${details.file}`; code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file)))
} url = `chrome-extension://${extensionId}${details.file}`
else { } else {
code = details.code; code = details.code
url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`; url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`
} }
return ipcMainUtils.invokeInWebContents(contents, false, 'CHROME_TABS_EXECUTE_SCRIPT', extensionId, url, code);
}); contents._sendInternal('CHROME_TABS_EXECUTESCRIPT', event.sender.id, requestId, extensionId, url, code)
})
// Transfer the content scripts to renderer. // Transfer the content scripts to renderer.
const contentScripts = {}; const contentScripts = {}
const injectContentScripts = function (manifest) { const injectContentScripts = function (manifest) {
if (contentScripts[manifest.name] || !manifest.content_scripts) if (contentScripts[manifest.name] || !manifest.content_scripts) return
return;
const readArrayOfFiles = function (relativePath) { const readArrayOfFiles = function (relativePath) {
return { return {
url: `chrome-extension://${manifest.extensionId}/${relativePath}`, url: `chrome-extension://${manifest.extensionId}/${relativePath}`,
code: String(fs.readFileSync(path.join(manifest.srcDirectory, relativePath))) code: String(fs.readFileSync(path.join(manifest.srcDirectory, relativePath)))
};
};
const contentScriptToEntry = function (script) {
return {
matches: script.matches,
js: script.js ? script.js.map(readArrayOfFiles) : [],
css: script.css ? script.css.map(readArrayOfFiles) : [],
runAt: script.run_at || 'document_idle',
allFrames: script.all_frames || false
};
};
try {
const entry = {
extensionId: manifest.extensionId,
contentScripts: manifest.content_scripts.map(contentScriptToEntry)
};
contentScripts[manifest.name] = renderProcessPreferences.addEntry(entry);
} }
catch (e) { }
console.error('Failed to read content scripts', e);
const contentScriptToEntry = function (script) {
return {
matches: script.matches,
js: script.js ? script.js.map(readArrayOfFiles) : [],
css: script.css ? script.css.map(readArrayOfFiles) : [],
runAt: script.run_at || 'document_idle'
} }
}; }
try {
const entry = {
extensionId: manifest.extensionId,
contentScripts: manifest.content_scripts.map(contentScriptToEntry)
}
contentScripts[manifest.name] = renderProcessPreferences.addEntry(entry)
} catch (e) {
console.error('Failed to read content scripts', e)
}
}
const removeContentScripts = function (manifest) { const removeContentScripts = function (manifest) {
if (!contentScripts[manifest.name]) if (!contentScripts[manifest.name]) return
return;
renderProcessPreferences.removeEntry(contentScripts[manifest.name]); renderProcessPreferences.removeEntry(contentScripts[manifest.name])
delete contentScripts[manifest.name]; delete contentScripts[manifest.name]
}; }
// Transfer the |manifest| to a format that can be recognized by the // Transfer the |manifest| to a format that can be recognized by the
// |DevToolsAPI.addExtensions|. // |DevToolsAPI.addExtensions|.
const manifestToExtensionInfo = function (manifest) { const manifestToExtensionInfo = function (manifest) {
return { return {
startPage: manifest.startPage, startPage: manifest.startPage,
srcDirectory: manifest.srcDirectory, srcDirectory: manifest.srcDirectory,
name: manifest.name, name: manifest.name,
exposeExperimentalAPIs: true exposeExperimentalAPIs: true
}; }
}; }
// Load the extensions for the window. // Load the extensions for the window.
const loadExtension = function (manifest) { const loadExtension = function (manifest) {
startBackgroundPages(manifest); startBackgroundPages(manifest)
injectContentScripts(manifest); injectContentScripts(manifest)
}; }
const loadDevToolsExtensions = function (win, manifests) { const loadDevToolsExtensions = function (win, manifests) {
if (!win.devToolsWebContents) if (!win.devToolsWebContents) return
return;
manifests.forEach(loadExtension); manifests.forEach(loadExtension)
const extensionInfoArray = manifests.map(manifestToExtensionInfo);
extensionInfoArray.forEach((extension) => { const extensionInfoArray = manifests.map(manifestToExtensionInfo)
win.devToolsWebContents._grantOriginAccess(extension.startPage); extensionInfoArray.forEach((extension) => {
}); win.devToolsWebContents._grantOriginAccess(extension.startPage)
win.devToolsWebContents.executeJavaScript(`InspectorFrontendAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`); })
}; win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
}
app.on('web-contents-created', function (event, webContents) { app.on('web-contents-created', function (event, webContents) {
if (!isWindowOrWebView(webContents)) if (!isWindowOrWebView(webContents)) return
return;
hookWebContentsEvents(webContents); hookWebContentsEvents(webContents)
webContents.on('devtools-opened', function () { webContents.on('devtools-opened', function () {
loadDevToolsExtensions(webContents, Object.values(manifestMap)); loadDevToolsExtensions(webContents, Object.values(manifestMap))
}); })
}); })
// The chrome-extension: can map a extension URL request to real file path. // The chrome-extension: can map a extension URL request to real file path.
const chromeExtensionHandler = function (request, callback) { const chromeExtensionHandler = function (request, callback) {
const parsed = url.parse(request.url); const parsed = url.parse(request.url)
if (!parsed.hostname || !parsed.path) if (!parsed.hostname || !parsed.path) return callback()
return callback();
const manifest = manifestMap[parsed.hostname]; const manifest = manifestMap[parsed.hostname]
if (!manifest) if (!manifest) return callback()
return callback();
const page = backgroundPages[parsed.hostname]; const page = backgroundPages[parsed.hostname]
if (page && parsed.path === `/${page.name}`) { if (page && parsed.path === `/${page.name}`) {
// Disabled due to false positive in StandardJS // Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal // eslint-disable-next-line standard/no-callback-literal
return callback({ return callback({
mimeType: 'text/html', mimeType: 'text/html',
data: page.html data: page.html
}); })
}
fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) {
if (err) {
// Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
return callback(-6) // FILE_NOT_FOUND
} else {
return callback(content)
} }
fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) { })
if (err) { }
// Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
return callback(-6); // FILE_NOT_FOUND
}
else {
return callback(content);
}
});
};
app.on('session-created', function (ses) { app.on('session-created', function (ses) {
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) { ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
if (error) { if (error) {
console.error(`Unable to register chrome-extension protocol: ${error}`); console.error(`Unable to register chrome-extension protocol: ${error}`)
} }
}); })
}); })
// The persistent path of "DevTools Extensions" preference file. // The persistent path of "DevTools Extensions" preference file.
let loadedDevToolsExtensionsPath = null; let loadedDevToolsExtensionsPath = null
app.on('will-quit', function () { app.on('will-quit', function () {
try { try {
const loadedDevToolsExtensions = Array.from(devToolsExtensionNames) const loadedDevToolsExtensions = Array.from(devToolsExtensionNames)
.map(name => manifestNameMap[name].srcDirectory); .map(name => manifestNameMap[name].srcDirectory)
if (loadedDevToolsExtensions.length > 0) { if (loadedDevToolsExtensions.length > 0) {
try { try {
fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath)); fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath))
} } catch (error) {
catch (_a) {
// Ignore error
}
fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions));
}
else {
fs.unlinkSync(loadedDevToolsExtensionsPath);
}
}
catch (_b) {
// Ignore error // Ignore error
}
fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions))
} else {
fs.unlinkSync(loadedDevToolsExtensionsPath)
} }
}); } catch (error) {
// Ignore error
}
})
// We can not use protocol or BrowserWindow until app is ready. // We can not use protocol or BrowserWindow until app is ready.
app.once('ready', function () { app.once('ready', function () {
// The public API to add/remove extensions. // The public API to add/remove extensions.
BrowserWindow.addExtension = function (srcDirectory) { BrowserWindow.addExtension = function (srcDirectory) {
const manifest = getManifestFromPath(srcDirectory); const manifest = getManifestFromPath(srcDirectory)
if (manifest) { if (manifest) {
loadExtension(manifest); loadExtension(manifest)
for (const webContents of getAllWebContents()) { for (const webContents of getAllWebContents()) {
if (isWindowOrWebView(webContents)) { if (isWindowOrWebView(webContents)) {
loadDevToolsExtensions(webContents, [manifest]); loadDevToolsExtensions(webContents, [manifest])
}
}
return manifest.name;
}
};
BrowserWindow.removeExtension = function (name) {
const manifest = manifestNameMap[name];
if (!manifest)
return;
removeBackgroundPages(manifest);
removeContentScripts(manifest);
delete manifestMap[manifest.extensionId];
delete manifestNameMap[name];
};
BrowserWindow.getExtensions = function () {
const extensions = {};
Object.keys(manifestNameMap).forEach(function (name) {
const manifest = manifestNameMap[name];
extensions[name] = { name: manifest.name, version: manifest.version };
});
return extensions;
};
BrowserWindow.addDevToolsExtension = function (srcDirectory) {
const manifestName = BrowserWindow.addExtension(srcDirectory);
if (manifestName) {
devToolsExtensionNames.add(manifestName);
}
return manifestName;
};
BrowserWindow.removeDevToolsExtension = function (name) {
BrowserWindow.removeExtension(name);
devToolsExtensionNames.delete(name);
};
BrowserWindow.getDevToolsExtensions = function () {
const extensions = BrowserWindow.getExtensions();
const devExtensions = {};
Array.from(devToolsExtensionNames).forEach(function (name) {
if (!extensions[name])
return;
devExtensions[name] = extensions[name];
});
return devExtensions;
};
// Load persisted extensions.
loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions');
try {
const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath));
if (Array.isArray(loadedDevToolsExtensions)) {
for (const srcDirectory of loadedDevToolsExtensions) {
// Start background pages and set content scripts.
BrowserWindow.addDevToolsExtension(srcDirectory);
}
} }
}
return manifest.name
} }
catch (error) { }
if (process.env.ELECTRON_ENABLE_LOGGING && error.code !== 'ENOENT') {
console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath); BrowserWindow.removeExtension = function (name) {
console.error(error); const manifest = manifestNameMap[name]
} if (!manifest) return
removeBackgroundPages(manifest)
removeContentScripts(manifest)
delete manifestMap[manifest.extensionId]
delete manifestNameMap[name]
}
BrowserWindow.getExtensions = function () {
const extensions = {}
Object.keys(manifestNameMap).forEach(function (name) {
const manifest = manifestNameMap[name]
extensions[name] = { name: manifest.name, version: manifest.version }
})
return extensions
}
BrowserWindow.addDevToolsExtension = function (srcDirectory) {
const manifestName = BrowserWindow.addExtension(srcDirectory)
if (manifestName) {
devToolsExtensionNames.add(manifestName)
} }
}); return manifestName
//# sourceMappingURL=chrome-extension.js.map }
BrowserWindow.removeDevToolsExtension = function (name) {
BrowserWindow.removeExtension(name)
devToolsExtensionNames.delete(name)
}
BrowserWindow.getDevToolsExtensions = function () {
const extensions = BrowserWindow.getExtensions()
const devExtensions = {}
Array.from(devToolsExtensionNames).forEach(function (name) {
if (!extensions[name]) return
devExtensions[name] = extensions[name]
})
return devExtensions
}
// Load persisted extensions.
loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions')
try {
const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath))
if (Array.isArray(loadedDevToolsExtensions)) {
for (const srcDirectory of loadedDevToolsExtensions) {
// Start background pages and set content scripts.
BrowserWindow.addDevToolsExtension(srcDirectory)
}
}
} catch (error) {
if (process.env.ELECTRON_ENABLE_LOGGING && error.code !== 'ENOENT') {
console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath)
console.error(error)
}
}
})

View File

@ -1,23 +0,0 @@
'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 (_a) {
return os.tmpdir();
}
};
exports.crashReporterInit = function (options) {
const productName = options.productName || app.getName();
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`);
return {
productName,
crashesDirectory,
appVersion: app.getVersion()
};
};
//# sourceMappingURL=crash-reporter-init.js.map

View File

@ -1,50 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron");
const v8Util = process.electronBinding('v8_util');
const isMac = process.platform === 'darwin';
exports.setDefaultApplicationMenu = () => {
if (v8Util.getHiddenValue(global, 'applicationMenuSet'))
return;
const helpMenu = {
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
await electron_1.shell.openExternal('https://electronjs.org');
}
},
{
label: 'Documentation',
click: async () => {
await electron_1.shell.openExternal(`https://github.com/electron/electron/tree/v${process.versions.electron}/docs#readme`);
}
},
{
label: 'Community Discussions',
click: async () => {
await electron_1.shell.openExternal('https://discuss.atom.io/c/electron');
}
},
{
label: 'Search Issues',
click: async () => {
await electron_1.shell.openExternal('https://github.com/electron/electron/issues');
}
}
]
};
const macAppMenu = { role: 'appMenu' };
const template = [
...(isMac ? [macAppMenu] : []),
{ role: 'fileMenu' },
{ role: 'editMenu' },
{ role: 'viewMenu' },
{ role: 'windowMenu' },
helpMenu
];
const menu = electron_1.Menu.buildFromTemplate(template);
electron_1.Menu.setApplicationMenu(menu);
};
//# sourceMappingURL=default-menu.js.map

View File

@ -1,86 +1,73 @@
'use strict'; 'use strict'
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const { createDesktopCapturer } = process.electronBinding('desktop_capturer'); const ipcMain = require('@electron/internal/browser/ipc-main-internal')
const eventBinding = process.electronBinding('event'); const { desktopCapturer } = process.atomBinding('desktop_capturer')
const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
let currentlyRunning = []; const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b)
ipcMainUtils.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, fetchWindowIcons) => {
const customEvent = eventBinding.createWithSender(event.sender); // A queue for holding all requests from renderer process.
event.sender.emit('desktop-capturer-get-sources', customEvent); let requestsQueue = []
if (customEvent.defaultPrevented) {
return []; 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) => {
const request = {
id,
options: {
captureWindow,
captureScreen,
thumbnailSize
},
webContents: event.sender
}
requestsQueue.push(request)
if (requestsQueue.length === 1) {
desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize)
}
// If the WebContents is destroyed before receiving result, just remove the
// reference from requestsQueue to make the module not send the result to it.
event.sender.once('destroyed', () => {
request.webContents = null
})
})
desktopCapturer.emit = (event, name, sources) => {
// Receiving sources result from main process, now send them back to renderer.
const handledRequest = requestsQueue.shift()
const handledWebContents = handledRequest.webContents
const unhandledRequestsQueue = []
const result = sources.map(source => {
return {
id: source.id,
name: source.name,
thumbnail: source.thumbnail.toDataURL(),
display_id: source.display_id
} }
const options = { })
captureWindow,
captureScreen, if (handledWebContents) {
thumbnailSize, handledWebContents._sendInternal(capturerResult(handledRequest.id), result)
fetchWindowIcons }
};
for (const running of currentlyRunning) { // Check the queue to see whether there is another identical request & handle
if (deepEqual(running.options, options)) { requestsQueue.forEach(request => {
// If a request is currently running for the same options const webContents = request.webContents
// return that promise if (deepEqual(handledRequest.options, request.options)) {
return running.getSources; if (webContents) {
} webContents._sendInternal(capturerResult(request.id), result)
}
} else {
unhandledRequestsQueue.push(request)
} }
const getSources = new Promise((resolve, reject) => { })
const stopRunning = () => { requestsQueue = unhandledRequestsQueue
// Remove from currentlyRunning once we resolve or reject
currentlyRunning = currentlyRunning.filter(running => running.options !== options); // If the requestsQueue is not empty, start a new request handling.
}; if (requestsQueue.length > 0) {
const request = { const { captureWindow, captureScreen, thumbnailSize } = requestsQueue[0].options
options, return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize)
resolve: (value) => { }
stopRunning(); }
resolve(value);
},
reject: (err) => {
stopRunning();
reject(err);
},
capturer: createDesktopCapturer()
};
request.capturer.emit = createCapturerEmitHandler(request.capturer, request);
request.capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons);
// If the WebContents is destroyed before receiving result, just remove the
// reference to resolve, emit and the capturer itself so that it never dispatches
// back to the renderer
event.sender.once('destroyed', () => {
request.resolve = null;
delete request.capturer.emit;
delete request.capturer;
stopRunning();
});
});
currentlyRunning.push({
options,
getSources
});
return getSources;
});
const createCapturerEmitHandler = (capturer, request) => {
return function handlEmitOnCapturer(event, name, sources, fetchWindowIcons) {
// Ensure that this capturer instance can only ever receive a single event
// if we get more than one it is a bug but will also cause strange behavior
// if we still try to handle it
delete capturer.emit;
if (name === 'error') {
const error = sources;
request.reject(error);
return;
}
const result = sources.map(source => {
return {
id: source.id,
name: source.name,
thumbnail: source.thumbnail.toDataURL(),
display_id: source.display_id,
appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null
};
});
if (request.resolve) {
request.resolve(result);
}
};
};
//# sourceMappingURL=desktop-capturer.js.map

View File

@ -1,93 +0,0 @@
'use strict';
const { dialog, Menu } = require('electron');
const fs = require('fs');
const url = require('url');
const util = require('util');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const readFile = util.promisify(fs.readFile);
const convertToMenuTemplate = function (items, handler) {
return items.map(function (item) {
const transformed = item.type === 'subMenu' ? {
type: 'submenu',
label: item.label,
enabled: item.enabled,
submenu: convertToMenuTemplate(item.subItems, handler)
} : item.type === 'separator' ? {
type: 'separator'
} : item.type === 'checkbox' ? {
type: 'checkbox',
label: item.label,
enabled: item.enabled,
checked: item.checked
} : {
type: 'normal',
label: item.label,
enabled: item.enabled
};
if (item.id != null) {
transformed.click = () => handler(item.id);
}
return transformed;
});
};
const getEditMenuItems = function () {
return [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' }
];
};
const isChromeDevTools = function (pageURL) {
const { protocol } = url.parse(pageURL);
return protocol === 'devtools:';
};
const assertChromeDevTools = function (contents, api) {
const pageURL = contents._getURL();
if (!isChromeDevTools(pageURL)) {
console.error(`Blocked ${pageURL} from calling ${api}`);
throw new Error(`Blocked ${api}`);
}
};
ipcMainUtils.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event, items, isEditMenu) {
return new Promise(resolve => {
assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()');
const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve);
const menu = Menu.buildFromTemplate(template);
const window = event.sender.getOwnerBrowserWindow();
menu.once('menu-will-close', () => {
// menu-will-close is emitted before the click handler, but needs to be sent after.
// otherwise, DevToolsAPI.contextMenuCleared() would be called before
// DevToolsAPI.contextMenuItemSelected(id) and the menu will not work properly.
setTimeout(() => resolve());
});
menu.popup({ window });
});
});
ipcMainUtils.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event) {
assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()');
const result = await dialog.showOpenDialog({});
if (result.canceled)
return [];
const path = result.filePaths[0];
const data = await readFile(path);
return [path, data];
});
ipcMainUtils.handle('ELECTRON_INSPECTOR_CONFIRM', async function (event, message = '', title = '') {
assertChromeDevTools(event.sender, 'window.confirm()');
const options = {
message: String(message),
title: String(title),
buttons: ['OK', 'Cancel'],
cancelId: 1
};
const window = event.sender.getOwnerBrowserWindow();
const { response } = await dialog.showMessageBox(window, options);
return response === 0;
});
//# sourceMappingURL=devtools.js.map

View File

@ -1,365 +1,451 @@
'use strict'; 'use strict'
const { webContents } = require('electron');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); const { webContents } = require('electron')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); const ipcMain = require('@electron/internal/browser/ipc-main-internal')
const parseFeaturesString = require('@electron/internal/common/parse-features-string'); const parseFeaturesString = require('@electron/internal/common/parse-features-string')
const { syncMethods, asyncCallbackMethods, asyncPromiseMethods } = require('@electron/internal/common/web-view-methods'); const errorUtils = require('@electron/internal/common/error-utils')
const {
syncMethods,
asyncCallbackMethods,
asyncPromiseMethods
} = require('@electron/internal/common/web-view-methods')
// Doesn't exist in early initialization. // Doesn't exist in early initialization.
let webViewManager = null; let webViewManager = null
const supportedWebViewEvents = [ const supportedWebViewEvents = [
'load-commit', 'load-commit',
'did-attach', 'did-attach',
'did-finish-load', 'did-finish-load',
'did-fail-load', 'did-fail-load',
'did-frame-finish-load', 'did-frame-finish-load',
'did-start-loading', 'did-start-loading',
'did-stop-loading', 'did-stop-loading',
'dom-ready', 'dom-ready',
'console-message', 'console-message',
'context-menu', 'context-menu',
'devtools-opened', 'devtools-opened',
'devtools-closed', 'devtools-closed',
'devtools-focused', 'devtools-focused',
'new-window', 'new-window',
'will-navigate', 'will-navigate',
'did-start-navigation', 'did-start-navigation',
'did-navigate', 'did-navigate',
'did-frame-navigate', 'did-frame-navigate',
'did-navigate-in-page', 'did-navigate-in-page',
'focus-change', 'focus-change',
'close', 'close',
'crashed', 'crashed',
'plugin-crashed', 'gpu-crashed',
'destroyed', 'plugin-crashed',
'page-title-updated', 'destroyed',
'page-favicon-updated', 'page-title-updated',
'enter-html-full-screen', 'page-favicon-updated',
'leave-html-full-screen', 'enter-html-full-screen',
'media-started-playing', 'leave-html-full-screen',
'media-paused', 'media-started-playing',
'found-in-page', 'media-paused',
'did-change-theme-color', 'found-in-page',
'update-target-url' 'did-change-theme-color',
]; 'update-target-url'
const guestInstances = {}; ]
const embedderElementsMap = {};
let nextGuestInstanceId = 0
const guestInstances = {}
const embedderElementsMap = {}
// Generate guestInstanceId.
const getNextGuestInstanceId = function () {
return ++nextGuestInstanceId
}
// Create a new guest instance. // Create a new guest instance.
const createGuest = function (embedder, params) { const createGuest = function (embedder, params) {
if (webViewManager == null) { if (webViewManager == null) {
webViewManager = process.electronBinding('web_view_manager'); webViewManager = process.atomBinding('web_view_manager')
}
const guestInstanceId = getNextGuestInstanceId(embedder)
const guest = webContents.create({
isGuest: true,
partition: params.partition,
embedder: embedder
})
guestInstances[guestInstanceId] = {
guest: guest,
embedder: embedder
}
// Clear the guest from map when it is destroyed.
//
// The guest WebContents is usually destroyed in 2 cases:
// 1. The embedder frame is closed (reloaded or destroyed), and it
// automatically closes the guest frame.
// 2. The guest frame is detached dynamically via JS, and it is manually
// destroyed when the renderer sends the GUEST_VIEW_MANAGER_DESTROY_GUEST
// message.
// The second case relies on the libcc patch:
// https://github.com/electron/libchromiumcontent/pull/676
// The patch was introduced to work around a bug in Chromium:
// https://github.com/electron/electron/issues/14211
// We should revisit the bug to see if we can remove our libcc patch, the
// patch was introduced in Chrome 66.
guest.once('destroyed', () => {
if (guestInstanceId in guestInstances) {
detachGuest(embedder, guestInstanceId)
} }
const guest = webContents.create({ })
isGuest: true,
partition: params.partition, // Init guest web view after attached.
embedder: embedder guest.once('did-attach', function (event) {
}); params = this.attachParams
const guestInstanceId = guest.id; delete this.attachParams
guestInstances[guestInstanceId] = {
guest: guest, const previouslyAttached = this.viewInstanceId != null
embedder: embedder this.viewInstanceId = params.instanceId
};
// Clear the guest from map when it is destroyed. // Only load URL and set size on first attach
// if (previouslyAttached) {
// The guest WebContents is usually destroyed in 2 cases: return
// 1. The embedder frame is closed (reloaded or destroyed), and it
// automatically closes the guest frame.
// 2. The guest frame is detached dynamically via JS, and it is manually
// destroyed when the renderer sends the GUEST_VIEW_MANAGER_DESTROY_GUEST
// message.
// The second case relies on the libcc patch:
// https://github.com/electron/libchromiumcontent/pull/676
// The patch was introduced to work around a bug in Chromium:
// https://github.com/electron/electron/issues/14211
// We should revisit the bug to see if we can remove our libcc patch, the
// patch was introduced in Chrome 66.
guest.once('destroyed', () => {
if (guestInstanceId in guestInstances) {
detachGuest(embedder, guestInstanceId);
}
});
// Init guest web view after attached.
guest.once('did-attach', function (event) {
params = this.attachParams;
delete this.attachParams;
const previouslyAttached = this.viewInstanceId != null;
this.viewInstanceId = params.instanceId;
// Only load URL and set size on first attach
if (previouslyAttached) {
return;
}
if (params.src) {
const opts = {};
if (params.httpreferrer) {
opts.httpReferrer = params.httpreferrer;
}
if (params.useragent) {
opts.userAgent = params.useragent;
}
this.loadURL(params.src, opts);
}
embedder.emit('did-attach-webview', event, guest);
});
const sendToEmbedder = (channel, ...args) => {
if (!embedder.isDestroyed()) {
embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args);
}
};
// Dispatch events to embedder.
const fn = function (event) {
guest.on(event, function (_, ...args) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args);
});
};
for (const event of supportedWebViewEvents) {
fn(event);
} }
// Dispatch guest's IPC messages to embedder.
guest.on('ipc-message-host', function (_, channel, args) { if (params.src) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args); const opts = {}
}); if (params.httpreferrer) {
// Notify guest of embedder window visibility when it is ready opts.httpReferrer = params.httpreferrer
// FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed }
guest.on('dom-ready', function () { if (params.useragent) {
const guestInstance = guestInstances[guestInstanceId]; opts.userAgent = params.useragent
if (guestInstance != null && guestInstance.visibilityState != null) { }
guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState); this.loadURL(params.src, opts)
} }
}); guest.allowPopups = params.allowpopups
// Forward internal web contents event to embedder to handle embedder.emit('did-attach-webview', event, guest)
// native window.open setup })
guest.on('-add-new-contents', (...args) => {
if (guest.getLastWebPreferences().nativeWindowOpen === true) { const sendToEmbedder = (channel, ...args) => {
const embedder = getEmbedder(guestInstanceId); if (!embedder.isDestroyed()) {
if (embedder != null) { embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args)
embedder.emit('-add-new-contents', ...args); }
} }
}
}); // Dispatch events to embedder.
return guestInstanceId; const fn = function (event) {
}; guest.on(event, function (_, ...args) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args)
})
}
for (const event of supportedWebViewEvents) {
fn(event)
}
// Dispatch guest's IPC messages to embedder.
guest.on('ipc-message-host', function (_, [channel, ...args]) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args)
})
// Notify guest of embedder window visibility when it is ready
// FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed
guest.on('dom-ready', function () {
const guestInstance = guestInstances[guestInstanceId]
if (guestInstance != null && guestInstance.visibilityState != null) {
guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState)
}
})
// Forward internal web contents event to embedder to handle
// native window.open setup
guest.on('-add-new-contents', (...args) => {
if (guest.getLastWebPreferences().nativeWindowOpen === true) {
const embedder = getEmbedder(guestInstanceId)
if (embedder != null) {
embedder.emit('-add-new-contents', ...args)
}
}
})
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
}
// Attach the guest to an element of embedder. // Attach the guest to an element of embedder.
const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
const embedder = event.sender; const embedder = event.sender
// Destroy the old guest when attaching. // Destroy the old guest when attaching.
const key = `${embedder.id}-${elementInstanceId}`; const key = `${embedder.id}-${elementInstanceId}`
const oldGuestInstanceId = embedderElementsMap[key]; const oldGuestInstanceId = embedderElementsMap[key]
if (oldGuestInstanceId != null) { if (oldGuestInstanceId != null) {
// Reattachment to the same guest is just a no-op. // Reattachment to the same guest is just a no-op.
if (oldGuestInstanceId === guestInstanceId) { if (oldGuestInstanceId === guestInstanceId) {
return; return
}
const oldGuestInstance = guestInstances[oldGuestInstanceId];
if (oldGuestInstance) {
oldGuestInstance.guest.detachFromOuterFrame();
}
} }
const guestInstance = guestInstances[guestInstanceId];
// If this isn't a valid guest instance then do nothing. const oldGuestInstance = guestInstances[oldGuestInstanceId]
if (!guestInstance) { if (oldGuestInstance) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`); oldGuestInstance.guest.destroy()
} }
const { guest } = guestInstance; }
if (guest.hostWebContents !== event.sender) {
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`); const guestInstance = guestInstances[guestInstanceId]
// If this isn't a valid guest instance then do nothing.
if (!guestInstance) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
}
const { guest } = guestInstance
if (guest.hostWebContents !== event.sender) {
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`)
}
// If this guest is already attached to an element then remove it
if (guestInstance.elementInstanceId) {
const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}`
delete embedderElementsMap[oldKey]
// Remove guest from embedder if moving across web views
if (guest.viewInstanceId !== params.instanceId) {
webViewManager.removeGuest(guestInstance.embedder, guestInstanceId)
guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`)
} }
// If this guest is already attached to an element then remove it }
if (guestInstance.elementInstanceId) {
const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}`; const webPreferences = {
delete embedderElementsMap[oldKey]; guestInstanceId: guestInstanceId,
// Remove guest from embedder if moving across web views nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
if (guest.viewInstanceId !== params.instanceId) { enableRemoteModule: params.enableremotemodule,
webViewManager.removeGuest(guestInstance.embedder, guestInstanceId); plugins: params.plugins,
guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`); zoomFactor: embedder._getZoomFactor(),
} webSecurity: !params.disablewebsecurity,
enableBlinkFeatures: params.blinkfeatures,
disableBlinkFeatures: params.disableblinkfeatures
}
// parse the 'webpreferences' attribute string, if set
// this uses the same parsing rules as window.open uses for its features
if (typeof params.webpreferences === 'string') {
parseFeaturesString(params.webpreferences, function (key, value) {
if (value === undefined) {
// no value was specified, default it to true
value = true
}
webPreferences[key] = value
})
}
if (params.preload) {
webPreferences.preloadURL = params.preload
}
// Return null from native window.open if allowpopups is unset
if (webPreferences.nativeWindowOpen === true && !params.allowpopups) {
webPreferences.disablePopups = true
}
// Security options that guest will always inherit from embedder
const inheritedWebPreferences = new Map([
['contextIsolation', true],
['javascript', false],
['nativeWindowOpen', true],
['nodeIntegration', false],
['enableRemoteModule', false],
['sandbox', true]
])
// Inherit certain option values from embedder
const lastWebPreferences = embedder.getLastWebPreferences()
for (const [name, value] of inheritedWebPreferences) {
if (lastWebPreferences[name] === value) {
webPreferences[name] = value
} }
const webPreferences = { }
guestInstanceId: guestInstanceId,
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false, embedder.emit('will-attach-webview', event, webPreferences, params)
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false, if (event.defaultPrevented) {
enableRemoteModule: params.enableremotemodule, if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId
plugins: params.plugins, guest.destroy()
zoomFactor: embedder.getZoomFactor(), return
disablePopups: !params.allowpopups, }
webSecurity: !params.disablewebsecurity,
enableBlinkFeatures: params.blinkfeatures, guest.attachParams = params
disableBlinkFeatures: params.disableblinkfeatures embedderElementsMap[key] = guestInstanceId
};
// parse the 'webpreferences' attribute string, if set guest.setEmbedder(embedder)
// this uses the same parsing rules as window.open uses for its features guestInstance.embedder = embedder
if (typeof params.webpreferences === 'string') { guestInstance.elementInstanceId = elementInstanceId
parseFeaturesString(params.webpreferences, function (key, value) {
if (value === undefined) { watchEmbedder(embedder)
// no value was specified, default it to true
value = true; webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences)
} guest.attachToIframe(embedder, embedderFrameId)
webPreferences[key] = value; }
});
}
if (params.preload) {
webPreferences.preloadURL = params.preload;
}
// Security options that guest will always inherit from embedder
const inheritedWebPreferences = new Map([
['contextIsolation', true],
['javascript', false],
['nativeWindowOpen', true],
['nodeIntegration', false],
['enableRemoteModule', false],
['sandbox', true],
['nodeIntegrationInSubFrames', false]
]);
// Inherit certain option values from embedder
const lastWebPreferences = embedder.getLastWebPreferences();
for (const [name, value] of inheritedWebPreferences) {
if (lastWebPreferences[name] === value) {
webPreferences[name] = value;
}
}
embedder.emit('will-attach-webview', event, webPreferences, params);
if (event.defaultPrevented) {
if (guest.viewInstanceId == null)
guest.viewInstanceId = params.instanceId;
guest.destroy();
return;
}
guest.attachParams = params;
embedderElementsMap[key] = guestInstanceId;
guest.setEmbedder(embedder);
guestInstance.embedder = embedder;
guestInstance.elementInstanceId = elementInstanceId;
watchEmbedder(embedder);
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences);
guest.attachToIframe(embedder, embedderFrameId);
};
// Remove an guest-embedder relationship. // Remove an guest-embedder relationship.
const detachGuest = function (embedder, guestInstanceId) { const detachGuest = function (embedder, guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]; const guestInstance = guestInstances[guestInstanceId]
if (embedder !== guestInstance.embedder) { if (embedder !== guestInstance.embedder) {
return; return
} }
webViewManager.removeGuest(embedder, guestInstanceId);
delete guestInstances[guestInstanceId]; webViewManager.removeGuest(embedder, guestInstanceId)
const key = `${embedder.id}-${guestInstance.elementInstanceId}`; delete guestInstances[guestInstanceId]
delete embedderElementsMap[key];
}; const key = `${embedder.id}-${guestInstance.elementInstanceId}`
delete embedderElementsMap[key]
}
// Once an embedder has had a guest attached we watch it for destruction to // Once an embedder has had a guest attached we watch it for destruction to
// destroy any remaining guests. // destroy any remaining guests.
const watchedEmbedders = new Set(); const watchedEmbedders = new Set()
const watchEmbedder = function (embedder) { const watchEmbedder = function (embedder) {
if (watchedEmbedders.has(embedder)) { if (watchedEmbedders.has(embedder)) {
return; return
}
watchedEmbedders.add(embedder)
// Forward embedder window visiblity change events to guest
const onVisibilityChange = function (visibilityState) {
for (const guestInstanceId in guestInstances) {
const guestInstance = guestInstances[guestInstanceId]
guestInstance.visibilityState = visibilityState
if (guestInstance.embedder === embedder) {
guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState)
}
} }
watchedEmbedders.add(embedder); }
// Forward embedder window visiblity change events to guest embedder.on('-window-visibility-change', onVisibilityChange)
const onVisibilityChange = function (visibilityState) {
for (const guestInstanceId in guestInstances) { embedder.once('will-destroy', () => {
const guestInstance = guestInstances[guestInstanceId]; // Usually the guestInstances is cleared when guest is destroyed, but it
guestInstance.visibilityState = visibilityState; // may happen that the embedder gets manually destroyed earlier than guest,
if (guestInstance.embedder === embedder) { // and the embedder will be invalid in the usual code path.
guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState); for (const guestInstanceId in guestInstances) {
} const guestInstance = guestInstances[guestInstanceId]
} if (guestInstance.embedder === embedder) {
}; detachGuest(embedder, parseInt(guestInstanceId))
embedder.on('-window-visibility-change', onVisibilityChange); }
embedder.once('will-destroy', () => { }
// Usually the guestInstances is cleared when guest is destroyed, but it // Clear the listeners.
// may happen that the embedder gets manually destroyed earlier than guest, embedder.removeListener('-window-visibility-change', onVisibilityChange)
// and the embedder will be invalid in the usual code path. watchedEmbedders.delete(embedder)
for (const guestInstanceId in guestInstances) { })
const guestInstance = guestInstances[guestInstanceId]; }
if (guestInstance.embedder === embedder) {
detachGuest(embedder, parseInt(guestInstanceId)); const isWebViewTagEnabledCache = new WeakMap()
}
}
// Clear the listeners.
embedder.removeListener('-window-visibility-change', onVisibilityChange);
watchedEmbedders.delete(embedder);
});
};
const isWebViewTagEnabledCache = new WeakMap();
const isWebViewTagEnabled = function (contents) { const isWebViewTagEnabled = function (contents) {
if (!isWebViewTagEnabledCache.has(contents)) { if (!isWebViewTagEnabledCache.has(contents)) {
const webPreferences = contents.getLastWebPreferences() || {}; const value = contents.getLastWebPreferences().webviewTag
isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag); isWebViewTagEnabledCache.set(contents, value)
} }
return isWebViewTagEnabledCache.get(contents);
}; return isWebViewTagEnabledCache.get(contents)
}
const handleMessage = function (channel, handler) { const handleMessage = function (channel, handler) {
ipcMainUtils.handle(channel, (event, ...args) => { ipcMain.on(channel, (event, ...args) => {
if (isWebViewTagEnabled(event.sender)) { if (isWebViewTagEnabled(event.sender)) {
return handler(event, ...args); handler(event, ...args)
} } else {
else { event.returnValue = null
console.error(`<webview> IPC message ${channel} sent by WebContents with <webview> disabled (${event.sender.id})`); }
throw new Error('<webview> disabled'); })
} }
});
}; handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) { event.sender._sendInternal(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
return createGuest(event.sender, params); })
});
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) {
event.returnValue = createGuest(event.sender, params)
})
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) { handleMessage('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) {
try { try {
const guest = getGuestForWebContents(guestInstanceId, event.sender); const guest = getGuestForWebContents(guestInstanceId, event.sender)
guest.detachFromOuterFrame(); guest.destroy()
} } catch (error) {
catch (error) { console.error(`Guest destroy failed: ${error}`)
console.error(`Guest destroy failed: ${error}`); }
} })
});
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
try { try {
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params); attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
} } catch (error) {
catch (error) { console.error(`Guest attach failed: ${error}`)
console.error(`Guest attach failed: ${error}`); }
} })
});
// this message is sent by the actual <webview> // this message is sent by the actual <webview>
ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) { ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
const guest = getGuest(guestInstanceId); const guest = getGuest(guestInstanceId)
if (guest === event.sender) { if (guest === event.sender) {
event.sender.emit('focus-change', {}, focus, guestInstanceId); event.sender.emit('focus-change', {}, focus, guestInstanceId)
} else {
console.error(`focus-change for guestInstanceId: ${guestInstanceId}`)
}
})
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, requestId, guestInstanceId, method, args, hasCallback) {
new Promise(resolve => {
const guest = getGuestForWebContents(guestInstanceId, event.sender)
if (!asyncCallbackMethods.has(method) && !asyncPromiseMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
} }
else { if (hasCallback) {
console.error(`focus-change for guestInstanceId: ${guestInstanceId}`); guest[method](...args, resolve)
} else {
resolve(guest[method](...args))
} }
}); }).then(result => {
const allMethods = new Set([ return [null, result]
...syncMethods, }, error => {
...asyncCallbackMethods, return [errorUtils.serialize(error)]
...asyncPromiseMethods }).then(responseArgs => {
]); event.sender._sendInternal(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, ...responseArgs)
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) { })
const guest = getGuestForWebContents(guestInstanceId, event.sender); })
if (!allMethods.has(method)) {
throw new Error(`Invalid method: ${method}`); handleMessage('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', function (event, guestInstanceId, method, args) {
try {
const guest = getGuestForWebContents(guestInstanceId, event.sender)
if (!syncMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
} }
return guest[method](...args); event.returnValue = [null, guest[method](...args)]
}); } catch (error) {
event.returnValue = [errorUtils.serialize(error)]
}
})
// Returns WebContents from its guest id hosted in given webContents. // Returns WebContents from its guest id hosted in given webContents.
const getGuestForWebContents = function (guestInstanceId, contents) { const getGuestForWebContents = function (guestInstanceId, contents) {
const guest = getGuest(guestInstanceId); const guest = getGuest(guestInstanceId)
if (!guest) { if (!guest) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`); throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
} }
if (guest.hostWebContents !== contents) { if (guest.hostWebContents !== contents) {
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`); throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`)
} }
return guest; return guest
}; }
// Returns WebContents from its guest id. // Returns WebContents from its guest id.
const getGuest = function (guestInstanceId) { const getGuest = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]; const guestInstance = guestInstances[guestInstanceId]
if (guestInstance != null) if (guestInstance != null) return guestInstance.guest
return guestInstance.guest; }
};
// Returns the embedder of the guest. // Returns the embedder of the guest.
const getEmbedder = function (guestInstanceId) { const getEmbedder = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]; const guestInstance = guestInstances[guestInstanceId]
if (guestInstance != null) if (guestInstance != null) return guestInstance.embedder
return guestInstance.embedder; }
};
exports.getGuestForWebContents = getGuestForWebContents; exports.getGuestForWebContents = getGuestForWebContents
exports.isWebViewTagEnabled = isWebViewTagEnabled;
//# sourceMappingURL=guest-view-manager.js.map

View File

@ -1,148 +1,176 @@
'use strict'; 'use strict'
const { BrowserWindow, webContents } = require('electron');
const { isSameOrigin } = process.electronBinding('v8_util'); const { BrowserWindow, webContents } = require('electron')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); const { isSameOrigin } = process.atomBinding('v8_util')
const parseFeaturesString = require('@electron/internal/common/parse-features-string'); const ipcMain = require('@electron/internal/browser/ipc-main-internal')
const hasProp = {}.hasOwnProperty; const parseFeaturesString = require('@electron/internal/common/parse-features-string')
const frameToGuest = new Map();
const hasProp = {}.hasOwnProperty
const frameToGuest = new Map()
// Security options that child windows will always inherit from parent windows // Security options that child windows will always inherit from parent windows
const inheritedWebPreferences = new Map([ const inheritedWebPreferences = new Map([
['contextIsolation', true], ['contextIsolation', true],
['javascript', false], ['javascript', false],
['nativeWindowOpen', true], ['nativeWindowOpen', true],
['nodeIntegration', false], ['nodeIntegration', false],
['enableRemoteModule', false], ['enableRemoteModule', false],
['sandbox', true], ['sandbox', true],
['webviewTag', false], ['webviewTag', false]
['nodeIntegrationInSubFrames', false] ])
]);
// Copy attribute of |parent| to |child| if it is not defined in |child|. // Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent, visited) { const mergeOptions = function (child, parent, visited) {
// Check for circular reference. // Check for circular reference.
if (visited == null) if (visited == null) visited = new Set()
visited = new Set(); if (visited.has(parent)) return
if (visited.has(parent))
return; visited.add(parent)
visited.add(parent); for (const key in parent) {
for (const key in parent) { if (key === 'isBrowserView') continue
if (key === 'isBrowserView') if (!hasProp.call(parent, key)) continue
continue; if (key in child && key !== 'webPreferences') continue
if (!hasProp.call(parent, key))
continue; const value = parent[key]
if (key in child && key !== 'webPreferences') if (typeof value === 'object') {
continue; child[key] = mergeOptions(child[key] || {}, value, visited)
const value = parent[key]; } else {
if (typeof value === 'object') { child[key] = value
child[key] = mergeOptions(child[key] || {}, value, visited);
}
else {
child[key] = value;
}
} }
visited.delete(parent); }
return child; visited.delete(parent)
};
return child
}
// Merge |options| with the |embedder|'s window's options. // Merge |options| with the |embedder|'s window's options.
const mergeBrowserWindowOptions = function (embedder, options) { const mergeBrowserWindowOptions = function (embedder, options) {
if (options.webPreferences == null) { if (options.webPreferences == null) {
options.webPreferences = {}; options.webPreferences = {}
}
if (embedder.browserWindowOptions != null) {
let parentOptions = embedder.browserWindowOptions
// if parent's visibility is available, that overrides 'show' flag (#12125)
const win = BrowserWindow.fromWebContents(embedder.webContents)
if (win != null) {
parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() }
} }
if (embedder.browserWindowOptions != null) {
let parentOptions = embedder.browserWindowOptions; // Inherit the original options if it is a BrowserWindow.
// if parent's visibility is available, that overrides 'show' flag (#12125) mergeOptions(options, parentOptions)
const win = BrowserWindow.fromWebContents(embedder.webContents); } else {
if (win != null) { // Or only inherit webPreferences if it is a webview.
parentOptions = Object.assign({}, embedder.browserWindowOptions, { show: win.isVisible() }); mergeOptions(options.webPreferences, embedder.getLastWebPreferences())
} }
// Inherit the original options if it is a BrowserWindow.
mergeOptions(options, parentOptions); // Inherit certain option values from parent window
const webPreferences = embedder.getLastWebPreferences()
for (const [name, value] of inheritedWebPreferences) {
if (webPreferences[name] === value) {
options.webPreferences[name] = value
} }
else { }
// Or only inherit webPreferences if it is a webview.
mergeOptions(options.webPreferences, embedder.getLastWebPreferences()); // Sets correct openerId here to give correct options to 'new-window' event handler
} options.webPreferences.openerId = embedder.id
// Inherit certain option values from parent window
const webPreferences = embedder.getLastWebPreferences(); return options
for (const [name, value] of inheritedWebPreferences) { }
if (webPreferences[name] === value) {
options.webPreferences[name] = value;
}
}
// Sets correct openerId here to give correct options to 'new-window' event handler
options.webPreferences.openerId = embedder.id;
return options;
};
// Setup a new guest with |embedder| // Setup a new guest with |embedder|
const setupGuest = function (embedder, frameName, guest, options) { const setupGuest = function (embedder, frameName, guest, options) {
// When |embedder| is destroyed we should also destroy attached guest, and if // When |embedder| is destroyed we should also destroy attached guest, and if
// guest is closed by user then we should prevent |embedder| from double // guest is closed by user then we should prevent |embedder| from double
// closing guest. // closing guest.
const guestId = guest.webContents.id; const guestId = guest.webContents.id
const closedByEmbedder = function () { const closedByEmbedder = function () {
guest.removeListener('closed', closedByUser); guest.removeListener('closed', closedByUser)
guest.destroy(); guest.destroy()
}; }
const closedByUser = function () { const closedByUser = function () {
embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId); embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
embedder.removeListener('current-render-view-deleted', closedByEmbedder); embedder.removeListener('render-view-deleted', closedByEmbedder)
}; }
embedder.once('current-render-view-deleted', closedByEmbedder); embedder.once('render-view-deleted', closedByEmbedder)
guest.once('closed', closedByUser); guest.once('closed', closedByUser)
if (frameName) { if (frameName) {
frameToGuest.set(frameName, guest); frameToGuest.set(frameName, guest)
guest.frameName = frameName; guest.frameName = frameName
guest.once('closed', function () { guest.once('closed', function () {
frameToGuest.delete(frameName); frameToGuest.delete(frameName)
}); })
} }
return guestId; return guestId
}; }
// Create a new guest created by |embedder| with |options|. // Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, referrer, frameName, options, postData) { const createGuest = function (embedder, url, referrer, frameName, options, postData) {
let guest = frameToGuest.get(frameName); let guest = frameToGuest.get(frameName)
if (frameName && (guest != null)) { if (frameName && (guest != null)) {
guest.loadURL(url); guest.loadURL(url)
return guest.webContents.id; return guest.webContents.id
}
// Remember the embedder window's id.
if (options.webPreferences == null) {
options.webPreferences = {}
}
guest = new BrowserWindow(options)
if (!options.webContents || url !== 'about:blank') {
// We should not call `loadURL` if the window was constructed from an
// existing webContents(window.open in a sandboxed renderer) and if the url
// is not 'about:blank'.
//
// Navigating to the url when creating the window from an existing
// webContents would not be necessary(it will navigate there anyway), but
// apparently there's a bug that allows the child window to be scripted by
// the opener, even when the child window is from another origin.
//
// That's why the second condition(url !== "about:blank") is required: to
// force `OverrideSiteInstanceForNavigation` to be called and consequently
// spawn a new renderer if the new window is targeting a different origin.
//
// If the URL is "about:blank", then it is very likely that the opener just
// wants to synchronously script the popup, for example:
//
// let popup = window.open()
// popup.document.body.write('<h1>hello</h1>')
//
// The above code would not work if a navigation to "about:blank" is done
// here, since the window would be cleared of all changes in the next tick.
const loadOptions = {
httpReferrer: referrer
} }
// Remember the embedder window's id. if (postData != null) {
if (options.webPreferences == null) { loadOptions.postData = postData
options.webPreferences = {}; loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'
} if (postData.length > 0) {
guest = new BrowserWindow(options); const postDataFront = postData[0].bytes.toString()
if (!options.webContents) { const boundary = /^--.*[^-\r\n]/.exec(postDataFront)
// We should not call `loadURL` if the window was constructed from an if (boundary != null) {
// existing webContents (window.open in a sandboxed renderer). loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`
//
// Navigating to the url when creating the window from an existing
// webContents is not necessary (it will navigate there anyway).
const loadOptions = {
httpReferrer: referrer
};
if (postData != null) {
loadOptions.postData = postData;
loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded';
if (postData.length > 0) {
const postDataFront = postData[0].bytes.toString();
const boundary = /^--.*[^-\r\n]/.exec(postDataFront);
if (boundary != null) {
loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`;
}
}
} }
guest.loadURL(url, loadOptions); }
} }
return setupGuest(embedder, frameName, guest, options); guest.loadURL(url, loadOptions)
}; }
return setupGuest(embedder, frameName, guest, options)
}
const getGuestWindow = function (guestContents) { const getGuestWindow = function (guestContents) {
let guestWindow = BrowserWindow.fromWebContents(guestContents); let guestWindow = BrowserWindow.fromWebContents(guestContents)
if (guestWindow == null) { if (guestWindow == null) {
const hostContents = guestContents.hostWebContents; const hostContents = guestContents.hostWebContents
if (hostContents != null) { if (hostContents != null) {
guestWindow = BrowserWindow.fromWebContents(hostContents); guestWindow = BrowserWindow.fromWebContents(hostContents)
}
} }
return guestWindow; }
}; return guestWindow
}
// Checks whether |sender| can access the |target|: // Checks whether |sender| can access the |target|:
// 1. Check whether |sender| is the parent of |target|. // 1. Check whether |sender| is the parent of |target|.
// 2. Check whether |sender| has node integration, if so it is allowed to // 2. Check whether |sender| has node integration, if so it is allowed to
@ -154,175 +182,187 @@ const getGuestWindow = function (guestContents) {
// The W3C does not have anything on this, but from my understanding of the // The W3C does not have anything on this, but from my understanding of the
// security model of |window.opener|, this should be fine. // security model of |window.opener|, this should be fine.
const canAccessWindow = function (sender, target) { const canAccessWindow = function (sender, target) {
return (target.getLastWebPreferences().openerId === sender.id) || return (target.getLastWebPreferences().openerId === sender.id) ||
(sender.getLastWebPreferences().nodeIntegration === true) || (sender.getLastWebPreferences().nodeIntegration === true) ||
isSameOrigin(sender.getURL(), target.getURL()); isSameOrigin(sender.getURL(), target.getURL())
}; }
// Routed window.open messages with raw options // Routed window.open messages with raw options
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => { ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
if (url == null || url === '') if (url == null || url === '') url = 'about:blank'
url = 'about:blank'; if (frameName == null) frameName = ''
if (frameName == null) if (features == null) features = ''
frameName = '';
if (features == null) const options = {}
features = '';
const options = {}; const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']; const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag']
const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag']; const disposition = 'new-window'
const disposition = 'new-window';
// Used to store additional features // Used to store additional features
const additionalFeatures = []; const additionalFeatures = []
// Parse the features
parseFeaturesString(features, function (key, value) { // Parse the features
if (value === undefined) { parseFeaturesString(features, function (key, value) {
additionalFeatures.push(key); if (value === undefined) {
} additionalFeatures.push(key)
else { } else {
// Don't allow webPreferences to be set since it must be an object // Don't allow webPreferences to be set since it must be an object
// that cannot be directly overridden // that cannot be directly overridden
if (key === 'webPreferences') if (key === 'webPreferences') return
return;
if (webPreferences.includes(key)) { if (webPreferences.includes(key)) {
if (options.webPreferences == null) { if (options.webPreferences == null) {
options.webPreferences = {}; options.webPreferences = {}
}
options.webPreferences[key] = value;
}
else {
options[key] = value;
}
}
});
if (options.left) {
if (options.x == null) {
options.x = options.left;
} }
options.webPreferences[key] = value
} else {
options[key] = value
}
} }
if (options.top) { })
if (options.y == null) { if (options.left) {
options.y = options.top; if (options.x == null) {
} options.x = options.left
} }
if (options.title == null) { }
options.title = frameName; if (options.top) {
if (options.y == null) {
options.y = options.top
} }
if (options.width == null) { }
options.width = 800; if (options.title == null) {
options.title = frameName
}
if (options.width == null) {
options.width = 800
}
if (options.height == null) {
options.height = 600
}
for (const name of ints) {
if (options[name] != null) {
options[name] = parseInt(options[name], 10)
} }
if (options.height == null) { }
options.height = 600;
} const referrer = { url: '', policy: 'default' }
for (const name of ints) { ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
if (options[name] != null) { url, referrer, frameName, disposition, options, additionalFeatures)
options[name] = parseInt(options[name], 10); })
}
}
const referrer = { url: '', policy: 'default' };
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event, url, referrer, frameName, disposition, options, additionalFeatures);
});
// Routed window.open messages with fully parsed options // Routed window.open messages with fully parsed options
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer, frameName, disposition, options, additionalFeatures, postData) { ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer,
options = mergeBrowserWindowOptions(event.sender, options); frameName, disposition, options,
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer); additionalFeatures, postData) {
const { newGuest } = event; options = mergeBrowserWindowOptions(event.sender, options)
if ((event.sender.isGuest() && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) { event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer)
if (newGuest != null) { const { newGuest } = event
if (options.webContents === newGuest.webContents) { if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
// the webContents is not changed, so set defaultPrevented to false to if (newGuest != null) {
// stop the callers of this event from destroying the webContents. if (options.webContents === newGuest.webContents) {
event.defaultPrevented = false; // the webContents is not changed, so set defaultPrevented to false to
} // stop the callers of this event from destroying the webContents.
event.returnValue = setupGuest(event.sender, frameName, newGuest, options); event.defaultPrevented = false
} }
else { event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
event.returnValue = null; } else {
} event.returnValue = null
} }
else { } else {
event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData); event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData)
} }
}); })
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
const guestContents = webContents.fromId(guestId); ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
if (guestContents == null) const guestContents = webContents.fromId(guestId)
return; if (guestContents == null) return
if (!canAccessWindow(event.sender, guestContents)) {
console.error(`Blocked ${event.sender.getURL()} from closing its opener.`); if (!canAccessWindow(event.sender, guestContents)) {
return; console.error(`Blocked ${event.sender.getURL()} from closing its opener.`)
} return
const guestWindow = getGuestWindow(guestContents); }
if (guestWindow != null)
guestWindow.destroy(); const guestWindow = getGuestWindow(guestContents)
}); if (guestWindow != null) guestWindow.destroy()
})
const windowMethods = new Set([ const windowMethods = new Set([
'focus', 'focus',
'blur' 'blur'
]); ])
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
const guestContents = webContents.fromId(guestId); ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
if (guestContents == null) { const guestContents = webContents.fromId(guestId)
event.returnValue = null; if (guestContents == null) {
return; event.returnValue = null
} return
if (!canAccessWindow(event.sender, guestContents) || !windowMethods.has(method)) { }
console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`);
event.returnValue = null; if (!canAccessWindow(event.sender, guestContents) || !windowMethods.has(method)) {
return; console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
} event.returnValue = null
const guestWindow = getGuestWindow(guestContents); return
if (guestWindow != null) { }
event.returnValue = guestWindow[method](...args);
} const guestWindow = getGuestWindow(guestContents)
else { if (guestWindow != null) {
event.returnValue = null; event.returnValue = guestWindow[method](...args)
} } else {
}); event.returnValue = null
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) { }
if (targetOrigin == null) { })
targetOrigin = '*';
} ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
const guestContents = webContents.fromId(guestId); if (targetOrigin == null) {
if (guestContents == null) targetOrigin = '*'
return; }
// The W3C does not seem to have word on how postMessage should work when the
// origins do not match, so we do not do |canAccessWindow| check here since const guestContents = webContents.fromId(guestId)
// postMessage across origins is useful and not harmful. if (guestContents == null) return
if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
const sourceId = event.sender.id; // The W3C does not seem to have word on how postMessage should work when the
guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin); // origins do not match, so we do not do |canAccessWindow| check here since
} // postMessage across origins is useful and not harmful.
}); if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
const sourceId = event.sender.id
guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
}
})
const webContentsMethods = new Set([ const webContentsMethods = new Set([
'print', 'print',
'executeJavaScript' 'executeJavaScript'
]); ])
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
const guestContents = webContents.fromId(guestId); ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
if (guestContents == null) const guestContents = webContents.fromId(guestId)
return; if (guestContents == null) return
if (canAccessWindow(event.sender, guestContents) && webContentsMethods.has(method)) {
guestContents[method](...args); if (canAccessWindow(event.sender, guestContents) && webContentsMethods.has(method)) {
} guestContents[method](...args)
else { } else {
console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`); console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
} }
}); })
const webContentsSyncMethods = new Set([ const webContentsSyncMethods = new Set([
'getURL', 'getURL',
'loadURL' 'loadURL'
]); ])
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
const guestContents = webContents.fromId(guestId); ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
if (guestContents == null) { const guestContents = webContents.fromId(guestId)
event.returnValue = null; if (guestContents == null) {
return; event.returnValue = null
} return
if (canAccessWindow(event.sender, guestContents) && webContentsSyncMethods.has(method)) { }
event.returnValue = guestContents[method](...args);
} if (canAccessWindow(event.sender, guestContents) && webContentsSyncMethods.has(method)) {
else { event.returnValue = guestContents[method](...args)
console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`); } else {
event.returnValue = null; console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
} event.returnValue = null
}); }
//# sourceMappingURL=guest-window-manager.js.map })

View File

@ -1,187 +1,188 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const buffer_1 = require("buffer"); const { Buffer } = require('buffer')
const fs = require("fs"); const fs = require('fs')
const path = require("path"); const path = require('path')
const util = require("util"); const util = require('util')
const v8 = require("v8"); const Module = require('module')
const Module = require('module'); const v8 = require('v8')
// We modified the original process.argv to let node.js load the init.js, // We modified the original process.argv to let node.js load the init.js,
// we need to restore it here. // we need to restore it here.
process.argv.splice(1, 1); process.argv.splice(1, 1)
// Clear search paths. // Clear search paths.
require('../common/reset-search-paths'); require('../common/reset-search-paths')
// Import common settings. // Import common settings.
require('@electron/internal/common/init'); require('@electron/internal/common/init')
const globalPaths = Module.globalPaths;
const globalPaths = Module.globalPaths
// Expose public APIs. // Expose public APIs.
globalPaths.push(path.join(__dirname, 'api', 'exports')); globalPaths.push(path.join(__dirname, 'api', 'exports'))
if (process.platform === 'win32') { if (process.platform === 'win32') {
// Redirect node's console to use our own implementations, since node can not // Redirect node's console to use our own implementations, since node can not
// handle console output when running as GUI program. // handle console output when running as GUI program.
const consoleLog = (format, ...args) => { const consoleLog = function (...args) {
return process.log(util.format(format, ...args) + '\n'); return process.log(util.format(...args) + '\n')
}; }
const streamWrite = function (chunk, encoding, callback) { const streamWrite = function (chunk, encoding, callback) {
if (buffer_1.Buffer.isBuffer(chunk)) { if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString(encoding); chunk = chunk.toString(encoding)
} }
process.log(chunk); process.log(chunk)
if (callback) { if (callback) {
callback(); callback()
} }
return true; return true
}; }
console.log = console.error = console.warn = consoleLog; console.log = console.error = console.warn = consoleLog
process.stdout.write = process.stderr.write = streamWrite; process.stdout.write = process.stderr.write = streamWrite
} }
// Don't quit on fatal error. // Don't quit on fatal error.
process.on('uncaughtException', function (error) { process.on('uncaughtException', function (error) {
// Do nothing if the user has a custom uncaught exception handler. // Do nothing if the user has a custom uncaught exception handler.
if (process.listeners('uncaughtException').length > 1) { if (process.listeners('uncaughtException').length > 1) {
return; return
} }
// Show error in GUI.
// We can't import { dialog } at the top of this file as this file is // Show error in GUI.
// responsible for setting up the require hook for the "electron" module const dialog = require('electron').dialog
// so we import it inside the handler down here const stack = error.stack ? error.stack : `${error.name}: ${error.message}`
Promise.resolve().then(() => require('electron')).then(({ dialog }) => { const message = 'Uncaught Exception:\n' + stack
const stack = error.stack ? error.stack : `${error.name}: ${error.message}`; dialog.showErrorBox('A JavaScript error occurred in the main process', message)
const message = 'Uncaught Exception:\n' + stack; })
dialog.showErrorBox('A JavaScript error occurred in the main process', message);
});
});
// Emit 'exit' event on quit. // Emit 'exit' event on quit.
const { app } = require('electron'); const { app } = require('electron')
app.on('quit', function (event, exitCode) { app.on('quit', function (event, exitCode) {
process.emit('exit', exitCode); process.emit('exit', exitCode)
}); })
if (process.platform === 'win32') { if (process.platform === 'win32') {
// If we are a Squirrel.Windows-installed app, set app user model ID // If we are a Squirrel.Windows-installed app, set app user model ID
// so that users don't have to do this. // so that users don't have to do this.
// //
// Squirrel packages are always of the form: // Squirrel packages are always of the form:
// //
// PACKAGE-NAME // PACKAGE-NAME
// - Update.exe // - Update.exe
// - app-VERSION // - app-VERSION
// - OUREXE.exe // - OUREXE.exe
// //
// Squirrel itself will always set the shortcut's App User Model ID to the // Squirrel itself will always set the shortcut's App User Model ID to the
// form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call // form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
// app.setAppUserModelId with a matching identifier so that renderer processes // app.setAppUserModelId with a matching identifier so that renderer processes
// will inherit this value. // will inherit this value.
const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe'); const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe')
if (fs.existsSync(updateDotExe)) {
const packageDir = path.dirname(path.resolve(updateDotExe)); if (fs.existsSync(updateDotExe)) {
const packageName = path.basename(packageDir).replace(/\s/g, ''); const packageDir = path.dirname(path.resolve(updateDotExe))
const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, ''); const packageName = path.basename(packageDir).replace(/\s/g, '')
app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`); const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '')
}
app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`)
}
} }
// Map process.exit to app.exit, which quits gracefully. // Map process.exit to app.exit, which quits gracefully.
process.exit = app.exit; process.exit = app.exit
// Load the RPC server. // Load the RPC server.
require('@electron/internal/browser/rpc-server'); require('@electron/internal/browser/rpc-server')
// Load the guest view manager. // Load the guest view manager.
require('@electron/internal/browser/guest-view-manager'); require('@electron/internal/browser/guest-view-manager')
require('@electron/internal/browser/guest-window-manager'); require('@electron/internal/browser/guest-window-manager')
// Now we try to load app's package.json. // Now we try to load app's package.json.
let packagePath = null; let packagePath = null
let packageJson = null; let packageJson = null
const searchPaths = ['app', 'app.asar', 'default_app.asar']; const searchPaths = ['app', 'app.asar', 'default_app.asar']
if (process.resourcesPath) { for (packagePath of searchPaths) {
for (packagePath of searchPaths) { try {
try { packagePath = path.join(process.resourcesPath, packagePath)
packagePath = path.join(process.resourcesPath, packagePath); packageJson = require(path.join(packagePath, 'package.json'))
packageJson = require(path.join(packagePath, 'package.json')); break
break; } catch (error) {
} continue
catch (_a) { }
continue;
}
}
} }
if (packageJson == null) { if (packageJson == null) {
process.nextTick(function () { process.nextTick(function () {
return process.exit(1); return process.exit(1)
}); })
throw new Error('Unable to find a valid app'); throw new Error('Unable to find a valid app')
} }
// Set application's version. // Set application's version.
if (packageJson.version != null) { if (packageJson.version != null) {
app.setVersion(packageJson.version); app.setVersion(packageJson.version)
} }
// Set application's name. // Set application's name.
if (packageJson.productName != null) { if (packageJson.productName != null) {
app.setName(`${packageJson.productName}`.trim()); app.setName(`${packageJson.productName}`.trim())
} } else if (packageJson.name != null) {
else if (packageJson.name != null) { app.setName(`${packageJson.name}`.trim())
app.setName(`${packageJson.name}`.trim());
} }
// Set application's desktop name. // Set application's desktop name.
if (packageJson.desktopName != null) { if (packageJson.desktopName != null) {
app.setDesktopName(packageJson.desktopName); app.setDesktopName(packageJson.desktopName)
} } else {
else { app.setDesktopName((app.getName()) + '.desktop')
app.setDesktopName((app.getName()) + '.desktop');
} }
// Set v8 flags // Set v8 flags
if (packageJson.v8Flags != null) { if (packageJson.v8Flags != null) {
v8.setFlagsFromString(packageJson.v8Flags); v8.setFlagsFromString(packageJson.v8Flags)
} }
app._setDefaultAppPaths(packagePath);
// Load the chrome devtools support. // Set the user path according to application's name.
require('@electron/internal/browser/devtools'); app.setPath('userData', path.join(app.getPath('appData'), app.getName()))
app.setPath('userCache', path.join(app.getPath('cache'), app.getName()))
app.setAppPath(packagePath)
// Load the chrome extension support. // Load the chrome extension support.
require('@electron/internal/browser/chrome-extension'); require('@electron/internal/browser/chrome-extension')
const features = process.electronBinding('features');
const features = process.atomBinding('features')
if (features.isDesktopCapturerEnabled()) { if (features.isDesktopCapturerEnabled()) {
// Load internal desktop-capturer module. // Load internal desktop-capturer module.
require('@electron/internal/browser/desktop-capturer'); require('@electron/internal/browser/desktop-capturer')
} }
// Load protocol module to ensure it is populated on app ready // Load protocol module to ensure it is populated on app ready
require('@electron/internal/browser/api/protocol'); require('@electron/internal/browser/api/protocol')
// Set main startup script of the app. // Set main startup script of the app.
const mainStartupScript = packageJson.main || 'index.js'; const mainStartupScript = packageJson.main || 'index.js'
const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME'];
function currentPlatformSupportsAppIndicator() { const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME']
if (process.platform !== 'linux')
return false; function currentPlatformSupportsAppIndicator () {
const currentDesktop = process.env.XDG_CURRENT_DESKTOP; if (process.platform !== 'linux') return false
if (!currentDesktop) const currentDesktop = process.env.XDG_CURRENT_DESKTOP
return false;
if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) if (!currentDesktop) return false
return true; if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true
// ubuntu based or derived session (default ubuntu one, communitheme…) supports // ubuntu based or derived session (default ubuntu one, communitheme…) supports
// indicator too. // indicator too.
if (/ubuntu/ig.test(currentDesktop)) if (/ubuntu/ig.test(currentDesktop)) return true
return true;
return false; return false
} }
// Workaround for electron/electron#5050 and electron/electron#9046 // Workaround for electron/electron#5050 and electron/electron#9046
if (currentPlatformSupportsAppIndicator()) { if (currentPlatformSupportsAppIndicator()) {
process.env.XDG_CURRENT_DESKTOP = 'Unity'; 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', () => { // Finally load app's main.js and transfer control to C++.
if (app.listenerCount('window-all-closed') === 1) { Module._load(path.join(packagePath, mainStartupScript), Module, true)
app.quit();
}
});
Promise.all([
Promise.resolve().then(() => require('@electron/internal/browser/default-menu')),
app.whenReady
]).then(([{ setDefaultApplicationMenu }]) => {
// Create default menu
setDefaultApplicationMenu();
});
if (packagePath) {
// Finally load app's main.js and transfer control to C++.
Module._load(path.join(packagePath, mainStartupScript), Module, true);
}
else {
console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)');
console.error('This normally means you\'ve damaged the Electron package somehow');
}
//# sourceMappingURL=init.js.map

View File

@ -1,53 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ipc_main_internal_1 = require("@electron/internal/browser/ipc-main-internal");
const errorUtils = require("@electron/internal/common/error-utils");
const callHandler = async function (handler, event, args, reply) {
try {
const result = await handler(event, ...args);
reply([null, result]);
}
catch (error) {
reply([errorUtils.serialize(error)]);
}
};
exports.handle = function (channel, handler) {
ipc_main_internal_1.ipcMainInternal.on(channel, (event, requestId, ...args) => {
callHandler(handler, event, args, responseArgs => {
if (requestId) {
event._replyInternal(`${channel}_RESPONSE_${requestId}`, ...responseArgs);
}
else {
event.returnValue = responseArgs;
}
});
});
};
let nextId = 0;
function invokeInWebContents(sender, sendToAll, command, ...args) {
return new Promise((resolve, reject) => {
const requestId = ++nextId;
const channel = `${command}_RESPONSE_${requestId}`;
ipc_main_internal_1.ipcMainInternal.on(channel, function handler(event, error, result) {
if (event.sender !== sender) {
console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`);
return;
}
ipc_main_internal_1.ipcMainInternal.removeListener(channel, handler);
if (error) {
reject(errorUtils.deserialize(error));
}
else {
resolve(result);
}
});
if (sendToAll) {
sender._sendInternalToAll(command, requestId, ...args);
}
else {
sender._sendInternal(command, requestId, ...args);
}
});
}
exports.invokeInWebContents = invokeInWebContents;
//# sourceMappingURL=ipc-main-internal-utils.js.map

View File

@ -1,8 +1,10 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events"); const { EventEmitter } = require('events')
const emitter = new events_1.EventEmitter();
const emitter = new EventEmitter()
// Do not throw exception when channel name is "error". // Do not throw exception when channel name is "error".
emitter.on('error', () => { }); emitter.on('error', () => {})
exports.ipcMainInternal = emitter;
//# sourceMappingURL=ipc-main-internal.js.map module.exports = emitter

View File

@ -1,225 +0,0 @@
'use strict';
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
// The history operation in renderer is redirected to browser.
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) {
event.sender.goBack();
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD', function (event) {
event.sender.goForward();
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (event, offset) {
event.sender.goToOffset(offset);
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
event.returnValue = event.sender.length();
});
// JavaScript implementation of Chromium's NavigationController.
// Instead of relying on Chromium for history control, we compeletely do history
// control on user land, and only rely on WebContents.loadURL for navigation.
// This helps us avoid Chromium's various optimizations so we can ensure renderer
// process is restarted everytime.
const NavigationController = (function () {
function NavigationController(webContents) {
this.webContents = webContents;
this.clearHistory();
// webContents may have already navigated to a page.
if (this.webContents._getURL()) {
this.currentIndex++;
this.history.push(this.webContents._getURL());
}
this.webContents.on('navigation-entry-commited', (event, url, inPage, replaceEntry) => {
if (this.inPageIndex > -1 && !inPage) {
// Navigated to a new page, clear in-page mark.
this.inPageIndex = -1;
}
else if (this.inPageIndex === -1 && inPage && !replaceEntry) {
// Started in-page navigations.
this.inPageIndex = this.currentIndex;
}
if (this.pendingIndex >= 0) {
// Go to index.
this.currentIndex = this.pendingIndex;
this.pendingIndex = -1;
this.history[this.currentIndex] = url;
}
else if (replaceEntry) {
// Non-user initialized navigation.
this.history[this.currentIndex] = url;
}
else {
// Normal navigation. Clear history.
this.history = this.history.slice(0, this.currentIndex + 1);
this.currentIndex++;
this.history.push(url);
}
});
}
NavigationController.prototype.loadURL = function (url, options) {
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 && !isSameDocument) {
// the webcontents has started another unrelated navigation in the
// main frame (probably from the app calling `loadURL` again); reject
// the promise
// We should only consider the request aborted if the "navigation" is
// actually navigating and not simply transitioning URL state in the
// current context. E.g. pushState and `location.hash` changes are
// considered navigation events but are triggered with isSameDocument.
// We can ignore these to allow virtual routing on page load as long
// as the routing does not leave the document
return rejectAndCleanup(-3, 'ERR_ABORTED', url);
}
navigationStarted = true;
}
};
const stopLoadingListener = () => {
// By the time we get here, either 'finish' or 'fail' should have fired
// if the navigation occurred. However, in some situations (e.g. when
// attempting to load a page with a bad scheme), loading will stop
// without emitting finish or fail. In this case, we reject the promise
// with a generic failure.
// TODO(jeremy): enumerate all the cases in which this can happen. If
// the only one is with a bad scheme, perhaps ERR_INVALID_ARGUMENT
// would be more appropriate.
rejectAndCleanup(-2, 'ERR_FAILED', url);
};
const removeListeners = () => {
this.webContents.removeListener('did-finish-load', finishListener);
this.webContents.removeListener('did-fail-load', failListener);
this.webContents.removeListener('did-start-navigation', navigationListener);
this.webContents.removeListener('did-stop-loading', stopLoadingListener);
};
this.webContents.on('did-finish-load', finishListener);
this.webContents.on('did-fail-load', failListener);
this.webContents.on('did-start-navigation', navigationListener);
this.webContents.on('did-stop-loading', stopLoadingListener);
});
// Add a no-op rejection handler to silence the unhandled rejection error.
p.catch(() => { });
this.pendingIndex = -1;
this.webContents._loadURL(url, options);
this.webContents.emit('load-url', url, options);
return p;
};
NavigationController.prototype.getURL = function () {
if (this.currentIndex === -1) {
return '';
}
else {
return this.history[this.currentIndex];
}
};
NavigationController.prototype.stop = function () {
this.pendingIndex = -1;
return this.webContents._stop();
};
NavigationController.prototype.reload = function () {
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {});
};
NavigationController.prototype.reloadIgnoringCache = function () {
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {
extraHeaders: 'pragma: no-cache\n'
});
};
NavigationController.prototype.canGoBack = function () {
return this.getActiveIndex() > 0;
};
NavigationController.prototype.canGoForward = function () {
return this.getActiveIndex() < this.history.length - 1;
};
NavigationController.prototype.canGoToIndex = function (index) {
return index >= 0 && index < this.history.length;
};
NavigationController.prototype.canGoToOffset = function (offset) {
return this.canGoToIndex(this.currentIndex + offset);
};
NavigationController.prototype.clearHistory = function () {
this.history = [];
this.currentIndex = -1;
this.pendingIndex = -1;
this.inPageIndex = -1;
};
NavigationController.prototype.goBack = function () {
if (!this.canGoBack()) {
return;
}
this.pendingIndex = this.getActiveIndex() - 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goBack();
}
else {
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
};
NavigationController.prototype.goForward = function () {
if (!this.canGoForward()) {
return;
}
this.pendingIndex = this.getActiveIndex() + 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goForward();
}
else {
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
};
NavigationController.prototype.goToIndex = function (index) {
if (!this.canGoToIndex(index)) {
return;
}
this.pendingIndex = index;
return this.webContents._loadURL(this.history[this.pendingIndex], {});
};
NavigationController.prototype.goToOffset = function (offset) {
if (!this.canGoToOffset(offset)) {
return;
}
const pendingIndex = this.currentIndex + offset;
if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
this.pendingIndex = pendingIndex;
return this.webContents._goToOffset(offset);
}
else {
return this.goToIndex(pendingIndex);
}
};
NavigationController.prototype.getActiveIndex = function () {
if (this.pendingIndex === -1) {
return this.currentIndex;
}
else {
return this.pendingIndex;
}
};
NavigationController.prototype.length = function () {
return this.history.length;
};
return NavigationController;
})();
module.exports = NavigationController;
//# sourceMappingURL=navigation-controller.js.map

View File

@ -1,121 +1,116 @@
'use strict'; 'use strict'
const v8Util = process.electronBinding('v8_util');
const v8Util = process.atomBinding('v8_util')
const getOwnerKey = (webContents, contextId) => { const getOwnerKey = (webContents, contextId) => {
return `${webContents.id}-${contextId}`; return `${webContents.id}-${contextId}`
};
class ObjectsRegistry {
constructor() {
this.nextId = 0;
// Stores all objects by ref-counting.
// (id) => {object, count}
this.storage = {};
// Stores the IDs + refCounts of objects referenced by WebContents.
// (ownerKey) => { id: refCount }
this.owners = {};
}
// Register a new object and return its assigned ID. If the object is already
// registered then the already assigned ID would be returned.
add(webContents, contextId, obj) {
// Get or assign an ID to the object.
const id = this.saveToStorage(obj);
// Add object to the set of referenced objects.
const ownerKey = getOwnerKey(webContents, contextId);
let owner = this.owners[ownerKey];
if (!owner) {
owner = this.owners[ownerKey] = new Map();
this.registerDeleteListener(webContents, contextId);
}
if (!owner.has(id)) {
owner.set(id, 0);
// Increase reference count if not referenced before.
this.storage[id].count++;
}
owner.set(id, owner.get(id) + 1);
return id;
}
// Get an object according to its ID.
get(id) {
const pointer = this.storage[id];
if (pointer != null)
return pointer.object;
}
// Dereference an object according to its ID.
// Note that an object may be double-freed (cleared when page is reloaded, and
// then garbage collected in old page).
// rendererSideRefCount is the ref count that the renderer process reported
// at time of GC if this is different to the number of references we sent to
// the given owner then a GC occurred between a ref being sent and the value
// being pulled out of the weak map.
// In this case we decrement out ref count and do not delete the stored
// object
// For more details on why we do renderer side ref counting see
// https://github.com/electron/electron/pull/17464
remove(webContents, contextId, id, rendererSideRefCount) {
const ownerKey = getOwnerKey(webContents, contextId);
const owner = this.owners[ownerKey];
if (owner && owner.has(id)) {
const newRefCount = owner.get(id) - rendererSideRefCount;
// Only completely remove if the number of references GCed in the
// renderer is the same as the number of references we sent them
if (newRefCount <= 0) {
// Remove the reference in owner.
owner.delete(id);
// Dereference from the storage.
this.dereference(id);
}
else {
owner.set(id, newRefCount);
}
}
}
// Clear all references to objects refrenced by the WebContents.
clear(webContents, contextId) {
const ownerKey = getOwnerKey(webContents, contextId);
const owner = this.owners[ownerKey];
if (!owner)
return;
for (const id of owner.keys())
this.dereference(id);
delete this.owners[ownerKey];
}
// Private: Saves the object into storage and assigns an ID for it.
saveToStorage(object) {
let id = v8Util.getHiddenValue(object, 'atomId');
if (!id) {
id = ++this.nextId;
this.storage[id] = {
count: 0,
object: object
};
v8Util.setHiddenValue(object, 'atomId', id);
}
return id;
}
// Private: Dereference the object from store.
dereference(id) {
const pointer = this.storage[id];
if (pointer == null) {
return;
}
pointer.count -= 1;
if (pointer.count === 0) {
v8Util.deleteHiddenValue(pointer.object, 'atomId');
delete this.storage[id];
}
}
// Private: Clear the storage when renderer process is destroyed.
registerDeleteListener(webContents, contextId) {
// contextId => ${processHostId}-${contextCount}
const processHostId = contextId.split('-')[0];
const listener = (event, deletedProcessHostId) => {
if (deletedProcessHostId &&
deletedProcessHostId.toString() === processHostId) {
webContents.removeListener('render-view-deleted', listener);
this.clear(webContents, contextId);
}
};
webContents.on('render-view-deleted', listener);
}
} }
module.exports = new ObjectsRegistry();
//# sourceMappingURL=objects-registry.js.map class ObjectsRegistry {
constructor () {
this.nextId = 0
// Stores all objects by ref-counting.
// (id) => {object, count}
this.storage = {}
// Stores the IDs of objects referenced by WebContents.
// (ownerKey) => [id]
this.owners = {}
}
// Register a new object and return its assigned ID. If the object is already
// registered then the already assigned ID would be returned.
add (webContents, contextId, obj) {
// Get or assign an ID to the object.
const id = this.saveToStorage(obj)
// Add object to the set of referenced objects.
const ownerKey = getOwnerKey(webContents, contextId)
let owner = this.owners[ownerKey]
if (!owner) {
owner = this.owners[ownerKey] = new Set()
this.registerDeleteListener(webContents, contextId)
}
if (!owner.has(id)) {
owner.add(id)
// Increase reference count if not referenced before.
this.storage[id].count++
}
return id
}
// Get an object according to its ID.
get (id) {
const pointer = this.storage[id]
if (pointer != null) return pointer.object
}
// Dereference an object according to its ID.
// Note that an object may be double-freed (cleared when page is reloaded, and
// then garbage collected in old page).
remove (webContents, contextId, id) {
const ownerKey = getOwnerKey(webContents, contextId)
const owner = this.owners[ownerKey]
if (owner) {
// Remove the reference in owner.
owner.delete(id)
// Dereference from the storage.
this.dereference(id)
}
}
// Clear all references to objects refrenced by the WebContents.
clear (webContents, contextId) {
const ownerKey = getOwnerKey(webContents, contextId)
const owner = this.owners[ownerKey]
if (!owner) return
for (const id of owner) this.dereference(id)
delete this.owners[ownerKey]
}
// Private: Saves the object into storage and assigns an ID for it.
saveToStorage (object) {
let id = v8Util.getHiddenValue(object, 'atomId')
if (!id) {
id = ++this.nextId
this.storage[id] = {
count: 0,
object: object
}
v8Util.setHiddenValue(object, 'atomId', id)
}
return id
}
// Private: Dereference the object from store.
dereference (id) {
const pointer = this.storage[id]
if (pointer == null) {
return
}
pointer.count -= 1
if (pointer.count === 0) {
v8Util.deleteHiddenValue(pointer.object, 'atomId')
delete this.storage[id]
}
}
// Private: Clear the storage when renderer process is destroyed.
registerDeleteListener (webContents, contextId) {
// contextId => ${processHostId}-${contextCount}
const processHostId = contextId.split('-')[0]
const listener = (event, deletedProcessHostId) => {
if (deletedProcessHostId &&
deletedProcessHostId.toString() === processHostId) {
webContents.removeListener('render-view-deleted', listener)
this.clear(webContents, contextId)
}
}
webContents.on('render-view-deleted', listener)
}
}
module.exports = new ObjectsRegistry()

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,31 @@
'use strict'; 'use strict'
const clipboard = process.electronBinding('clipboard');
if (process.type === 'renderer') { if (process.platform === 'linux' && process.type === 'renderer') {
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); // On Linux we could not access clipboard in renderer process.
const clipboardUtils = require('@electron/internal/common/clipboard-utils'); const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
const makeRemoteMethod = function (method) { module.exports = getRemoteForUsage('clipboard').clipboard
return (...args) => { } else {
args = clipboardUtils.serialize(args); const clipboard = process.atomBinding('clipboard')
const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args);
return clipboardUtils.deserialize(result); // Read/write to find pasteboard over IPC since only main process is notified
}; // of changes
}; if (process.platform === 'darwin' && process.type === 'renderer') {
if (process.platform === 'linux') { const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
// On Linux we could not access clipboard in renderer process. const errorUtils = require('@electron/internal/common/error-utils')
for (const method of Object.keys(clipboard)) {
clipboard[method] = makeRemoteMethod(method); const invoke = function (command, ...args) {
} const [ error, result ] = ipcRenderer.sendSync(command, ...args)
}
else if (process.platform === 'darwin') { if (error) {
// Read/write to find pasteboard over IPC since only main process is notified of changes throw errorUtils.deserialize(error)
clipboard.readFindText = makeRemoteMethod('readFindText'); } else {
clipboard.writeFindText = makeRemoteMethod('writeFindText'); return result
}
} }
clipboard.readFindText = (...args) => invoke('ELECTRON_BROWSER_CLIPBOARD_READ_FIND_TEXT', ...args)
clipboard.writeFindText = (...args) => invoke('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', ...args)
}
module.exports = clipboard
} }
module.exports = clipboard;
//# sourceMappingURL=clipboard.js.map

View File

@ -1,175 +1,95 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
let deprecationHandler = null; let deprecationHandler = null
function warnOnce(oldName, newName) {
let warned = false; function warnOnce (oldName, newName) {
const msg = newName let warned = false
? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.` const msg = newName
: `'${oldName}' is deprecated and will be removed.`; ? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.`
return () => { : `'${oldName}' is deprecated and will be removed.`
if (!warned && !process.noDeprecation) { return () => {
warned = true; if (!warned && !process.noDeprecation) {
deprecate.log(msg); warned = true
} deprecate.log(msg)
};
}
const deprecate = {
setHandler: (handler) => { deprecationHandler = handler; },
getHandler: () => deprecationHandler,
warn: (oldName, newName) => {
if (!process.noDeprecation) {
deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`);
}
},
log: (message) => {
if (typeof deprecationHandler === 'function') {
deprecationHandler(message);
}
else if (process.throwDeprecation) {
throw new Error(message);
}
else if (process.traceDeprecation) {
return console.trace(message);
}
else {
return console.warn(`(electron) ${message}`);
}
},
// change the name of a function
function: (fn, newName) => {
const warn = warnOnce(fn.name, newName);
return function () {
warn();
fn.apply(this, arguments);
};
},
// change the name of an event
event: (emitter, oldName, newName) => {
const warn = newName.startsWith('-') /* internal event */
? warnOnce(`${oldName} event`)
: warnOnce(`${oldName} event`, `${newName} event`);
return emitter.on(newName, function (...args) {
if (this.listenerCount(oldName) !== 0) {
warn();
this.emit(oldName, ...args);
}
});
},
// deprecate a getter/setter function pair in favor of a property
fnToProperty: (module, prop, getter, setter) => {
const withWarnOnce = (obj, key, oldName, newName) => {
const warn = warnOnce(oldName, newName);
return (...args) => {
warn();
return obj[key](...args);
};
};
module[getter.substr(1)] = withWarnOnce(module, getter, `${getter.substr(1)} function`, `${prop} property`);
module[setter.substr(1)] = withWarnOnce(module, setter, `${setter.substr(1)} function`, `${prop} property`);
},
// remove a property with no replacement
removeProperty: (o, removedName) => {
// if the property's already been removed, warn about it
if (!(removedName in o)) {
deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`);
}
// wrap the deprecated property in an accessor to warn
const warn = warnOnce(removedName);
let val = o[removedName];
return Object.defineProperty(o, removedName, {
configurable: true,
get: () => {
warn();
return val;
},
set: newVal => {
warn();
val = newVal;
}
});
},
// deprecate a callback-based function in favor of one returning a Promise
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);
});
return res;
}, (err) => {
process.nextTick(() => {
cb.length === 2 ? cb(err) : cb();
});
throw err;
});
};
},
// deprecate a callback-based function in favor of one returning a Promise
promisifyMultiArg: (fn, convertPromiseValue) => {
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) => {
if (convertPromiseValue) {
res = convertPromiseValue(res);
}
process.nextTick(() => {
// eslint-disable-next-line standard/no-callback-literal
cb.length > 2 ? cb(null, ...res) : cb(...res);
});
}, (err) => {
process.nextTick(() => cb(err));
});
};
},
// change the name of a property
renameProperty: (o, oldName, newName) => {
const warn = warnOnce(oldName, newName);
// if the new property isn't there yet,
// inject it and warn about it
if ((oldName in o) && !(newName in o)) {
warn();
o[newName] = o[oldName];
}
// wrap the deprecated property in an accessor to warn
// and redirect to the new property
return Object.defineProperty(o, oldName, {
get: () => {
warn();
return o[newName];
},
set: value => {
warn();
o[newName] = value;
}
});
} }
}; }
exports.default = deprecate; }
//# sourceMappingURL=deprecate.js.map
const deprecate = {
setHandler: (handler) => { deprecationHandler = handler },
getHandler: () => deprecationHandler,
warn: (oldName, newName) => {
return deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`)
},
log: (message) => {
if (typeof deprecationHandler === 'function') {
deprecationHandler(message)
} else if (process.throwDeprecation) {
throw new Error(message)
} else if (process.traceDeprecation) {
return console.trace(message)
} else {
return console.warn(`(electron) ${message}`)
}
},
event: (emitter, oldName, newName) => {
const warn = newName.startsWith('-') /* internal event */
? warnOnce(`${oldName} event`)
: warnOnce(`${oldName} event`, `${newName} event`)
return emitter.on(newName, function (...args) {
if (this.listenerCount(oldName) !== 0) {
warn()
this.emit(oldName, ...args)
}
})
},
removeProperty: (o, removedName) => {
// if the property's already been removed, warn about it
if (!(removedName in o)) {
deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`)
}
// wrap the deprecated property in an accessor to warn
const warn = warnOnce(removedName)
let val = o[removedName]
return Object.defineProperty(o, removedName, {
configurable: true,
get: () => {
warn()
return val
},
set: newVal => {
warn()
val = newVal
}
})
},
renameProperty: (o, oldName, newName) => {
const warn = warnOnce(oldName, newName)
// if the new property isn't there yet,
// inject it and warn about it
if ((oldName in o) && !(newName in o)) {
warn()
o[newName] = o[oldName]
}
// wrap the deprecated property in an accessor to warn
// and redirect to the new property
return Object.defineProperty(o, oldName, {
get: () => {
warn()
return o[newName]
},
set: value => {
warn()
o[newName] = value
}
})
}
}
module.exports = deprecate

View File

@ -1,9 +1,11 @@
'use strict'; 'use strict'
const deprecate = require('electron').deprecate;
const deprecate = require('electron').deprecate
exports.setHandler = function (deprecationHandler) { exports.setHandler = function (deprecationHandler) {
deprecate.setHandler(deprecationHandler); deprecate.setHandler(deprecationHandler)
}; }
exports.getHandler = function () { exports.getHandler = function () {
return deprecate.getHandler(); return deprecate.getHandler()
}; }
//# sourceMappingURL=deprecations.js.map

View File

@ -1,34 +1,31 @@
'use strict'; 'use strict'
const moduleList = require('@electron/internal/common/api/module-list');
const moduleList = require('@electron/internal/common/api/module-list')
exports.memoizedGetter = (getter) => { exports.memoizedGetter = (getter) => {
/* /*
* It's ok to leak this value as it would be leaked by the global * It's ok to leak this value as it would be leaked by the global
* node module cache anyway at `Module._cache`. This memoization * node module cache anyway at `Module._cache`. This memoization
* is dramatically faster than relying on nodes module cache however * is dramatically faster than relying on nodes module cache however
*/ */
let memoizedValue = null; let memoizedValue = null
return () => {
if (memoizedValue === null) { return () => {
memoizedValue = getter(); if (memoizedValue === null) {
} memoizedValue = getter()
return memoizedValue; }
}; return memoizedValue
}; }
}
// Attaches properties to |targetExports|. // Attaches properties to |targetExports|.
exports.defineProperties = function (targetExports) { exports.defineProperties = function (targetExports) {
const descriptors = {}; const descriptors = {}
for (const module of moduleList) { for (const module of moduleList) {
descriptors[module.name] = { descriptors[module.name] = {
enumerable: !module.private, enumerable: !module.private,
get: exports.memoizedGetter(() => { get: exports.memoizedGetter(() => require(`@electron/internal/common/api/${module.file}`))
const value = require(`@electron/internal/common/api/${module.file}.js`);
// Handle Typescript modules with an "export default X" statement
if (value.__esModule)
return value.default;
return value;
})
};
} }
return Object.defineProperties(targetExports, descriptors); }
}; return Object.defineProperties(targetExports, descriptors)
//# sourceMappingURL=electron.js.map }

View File

@ -1,12 +1,14 @@
'use strict'; 'use strict'
module.exports = function isPromise(val) {
return (val && module.exports = function isPromise (val) {
val.then && return (
val.then instanceof Function && val &&
val.constructor && val.then &&
val.constructor.reject && val.then instanceof Function &&
val.constructor.reject instanceof Function && val.constructor &&
val.constructor.resolve && val.constructor.reject &&
val.constructor.resolve instanceof Function); val.constructor.reject instanceof Function &&
}; val.constructor.resolve &&
//# sourceMappingURL=is-promise.js.map val.constructor.resolve instanceof Function
)
}

View File

@ -1,12 +1,12 @@
'use strict'; 'use strict'
// Common modules, please sort alphabetically // Common modules, please sort alphabetically
module.exports = [ module.exports = [
{ name: 'clipboard', file: 'clipboard' }, { name: 'clipboard', file: 'clipboard' },
{ name: 'nativeImage', file: 'native-image' }, { name: 'nativeImage', file: 'native-image' },
{ name: 'shell', file: 'shell' }, { name: 'shell', file: 'shell' },
// The internal modules, invisible unless you know their names. // The internal modules, invisible unless you know their names.
{ name: 'deprecate', file: 'deprecate', private: true }, { name: 'deprecate', file: 'deprecate', private: true },
{ name: 'deprecations', file: 'deprecations', private: true }, { name: 'deprecations', file: 'deprecations', private: true },
{ name: 'isPromise', file: 'is-promise', private: true } { name: 'isPromise', file: 'is-promise', private: true }
]; ]
//# sourceMappingURL=module-list.js.map

View File

@ -1,4 +1,3 @@
'use strict'; 'use strict'
const { nativeImage } = process.electronBinding('native_image');
module.exports = nativeImage; module.exports = process.atomBinding('native_image')
//# sourceMappingURL=native-image.js.map

View File

@ -1,3 +1,3 @@
'use strict'; 'use strict'
module.exports = process.electronBinding('shell');
//# sourceMappingURL=shell.js.map module.exports = process.atomBinding('shell')

View File

@ -1,19 +1,15 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
function electronBindingSetup(binding, processType) { module.exports = function atomBindingSetup (binding, processType) {
return function electronBinding(name) { return function atomBinding (name) {
try { try {
return binding(`atom_${processType}_${name}`); return binding(`atom_${processType}_${name}`)
} } catch (error) {
catch (error) { if (/No such module/.test(error.message)) {
if (/No such module/.test(error.message)) { return binding(`atom_common_${name}`)
return binding(`atom_common_${name}`); } else {
} throw error
else { }
throw error; }
} }
}
};
} }
exports.electronBindingSetup = electronBindingSetup;
//# sourceMappingURL=atom-binding-setup.js.map

View File

@ -1,64 +1,66 @@
'use strict'; 'use strict'
// Note: Don't use destructuring assignment for `Buffer`, or we'll hit a // Note: Don't use destructuring assignment for `Buffer`, or we'll hit a
// browserify bug that makes the statement invalid, throwing an error in // browserify bug that makes the statement invalid, throwing an error in
// sandboxed renderer. // sandboxed renderer.
const Buffer = require('buffer').Buffer; const Buffer = require('buffer').Buffer
const typedArrays = { const typedArrays = {
Buffer, Buffer,
ArrayBuffer, ArrayBuffer,
Int8Array, Int8Array,
Uint8Array, Uint8Array,
Uint8ClampedArray, Uint8ClampedArray,
Int16Array, Int16Array,
Uint16Array, Uint16Array,
Int32Array, Int32Array,
Uint32Array, Uint32Array,
Float32Array, Float32Array,
Float64Array Float64Array
};
function getType(value) {
for (const type of Object.keys(typedArrays)) {
if (value instanceof typedArrays[type]) {
return type;
}
}
return null;
} }
function getBuffer(value) {
if (value instanceof Buffer) { function getType (value) {
return value; for (const type of Object.keys(typedArrays)) {
} if (value instanceof typedArrays[type]) {
else if (value instanceof ArrayBuffer) { return type
return Buffer.from(value);
}
else {
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
} }
}
return null
} }
function getBuffer (value) {
if (value instanceof Buffer) {
return value
} else if (value instanceof ArrayBuffer) {
return Buffer.from(value)
} else {
return Buffer.from(value.buffer, value.byteOffset, value.byteLength)
}
}
exports.isBuffer = function (value) { exports.isBuffer = function (value) {
return ArrayBuffer.isView(value) || value instanceof ArrayBuffer; return ArrayBuffer.isView(value) || value instanceof ArrayBuffer
}; }
exports.bufferToMeta = function (value) { exports.bufferToMeta = function (value) {
return { return {
type: getType(value), type: getType(value),
data: getBuffer(value), data: getBuffer(value),
length: value.length length: value.length
}; }
}; }
exports.metaToBuffer = function (value) { exports.metaToBuffer = function (value) {
const constructor = typedArrays[value.type]; const constructor = typedArrays[value.type]
const data = getBuffer(value.data); const data = getBuffer(value.data)
if (constructor === Buffer) {
return data; if (constructor === Buffer) {
} return data
else if (constructor === ArrayBuffer) { } else if (constructor === ArrayBuffer) {
return data.buffer; return data.buffer
} } else if (constructor) {
else if (constructor) { return new constructor(data.buffer, data.byteOffset, value.length)
return new constructor(data.buffer, data.byteOffset, value.length); } else {
} return data
else { }
return data; }
}
};
//# sourceMappingURL=buffer-utils.js.map

View File

@ -1,50 +0,0 @@
'use strict';
const { nativeImage, NativeImage } = process.electronBinding('native_image');
const objectMap = function (source, mapper) {
const sourceEntries = Object.entries(source);
const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]);
return Object.fromEntries(targetEntries);
};
const serialize = function (value) {
if (value instanceof NativeImage) {
return {
buffer: value.toBitmap(),
size: value.getSize(),
__ELECTRON_SERIALIZED_NativeImage__: true
};
}
else if (Array.isArray(value)) {
return value.map(serialize);
}
else if (value instanceof Buffer) {
return value;
}
else if (value instanceof Object) {
return objectMap(value, serialize);
}
else {
return value;
}
};
const deserialize = function (value) {
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
return nativeImage.createFromBitmap(value.buffer, value.size);
}
else if (Array.isArray(value)) {
return value.map(deserialize);
}
else if (value instanceof Buffer) {
return value;
}
else if (value instanceof Object) {
return objectMap(value, deserialize);
}
else {
return value;
}
};
module.exports = {
serialize,
deserialize
};
//# sourceMappingURL=clipboard-utils.js.map

View File

@ -1,78 +1,123 @@
'use strict'; 'use strict'
const binding = process.electronBinding('crash_reporter');
const binding = process.atomBinding('crash_reporter')
const errorUtils = require('@electron/internal/common/error-utils')
class CrashReporter { class CrashReporter {
contructor() { contructor () {
this.productName = null; this.productName = null
this.crashesDirectory = null; this.crashesDirectory = null
}
sendSync (channel, ...args) {
throw new Error('Not implemented')
}
invoke (command, ...args) {
const [ error, result ] = this.sendSync(command, ...args)
if (error) {
throw errorUtils.deserialize(error)
} }
init(options) {
throw new Error('Not implemented'); return result
}
start (options) {
if (options == null) options = {}
let {
productName,
companyName,
extra,
ignoreSystemCrashHandler,
submitURL,
uploadToServer
} = options
if (uploadToServer == null) {
uploadToServer = true
} }
start(options) {
if (options == null) if (ignoreSystemCrashHandler == null) {
options = {}; ignoreSystemCrashHandler = false
const { productName, companyName, extra = {}, ignoreSystemCrashHandler = false, submitURL, uploadToServer = true } = options;
if (companyName == null)
throw new Error('companyName is a required option to crashReporter.start');
if (submitURL == null)
throw new Error('submitURL is a required option to crashReporter.start');
const ret = this.init({
submitURL,
productName
});
this.productName = ret.productName;
this.crashesDirectory = ret.crashesDirectory;
if (extra._productName == null)
extra._productName = ret.productName;
if (extra._companyName == null)
extra._companyName = companyName;
if (extra._version == null)
extra._version = ret.appVersion;
binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra);
} }
getLastCrashReport() {
const reports = this.getUploadedReports() if (companyName == null) {
.sort((a, b) => { throw new Error('companyName is a required option to crashReporter.start')
const ats = (a && a.date) ? new Date(a.date).getTime() : 0;
const bts = (b && b.date) ? new Date(b.date).getTime() : 0;
return bts - ats;
});
return (reports.length > 0) ? reports[0] : null;
} }
getUploadedReports() { if (submitURL == null) {
return binding.getUploadedReports(this.getCrashesDirectory()); throw new Error('submitURL is a required option to crashReporter.start')
} }
getCrashesDirectory() {
return this.crashesDirectory; const ret = this.invoke('ELECTRON_CRASH_REPORTER_INIT', {
submitURL,
productName
})
this.productName = ret.productName
this.crashesDirectory = ret.crashesDirectory
this.crashServicePid = ret.crashServicePid
if (extra == null) extra = {}
if (extra._productName == null) extra._productName = ret.productName
if (extra._companyName == null) extra._companyName = companyName
if (extra._version == null) extra._version = ret.appVersion
binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra)
}
getLastCrashReport () {
const reports = this.getUploadedReports()
.sort((a, b) => {
const ats = (a && a.date) ? new Date(a.date).getTime() : 0
const bts = (b && b.date) ? new Date(b.date).getTime() : 0
return bts - ats
})
return (reports.length > 0) ? reports[0] : null
}
getUploadedReports () {
return binding.getUploadedReports(this.getCrashesDirectory())
}
getCrashesDirectory () {
return this.crashesDirectory
}
getProductName () {
return this.productName
}
getUploadToServer () {
if (process.type === 'browser') {
return binding.getUploadToServer()
} else {
throw new Error('getUploadToServer can only be called from the main process')
} }
getProductName() { }
return this.productName;
} setUploadToServer (uploadToServer) {
getUploadToServer() { if (process.type === 'browser') {
if (process.type === 'browser') { return binding.setUploadToServer(uploadToServer)
return binding.getUploadToServer(); } else {
} throw new Error('setUploadToServer can only be called from the main process')
else {
throw new Error('getUploadToServer can only be called from the main process');
}
}
setUploadToServer(uploadToServer) {
if (process.type === 'browser') {
return binding.setUploadToServer(uploadToServer);
}
else {
throw new Error('setUploadToServer can only be called from the main process');
}
}
addExtraParameter(key, value) {
binding.addExtraParameter(key, value);
}
removeExtraParameter(key) {
binding.removeExtraParameter(key);
}
getParameters() {
return binding.getParameters();
} }
}
addExtraParameter (key, value) {
binding.addExtraParameter(key, value)
}
removeExtraParameter (key) {
binding.removeExtraParameter(key)
}
getParameters (key, value) {
return binding.getParameters()
}
} }
module.exports = CrashReporter;
//# sourceMappingURL=crash-reporter.js.map module.exports = CrashReporter

View File

@ -1,37 +1,39 @@
'use strict'; 'use strict'
const constructors = new Map([ const constructors = new Map([
[Error.name, Error], [Error.name, Error],
[EvalError.name, EvalError], [EvalError.name, EvalError],
[RangeError.name, RangeError], [RangeError.name, RangeError],
[ReferenceError.name, ReferenceError], [ReferenceError.name, ReferenceError],
[SyntaxError.name, SyntaxError], [SyntaxError.name, SyntaxError],
[TypeError.name, TypeError], [TypeError.name, TypeError],
[URIError.name, URIError] [URIError.name, URIError]
]); ])
exports.deserialize = function (error) { exports.deserialize = function (error) {
if (error && error.__ELECTRON_SERIALIZED_ERROR__ && constructors.has(error.name)) { if (error && error.__ELECTRON_SERIALIZED_ERROR__ && constructors.has(error.name)) {
const constructor = constructors.get(error.name); const constructor = constructors.get(error.name)
const deserializedError = new constructor(error.message); const deserializedError = new constructor(error.message)
deserializedError.stack = error.stack; deserializedError.stack = error.stack
deserializedError.from = error.from; deserializedError.from = error.from
deserializedError.cause = exports.deserialize(error.cause); deserializedError.cause = exports.deserialize(error.cause)
return deserializedError; return deserializedError
} }
return error; return error
}; }
exports.serialize = function (error) { exports.serialize = function (error) {
if (error instanceof Error) { if (error instanceof Error) {
// Errors get lost, because: JSON.stringify(new Error('Message')) === {} // Errors get lost, because: JSON.stringify(new Error('Message')) === {}
// Take the serializable properties and construct a generic object // Take the serializable properties and construct a generic object
return { return {
message: error.message, message: error.message,
stack: error.stack, stack: error.stack,
name: error.name, name: error.name,
from: process.type, from: process.type,
cause: exports.serialize(error.cause), cause: exports.serialize(error.cause),
__ELECTRON_SERIALIZED_ERROR__: true __ELECTRON_SERIALIZED_ERROR__: true
};
} }
return error; }
}; return error
//# sourceMappingURL=error-utils.js.map }

View File

@ -1,9 +1,10 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const timers = require("timers"); const timers = require('timers')
const util = require("util"); const util = require('util')
const atom_binding_setup_1 = require("@electron/internal/common/atom-binding-setup");
process.electronBinding = atom_binding_setup_1.electronBindingSetup(process._linkedBinding, process.type); process.atomBinding = require('@electron/internal/common/atom-binding-setup')(process.binding, process.type)
// setImmediate and process.nextTick makes use of uv_check and uv_prepare to // setImmediate and process.nextTick makes use of uv_check and uv_prepare to
// run the callbacks, however since we only run uv loop on requests, the // run the callbacks, however since we only run uv loop on requests, the
// callbacks wouldn't be called until something else activated the uv loop, // callbacks wouldn't be called until something else activated the uv loop,
@ -11,48 +12,42 @@ process.electronBinding = atom_binding_setup_1.electronBindingSetup(process._lin
// initiatively activate the uv loop once setImmediate and process.nextTick is // initiatively activate the uv loop once setImmediate and process.nextTick is
// called. // called.
const wrapWithActivateUvLoop = function (func) { const wrapWithActivateUvLoop = function (func) {
return wrap(func, function (func) { return wrap(func, function (func) {
return function (...args) { return function () {
process.activateUvLoop(); process.activateUvLoop()
return func.apply(this, args); return func.apply(this, arguments)
};
});
};
/**
* Casts to any below for func are due to Typescript not supporting symbols
* in index signatures
*
* Refs: https://github.com/Microsoft/TypeScript/issues/1863
*/
function wrap(func, wrapper) {
const wrapped = wrapper(func);
if (func[util.promisify.custom]) {
wrapped[util.promisify.custom] = wrapper(func[util.promisify.custom]);
} }
return wrapped; })
} }
process.nextTick = wrapWithActivateUvLoop(process.nextTick);
global.setImmediate = wrapWithActivateUvLoop(timers.setImmediate); function wrap (func, wrapper) {
global.clearImmediate = timers.clearImmediate; const wrapped = wrapper(func)
if (func[util.promisify.custom]) {
wrapped[util.promisify.custom] = wrapper(func[util.promisify.custom])
}
return wrapped
}
process.nextTick = wrapWithActivateUvLoop(process.nextTick)
global.setImmediate = wrapWithActivateUvLoop(timers.setImmediate)
global.clearImmediate = timers.clearImmediate
if (process.type === 'browser') { if (process.type === 'browser') {
// setTimeout needs to update the polling timeout of the event loop, when // setTimeout needs to update the polling timeout of the event loop, when
// called under Chromium's event loop the node's event loop won't get a chance // called under Chromium's event loop the node's event loop won't get a chance
// to update the timeout, so we have to force the node's event loop to // to update the timeout, so we have to force the node's event loop to
// recalculate the timeout in browser process. // recalculate the timeout in browser process.
global.setTimeout = wrapWithActivateUvLoop(timers.setTimeout); global.setTimeout = wrapWithActivateUvLoop(timers.setTimeout)
global.setInterval = wrapWithActivateUvLoop(timers.setInterval); global.setInterval = wrapWithActivateUvLoop(timers.setInterval)
} }
if (process.platform === 'win32') { if (process.platform === 'win32') {
// Always returns EOF for stdin stream. // Always returns EOF for stdin stream.
const { Readable } = require('stream'); const { Readable } = require('stream')
const stdin = new Readable(); const stdin = new Readable()
stdin.push(null); stdin.push(null)
Object.defineProperty(process, 'stdin', { process.__defineGetter__('stdin', function () {
configurable: false, return stdin
enumerable: true, })
get() {
return stdin;
}
});
} }
//# sourceMappingURL=init.js.map

View File

@ -1,20 +1,21 @@
'use strict'; 'use strict'
// parses a feature string that has the format used in window.open() // parses a feature string that has the format used in window.open()
// - `features` input string // - `features` input string
// - `emit` function(key, value) - called for each parsed KV // - `emit` function(key, value) - called for each parsed KV
module.exports = function parseFeaturesString(features, emit) { module.exports = function parseFeaturesString (features, emit) {
features = `${features}`.trim(); features = `${features}`
// split the string by ',' // split the string by ','
features.split(/\s*,\s*/).forEach((feature) => { features.split(/,\s*/).forEach((feature) => {
// expected form is either a key by itself or a key/value pair in the form of // expected form is either a key by itself or a key/value pair in the form of
// 'key=value' // 'key=value'
let [key, value] = feature.split(/\s*=\s*/); let [key, value] = feature.split(/\s*=/)
if (!key) if (!key) return
return;
// interpret the value as a boolean, if possible // interpret the value as a boolean, if possible
value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value; value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value
// emit the parsed pair
emit(key, value); // emit the parsed pair
}); emit(key, value)
}; })
//# sourceMappingURL=parse-features-string.js.map }

View File

@ -1,8 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
exports.isParentDir = function (parent, dir) {
const relative = path.relative(parent, dir);
return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
};
//# sourceMappingURL=path-utils.js.map

View File

@ -1,44 +1,45 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path"); const path = require('path')
const Module = require('module'); const Module = require('module')
// Clear Node's global search paths. // Clear Node's global search paths.
Module.globalPaths.length = 0; Module.globalPaths.length = 0
// Clear current and parent(init.js)'s search paths. // Clear current and parent(init.js)'s search paths.
module.paths = []; module.paths = []
module.parent.paths = []; module.parent.paths = []
// Prevent Node from adding paths outside this app to search paths. // Prevent Node from adding paths outside this app to search paths.
const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep; const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep
const originalNodeModulePaths = Module._nodeModulePaths; const originalNodeModulePaths = Module._nodeModulePaths
Module._nodeModulePaths = function (from) { Module._nodeModulePaths = function (from) {
const paths = originalNodeModulePaths(from); const paths = originalNodeModulePaths(from)
const fromPath = path.resolve(from) + path.sep; const fromPath = path.resolve(from) + path.sep
// If "from" is outside the app then we do nothing. // If "from" is outside the app then we do nothing.
if (fromPath.startsWith(resourcesPathWithTrailingSlash)) { if (fromPath.startsWith(resourcesPathWithTrailingSlash)) {
return paths.filter(function (candidate) { return paths.filter(function (candidate) {
return candidate.startsWith(resourcesPathWithTrailingSlash); return candidate.startsWith(resourcesPathWithTrailingSlash)
}); })
} } else {
else { return paths
return paths; }
} }
};
const BASE_INTERNAL_PATH = path.resolve(__dirname, '..'); const BASE_INTERNAL_PATH = path.resolve(__dirname, '..')
const INTERNAL_MODULE_PREFIX = '@electron/internal/'; const INTERNAL_MODULE_PREFIX = '@electron/internal/'
// Patch Module._resolveFilename to always require the Electron API when // Patch Module._resolveFilename to always require the Electron API when
// require('electron') is done. // require('electron') is done.
const electronPath = path.join(__dirname, '..', process.type, 'api', 'exports', 'electron.js'); const electronPath = path.join(__dirname, '..', process.type, 'api', 'exports', 'electron.js')
const originalResolveFilename = Module._resolveFilename; const originalResolveFilename = Module._resolveFilename
Module._resolveFilename = function (request, parent, isMain) { Module._resolveFilename = function (request, parent, isMain) {
if (request === 'electron') { if (request === 'electron') {
return electronPath; return electronPath
} } else if (request.startsWith(INTERNAL_MODULE_PREFIX) && request.length > INTERNAL_MODULE_PREFIX.length) {
else if (request.startsWith(INTERNAL_MODULE_PREFIX) && request.length > INTERNAL_MODULE_PREFIX.length) { const slicedRequest = request.slice(INTERNAL_MODULE_PREFIX.length)
const slicedRequest = request.slice(INTERNAL_MODULE_PREFIX.length); return path.resolve(BASE_INTERNAL_PATH, `${slicedRequest}${slicedRequest.endsWith('.js') ? '' : '.js'}`)
return path.resolve(BASE_INTERNAL_PATH, `${slicedRequest}${slicedRequest.endsWith('.js') ? '' : '.js'}`); } else {
} return originalResolveFilename(request, parent, isMain)
else { }
return originalResolveFilename(request, parent, isMain); }
}
};
//# sourceMappingURL=reset-search-paths.js.map

View File

@ -1,69 +1,70 @@
'use strict'; 'use strict'
// Public-facing API methods. // Public-facing API methods.
exports.syncMethods = new Set([ exports.syncMethods = new Set([
'getURL', 'getURL',
'loadURL', 'loadURL',
'getTitle', 'getTitle',
'isLoading', 'isLoading',
'isLoadingMainFrame', 'isLoadingMainFrame',
'isWaitingForResponse', 'isWaitingForResponse',
'stop', 'stop',
'reload', 'reload',
'reloadIgnoringCache', 'reloadIgnoringCache',
'canGoBack', 'canGoBack',
'canGoForward', 'canGoForward',
'canGoToOffset', 'canGoToOffset',
'clearHistory', 'clearHistory',
'goBack', 'goBack',
'goForward', 'goForward',
'goToIndex', 'goToIndex',
'goToOffset', 'goToOffset',
'isCrashed', 'isCrashed',
'setUserAgent', 'setUserAgent',
'getUserAgent', 'getUserAgent',
'openDevTools', 'openDevTools',
'closeDevTools', 'closeDevTools',
'isDevToolsOpened', 'isDevToolsOpened',
'isDevToolsFocused', 'isDevToolsFocused',
'inspectElement', 'inspectElement',
'setAudioMuted', 'setAudioMuted',
'isAudioMuted', 'isAudioMuted',
'isCurrentlyAudible', 'isCurrentlyAudible',
'undo', 'undo',
'redo', 'redo',
'cut', 'cut',
'copy', 'copy',
'paste', 'paste',
'pasteAndMatchStyle', 'pasteAndMatchStyle',
'delete', 'delete',
'selectAll', 'selectAll',
'unselect', 'unselect',
'replace', 'replace',
'replaceMisspelling', 'replaceMisspelling',
'findInPage', 'findInPage',
'stopFindInPage', 'stopFindInPage',
'downloadURL', 'downloadURL',
'inspectSharedWorker', 'inspectServiceWorker',
'inspectServiceWorker', 'showDefinitionForSelection',
'showDefinitionForSelection', 'setZoomFactor',
'getZoomFactor', 'setZoomLevel',
'getZoomLevel', 'sendImeEvent'
'setZoomFactor', ])
'setZoomLevel',
'sendImeEvent'
]);
exports.asyncCallbackMethods = new Set([ exports.asyncCallbackMethods = new Set([
'insertCSS', 'insertCSS',
'insertText', 'insertText',
'send', 'send',
'sendInputEvent', 'sendInputEvent',
'setLayoutZoomLevelLimits', 'setLayoutZoomLevelLimits',
'setVisualZoomLevelLimits', 'setVisualZoomLevelLimits',
'print' 'getZoomFactor',
]); 'getZoomLevel',
'print',
'printToPDF'
])
exports.asyncPromiseMethods = new Set([ exports.asyncPromiseMethods = new Set([
'capturePage', 'capturePage',
'executeJavaScript', 'executeJavaScript'
'printToPDF' ])
]);
//# sourceMappingURL=web-view-methods.js.map

View File

@ -1,10 +1,12 @@
'use strict'; 'use strict'
const CrashReporter = require('@electron/internal/common/crash-reporter');
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); const CrashReporter = require('@electron/internal/common/crash-reporter')
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
class CrashReporterRenderer extends CrashReporter { class CrashReporterRenderer extends CrashReporter {
init(options) { sendSync (channel, ...args) {
return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options); return ipcRenderer.sendSync(channel, ...args)
} }
} }
module.exports = new CrashReporterRenderer();
//# sourceMappingURL=crash-reporter.js.map module.exports = new CrashReporterRenderer()

View File

@ -1,38 +1,48 @@
'use strict'; 'use strict'
const { nativeImage, deprecate } = require('electron');
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); const { nativeImage } = require('electron')
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const includes = [].includes
let currentId = 0
const incrementId = () => {
currentId += 1
return currentId
}
// |options.types| can't be empty and must be an array // |options.types| can't be empty and must be an array
function isValid(options) { function isValid (options) {
const types = options ? options.types : undefined; const types = options ? options.types : undefined
return Array.isArray(types); return Array.isArray(types)
} }
function mapSources(sources) {
return sources.map(source => ({ exports.getSources = function (options, callback) {
id: source.id, if (!isValid(options)) return callback(new Error('Invalid options'))
name: source.name, const captureWindow = includes.call(options.types, 'window')
thumbnail: nativeImage.createFromDataURL(source.thumbnail), const captureScreen = includes.call(options.types, 'screen')
display_id: source.display_id,
appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null if (options.thumbnailSize == null) {
})); options.thumbnailSize = {
width: 150,
height: 150
}
}
const id = incrementId()
ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, 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 getSources = (options) => {
return new Promise((resolve, reject) => {
if (!isValid(options))
throw new Error('Invalid options');
const captureWindow = options.types.includes('window');
const captureScreen = options.types.includes('screen');
if (options.thumbnailSize == null) {
options.thumbnailSize = {
width: 150,
height: 150
};
}
if (options.fetchWindowIcons == null) {
options.fetchWindowIcons = false;
}
ipcRendererUtils.invoke('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, options.fetchWindowIcons)
.then(sources => resolve(mapSources(sources)), reject);
});
};
exports.getSources = deprecate.promisify(getSources);
//# sourceMappingURL=desktop-capturer.js.map

View File

@ -1,21 +1,23 @@
'use strict'; 'use strict'
const common = require('@electron/internal/common/api/exports/electron');
const moduleList = require('@electron/internal/renderer/api/module-list'); const common = require('@electron/internal/common/api/exports/electron')
const moduleList = require('@electron/internal/renderer/api/module-list')
// Import common modules. // Import common modules.
common.defineProperties(exports); common.defineProperties(exports)
for (const { name, file, enabled = true, private: isPrivate = false } of moduleList) {
if (!enabled) { for (const {
continue; name,
} file,
Object.defineProperty(exports, name, { enabled = true,
enumerable: !isPrivate, private: isPrivate = false
get: common.memoizedGetter(() => { } of moduleList) {
const value = require(`@electron/internal/renderer/api/${file}.js`); if (!enabled) {
// Handle Typescript modules with an "export default X" statement continue
if (value.__esModule) }
return value.default;
return value; Object.defineProperty(exports, name, {
}) enumerable: !isPrivate,
}); get: common.memoizedGetter(() => require(`@electron/internal/renderer/api/${file}`))
})
} }
//# sourceMappingURL=electron.js.map

View File

@ -1,23 +1,30 @@
'use strict'; 'use strict'
const { ipc } = process.electronBinding('ipc');
const v8Util = process.electronBinding('v8_util'); const binding = process.atomBinding('ipc')
const v8Util = process.atomBinding('v8_util')
// Created by init.js. // Created by init.js.
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc'); const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
const internal = false; const internal = false
ipcRenderer.send = function (channel, ...args) {
return ipc.send(internal, channel, args); ipcRenderer.send = function (...args) {
}; return binding.send('ipc-message', args)
ipcRenderer.sendSync = function (channel, ...args) { }
return ipc.sendSync(internal, channel, args)[0];
}; ipcRenderer.sendSync = function (...args) {
ipcRenderer.sendToHost = function (channel, ...args) { return binding.sendSync('ipc-message-sync', args)[0]
return ipc.sendToHost(channel, args); }
};
ipcRenderer.sendToHost = function (...args) {
return binding.send('ipc-message-host', args)
}
ipcRenderer.sendTo = function (webContentsId, channel, ...args) { ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
return ipc.sendTo(internal, false, webContentsId, channel, args); return binding.sendTo(internal, false, webContentsId, channel, args)
}; }
ipcRenderer.sendToAll = function (webContentsId, channel, ...args) { ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
return ipc.sendTo(internal, true, webContentsId, channel, args); return binding.sendTo(internal, true, webContentsId, channel, args)
}; }
module.exports = ipcRenderer;
//# sourceMappingURL=ipc-renderer.js.map module.exports = ipcRenderer

View File

@ -1,18 +1,21 @@
'use strict'; 'use strict'
const features = process.electronBinding('features');
const v8Util = process.electronBinding('v8_util'); const features = process.atomBinding('features')
const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule'); const v8Util = process.atomBinding('v8_util')
const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule')
// Renderer side modules, please sort alphabetically. // Renderer side modules, please sort alphabetically.
// A module is `enabled` if there is no explicit condition defined. // A module is `enabled` if there is no explicit condition defined.
module.exports = [ module.exports = [
{ name: 'crashReporter', file: 'crash-reporter', enabled: true }, { name: 'crashReporter', file: 'crash-reporter', enabled: true },
{ {
name: 'desktopCapturer', name: 'desktopCapturer',
file: 'desktop-capturer', file: 'desktop-capturer',
enabled: features.isDesktopCapturerEnabled() enabled: features.isDesktopCapturerEnabled()
}, },
{ name: 'ipcRenderer', file: 'ipc-renderer' }, { name: 'ipcRenderer', file: 'ipc-renderer' },
{ name: 'remote', file: 'remote', enabled: enableRemoteModule }, { name: 'remote', file: 'remote', enabled: enableRemoteModule },
{ name: 'webFrame', file: 'web-frame' } { name: 'screen', file: 'screen' },
]; { name: 'webFrame', file: 'web-frame' }
//# sourceMappingURL=module-list.js.map ]

View File

@ -1,340 +1,355 @@
'use strict'; 'use strict'
const v8Util = process.electronBinding('v8_util');
const { isPromise } = require('electron'); const v8Util = process.atomBinding('v8_util')
const resolvePromise = Promise.resolve.bind(Promise); const { isPromise } = require('electron')
const CallbacksRegistry = require('@electron/internal/renderer/callbacks-registry'); const resolvePromise = Promise.resolve.bind(Promise)
const bufferUtils = require('@electron/internal/common/buffer-utils');
const errorUtils = require('@electron/internal/common/error-utils'); const CallbacksRegistry = require('@electron/internal/renderer/callbacks-registry')
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); const bufferUtils = require('@electron/internal/common/buffer-utils')
const callbacksRegistry = new CallbacksRegistry(); const errorUtils = require('@electron/internal/common/error-utils')
const remoteObjectCache = v8Util.createIDWeakMap(); const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const callbacksRegistry = new CallbacksRegistry()
const remoteObjectCache = v8Util.createIDWeakMap()
// An unique ID that can represent current context. // An unique ID that can represent current context.
const contextId = v8Util.getHiddenValue(global, 'contextId'); const contextId = v8Util.getHiddenValue(global, 'contextId')
// Notify the main process when current context is going to be released. // Notify the main process when current context is going to be released.
// Note that when the renderer process is destroyed, the message may not be // Note that when the renderer process is destroyed, the message may not be
// sent, we also listen to the "render-view-deleted" event in the main process // sent, we also listen to the "render-view-deleted" event in the main process
// to guard that situation. // to guard that situation.
process.on('exit', () => { process.on('exit', () => {
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'; const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
ipcRendererInternal.sendSync(command, contextId); ipcRenderer.sendSync(command, contextId)
}); })
// Convert the arguments object into an array of meta data. // Convert the arguments object into an array of meta data.
function wrapArgs(args, visited = new Set()) { function wrapArgs (args, visited = new Set()) {
const valueToMeta = (value) => { const valueToMeta = (value) => {
// Check for circular reference. // Check for circular reference.
if (visited.has(value)) { if (visited.has(value)) {
return { return {
type: 'value', type: 'value',
value: null value: null
}; }
}
if (Array.isArray(value)) {
visited.add(value)
const meta = {
type: 'array',
value: wrapArgs(value, visited)
}
visited.delete(value)
return meta
} else if (bufferUtils.isBuffer(value)) {
return {
type: 'buffer',
value: bufferUtils.bufferToMeta(value)
}
} else if (value instanceof Date) {
return {
type: 'date',
value: value.getTime()
}
} else if ((value != null) && typeof value === 'object') {
if (isPromise(value)) {
return {
type: 'promise',
then: valueToMeta(function (onFulfilled, onRejected) {
value.then(onFulfilled, onRejected)
})
} }
if (Array.isArray(value)) { } else if (v8Util.getHiddenValue(value, 'atomId')) {
visited.add(value); return {
const meta = { type: 'remote-object',
type: 'array', id: v8Util.getHiddenValue(value, 'atomId')
value: wrapArgs(value, visited)
};
visited.delete(value);
return meta;
} }
else if (bufferUtils.isBuffer(value)) { }
return {
type: 'buffer', const meta = {
value: bufferUtils.bufferToMeta(value) type: 'object',
}; name: value.constructor ? value.constructor.name : '',
} members: []
else if (value instanceof Date) { }
return { visited.add(value)
type: 'date', for (const prop in value) {
value: value.getTime() meta.members.push({
}; name: prop,
} value: valueToMeta(value[prop])
else if ((value != null) && typeof value === 'object') { })
if (isPromise(value)) { }
return { visited.delete(value)
type: 'promise', return meta
then: valueToMeta(function (onFulfilled, onRejected) { } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
value.then(onFulfilled, onRejected); return {
}) type: 'function-with-return-value',
}; value: valueToMeta(value())
} }
else if (v8Util.getHiddenValue(value, 'atomId')) { } else if (typeof value === 'function') {
return { return {
type: 'remote-object', type: 'function',
id: v8Util.getHiddenValue(value, 'atomId') id: callbacksRegistry.add(value),
}; location: v8Util.getHiddenValue(value, 'location'),
} length: value.length
const meta = { }
type: 'object', } else {
name: value.constructor ? value.constructor.name : '', return {
members: [] type: 'value',
}; value: value
visited.add(value); }
for (const prop in value) { }
meta.members.push({ }
name: prop, return args.map(valueToMeta)
value: valueToMeta(value[prop])
});
}
visited.delete(value);
return meta;
}
else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
return {
type: 'function-with-return-value',
value: valueToMeta(value())
};
}
else if (typeof value === 'function') {
return {
type: 'function',
id: callbacksRegistry.add(value),
location: v8Util.getHiddenValue(value, 'location'),
length: value.length
};
}
else {
return {
type: 'value',
value: value
};
}
};
return args.map(valueToMeta);
} }
// Populate object's members from descriptors. // Populate object's members from descriptors.
// The |ref| will be kept referenced by |members|. // The |ref| will be kept referenced by |members|.
// This matches |getObjectMemebers| in rpc-server. // This matches |getObjectMemebers| in rpc-server.
function setObjectMembers(ref, object, metaId, members) { function setObjectMembers (ref, object, metaId, members) {
if (!Array.isArray(members)) if (!Array.isArray(members)) return
return;
for (const member of members) { for (const member of members) {
if (object.hasOwnProperty(member.name)) if (object.hasOwnProperty(member.name)) continue
continue;
const descriptor = { enumerable: member.enumerable }; const descriptor = { enumerable: member.enumerable }
if (member.type === 'method') { if (member.type === 'method') {
const remoteMemberFunction = function (...args) { const remoteMemberFunction = function (...args) {
let command; let command
if (this && this.constructor === remoteMemberFunction) { if (this && this.constructor === remoteMemberFunction) {
command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'; command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'
} } else {
else { command = 'ELECTRON_BROWSER_MEMBER_CALL'
command = 'ELECTRON_BROWSER_MEMBER_CALL';
}
const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args));
return metaToValue(ret);
};
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name);
descriptor.get = () => {
descriptorFunction.ref = ref; // The member should reference its object.
return descriptorFunction;
};
// Enable monkey-patch the method
descriptor.set = (value) => {
descriptorFunction = value;
return value;
};
descriptor.configurable = true;
} }
else if (member.type === 'get') { const ret = ipcRenderer.sendSync(command, contextId, metaId, member.name, wrapArgs(args))
descriptor.get = () => { return metaToValue(ret)
const command = 'ELECTRON_BROWSER_MEMBER_GET'; }
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name);
return metaToValue(meta); let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)
};
if (member.writable) { descriptor.get = () => {
descriptor.set = (value) => { descriptorFunction.ref = ref // The member should reference its object.
const args = wrapArgs([value]); return descriptorFunction
const command = 'ELECTRON_BROWSER_MEMBER_SET'; }
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args); // Enable monkey-patch the method
if (meta != null) descriptor.set = (value) => {
metaToValue(meta); descriptorFunction = value
return value; return value
}; }
} descriptor.configurable = true
} else if (member.type === 'get') {
descriptor.get = () => {
const command = 'ELECTRON_BROWSER_MEMBER_GET'
const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name)
return metaToValue(meta)
}
if (member.writable) {
descriptor.set = (value) => {
const args = wrapArgs([value])
const command = 'ELECTRON_BROWSER_MEMBER_SET'
const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name, args)
if (meta != null) metaToValue(meta)
return value
} }
Object.defineProperty(object, member.name, descriptor); }
} }
Object.defineProperty(object, member.name, descriptor)
}
} }
// Populate object's prototype from descriptor. // Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server. // This matches |getObjectPrototype| in rpc-server.
function setObjectPrototype(ref, object, metaId, descriptor) { function setObjectPrototype (ref, object, metaId, descriptor) {
if (descriptor === null) if (descriptor === null) return
return; const proto = {}
const proto = {}; setObjectMembers(ref, proto, metaId, descriptor.members)
setObjectMembers(ref, proto, metaId, descriptor.members); setObjectPrototype(ref, proto, metaId, descriptor.proto)
setObjectPrototype(ref, proto, metaId, descriptor.proto); Object.setPrototypeOf(object, proto)
Object.setPrototypeOf(object, proto);
} }
// Wrap function in Proxy for accessing remote properties // Wrap function in Proxy for accessing remote properties
function proxyFunctionProperties(remoteMemberFunction, metaId, name) { function proxyFunctionProperties (remoteMemberFunction, metaId, name) {
let loaded = false; let loaded = false
// Lazily load function properties
const loadRemoteProperties = () => { // Lazily load function properties
if (loaded) const loadRemoteProperties = () => {
return; if (loaded) return
loaded = true; loaded = true
const command = 'ELECTRON_BROWSER_MEMBER_GET'; const command = 'ELECTRON_BROWSER_MEMBER_GET'
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name); const meta = ipcRenderer.sendSync(command, contextId, metaId, name)
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members); setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
}; }
return new Proxy(remoteMemberFunction, {
set: (target, property, value, receiver) => { return new Proxy(remoteMemberFunction, {
if (property !== 'ref') set: (target, property, value, receiver) => {
loadRemoteProperties(); if (property !== 'ref') loadRemoteProperties()
target[property] = value; target[property] = value
return true; return true
}, },
get: (target, property, receiver) => { get: (target, property, receiver) => {
if (!target.hasOwnProperty(property)) if (!target.hasOwnProperty(property)) loadRemoteProperties()
loadRemoteProperties(); const value = target[property]
const value = target[property]; if (property === 'toString' && typeof value === 'function') {
if (property === 'toString' && typeof value === 'function') { return value.bind(target)
return value.bind(target); }
} return value
return value; },
}, ownKeys: (target) => {
ownKeys: (target) => { loadRemoteProperties()
loadRemoteProperties(); return Object.getOwnPropertyNames(target)
return Object.getOwnPropertyNames(target); },
}, getOwnPropertyDescriptor: (target, property) => {
getOwnPropertyDescriptor: (target, property) => { const descriptor = Object.getOwnPropertyDescriptor(target, property)
const descriptor = Object.getOwnPropertyDescriptor(target, property); if (descriptor) return descriptor
if (descriptor) loadRemoteProperties()
return descriptor; return Object.getOwnPropertyDescriptor(target, property)
loadRemoteProperties(); }
return Object.getOwnPropertyDescriptor(target, property); })
}
});
} }
// Convert meta data from browser into real value. // Convert meta data from browser into real value.
function metaToValue(meta) { function metaToValue (meta) {
const types = { const types = {
value: () => meta.value, value: () => meta.value,
array: () => meta.members.map((member) => metaToValue(member)), array: () => meta.members.map((member) => metaToValue(member)),
buffer: () => bufferUtils.metaToBuffer(meta.value), buffer: () => bufferUtils.metaToBuffer(meta.value),
promise: () => resolvePromise({ then: metaToValue(meta.then) }), promise: () => resolvePromise({ then: metaToValue(meta.then) }),
error: () => metaToPlainObject(meta), error: () => metaToPlainObject(meta),
date: () => new Date(meta.value), date: () => new Date(meta.value),
exception: () => { throw errorUtils.deserialize(meta.value); } exception: () => { throw errorUtils.deserialize(meta.value) }
}; }
if (meta.type in types) {
return types[meta.type](); if (meta.type in types) {
return types[meta.type]()
} else {
let ret
if (remoteObjectCache.has(meta.id)) {
return remoteObjectCache.get(meta.id)
} }
else {
let ret; // A shadow class to represent the remote function object.
if (remoteObjectCache.has(meta.id)) { if (meta.type === 'function') {
v8Util.addRemoteObjectRef(contextId, meta.id); const remoteFunction = function (...args) {
return remoteObjectCache.get(meta.id); let command
if (this && this.constructor === remoteFunction) {
command = 'ELECTRON_BROWSER_CONSTRUCTOR'
} else {
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
} }
// A shadow class to represent the remote function object. const obj = ipcRenderer.sendSync(command, contextId, meta.id, wrapArgs(args))
if (meta.type === 'function') { return metaToValue(obj)
const remoteFunction = function (...args) { }
let command; ret = remoteFunction
if (this && this.constructor === remoteFunction) { } else {
command = 'ELECTRON_BROWSER_CONSTRUCTOR'; ret = {}
}
else {
command = 'ELECTRON_BROWSER_FUNCTION_CALL';
}
const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args));
return metaToValue(obj);
};
ret = remoteFunction;
}
else {
ret = {};
}
setObjectMembers(ret, ret, meta.id, meta.members);
setObjectPrototype(ret, ret, meta.id, meta.proto);
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
v8Util.setRemoteObjectFreer(ret, contextId, meta.id);
v8Util.setHiddenValue(ret, 'atomId', meta.id);
v8Util.addRemoteObjectRef(contextId, meta.id);
remoteObjectCache.set(meta.id, ret);
return ret;
} }
setObjectMembers(ret, ret, meta.id, meta.members)
setObjectPrototype(ret, ret, meta.id, meta.proto)
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
v8Util.setRemoteObjectFreer(ret, contextId, meta.id)
v8Util.setHiddenValue(ret, 'atomId', meta.id)
remoteObjectCache.set(meta.id, ret)
return ret
}
} }
// Construct a plain object from the meta. // Construct a plain object from the meta.
function metaToPlainObject(meta) { function metaToPlainObject (meta) {
const obj = (() => meta.type === 'error' ? new Error() : {})(); const obj = (() => meta.type === 'error' ? new Error() : {})()
for (let i = 0; i < meta.members.length; i++) { for (let i = 0; i < meta.members.length; i++) {
const { name, value } = meta.members[i]; const { name, value } = meta.members[i]
obj[name] = value; obj[name] = value
}
return obj
}
function handleMessage (channel, handler) {
ipcRenderer.on(channel, (event, passedContextId, id, ...args) => {
if (passedContextId === contextId) {
handler(id, ...args)
} else {
// Message sent to an un-exist context, notify the error to main process.
ipcRenderer.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id)
} }
return obj; })
}
function handleMessage(channel, handler) {
ipcRendererInternal.on(channel, (event, passedContextId, id, ...args) => {
if (passedContextId === contextId) {
handler(id, ...args);
}
else {
// Message sent to an un-exist context, notify the error to main process.
ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id);
}
});
} }
// Browser calls a callback in renderer. // Browser calls a callback in renderer.
handleMessage('ELECTRON_RENDERER_CALLBACK', (id, args) => { handleMessage('ELECTRON_RENDERER_CALLBACK', (id, args) => {
callbacksRegistry.apply(id, metaToValue(args)); callbacksRegistry.apply(id, metaToValue(args))
}); })
// A callback in browser is released. // A callback in browser is released.
handleMessage('ELECTRON_RENDERER_RELEASE_CALLBACK', (id) => { handleMessage('ELECTRON_RENDERER_RELEASE_CALLBACK', (id) => {
callbacksRegistry.remove(id); callbacksRegistry.remove(id)
}); })
exports.require = (module) => { exports.require = (module) => {
const command = 'ELECTRON_BROWSER_REQUIRE'; const command = 'ELECTRON_BROWSER_REQUIRE'
const meta = ipcRendererInternal.sendSync(command, contextId, module); const meta = ipcRenderer.sendSync(command, contextId, module)
return metaToValue(meta); return metaToValue(meta)
}; }
// Alias to remote.require('electron').xxx. // Alias to remote.require('electron').xxx.
exports.getBuiltin = (module) => { exports.getBuiltin = (module) => {
const command = 'ELECTRON_BROWSER_GET_BUILTIN'; const command = 'ELECTRON_BROWSER_GET_BUILTIN'
const meta = ipcRendererInternal.sendSync(command, contextId, module); const meta = ipcRenderer.sendSync(command, contextId, module)
return metaToValue(meta); return metaToValue(meta)
}; }
exports.getCurrentWindow = () => { exports.getCurrentWindow = () => {
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'; const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
const meta = ipcRendererInternal.sendSync(command, contextId); const meta = ipcRenderer.sendSync(command, contextId)
return metaToValue(meta); return metaToValue(meta)
}; }
// Get current WebContents object. // Get current WebContents object.
exports.getCurrentWebContents = () => { exports.getCurrentWebContents = () => {
const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'; return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', contextId))
const meta = ipcRendererInternal.sendSync(command, contextId); }
return metaToValue(meta);
};
// Get a global object in browser. // Get a global object in browser.
exports.getGlobal = (name) => { exports.getGlobal = (name) => {
const command = 'ELECTRON_BROWSER_GLOBAL'; const command = 'ELECTRON_BROWSER_GLOBAL'
const meta = ipcRendererInternal.sendSync(command, contextId, name); const meta = ipcRenderer.sendSync(command, contextId, name)
return metaToValue(meta); return metaToValue(meta)
}; }
// Get the process object in browser. // Get the process object in browser.
exports.__defineGetter__('process', () => exports.getGlobal('process')); exports.__defineGetter__('process', () => exports.getGlobal('process'))
// Create a function that will return the specified value when called in browser. // Create a function that will return the specified value when called in browser.
exports.createFunctionWithReturnValue = (returnValue) => { exports.createFunctionWithReturnValue = (returnValue) => {
const func = () => returnValue; const func = () => returnValue
v8Util.setHiddenValue(func, 'returnValue', true); v8Util.setHiddenValue(func, 'returnValue', true)
return func; return func
}; }
// Get the guest WebContents from guestInstanceId. // Get the guest WebContents from guestInstanceId.
exports.getGuestWebContents = (guestInstanceId) => { exports.getGuestWebContents = (guestInstanceId) => {
const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'; const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'
const meta = ipcRendererInternal.sendSync(command, contextId, guestInstanceId); const meta = ipcRenderer.sendSync(command, contextId, guestInstanceId)
return metaToValue(meta); return metaToValue(meta)
}; }
const addBuiltinProperty = (name) => { const addBuiltinProperty = (name) => {
Object.defineProperty(exports, name, { Object.defineProperty(exports, name, {
get: () => exports.getBuiltin(name) get: () => exports.getBuiltin(name)
}); })
}; }
const browserModules = require('@electron/internal/common/api/module-list').concat(require('@electron/internal/browser/api/module-list'));
const browserModules =
require('@electron/internal/common/api/module-list').concat(
require('@electron/internal/browser/api/module-list'))
// And add a helper receiver for each one. // And add a helper receiver for each one.
browserModules browserModules
.filter((m) => !m.private) .filter((m) => !m.private)
.map((m) => m.name) .map((m) => m.name)
.forEach(addBuiltinProperty); .forEach(addBuiltinProperty)
//# sourceMappingURL=remote.js.map

View File

@ -0,0 +1,4 @@
'use strict'
const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
module.exports = getRemoteForUsage('screen').screen

View File

@ -1,82 +1,67 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events"); const { EventEmitter } = require('events')
const electron_1 = require("electron"); const binding = process.atomBinding('web_frame')
const binding = process.electronBinding('web_frame');
class WebFrame extends events_1.EventEmitter { class WebFrame extends EventEmitter {
constructor(context) { constructor (context) {
super(); super()
this.context = context;
// Lots of webview would subscribe to webFrame's events. this.context = context
this.setMaxListeners(0); // Lots of webview would subscribe to webFrame's events.
} this.setMaxListeners(0)
findFrameByRoutingId(...args) { }
return getWebFrame(binding._findFrameByRoutingId(this.context, ...args));
} findFrameByRoutingId (...args) {
getFrameForSelector(...args) { return getWebFrame(binding._findFrameByRoutingId(this.context, ...args))
return getWebFrame(binding._getFrameForSelector(this.context, ...args)); }
}
findFrameByName(...args) { getFrameForSelector (...args) {
return getWebFrame(binding._findFrameByName(this.context, ...args)); return getWebFrame(binding._getFrameForSelector(this.context, ...args))
} }
get opener() {
return getWebFrame(binding._getOpener(this.context)); findFrameByName (...args) {
} return getWebFrame(binding._findFrameByName(this.context, ...args))
get parent() { }
return getWebFrame(binding._getParent(this.context));
} get opener () {
get top() { return getWebFrame(binding._getOpener(this.context))
return getWebFrame(binding._getTop(this.context)); }
}
get firstChild() { get parent () {
return getWebFrame(binding._getFirstChild(this.context)); return getWebFrame(binding._getParent(this.context))
} }
get nextSibling() {
return getWebFrame(binding._getNextSibling(this.context)); get top () {
} return getWebFrame(binding._getTop(this.context))
get routingId() { }
return binding._getRoutingId(this.context);
} get firstChild () {
// Deprecations return getWebFrame(binding._getFirstChild(this.context))
// TODO(nitsakh): Remove in 6.0 }
setIsolatedWorldSecurityOrigin(worldId, securityOrigin) {
electron_1.deprecate.warn('webFrame.setIsolatedWorldSecurityOrigin', 'webFrame.setIsolatedWorldInfo'); get nextSibling () {
binding.setIsolatedWorldInfo(this.context, worldId, { securityOrigin }); return getWebFrame(binding._getNextSibling(this.context))
} }
setIsolatedWorldContentSecurityPolicy(worldId, csp) {
electron_1.deprecate.warn('webFrame.setIsolatedWorldContentSecurityPolicy', 'webFrame.setIsolatedWorldInfo'); get routingId () {
binding.setIsolatedWorldInfo(this.context, worldId, { return binding._getRoutingId(this.context)
securityOrigin: window.location.origin, }
csp
});
}
setIsolatedWorldHumanReadableName(worldId, name) {
electron_1.deprecate.warn('webFrame.setIsolatedWorldHumanReadableName', 'webFrame.setIsolatedWorldInfo');
binding.setIsolatedWorldInfo(this.context, worldId, { name });
}
} }
// Populate the methods. // Populate the methods.
for (const name in binding) { for (const name in binding) {
if (!name.startsWith('_')) { // some methods are manually populated above if (!name.startsWith('_')) { // some methods are manully populated above
// TODO(felixrieseberg): Once we can type web_frame natives, we could WebFrame.prototype[name] = function (...args) {
// use a neat `keyof` here return binding[name](this.context, ...args)
WebFrame.prototype[name] = function (...args) {
return binding[name](this.context, ...args);
};
} }
}
} }
// Helper to return WebFrame or null depending on context. // Helper to return WebFrame or null depending on context.
// TODO(zcbenz): Consider returning same WebFrame for the same frame. // TODO(zcbenz): Consider returning same WebFrame for the same context.
function getWebFrame(context) { function getWebFrame (context) {
return context ? new WebFrame(context) : null; return context ? new WebFrame(context) : null
} }
const promisifiedMethods = new Set([
'executeJavaScript', module.exports = new WebFrame(window)
'executeJavaScriptInIsolatedWorld'
]);
for (const method of promisifiedMethods) {
WebFrame.prototype[method] = electron_1.deprecate.promisify(WebFrame.prototype[method]);
}
const _webFrame = new WebFrame(window);
exports.default = _webFrame;
//# sourceMappingURL=web-frame.js.map

View File

@ -1,52 +1,59 @@
'use strict'; 'use strict'
const v8Util = process.electronBinding('v8_util');
const v8Util = process.atomBinding('v8_util')
class CallbacksRegistry { class CallbacksRegistry {
constructor() { constructor () {
this.nextId = 0; this.nextId = 0
this.callbacks = {}; this.callbacks = {}
}
add (callback) {
// The callback is already added.
let id = v8Util.getHiddenValue(callback, 'callbackId')
if (id != null) return id
id = this.nextId += 1
// Capture the location of the function and put it in the ID string,
// so that release errors can be tracked down easily.
const regexp = /at (.*)/gi
const stackString = (new Error()).stack
let filenameAndLine
let match
while ((match = regexp.exec(stackString)) !== null) {
const location = match[1]
if (location.includes('(native)')) continue
if (location.includes('(<anonymous>)')) continue
if (location.includes('electron.asar')) continue
const ref = /([^/^)]*)\)?$/gi.exec(location)
filenameAndLine = ref[1]
break
} }
add(callback) { this.callbacks[id] = callback
// The callback is already added. v8Util.setHiddenValue(callback, 'callbackId', id)
let id = v8Util.getHiddenValue(callback, 'callbackId'); v8Util.setHiddenValue(callback, 'location', filenameAndLine)
if (id != null) return id
return id; }
id = this.nextId += 1;
// Capture the location of the function and put it in the ID string, get (id) {
// so that release errors can be tracked down easily. return this.callbacks[id] || function () {}
const regexp = /at (.*)/gi; }
const stackString = (new Error()).stack;
let filenameAndLine; apply (id, ...args) {
let match; return this.get(id).apply(global, ...args)
while ((match = regexp.exec(stackString)) !== null) { }
const location = match[1];
if (location.includes('(native)')) remove (id) {
continue; const callback = this.callbacks[id]
if (location.includes('(<anonymous>)')) if (callback) {
continue; v8Util.deleteHiddenValue(callback, 'callbackId')
if (location.includes('electron.asar')) delete this.callbacks[id]
continue;
const ref = /([^/^)]*)\)?$/gi.exec(location);
filenameAndLine = ref[1];
break;
}
this.callbacks[id] = callback;
v8Util.setHiddenValue(callback, 'callbackId', id);
v8Util.setHiddenValue(callback, 'location', filenameAndLine);
return id;
}
get(id) {
return this.callbacks[id] || function () { };
}
apply(id, ...args) {
return this.get(id).apply(global, ...args);
}
remove(id) {
const callback = this.callbacks[id];
if (callback) {
v8Util.deleteHiddenValue(callback, 'callbackId');
delete this.callbacks[id];
}
} }
}
} }
module.exports = CallbacksRegistry;
//# sourceMappingURL=callbacks-registry.js.map module.exports = CallbacksRegistry

View File

@ -1,174 +1,190 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-renderer-internal"); const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils"); const Event = require('@electron/internal/renderer/extensions/event')
const url = require("url"); const url = require('url')
// Todo: Import once extensions have been turned into TypeScript
const Event = require('@electron/internal/renderer/extensions/event');
class Tab { class Tab {
constructor(tabId) { constructor (tabId) {
this.id = tabId; this.id = tabId
} }
} }
class MessageSender { class MessageSender {
constructor(tabId, extensionId) { constructor (tabId, extensionId) {
this.tab = tabId ? new Tab(tabId) : null; this.tab = tabId ? new Tab(tabId) : null
this.id = extensionId; this.id = extensionId
this.url = `chrome-extension://${extensionId}`; this.url = `chrome-extension://${extensionId}`
} }
} }
class Port { class Port {
constructor(tabId, portId, extensionId, name) { constructor (tabId, portId, extensionId, name) {
this.tabId = tabId; this.tabId = tabId
this.portId = portId; this.portId = portId
this.name = name; this.disconnected = false
this.disconnected = false;
this.onDisconnect = new Event(); this.name = name
this.onMessage = new Event(); this.onDisconnect = new Event()
this.onDisconnect = new Event(); this.onMessage = new Event()
this.onMessage = new Event(); this.sender = new MessageSender(tabId, extensionId)
this.sender = new MessageSender(tabId, extensionId);
ipc_renderer_internal_1.ipcRendererInternal.once(`CHROME_PORT_DISCONNECT_${portId}`, () => { ipcRenderer.once(`CHROME_PORT_DISCONNECT_${portId}`, () => {
this._onDisconnect(); this._onDisconnect()
}); })
ipc_renderer_internal_1.ipcRendererInternal.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (_event, message) => { ipcRenderer.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (event, message) => {
const sendResponse = function () { console.error('sendResponse is not implemented'); }; const sendResponse = function () { console.error('sendResponse is not implemented') }
this.onMessage.emit(message, this.sender, sendResponse); this.onMessage.emit(message, this.sender, sendResponse)
}); })
} }
disconnect() {
if (this.disconnected) disconnect () {
return; if (this.disconnected) return
ipc_renderer_internal_1.ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`);
this._onDisconnect(); ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`)
} this._onDisconnect()
postMessage(message) { }
ipc_renderer_internal_1.ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, message);
} postMessage (message) {
_onDisconnect() { ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, message)
this.disconnected = true; }
ipc_renderer_internal_1.ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`);
this.onDisconnect.emit(); _onDisconnect () {
} this.disconnected = true
ipcRenderer.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`)
this.onDisconnect.emit()
}
} }
// Inject chrome API to the |context| // Inject chrome API to the |context|
function injectTo(extensionId, context) { exports.injectTo = function (extensionId, isBackgroundPage, context) {
const chrome = context.chrome = context.chrome || {}; const chrome = context.chrome = context.chrome || {}
ipc_renderer_internal_1.ipcRendererInternal.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (_event, tabId, portId, connectInfo) => { let originResultID = 1
chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name));
}); ipcRenderer.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (event, tabId, portId, connectInfo) => {
ipcRendererUtils.handle(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (_event, tabId, message) => { chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name))
return new Promise(resolve => { })
chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve);
}); ipcRenderer.on(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (event, tabId, message, resultID) => {
}); chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), (messageResult) => {
ipc_renderer_internal_1.ipcRendererInternal.on('CHROME_TABS_ONCREATED', (_event, tabId) => { ipcRenderer.send(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, messageResult)
chrome.tabs.onCreated.emit(new Tab(tabId)); })
}); })
ipc_renderer_internal_1.ipcRendererInternal.on('CHROME_TABS_ONREMOVED', (_event, tabId) => {
chrome.tabs.onRemoved.emit(tabId); ipcRenderer.on('CHROME_TABS_ONCREATED', (event, tabId) => {
}); chrome.tabs.onCreated.emit(new Tab(tabId))
chrome.runtime = { })
id: extensionId,
// https://developer.chrome.com/extensions/runtime#method-getURL ipcRenderer.on('CHROME_TABS_ONREMOVED', (event, tabId) => {
getURL: function (path) { chrome.tabs.onRemoved.emit(tabId)
return url.format({ })
protocol: 'chrome-extension',
slashes: true, chrome.runtime = {
hostname: extensionId, id: extensionId,
pathname: path
}); getURL: function (path) {
}, return url.format({
// https://developer.chrome.com/extensions/runtime#method-getManifest protocol: 'chrome-extension',
getManifest: function () { slashes: true,
const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId); hostname: extensionId,
return manifest; pathname: path
}, })
// https://developer.chrome.com/extensions/runtime#method-connect },
connect(...args) {
// Parse the optional args. connect (...args) {
let targetExtensionId = extensionId; if (isBackgroundPage) {
let connectInfo = { name: '' }; console.error('chrome.runtime.connect is not supported in background page')
if (args.length === 1) { return
connectInfo = args[0]; }
}
else if (args.length === 2) { // Parse the optional args.
[targetExtensionId, connectInfo] = args; let targetExtensionId = extensionId
} let connectInfo = { name: '' }
const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo); if (args.length === 1) {
return new Port(tabId, portId, extensionId, connectInfo.name); connectInfo = args[0]
}, } else if (args.length === 2) {
// https://developer.chrome.com/extensions/runtime#method-sendMessage [targetExtensionId, connectInfo] = args
sendMessage(...args) { }
// Parse the optional args.
const targetExtensionId = extensionId; const { tabId, portId } = ipcRenderer.sendSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo)
let message; return new Port(tabId, portId, extensionId, connectInfo.name)
let options; },
let responseCallback = () => { };
if (typeof args[args.length - 1] === 'function') { sendMessage (...args) {
responseCallback = args.pop(); if (isBackgroundPage) {
} console.error('chrome.runtime.sendMessage is not supported in background page')
if (args.length === 1) { return
[message] = args; }
}
else if (args.length === 2) { // Parse the optional args.
if (typeof args[0] === 'string') { let targetExtensionId = extensionId
[extensionId, message] = args; let message
} if (args.length === 1) {
else { message = args[0]
[message, options] = args; } else if (args.length === 2) {
} // A case of not provide extension-id: (message, responseCallback)
} if (typeof args[1] === 'function') {
else { ipcRenderer.on(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, (event, result) => args[1](result))
[extensionId, message, options] = args; message = args[0]
} } else {
if (options) { [targetExtensionId, message] = args
console.error('options are not supported'); }
} } else {
ipcRendererUtils.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback); console.error('options is not supported')
}, ipcRenderer.on(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, (event, result) => args[2](result))
onConnect: new Event(), }
onMessage: new Event(),
onInstalled: new Event() ipcRenderer.send('CHROME_RUNTIME_SENDMESSAGE', targetExtensionId, message, originResultID)
}; originResultID++
chrome.tabs = { },
// https://developer.chrome.com/extensions/tabs#method-executeScript
executeScript(tabId, details, resultCallback = () => { }) { onConnect: new Event(),
ipcRendererUtils.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details) onMessage: new Event(),
.then((result) => resultCallback([result])); onInstalled: new Event()
}, }
// https://developer.chrome.com/extensions/tabs#method-sendMessage
sendMessage(tabId, message, _options, responseCallback = () => { }) { chrome.tabs = {
ipcRendererUtils.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback); executeScript (tabId, details, resultCallback) {
}, if (resultCallback) {
onUpdated: new Event(), ipcRenderer.once(`CHROME_TABS_EXECUTESCRIPT_RESULT_${originResultID}`, (event, result) => resultCallback([result]))
onCreated: new Event(), }
onRemoved: new Event() ipcRenderer.send('CHROME_TABS_EXECUTESCRIPT', originResultID, tabId, extensionId, details)
}; originResultID++
chrome.extension = { },
getURL: chrome.runtime.getURL,
connect: chrome.runtime.connect, sendMessage (tabId, message, options, responseCallback) {
onConnect: chrome.runtime.onConnect, if (responseCallback) {
sendMessage: chrome.runtime.sendMessage, ipcRenderer.on(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, (event, result) => responseCallback(result))
onMessage: chrome.runtime.onMessage }
}; ipcRenderer.send('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, isBackgroundPage, message, originResultID)
chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId); originResultID++
chrome.pageAction = { },
show() { },
hide() { }, onUpdated: new Event(),
setTitle() { }, onCreated: new Event(),
getTitle() { }, onRemoved: new Event()
setIcon() { }, }
setPopup() { },
getPopup() { } chrome.extension = {
}; getURL: chrome.runtime.getURL,
chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId); connect: chrome.runtime.connect,
chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup(); onConnect: chrome.runtime.onConnect,
// Electron has no concept of a browserAction but we should stub these APIs for compatibility sendMessage: chrome.runtime.sendMessage,
chrome.browserAction = { onMessage: chrome.runtime.onMessage
setIcon() { }, }
setPopup() { }
}; chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId)
chrome.pageAction = {
show () {},
hide () {},
setTitle () {},
getTitle () {},
setIcon () {},
setPopup () {},
getPopup () {}
}
chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId)
chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup()
} }
exports.injectTo = injectTo;
//# sourceMappingURL=chrome-api.js.map

View File

@ -1,109 +1,101 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron"); const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils"); const { runInThisContext } = require('vm')
const v8Util = process.electronBinding('v8_util');
const IsolatedWorldIDs = {
/**
* Start of extension isolated world IDs, as defined in
* atom_render_frame_observer.h
*/
ISOLATED_WORLD_EXTENSIONS: 1 << 20
};
let isolatedWorldIds = IsolatedWorldIDs.ISOLATED_WORLD_EXTENSIONS;
const extensionWorldId = {};
// https://cs.chromium.org/chromium/src/extensions/renderer/script_injection.cc?type=cs&sq=package:chromium&g=0&l=52
const getIsolatedWorldIdForInstance = () => {
// TODO(samuelmaddock): allocate and cleanup IDs
return isolatedWorldIds++;
};
const escapePattern = function (pattern) {
return pattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&');
};
// Check whether pattern matches. // Check whether pattern matches.
// https://developer.chrome.com/extensions/match_patterns // https://developer.chrome.com/extensions/match_patterns
const matchesPattern = function (pattern) { const matchesPattern = function (pattern) {
if (pattern === '<all_urls>') if (pattern === '<all_urls>') return true
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}`
const url = `${location.protocol}//${location.host}${location.pathname}`; return url.match(regexp)
return url.match(regexp); }
};
// Run the code with chrome API integrated. // Run the code with chrome API integrated.
const runContentScript = function (extensionId, url, code) { const runContentScript = function (extensionId, url, code) {
// Assign unique world ID to each extension const context = {}
const worldId = extensionWorldId[extensionId] || require('@electron/internal/renderer/chrome-api').injectTo(extensionId, false, context)
(extensionWorldId[extensionId] = getIsolatedWorldIdForInstance()); const wrapper = `((chrome) => {\n ${code}\n })`
// store extension ID for content script to read in isolated world const compiledWrapper = runInThisContext(wrapper, {
v8Util.setHiddenValue(global, `extension-${worldId}`, extensionId); filename: url,
electron_1.webFrame.setIsolatedWorldInfo(worldId, { lineOffset: 1,
name: `${extensionId} [${worldId}]` displayErrors: true
// TODO(samuelmaddock): read `content_security_policy` from extension manifest })
// csp: manifest.content_security_policy, return compiledWrapper.call(this, context.chrome)
}); }
const sources = [{ code, url }];
return electron_1.webFrame.executeJavaScriptInIsolatedWorld(worldId, sources);
};
const runAllContentScript = function (scripts, extensionId) { const runAllContentScript = function (scripts, extensionId) {
for (const { url, code } of scripts) { for (const { url, code } of scripts) {
runContentScript.call(window, extensionId, url, code); runContentScript.call(window, extensionId, url, code)
} }
}; }
const runStylesheet = function (url, code) { const runStylesheet = function (url, code) {
electron_1.webFrame.insertCSS(code); const wrapper = `((code) => {
}; function init() {
const runAllStylesheet = function (css) { const styleElement = document.createElement('style');
for (const { url, code } of css) { styleElement.textContent = code;
runStylesheet.call(window, url, code); document.head.append(styleElement);
} }
}; document.addEventListener('DOMContentLoaded', init);
})`
const compiledWrapper = runInThisContext(wrapper, {
filename: url,
lineOffset: 1,
displayErrors: true
})
return compiledWrapper.call(this, code)
}
const runAllStylesheet = function (css) {
for (const { url, code } of css) {
runStylesheet.call(window, url, code)
}
}
// Run injected scripts. // Run injected scripts.
// https://developer.chrome.com/extensions/content_scripts // https://developer.chrome.com/extensions/content_scripts
const injectContentScript = function (extensionId, script) { const injectContentScript = function (extensionId, script) {
if (!process.isMainFrame && !script.allFrames) if (!script.matches.some(matchesPattern)) return
return;
if (!script.matches.some(matchesPattern)) if (script.js) {
return; const fire = runAllContentScript.bind(window, script.js, extensionId)
if (script.js) { if (script.runAt === 'document_start') {
const fire = runAllContentScript.bind(window, script.js, extensionId); process.once('document-start', fire)
if (script.runAt === 'document_start') { } else if (script.runAt === 'document_end') {
process.once('document-start', fire); process.once('document-end', fire)
} } else {
else if (script.runAt === 'document_end') { document.addEventListener('DOMContentLoaded', fire)
process.once('document-end', fire);
}
else {
document.addEventListener('DOMContentLoaded', fire);
}
} }
if (script.css) { }
const fire = runAllStylesheet.bind(window, script.css);
if (script.runAt === 'document_start') { if (script.css) {
process.once('document-start', fire); const fire = runAllStylesheet.bind(window, script.css)
} if (script.runAt === 'document_start') {
else if (script.runAt === 'document_end') { process.once('document-start', fire)
process.once('document-end', fire); } else if (script.runAt === 'document_end') {
} process.once('document-end', fire)
else { } else {
document.addEventListener('DOMContentLoaded', fire); document.addEventListener('DOMContentLoaded', fire)
}
} }
}; }
}
// Handle the request of chrome.tabs.executeJavaScript. // Handle the request of chrome.tabs.executeJavaScript.
ipcRendererUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', function (event, extensionId, url, code) { ipcRenderer.on('CHROME_TABS_EXECUTESCRIPT', function (event, senderWebContentsId, requestId, extensionId, url, code) {
return runContentScript.call(window, extensionId, url, code); const result = runContentScript.call(window, extensionId, url, code)
}); ipcRenderer.sendToAll(senderWebContentsId, `CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, result)
module.exports = (getRenderProcessPreferences) => { })
// Read the renderer process preferences.
const preferences = getRenderProcessPreferences(); // Read the renderer process preferences.
if (preferences) { const preferences = process.getRenderProcessPreferences()
for (const pref of preferences) { if (preferences) {
if (pref.contentScripts) { for (const pref of preferences) {
for (const script of pref.contentScripts) { if (pref.contentScripts) {
injectContentScript(pref.extensionId, script); for (const script of pref.contentScripts) {
} injectContentScript(pref.extensionId, script)
} }
}
} }
}; }
//# sourceMappingURL=content-scripts-injector.js.map }

View File

@ -1,22 +1,26 @@
'use strict'; 'use strict'
class Event { class Event {
constructor() { constructor () {
this.listeners = []; this.listeners = []
}
addListener (callback) {
this.listeners.push(callback)
}
removeListener (callback) {
const index = this.listeners.indexOf(callback)
if (index !== -1) {
this.listeners.splice(index, 1)
} }
addListener(callback) { }
this.listeners.push(callback);
} emit (...args) {
removeListener(callback) { for (const listener of this.listeners) {
const index = this.listeners.indexOf(callback); listener(...args)
if (index !== -1) {
this.listeners.splice(index, 1);
}
}
emit(...args) {
for (const listener of this.listeners) {
listener(...args);
}
} }
}
} }
module.exports = Event;
//# sourceMappingURL=event.js.map module.exports = Event

View File

@ -1,53 +1,88 @@
'use strict'; 'use strict'
// Implementation of chrome.i18n.getMessage // Implementation of chrome.i18n.getMessage
// https://developer.chrome.com/extensions/i18n#method-getMessage // https://developer.chrome.com/extensions/i18n#method-getMessage
// //
// Does not implement predefined messages: // Does not implement predefined messages:
// https://developer.chrome.com/extensions/i18n#overview-predefined // https://developer.chrome.com/extensions/i18n#overview-predefined
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
const getMessages = (extensionId) => { const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
try { const fs = require('fs')
const data = ipcRendererUtils.invokeSync('CHROME_GET_MESSAGES', extensionId); const path = require('path')
return JSON.parse(data) || {};
} let metadata
catch (_a) {
return {}; const getExtensionMetadata = (extensionId) => {
} if (!metadata) {
}; metadata = ipcRenderer.sendSync('CHROME_I18N_MANIFEST', extensionId)
}
return metadata
}
const getMessagesPath = (extensionId, language) => {
const metadata = getExtensionMetadata(extensionId)
const localesDirectory = path.join(metadata.srcDirectory, '_locales')
try {
const filename = path.join(localesDirectory, language, 'messages.json')
fs.accessSync(filename, fs.constants.R_OK)
return filename
} catch (err) {
const defaultLocale = metadata.default_locale || 'en'
return path.join(localesDirectory, defaultLocale, 'messages.json')
}
}
const getMessages = (extensionId, language) => {
try {
const messagesPath = getMessagesPath(extensionId, language)
return JSON.parse(fs.readFileSync(messagesPath)) || {}
} catch (error) {
return {}
}
}
const getLanguage = () => {
return navigator.language.replace(/-.*$/, '').toLowerCase()
}
const replaceNumberedSubstitutions = (message, substitutions) => { const replaceNumberedSubstitutions = (message, substitutions) => {
return message.replace(/\$(\d+)/, (_, number) => { return message.replace(/\$(\d+)/, (_, number) => {
const index = parseInt(number, 10) - 1; const index = parseInt(number, 10) - 1
return substitutions[index] || ''; return substitutions[index] || ''
}); })
}; }
const replacePlaceholders = (message, placeholders, substitutions) => { const replacePlaceholders = (message, placeholders, substitutions) => {
if (typeof substitutions === 'string') { if (typeof substitutions === 'string') {
substitutions = [substitutions]; substitutions = [substitutions]
} }
if (!Array.isArray(substitutions)) { if (!Array.isArray(substitutions)) {
substitutions = []; substitutions = []
} }
if (placeholders) {
Object.keys(placeholders).forEach((name) => { if (placeholders) {
let { content } = placeholders[name]; Object.keys(placeholders).forEach((name) => {
content = replaceNumberedSubstitutions(content, substitutions); let { content } = placeholders[name]
message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content); content = replaceNumberedSubstitutions(content, substitutions)
}); message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content)
} })
return replaceNumberedSubstitutions(message, substitutions); }
};
return replaceNumberedSubstitutions(message, substitutions)
}
const getMessage = (extensionId, messageName, substitutions) => { const getMessage = (extensionId, messageName, substitutions) => {
const messages = getMessages(extensionId); const messages = getMessages(extensionId, getLanguage())
if (messages.hasOwnProperty(messageName)) { if (messages.hasOwnProperty(messageName)) {
const { message, placeholders } = messages[messageName]; const { message, placeholders } = messages[messageName]
return replacePlaceholders(message, placeholders, substitutions); return replacePlaceholders(message, placeholders, substitutions)
} }
}; }
exports.setup = (extensionId) => { exports.setup = (extensionId) => {
return { return {
getMessage(messageName, substitutions) { getMessage (messageName, substitutions) {
return getMessage(extensionId, messageName, substitutions); return getMessage(extensionId, messageName, substitutions)
} }
}; }
}; }
//# sourceMappingURL=i18n.js.map

View File

@ -1,88 +1,138 @@
'use strict'; 'use strict'
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
const getStorage = (storageType, extensionId, callback) => { const fs = require('fs')
if (typeof callback !== 'function') const path = require('path')
throw new TypeError('No callback provided'); const { remote } = require('electron')
ipcRendererUtils.invoke('CHROME_STORAGE_READ', storageType, extensionId) const { app } = remote
.then(data => {
if (data !== null) { const getChromeStoragePath = (storageType, extensionId) => {
callback(JSON.parse(data)); return path.join(
app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`)
}
const mkdirp = (dir, callback) => {
fs.mkdir(dir, (error) => {
if (error && error.code === 'ENOENT') {
mkdirp(path.dirname(dir), (error) => {
if (!error) {
mkdirp(dir, callback)
} }
else { })
// Disabled due to false positive in StandardJS } else if (error && error.code === 'EEXIST') {
// eslint-disable-next-line standard/no-callback-literal callback(null)
callback({}); } else {
} callback(error)
}); }
}; })
const setStorage = (storageType, extensionId, storage, callback) => { }
const json = JSON.stringify(storage);
ipcRendererUtils.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json) const readChromeStorageFile = (storageType, extensionId, cb) => {
.then(() => { const filePath = getChromeStoragePath(storageType, extensionId)
if (callback) fs.readFile(filePath, 'utf8', (err, data) => {
callback(); if (err && err.code === 'ENOENT') {
}); return cb(null, null)
}; }
cb(err, data)
})
}
const writeChromeStorageFile = (storageType, extensionId, data, cb) => {
const filePath = getChromeStoragePath(storageType, extensionId)
mkdirp(path.dirname(filePath), err => {
if (err) { /* we just ignore the errors of mkdir or mkdirp */ }
fs.writeFile(filePath, data, cb)
})
}
const getStorage = (storageType, extensionId, cb) => {
readChromeStorageFile(storageType, extensionId, (err, data) => {
if (err) throw err
if (!cb) throw new TypeError('No callback provided')
if (data !== null) {
cb(JSON.parse(data))
} else {
// Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
cb({})
}
})
}
const setStorage = (storageType, extensionId, storage, cb) => {
const json = JSON.stringify(storage)
writeChromeStorageFile(storageType, extensionId, json, err => {
if (err) throw err
if (cb) cb()
})
}
const getStorageManager = (storageType, extensionId) => { const getStorageManager = (storageType, extensionId) => {
return { return {
get(keys, callback) { get (keys, callback) {
getStorage(storageType, extensionId, storage => { getStorage(storageType, extensionId, storage => {
if (keys == null) if (keys == null) return callback(storage)
return callback(storage);
let defaults = {}; let defaults = {}
switch (typeof keys) { switch (typeof keys) {
case 'string': case 'string':
keys = [keys]; keys = [keys]
break; break
case 'object': case 'object':
if (!Array.isArray(keys)) { if (!Array.isArray(keys)) {
defaults = keys; defaults = keys
keys = Object.keys(keys); keys = Object.keys(keys)
} }
break; break
}
// Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
if (keys.length === 0)
return callback({});
const items = {};
keys.forEach(function (key) {
let value = storage[key];
if (value == null)
value = defaults[key];
items[key] = value;
});
callback(items);
});
},
set(items, callback) {
getStorage(storageType, extensionId, storage => {
Object.keys(items).forEach(function (name) {
storage[name] = items[name];
});
setStorage(storageType, extensionId, storage, callback);
});
},
remove(keys, callback) {
getStorage(storageType, extensionId, storage => {
if (!Array.isArray(keys)) {
keys = [keys];
}
keys.forEach(function (key) {
delete storage[key];
});
setStorage(storageType, extensionId, storage, callback);
});
},
clear(callback) {
setStorage(storageType, extensionId, {}, callback);
} }
};
}; // Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
if (keys.length === 0) return callback({})
const items = {}
keys.forEach(function (key) {
let value = storage[key]
if (value == null) value = defaults[key]
items[key] = value
})
callback(items)
})
},
set (items, callback) {
getStorage(storageType, extensionId, storage => {
Object.keys(items).forEach(function (name) {
storage[name] = items[name]
})
setStorage(storageType, extensionId, storage, callback)
})
},
remove (keys, callback) {
getStorage(storageType, extensionId, storage => {
if (!Array.isArray(keys)) {
keys = [keys]
}
keys.forEach(function (key) {
delete storage[key]
})
setStorage(storageType, extensionId, storage, callback)
})
},
clear (callback) {
setStorage(storageType, extensionId, {}, callback)
}
}
}
module.exports = { module.exports = {
setup: extensionId => ({ setup: extensionId => ({
sync: getStorageManager('sync', extensionId), sync: getStorageManager('sync', extensionId),
local: getStorageManager('local', extensionId) local: getStorageManager('local', extensionId)
}) })
}; }
//# sourceMappingURL=storage.js.map

View File

@ -1,19 +1,23 @@
'use strict'; 'use strict'
const Event = require('@electron/internal/renderer/extensions/event');
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); const Event = require('@electron/internal/renderer/extensions/event')
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
class WebNavigation { class WebNavigation {
constructor() { constructor () {
this.onBeforeNavigate = new Event(); this.onBeforeNavigate = new Event()
this.onCompleted = new Event(); this.onCompleted = new Event()
ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event, details) => {
this.onBeforeNavigate.emit(details); ipcRenderer.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event, details) => {
}); this.onBeforeNavigate.emit(details)
ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event, details) => { })
this.onCompleted.emit(details);
}); ipcRenderer.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event, details) => {
} this.onCompleted.emit(details)
})
}
} }
exports.setup = () => { exports.setup = () => {
return new WebNavigation(); return new WebNavigation()
}; }
//# sourceMappingURL=web-navigation.js.map

View File

@ -1,176 +1,176 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events"); const { EventEmitter } = require('events')
const path = require("path"); const path = require('path')
const Module = require('module'); const Module = require('module')
// Make sure globals like "process" and "global" are always available in preload
// scripts even after they are deleted in "loaded" script.
//
// Note 1: We rely on a Node patch to actually pass "process" and "global" and
// other arguments to the wrapper.
//
// Note 2: Node introduced a new code path to use native code to wrap module
// code, which does not work with this hack. However by modifying the
// "Module.wrapper" we can force Node to use the old code path to wrap module
// code with JavaScript.
Module.wrapper = [
'(function (exports, require, module, __filename, __dirname, process, global, Buffer) { ' +
// By running the code in a new closure, it would be possible for the module
// code to override "process" and "Buffer" with local variables.
'return function (exports, require, module, __filename, __dirname) { ',
'\n}.call(this, exports, require, module, __filename, __dirname); });'
];
// We modified the original process.argv to let node.js load the // We modified the original process.argv to let node.js load the
// init.js, we need to restore it here. // init.js, we need to restore it here.
process.argv.splice(1, 1); process.argv.splice(1, 1)
// Clear search paths. // Clear search paths.
require('../common/reset-search-paths'); require('../common/reset-search-paths')
// Import common settings. // Import common settings.
require('@electron/internal/common/init'); require('@electron/internal/common/init')
const globalPaths = Module.globalPaths;
const globalPaths = Module.globalPaths
// Expose public APIs. // Expose public APIs.
globalPaths.push(path.join(__dirname, 'api', 'exports')); globalPaths.push(path.join(__dirname, 'api', 'exports'))
// The global variable will be used by ipc for event dispatching // The global variable will be used by ipc for event dispatching
const v8Util = process.electronBinding('v8_util'); const v8Util = process.atomBinding('v8_util')
const ipcEmitter = new events_1.EventEmitter();
const ipcInternalEmitter = new events_1.EventEmitter(); v8Util.setHiddenValue(global, 'ipc', new EventEmitter())
v8Util.setHiddenValue(global, 'ipc', ipcEmitter); v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter())
v8Util.setHiddenValue(global, 'ipc-internal', ipcInternalEmitter);
v8Util.setHiddenValue(global, 'ipcNative', {
onMessage(internal, channel, args, senderId) {
const sender = internal ? ipcInternalEmitter : ipcEmitter;
sender.emit(channel, { sender, senderId }, ...args);
}
});
// Use electron module after everything is ready. // Use electron module after everything is ready.
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const { webFrameInit } = require('@electron/internal/renderer/web-frame-init');
webFrameInit(); require('@electron/internal/renderer/web-frame-init')()
// Process command line arguments. // Process command line arguments.
const { hasSwitch, getSwitchValue } = process.electronBinding('command_line'); let nodeIntegration = false
const parseOption = function (name, defaultValue, converter) { let webviewTag = false
return hasSwitch(name) let preloadScript = null
? (converter let preloadScripts = []
? converter(getSwitchValue(name)) let isBackgroundPage = false
: getSwitchValue(name)) let appPath = null
: defaultValue; for (const arg of process.argv) {
}; if (arg.indexOf('--guest-instance-id=') === 0) {
const contextIsolation = hasSwitch('context-isolation'); // This is a guest web view.
const nodeIntegration = hasSwitch('node-integration'); process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1))
const webviewTag = hasSwitch('webview-tag'); } else if (arg.indexOf('--opener-id=') === 0) {
const isHiddenPage = hasSwitch('hidden-page'); // This is a guest BrowserWindow.
const usesNativeWindowOpen = hasSwitch('native-window-open'); process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1))
const preloadScript = parseOption('preload', null); } else if (arg.indexOf('--node-integration=') === 0) {
const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter)); nodeIntegration = arg.substr(arg.indexOf('=') + 1) === 'true'
const appPath = parseOption('app-path', null); } else if (arg.indexOf('--preload=') === 0) {
const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value)); preloadScript = arg.substr(arg.indexOf('=') + 1)
const openerId = parseOption('opener-id', null, value => parseInt(value)); } else if (arg === '--background-page') {
// The arguments to be passed to isolated world. isBackgroundPage = true
const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen }; } 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)
}
}
// The webContents preload script is loaded after the session preload scripts. // The webContents preload script is loaded after the session preload scripts.
if (preloadScript) { if (preloadScript) {
preloadScripts.push(preloadScript); preloadScripts.push(preloadScript)
} }
switch (window.location.protocol) {
case 'devtools:': { if (window.location.protocol === 'chrome-devtools:') {
// Override some inspector APIs. // Override some inspector APIs.
require('@electron/internal/renderer/inspector'); require('@electron/internal/renderer/inspector')
break; nodeIntegration = false
} } else if (window.location.protocol === 'chrome-extension:') {
case 'chrome-extension:': { // Add implementations of chrome API.
// Inject the chrome.* APIs that chrome extensions require require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window); nodeIntegration = false
break; } else if (window.location.protocol === 'chrome:') {
} // Disable node integration for chrome UI scheme.
case 'chrome:': nodeIntegration = false
break; } else {
default: { // Override default web functions.
// Override default web functions. require('@electron/internal/renderer/override')
const { windowSetup } = require('@electron/internal/renderer/window-setup');
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen); // Inject content scripts.
// Inject content scripts. require('@electron/internal/renderer/content-scripts-injector')
require('@electron/internal/renderer/content-scripts-injector')(process.getRenderProcessPreferences);
// Load webview tag implementation.
if (webviewTag && process.guestInstanceId == null) {
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view')
if (process.argv.includes('--context-isolation')) {
v8Util.setHiddenValue(window, 'setup-webview', setupWebView)
} else {
setupWebView(window)
} }
}
} }
// Load webview tag implementation.
if (process.isMainFrame) {
const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init');
webViewInit(contextIsolation, webviewTag, guestInstanceId);
}
// Pass the arguments to isolatedWorld.
if (contextIsolation) {
v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs);
}
if (nodeIntegration) { if (nodeIntegration) {
// Export node bindings to global. // Export node bindings to global.
global.require = require; global.require = require
global.module = module; global.module = module
// Set the __filename to the path of html file if it is file: protocol.
if (window.location.protocol === 'file:') { // Set the __filename to the path of html file if it is file: protocol.
const location = window.location; if (window.location.protocol === 'file:') {
let pathname = location.pathname; const location = window.location
if (process.platform === 'win32') { let pathname = location.pathname
if (pathname[0] === '/')
pathname = pathname.substr(1); if (process.platform === 'win32') {
const isWindowsNetworkSharePath = location.hostname.length > 0 && globalPaths[0].startsWith('\\'); if (pathname[0] === '/') pathname = pathname.substr(1)
if (isWindowsNetworkSharePath) {
pathname = `//${location.host}/${pathname}`; const isWindowsNetworkSharePath = location.hostname.length > 0 && globalPaths[0].startsWith('\\')
} if (isWindowsNetworkSharePath) {
} pathname = `//${location.host}/${pathname}`
global.__filename = path.normalize(decodeURIComponent(pathname)); }
global.__dirname = path.dirname(global.__filename);
// Set module's filename so relative require can work as expected.
module.filename = global.__filename;
// Also search for module under the html file.
module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname));
} }
else {
global.__filename = __filename; global.__filename = path.normalize(decodeURIComponent(pathname))
global.__dirname = __dirname; global.__dirname = path.dirname(global.__filename)
if (appPath) {
// Search for module under the app directory // Set module's filename so relative require can work as expected.
module.paths = module.paths.concat(Module._nodeModulePaths(appPath)); module.filename = global.__filename
}
// Also search for module under the html file.
module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
} else {
global.__filename = __filename
global.__dirname = __dirname
if (appPath) {
// Search for module under the app directory
module.paths = module.paths.concat(Module._nodeModulePaths(appPath))
} }
// Redirect window.onerror to uncaughtException. }
window.onerror = function (_message, _filename, _lineno, _colno, error) {
if (global.process.listeners('uncaughtException').length > 0) { // Redirect window.onerror to uncaughtException.
// We do not want to add `uncaughtException` to our definitions window.onerror = function (message, filename, lineno, colno, error) {
// because we don't want anyone else (anywhere) to throw that kind if (global.process.listeners('uncaughtException').length > 0) {
// of error. global.process.emit('uncaughtException', error)
global.process.emit('uncaughtException', error); return true
return true; } else {
} return false
else { }
return false; }
} } else {
}; // Delete Node's symbols after the Environment has been loaded.
process.once('loaded', function () {
delete global.process
delete global.Buffer
delete global.setImmediate
delete global.clearImmediate
delete global.global
})
} }
else {
// Delete Node's symbols after the Environment has been loaded.
process.once('loaded', function () {
delete global.process;
delete global.Buffer;
delete global.setImmediate;
delete global.clearImmediate;
delete global.global;
});
}
const errorUtils = require('@electron/internal/common/error-utils');
// Load the preload scripts. // Load the preload scripts.
for (const preloadScript of preloadScripts) { for (const preloadScript of preloadScripts) {
try { try {
require(preloadScript); require(preloadScript)
} } catch (error) {
catch (error) { console.error('Unable to load preload script: ' + preloadScript)
console.error(`Unable to load preload script: ${preloadScript}`); console.error(error.stack || error.message)
console.error(`${error}`); }
ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, errorUtils.serialize(error));
}
} }
// Warn about security issues // Warn about security issues
if (process.isMainFrame) { require('@electron/internal/renderer/security-warnings')(nodeIntegration)
const { securityWarnings } = require('@electron/internal/renderer/security-warnings');
securityWarnings(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)
})
} }
//# sourceMappingURL=init.js.map

View File

@ -1,53 +1,138 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const ipc_renderer_internal_utils_1 = require("@electron/internal/renderer/ipc-renderer-internal-utils");
window.onload = function () { window.onload = function () {
// Use menu API to show context menu. // Use menu API to show context menu.
window.InspectorFrontendHost.showContextMenuAtPoint = createMenu; window.InspectorFrontendHost.showContextMenuAtPoint = createMenu
// correct for Chromium returning undefined for filesystem
window.Persistence.FileSystemWorkspaceBinding.completeURL = completeURL; // correct for Chromium returning undefined for filesystem
// Use dialog API to override file chooser dialog. window.Persistence.FileSystemWorkspaceBinding.completeURL = completeURL
window.UI.createFileSelectorElement = createFileSelectorElement;
}; // Use dialog API to override file chooser dialog.
// Extra / is needed as a result of MacOS requiring absolute paths window.UI.createFileSelectorElement = createFileSelectorElement
function completeURL(project, path) {
project = 'file:///';
return `${project}${path}`;
} }
// The DOM implementation expects (message?: string) => boolean
// Extra / is needed as a result of MacOS requiring absolute paths
function completeURL (project, path) {
project = 'file:///'
return `${project}${path}`
}
window.confirm = function (message, title) { window.confirm = function (message, title) {
return ipc_renderer_internal_utils_1.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title); const { dialog } = require('electron').remote
}; if (title == null) {
const useEditMenuItems = function (x, y, items) { title = ''
return items.length === 0 && document.elementsFromPoint(x, y).some(function (element) { }
return element.nodeName === 'INPUT' || return !dialog.showMessageBox({
element.nodeName === 'TEXTAREA' || message: message,
element.isContentEditable; title: title,
}); buttons: ['OK', 'Cancel'],
}; cancelId: 1
})
}
const convertToMenuTemplate = function (items) {
return items.map(function (item) {
const transformed = item.type === 'subMenu' ? {
type: 'submenu',
label: item.label,
enabled: item.enabled,
submenu: convertToMenuTemplate(item.subItems)
} : item.type === 'separator' ? {
type: 'separator'
} : item.type === 'checkbox' ? {
type: 'checkbox',
label: item.label,
enabled: item.enabled,
checked: item.checked
} : {
type: 'normal',
label: item.label,
enabled: item.enabled
}
if (item.id != null) {
transformed.click = function () {
window.DevToolsAPI.contextMenuItemSelected(item.id)
return window.DevToolsAPI.contextMenuCleared()
}
}
return transformed
})
}
const createMenu = function (x, y, items) { const createMenu = function (x, y, items) {
const isEditMenu = useEditMenuItems(x, y, items); const { remote } = require('electron')
ipc_renderer_internal_utils_1.invoke('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => { const { Menu } = remote
if (typeof id === 'number') {
window.DevToolsAPI.contextMenuItemSelected(id); let template = convertToMenuTemplate(items)
} if (useEditMenuItems(x, y, template)) {
window.DevToolsAPI.contextMenuCleared(); template = getEditMenuItems()
}); }
}; const menu = Menu.buildFromTemplate(template)
// The menu is expected to show asynchronously.
setTimeout(function () {
menu.popup({ window: remote.getCurrentWindow() })
})
}
const useEditMenuItems = function (x, y, items) {
return items.length === 0 && document.elementsFromPoint(x, y).some(function (element) {
return element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA' || element.isContentEditable
})
}
const getEditMenuItems = function () {
return [
{
role: 'undo'
},
{
role: 'redo'
},
{
type: 'separator'
},
{
role: 'cut'
},
{
role: 'copy'
},
{
role: 'paste'
},
{
role: 'pasteAndMatchStyle'
},
{
role: 'delete'
},
{
role: 'selectAll'
}
]
}
const showFileChooserDialog = function (callback) { const showFileChooserDialog = function (callback) {
ipc_renderer_internal_utils_1.invoke('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => { const { dialog } = require('electron').remote
if (path && data) { const files = dialog.showOpenDialog({})
callback(dataToHtml5FileObject(path, data)); if (files != null) {
} callback(pathToHtml5FileObject(files[0]))
}); }
}; }
const dataToHtml5FileObject = function (path, data) {
return new File([data], path); const pathToHtml5FileObject = function (path) {
}; const fs = require('fs')
const blob = new Blob([fs.readFileSync(path)])
blob.name = path
return blob
}
const createFileSelectorElement = function (callback) { const createFileSelectorElement = function (callback) {
const fileSelectorElement = document.createElement('span'); const fileSelectorElement = document.createElement('span')
fileSelectorElement.style.display = 'none'; fileSelectorElement.style.display = 'none'
fileSelectorElement.click = showFileChooserDialog.bind(this, callback); fileSelectorElement.click = showFileChooserDialog.bind(this, callback)
return fileSelectorElement; return fileSelectorElement
}; }
//# sourceMappingURL=inspector.js.map

View File

@ -1,42 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-renderer-internal");
const errorUtils = require("@electron/internal/common/error-utils");
exports.handle = function (channel, handler) {
ipc_renderer_internal_1.ipcRendererInternal.on(channel, (event, requestId, ...args) => {
new Promise(resolve => resolve(handler(event, ...args))).then(result => {
return [null, result];
}, error => {
return [errorUtils.serialize(error)];
}).then(responseArgs => {
event.sender.send(`${channel}_RESPONSE_${requestId}`, ...responseArgs);
});
});
};
let nextId = 0;
function invoke(command, ...args) {
return new Promise((resolve, reject) => {
const requestId = ++nextId;
ipc_renderer_internal_1.ipcRendererInternal.once(`${command}_RESPONSE_${requestId}`, (_event, error, result) => {
if (error) {
reject(errorUtils.deserialize(error));
}
else {
resolve(result);
}
});
ipc_renderer_internal_1.ipcRendererInternal.send(command, requestId, ...args);
});
}
exports.invoke = invoke;
function invokeSync(command, ...args) {
const [error, result] = ipc_renderer_internal_1.ipcRendererInternal.sendSync(command, null, ...args);
if (error) {
throw errorUtils.deserialize(error);
}
else {
return result;
}
}
exports.invokeSync = invokeSync;
//# sourceMappingURL=ipc-renderer-internal-utils.js.map

View File

@ -1,20 +1,26 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const binding = process.electronBinding('ipc'); const binding = process.atomBinding('ipc')
const v8Util = process.electronBinding('v8_util'); const v8Util = process.atomBinding('v8_util')
// Created by init.js. // Created by init.js.
exports.ipcRendererInternal = v8Util.getHiddenValue(global, 'ipc-internal'); const ipcRenderer = v8Util.getHiddenValue(global, 'ipc-internal')
const internal = true; const internal = true
exports.ipcRendererInternal.send = function (channel, ...args) {
return binding.ipc.send(internal, channel, args); ipcRenderer.send = function (...args) {
}; return binding.send('ipc-internal-message', args)
exports.ipcRendererInternal.sendSync = function (channel, ...args) { }
return binding.ipc.sendSync(internal, channel, args)[0];
}; ipcRenderer.sendSync = function (...args) {
exports.ipcRendererInternal.sendTo = function (webContentsId, channel, ...args) { return binding.sendSync('ipc-internal-message-sync', args)[0]
return binding.sendTo(internal, false, webContentsId, channel, args); }
};
exports.ipcRendererInternal.sendToAll = function (webContentsId, channel, ...args) { ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
return binding.sendTo(internal, true, webContentsId, channel, args); return binding.sendTo(internal, false, webContentsId, channel, args)
}; }
//# sourceMappingURL=ipc-renderer-internal.js.map
ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
return binding.sendTo(internal, true, webContentsId, channel, args)
}
module.exports = ipcRenderer

View File

@ -0,0 +1,18 @@
'use strict'
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const v8Util = process.atomBinding('v8_util')
const { guestInstanceId, openerId } = process
const hiddenPage = process.argv.includes('--hidden-page')
const usesNativeWindowOpen = process.argv.includes('--native-window-open')
const contextIsolation = process.argv.includes('--context-isolation')
// Pass the arguments to isolatedWorld.
if (contextIsolation) {
const isolatedWorldArgs = { ipcRenderer, guestInstanceId, hiddenPage, openerId, usesNativeWindowOpen }
v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs)
}
require('@electron/internal/renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen)

View File

@ -0,0 +1,10 @@
'use strict'
const { remote } = require('electron')
exports.getRemoteForUsage = function (usage) {
if (!remote) {
throw new Error(`${usage} requires remote, which is not enabled`)
}
return remote
}

View File

@ -1,8 +1,7 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron"); let shouldLog = null
const ipc_renderer_internal_utils_1 = require("@electron/internal/renderer/ipc-renderer-internal-utils");
let shouldLog = null;
/** /**
* This method checks if a security message should be logged. * This method checks if a security message should be logged.
* It does so by determining whether we're running as Electron, * It does so by determining whether we're running as Electron,
@ -12,64 +11,76 @@ let shouldLog = null;
* @returns {boolean} - Should we log? * @returns {boolean} - Should we log?
*/ */
const shouldLogSecurityWarnings = function () { const shouldLogSecurityWarnings = function () {
if (shouldLog !== null) { if (shouldLog !== null) {
return shouldLog; return shouldLog
} }
const { platform, execPath, env } = process;
switch (platform) { const { platform, execPath, env } = process
case 'darwin':
shouldLog = execPath.endsWith('MacOS/Electron') || switch (platform) {
execPath.includes('Electron.app/Contents/Frameworks/'); case 'darwin':
break; shouldLog = execPath.endsWith('MacOS/Electron') ||
case 'freebsd': execPath.includes('Electron.app/Contents/Frameworks/')
case 'linux': break
shouldLog = execPath.endsWith('/electron'); case 'freebsd':
break; case 'linux':
case 'win32': shouldLog = execPath.endsWith('/electron')
shouldLog = execPath.endsWith('\\electron.exe'); break
break; case 'win32':
default: shouldLog = execPath.endsWith('\\electron.exe')
shouldLog = false; break
} default:
if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) || shouldLog = false
(window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) { }
shouldLog = false;
} if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) ||
if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) || (window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) {
(window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) { shouldLog = false
shouldLog = true; }
}
return shouldLog; if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) ||
}; (window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) {
shouldLog = true
}
return shouldLog
}
/** /**
* Checks if the current window is remote. * Checks if the current window is remote.
* *
* @returns {boolean} - Is this a remote protocol? * @returns {boolean} - Is this a remote protocol?
*/ */
const getIsRemoteProtocol = function () { const getIsRemoteProtocol = function () {
if (window && window.location && window.location.protocol) { if (window && window.location && window.location.protocol) {
return /^(http|ftp)s?/gi.test(window.location.protocol); return /^(http|ftp)s?/gi.test(window.location.protocol)
} }
}; }
/** /**
* Tries to determine whether a CSP without `unsafe-eval` is set. * Tries to determine whether a CSP without `unsafe-eval` is set.
* *
* @returns {boolean} Is a CSP with `unsafe-eval` set? * @returns {boolean} Is a CSP with `unsafe-eval` set?
*/ */
const isUnsafeEvalEnabled = function () { const isUnsafeEvalEnabled = function () {
return electron_1.webFrame.executeJavaScript(`(${(() => { const { webFrame } = require('electron')
try {
new Function(''); // eslint-disable-line no-new,no-new-func return new Promise((resolve) => {
} webFrame.executeJavaScript(`(${(() => {
catch (_a) { try {
return false; new Function('') // eslint-disable-line no-new,no-new-func
} } catch (err) {
return true; return false
}).toString()})()`, false); }
}; return true
}).toString()})()`, resolve)
})
}
const moreInformation = `\nFor more information and help, consult const moreInformation = `\nFor more information and help, consult
https://electronjs.org/docs/tutorial/security.\n This warning will not show up https://electronjs.org/docs/tutorial/security.\n This warning will not show up
once the app is packaged.`; once the app is packaged.`
/** /**
* #1 Only load secure content * #1 Only load secure content
* *
@ -77,23 +88,29 @@ once the app is packaged.`;
* message about all resources loaded over HTTP or FTP. * message about all resources loaded over HTTP or FTP.
*/ */
const warnAboutInsecureResources = function () { const warnAboutInsecureResources = function () {
if (!window || !window.performance || !window.performance.getEntriesByType) { if (!window || !window.performance || !window.performance.getEntriesByType) {
return; return
} }
const resources = window.performance
.getEntriesByType('resource') const resources = window.performance
.filter(({ name }) => /^(http|ftp):/gi.test(name || '')) .getEntriesByType('resource')
.map(({ name }) => `- ${name}`) .filter(({ name }) => /^(http|ftp):/gi.test(name || ''))
.join('\n'); .map(({ name }) => `- ${name}`)
if (!resources || resources.length === 0) { .join('\n')
return;
} if (!resources || resources.length === 0) {
const warning = `This renderer process loads resources using insecure return
}
const warning = `This renderer process loads resources using insecure
protocols.This exposes users of this app to unnecessary security risks. protocols.This exposes users of this app to unnecessary security risks.
Consider loading the following resources over HTTPS or FTPS. \n ${resources} Consider loading the following resources over HTTPS or FTPS. \n ${resources}
\n ${moreInformation}`; \n ${moreInformation}`
console.warn('%cElectron Security Warning (Insecure Resources)', 'font-weight: bold;', warning);
}; console.warn('%cElectron Security Warning (Insecure Resources)',
'font-weight: bold;', warning)
}
/** /**
* #2 on the checklist: Disable the Node.js integration in all renderers that * #2 on the checklist: Disable the Node.js integration in all renderers that
* display remote content * display remote content
@ -101,32 +118,39 @@ const warnAboutInsecureResources = function () {
* Logs a warning message about Node integration. * Logs a warning message about Node integration.
*/ */
const warnAboutNodeWithRemoteContent = function (nodeIntegration) { const warnAboutNodeWithRemoteContent = function (nodeIntegration) {
if (!nodeIntegration) if (!nodeIntegration) return
return;
if (getIsRemoteProtocol()) { if (getIsRemoteProtocol()) {
const warning = `This renderer process has Node.js integration enabled const warning = `This renderer process has Node.js integration enabled
and attempted to load remote content from '${window.location}'. This and attempted to load remote content from '${window.location}'. This
exposes users of this app to severe security risks.\n ${moreInformation}`; exposes users of this app to severe security risks.\n ${moreInformation}`
console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)', 'font-weight: bold;', warning);
} console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)',
}; 'font-weight: bold;', warning)
}
}
// Currently missing since it has ramifications and is still experimental: // Currently missing since it has ramifications and is still experimental:
// #3 Enable context isolation in all renderers that display remote content // #3 Enable context isolation in all renderers that display remote content
// //
// Currently missing since we can't easily programmatically check for those cases: // Currently missing since we can't easily programmatically check for those cases:
// #4 Use ses.setPermissionRequestHandler() in all sessions that load remote content // #4 Use ses.setPermissionRequestHandler() in all sessions that load remote content
/** /**
* #5 on the checklist: Do not disable websecurity * #5 on the checklist: Do not disable websecurity
* *
* Logs a warning message about disabled webSecurity. * Logs a warning message about disabled webSecurity.
*/ */
const warnAboutDisabledWebSecurity = function (webPreferences) { const warnAboutDisabledWebSecurity = function (webPreferences) {
if (!webPreferences || webPreferences.webSecurity !== false) if (!webPreferences || webPreferences.webSecurity !== false) return
return;
const warning = `This renderer process has "webSecurity" disabled. This const warning = `This renderer process has "webSecurity" disabled. This
exposes users of this app to severe security risks.\n ${moreInformation}`; exposes users of this app to severe security risks.\n ${moreInformation}`
console.warn('%cElectron Security Warning (Disabled webSecurity)', 'font-weight: bold;', warning);
}; console.warn('%cElectron Security Warning (Disabled webSecurity)',
'font-weight: bold;', warning)
}
/** /**
* #6 on the checklist: Define a Content-Security-Policy and use restrictive * #6 on the checklist: Define a Content-Security-Policy and use restrictive
* rules (i.e. script-src 'self') * rules (i.e. script-src 'self')
@ -136,104 +160,180 @@ const warnAboutDisabledWebSecurity = function (webPreferences) {
* Logs a warning message about unset or insecure CSP * Logs a warning message about unset or insecure CSP
*/ */
const warnAboutInsecureCSP = function () { const warnAboutInsecureCSP = function () {
isUnsafeEvalEnabled().then((enabled) => { isUnsafeEvalEnabled().then((enabled) => {
if (!enabled) if (!enabled) return
return;
const warning = `This renderer process has either no Content Security const warning = `This renderer process has either no Content Security
Policy set or a policy with "unsafe-eval" enabled. This exposes users of Policy set or a policy with "unsafe-eval" enabled. This exposes users of
this app to unnecessary security risks.\n ${moreInformation}`; this app to unnecessary security risks.\n ${moreInformation}`
console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)', 'font-weight: bold;', warning);
}); console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
}; 'font-weight: bold;', warning)
})
}
/** /**
* #8 on the checklist: Do not set allowRunningInsecureContent to true * #8 on the checklist: Do not set allowRunningInsecureContent to true
* *
* Logs a warning message about disabled webSecurity. * Logs a warning message about disabled webSecurity.
*/ */
const warnAboutInsecureContentAllowed = function (webPreferences) { const warnAboutInsecureContentAllowed = function (webPreferences) {
if (!webPreferences || !webPreferences.allowRunningInsecureContent) if (!webPreferences || !webPreferences.allowRunningInsecureContent) return
return;
const warning = `This renderer process has "allowRunningInsecureContent" const warning = `This renderer process has "allowRunningInsecureContent"
enabled. This exposes users of this app to severe security risks.\n enabled. This exposes users of this app to severe security risks.\n
${moreInformation}`; ${moreInformation}`
console.warn('%cElectron Security Warning (allowRunningInsecureContent)', 'font-weight: bold;', warning);
}; console.warn('%cElectron Security Warning (allowRunningInsecureContent)',
'font-weight: bold;', warning)
}
/** /**
* #9 on the checklist: Do not enable experimental features * #9 on the checklist: Do not enable experimental features
* *
* Logs a warning message about experimental features. * Logs a warning message about experimental features.
*/ */
const warnAboutExperimentalFeatures = function (webPreferences) { const warnAboutExperimentalFeatures = function (webPreferences) {
if (!webPreferences || (!webPreferences.experimentalFeatures)) { if (!webPreferences || (!webPreferences.experimentalFeatures)) {
return; return
} }
const warning = `This renderer process has "experimentalFeatures" enabled.
const warning = `This renderer process has "experimentalFeatures" enabled.
This exposes users of this app to some security risk. If you do not need This exposes users of this app to some security risk. If you do not need
this feature, you should disable it.\n ${moreInformation}`; this feature, you should disable it.\n ${moreInformation}`
console.warn('%cElectron Security Warning (experimentalFeatures)', 'font-weight: bold;', warning);
}; console.warn('%cElectron Security Warning (experimentalFeatures)',
'font-weight: bold;', warning)
}
/** /**
* #10 on the checklist: Do not use enableBlinkFeatures * #10 on the checklist: Do not use enableBlinkFeatures
* *
* Logs a warning message about enableBlinkFeatures * Logs a warning message about enableBlinkFeatures
*/ */
const warnAboutEnableBlinkFeatures = function (webPreferences) { const warnAboutEnableBlinkFeatures = function (webPreferences) {
if (!webPreferences || if (webPreferences === null ||
!webPreferences.hasOwnProperty('enableBlinkFeatures') || !webPreferences.hasOwnProperty('enableBlinkFeatures') ||
(webPreferences.enableBlinkFeatures && webPreferences.enableBlinkFeatures.length === 0)) { webPreferences.enableBlinkFeatures.length === 0) {
return; return
} }
const warning = `This renderer process has additional "enableBlinkFeatures"
const warning = `This renderer process has additional "enableBlinkFeatures"
enabled. This exposes users of this app to some security risk. If you do not enabled. This exposes users of this app to some security risk. If you do not
need this feature, you should disable it.\n ${moreInformation}`; need this feature, you should disable it.\n ${moreInformation}`
console.warn('%cElectron Security Warning (enableBlinkFeatures)', 'font-weight: bold;', warning);
}; console.warn('%cElectron Security Warning (enableBlinkFeatures)',
'font-weight: bold;', warning)
}
/** /**
* #11 on the checklist: Do Not Use allowpopups * #11 on the checklist: Do Not Use allowpopups
* *
* Logs a warning message about allowed popups * Logs a warning message about allowed popups
*/ */
const warnAboutAllowedPopups = function () { const warnAboutAllowedPopups = function () {
if (document && document.querySelectorAll) { if (document && document.querySelectorAll) {
const domElements = document.querySelectorAll('[allowpopups]'); const domElements = document.querySelectorAll('[allowpopups]')
if (!domElements || domElements.length === 0) {
return; if (!domElements || domElements.length === 0) {
} return
const warning = `A <webview> has "allowpopups" set to true. This exposes }
const warning = `A <webview> has "allowpopups" set to true. This exposes
users of this app to some security risk, since popups are just users of this app to some security risk, since popups are just
BrowserWindows. If you do not need this feature, you should disable it.\n BrowserWindows. If you do not need this feature, you should disable it.\n
${moreInformation}`; ${moreInformation}`
console.warn('%cElectron Security Warning (allowpopups)', 'font-weight: bold;', warning);
} console.warn('%cElectron Security Warning (allowpopups)',
}; 'font-weight: bold;', warning)
}
}
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: // Currently missing since we can't easily programmatically check for it:
// #12WebViews: Verify the options and params of all `<webview>` tags // #12WebViews: Verify the options and params of all `<webview>` tags
const logSecurityWarnings = function (webPreferences, nodeIntegration) { const logSecurityWarnings = function (webPreferences, nodeIntegration) {
warnAboutNodeWithRemoteContent(nodeIntegration); warnAboutNodeWithRemoteContent(nodeIntegration)
warnAboutDisabledWebSecurity(webPreferences); warnAboutDisabledWebSecurity(webPreferences)
warnAboutInsecureResources(); warnAboutInsecureResources()
warnAboutInsecureContentAllowed(webPreferences); warnAboutInsecureContentAllowed(webPreferences)
warnAboutExperimentalFeatures(webPreferences); warnAboutExperimentalFeatures(webPreferences)
warnAboutEnableBlinkFeatures(webPreferences); warnAboutEnableBlinkFeatures(webPreferences)
warnAboutInsecureCSP(); warnAboutInsecureCSP()
warnAboutAllowedPopups(); warnAboutAllowedPopups()
}; warnAboutNodeIntegrationDefault(webPreferences)
const getWebPreferences = function () { warnAboutContextIsolationDefault(webPreferences)
try { warnAboutDeprecatedWebviewTagDefault(webPreferences)
return ipc_renderer_internal_utils_1.invokeSync('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES'); }
}
catch (error) { const getWebPreferences = function () {
console.warn(`getLastWebPreferences() failed: ${error}`); const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
} const errorUtils = require('@electron/internal/common/error-utils')
};
function securityWarnings(nodeIntegration) { const [ error, result ] = ipcRenderer.sendSync('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES')
const loadHandler = function () {
if (shouldLogSecurityWarnings()) { if (error) {
const webPreferences = getWebPreferences(); console.warn(`getLastWebPreferences() failed: ${errorUtils.deserialize(error)}`)
logSecurityWarnings(webPreferences, nodeIntegration); return null
} } else {
}; return result
window.addEventListener('load', loadHandler, { once: true }); }
}
module.exports = function (nodeIntegration) {
const loadHandler = function () {
if (shouldLogSecurityWarnings()) {
const webPreferences = getWebPreferences()
logSecurityWarnings(webPreferences, nodeIntegration)
}
}
window.addEventListener('load', loadHandler, { once: true })
} }
exports.securityWarnings = securityWarnings;
//# sourceMappingURL=security-warnings.js.map

View File

@ -1,14 +1,24 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron"); const { webFrame } = require('electron')
const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils"); const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
exports.webFrameInit = () => { const errorUtils = require('@electron/internal/common/error-utils')
// Call webFrame method
ipcRendererUtils.handle('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, ...args) => { module.exports = () => {
// The TypeScript compiler cannot handle the sheer number of // Call webFrame method
// call signatures here and simply gives up. Incorrect invocations ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
// will be caught by "keyof WebFrameMethod" though. webFrame[method](...args)
return electron_1.webFrame[method](...args); })
});
}; ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
//# sourceMappingURL=web-frame-init.js.map new Promise(resolve =>
webFrame[method](...args, resolve)
).then(result => {
return [null, result]
}, error => {
return [errorUtils.serialize(error)]
}).then(responseArgs => {
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, ...responseArgs)
})
})
}

View File

@ -1,111 +1,108 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron"); const { webFrame } = require('electron')
const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-renderer-internal"); const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const ipc_renderer_internal_utils_1 = require("@electron/internal/renderer/ipc-renderer-internal-utils");
let requestId = 0
const WEB_VIEW_EVENTS = { const WEB_VIEW_EVENTS = {
'load-commit': ['url', 'isMainFrame'], 'load-commit': ['url', 'isMainFrame'],
'did-attach': [], 'did-attach': [],
'did-finish-load': [], 'did-finish-load': [],
'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], 'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
'did-frame-finish-load': ['isMainFrame', 'frameProcessId', 'frameRoutingId'], 'did-frame-finish-load': ['isMainFrame', 'frameProcessId', 'frameRoutingId'],
'did-start-loading': [], 'did-start-loading': [],
'did-stop-loading': [], 'did-stop-loading': [],
'dom-ready': [], 'dom-ready': [],
'console-message': ['level', 'message', 'line', 'sourceId'], 'console-message': ['level', 'message', 'line', 'sourceId'],
'context-menu': ['params'], 'context-menu': ['params'],
'devtools-opened': [], 'devtools-opened': [],
'devtools-closed': [], 'devtools-closed': [],
'devtools-focused': [], 'devtools-focused': [],
'new-window': ['url', 'frameName', 'disposition', 'options'], 'new-window': ['url', 'frameName', 'disposition', 'options'],
'will-navigate': ['url'], 'will-navigate': ['url'],
'did-start-navigation': ['url', 'isInPlace', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], 'did-start-navigation': ['url', 'isInPlace', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
'did-navigate': ['url', 'httpResponseCode', 'httpStatusText'], 'did-navigate': ['url', 'httpResponseCode', 'httpStatusText'],
'did-frame-navigate': ['url', 'httpResponseCode', 'httpStatusText', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], 'did-frame-navigate': ['url', 'httpResponseCode', 'httpStatusText', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
'did-navigate-in-page': ['url', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], 'did-navigate-in-page': ['url', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
'focus-change': ['focus', 'guestInstanceId'], 'focus-change': ['focus', 'guestInstanceId'],
'close': [], 'close': [],
'crashed': [], 'crashed': [],
'plugin-crashed': ['name', 'version'], 'gpu-crashed': [],
'destroyed': [], 'plugin-crashed': ['name', 'version'],
'page-title-updated': ['title', 'explicitSet'], 'destroyed': [],
'page-favicon-updated': ['favicons'], 'page-title-updated': ['title', 'explicitSet'],
'enter-html-full-screen': [], 'page-favicon-updated': ['favicons'],
'leave-html-full-screen': [], 'enter-html-full-screen': [],
'media-started-playing': [], 'leave-html-full-screen': [],
'media-paused': [], 'media-started-playing': [],
'found-in-page': ['result'], 'media-paused': [],
'did-change-theme-color': ['themeColor'], 'found-in-page': ['result'],
'update-target-url': ['url'] 'did-change-theme-color': ['themeColor'],
}; 'update-target-url': ['url']
}
const DEPRECATED_EVENTS = { const DEPRECATED_EVENTS = {
'page-title-updated': 'page-title-set' 'page-title-updated': 'page-title-set'
}; }
const dispatchEvent = function (webView, eventName, eventKey, ...args) { const dispatchEvent = function (webView, eventName, eventKey, ...args) {
if (DEPRECATED_EVENTS[eventName] != null) { if (DEPRECATED_EVENTS[eventName] != null) {
dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args); dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args)
} }
const domEvent = new Event(eventName); const domEvent = new Event(eventName)
WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => { WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => {
domEvent[prop] = args[index]; domEvent[prop] = args[index]
}); })
webView.dispatchEvent(domEvent); webView.dispatchEvent(domEvent)
if (eventName === 'load-commit') { if (eventName === 'load-commit') {
webView.onLoadCommit(domEvent); webView.onLoadCommit(domEvent)
} } else if (eventName === 'focus-change') {
else if (eventName === 'focus-change') { webView.onFocusChange(domEvent)
webView.onFocusChange(); }
}
};
function registerEvents(webView, viewInstanceId) {
ipc_renderer_internal_1.ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () {
webView.guestInstanceId = undefined;
webView.reset();
const domEvent = new Event('destroyed');
webView.dispatchEvent(domEvent);
});
ipc_renderer_internal_1.ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) {
dispatchEvent(webView, eventName, eventName, ...args);
});
ipc_renderer_internal_1.ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) {
const domEvent = new Event('ipc-message');
domEvent.channel = channel;
domEvent.args = args;
webView.dispatchEvent(domEvent);
});
} }
exports.registerEvents = registerEvents;
function deregisterEvents(viewInstanceId) { module.exports = {
ipc_renderer_internal_1.ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`); registerEvents: function (webView, viewInstanceId) {
ipc_renderer_internal_1.ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`); ipcRenderer.on(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () {
ipc_renderer_internal_1.ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`); webView.guestInstanceId = undefined
} webView.reset()
exports.deregisterEvents = deregisterEvents; const domEvent = new Event('destroyed')
function createGuest(params) { webView.dispatchEvent(domEvent)
return ipc_renderer_internal_utils_1.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params); })
}
exports.createGuest = createGuest; ipcRenderer.on(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) {
function createGuestSync(params) { dispatchEvent(webView, eventName, eventName, ...args)
return ipc_renderer_internal_utils_1.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params); })
}
exports.createGuestSync = createGuestSync; ipcRenderer.on(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) {
function destroyGuest(guestInstanceId) { const domEvent = new Event('ipc-message')
ipc_renderer_internal_utils_1.invoke('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId); domEvent.channel = channel
} domEvent.args = args
exports.destroyGuest = destroyGuest; webView.dispatchEvent(domEvent)
function attachGuest(elementInstanceId, guestInstanceId, params, contentWindow) { })
const embedderFrameId = electron_1.webFrame.getWebFrameId(contentWindow); },
deregisterEvents: function (viewInstanceId) {
ipcRenderer.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`)
ipcRenderer.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`)
ipcRenderer.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`)
},
createGuest: function (params, callback) {
requestId++
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId)
ipcRenderer.once(`ELECTRON_RESPONSE_${requestId}`, callback)
},
createGuestSync: function (params) {
return ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', params)
},
destroyGuest: function (guestInstanceId) {
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId)
},
attachGuest: function (elementInstanceId, guestInstanceId, params, contentWindow) {
const embedderFrameId = webFrame.getWebFrameId(contentWindow)
if (embedderFrameId < 0) { // this error should not happen. if (embedderFrameId < 0) { // this error should not happen.
throw new Error('Invalid embedder frame'); throw new Error('Invalid embedder frame')
} }
ipc_renderer_internal_utils_1.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params); ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params)
}
} }
exports.attachGuest = attachGuest;
exports.guestViewInternalModule = {
deregisterEvents,
createGuest,
createGuestSync,
destroyGuest,
attachGuest
};
//# sourceMappingURL=guest-view-internal.js.map

View File

@ -1,251 +1,281 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils"); const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const web_view_impl_1 = require("@electron/internal/renderer/web-view/web-view-impl"); const { WebViewImpl } = require('@electron/internal/renderer/web-view/web-view')
const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
const errorUtils = require('@electron/internal/common/error-utils')
// Helper function to resolve url set in attribute. // Helper function to resolve url set in attribute.
const a = document.createElement('a'); const a = document.createElement('a')
const resolveURL = function (url) { const resolveURL = function (url) {
if (!url) if (url === '') return ''
return ''; a.href = url
a.href = url; return a.href
return a.href; }
};
// Attribute objects. // Attribute objects.
// Default implementation of a WebView attribute. // Default implementation of a WebView attribute.
class WebViewAttribute { class WebViewAttribute {
constructor(name, webViewImpl) { constructor (name, webViewImpl) {
this.name = name; this.name = name
this.webViewImpl = webViewImpl; this.value = webViewImpl.webviewNode[name] || ''
this.ignoreMutation = false; this.webViewImpl = webViewImpl
// Called when the attribute's value changes. this.ignoreMutation = false
this.handleMutation = () => undefined; this.defineProperty()
this.name = name; }
this.value = webViewImpl.webviewNode[name] || '';
this.webViewImpl = webViewImpl; // Retrieves and returns the attribute's value.
this.defineProperty(); getValue () {
} return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value
// Retrieves and returns the attribute's value. }
getValue() {
return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value; // Sets the attribute's value.
} setValue (value) {
// Sets the attribute's value. this.webViewImpl.webviewNode.setAttribute(this.name, value || '')
setValue(value) { }
this.webViewImpl.webviewNode.setAttribute(this.name, value || '');
} // Changes the attribute's value without triggering its mutation handler.
// Changes the attribute's value without triggering its mutation handler. setValueIgnoreMutation (value) {
setValueIgnoreMutation(value) { this.ignoreMutation = true
this.ignoreMutation = true; this.setValue(value)
this.setValue(value); this.ignoreMutation = false
this.ignoreMutation = false; }
}
// Defines this attribute as a property on the webview node. // Defines this attribute as a property on the webview node.
defineProperty() { defineProperty () {
return Object.defineProperty(this.webViewImpl.webviewNode, this.name, { return Object.defineProperty(this.webViewImpl.webviewNode, this.name, {
get: () => { get: () => {
return this.getValue(); return this.getValue()
}, },
set: (value) => { set: (value) => {
return this.setValue(value); return this.setValue(value)
}, },
enumerable: true enumerable: true
}); })
} }
// Called when the attribute's value changes.
handleMutation () {}
} }
// An attribute that is treated as a Boolean. // An attribute that is treated as a Boolean.
class BooleanAttribute extends WebViewAttribute { class BooleanAttribute extends WebViewAttribute {
getValue() { getValue () {
return this.webViewImpl.webviewNode.hasAttribute(this.name); return this.webViewImpl.webviewNode.hasAttribute(this.name)
} }
setValue(value) {
if (value) { setValue (value) {
this.webViewImpl.webviewNode.setAttribute(this.name, ''); if (value) {
} this.webViewImpl.webviewNode.setAttribute(this.name, '')
else { } else {
this.webViewImpl.webviewNode.removeAttribute(this.name); this.webViewImpl.webviewNode.removeAttribute(this.name)
}
} }
}
} }
// Attribute representing the state of the storage partition. // Attribute representing the state of the storage partition.
class PartitionAttribute extends WebViewAttribute { class PartitionAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("partition" /* ATTRIBUTE_PARTITION */, webViewImpl); super(webViewConstants.ATTRIBUTE_PARTITION, webViewImpl)
this.webViewImpl = webViewImpl; this.validPartitionId = true
this.validPartitionId = true; }
this.handleMutation = (oldValue, newValue) => {
newValue = newValue || ''; handleMutation (oldValue, newValue) {
// The partition cannot change if the webview has already navigated. newValue = newValue || ''
if (!this.webViewImpl.beforeFirstNavigation) {
console.error("The object has already navigated, so its partition cannot be changed." /* ERROR_MSG_ALREADY_NAVIGATED */); // The partition cannot change if the webview has already navigated.
this.setValueIgnoreMutation(oldValue); if (!this.webViewImpl.beforeFirstNavigation) {
return; console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED)
} this.setValueIgnoreMutation(oldValue)
if (newValue === 'persist:') { return
this.validPartitionId = false;
console.error("Invalid partition attribute." /* ERROR_MSG_INVALID_PARTITION_ATTRIBUTE */);
}
};
} }
if (newValue === 'persist:') {
this.validPartitionId = false
console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE)
}
}
} }
// Attribute that handles the location and navigation of the webview. // Attribute that handles the location and navigation of the webview.
class SrcAttribute extends WebViewAttribute { class SrcAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("src" /* ATTRIBUTE_SRC */, webViewImpl); super(webViewConstants.ATTRIBUTE_SRC, webViewImpl)
this.webViewImpl = webViewImpl; this.setupMutationObserver()
this.handleMutation = (oldValue, newValue) => { }
// Once we have navigated, we don't allow clearing the src attribute.
// Once <webview> enters a navigated state, it cannot return to a getValue () {
// placeholder state. if (this.webViewImpl.webviewNode.hasAttribute(this.name)) {
if (!newValue && oldValue) { return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name))
// src attribute changes normally initiate a navigation. We suppress } else {
// the next src attribute handler call to avoid reloading the page return this.value
// on every guest-initiated navigation.
this.setValueIgnoreMutation(oldValue);
return;
}
this.parse();
};
this.setupMutationObserver();
} }
getValue() { }
if (this.webViewImpl.webviewNode.hasAttribute(this.name)) {
return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)); setValueIgnoreMutation (value) {
} super.setValueIgnoreMutation(value)
else {
return this.value; // takeRecords() is needed to clear queued up src mutations. Without it, it
} // is possible for this change to get picked up asyncronously by src's
// mutation observer |observer|, and then get handled even though we do not
// want to handle this mutation.
this.observer.takeRecords()
}
handleMutation (oldValue, newValue) {
// Once we have navigated, we don't allow clearing the src attribute.
// Once <webview> enters a navigated state, it cannot return to a
// placeholder state.
if (!newValue && oldValue) {
// src attribute changes normally initiate a navigation. We suppress
// the next src attribute handler call to avoid reloading the page
// on every guest-initiated navigation.
this.setValueIgnoreMutation(oldValue)
return
} }
setValueIgnoreMutation(value) { this.parse()
super.setValueIgnoreMutation(value); }
// takeRecords() is needed to clear queued up src mutations. Without it, it
// is possible for this change to get picked up asyncronously by src's // The purpose of this mutation observer is to catch assignment to the src
// mutation observer |observer|, and then get handled even though we do not // attribute without any changes to its value. This is useful in the case
// want to handle this mutation. // where the webview guest has crashed and navigating to the same address
this.observer.takeRecords(); // spawns off a new process.
setupMutationObserver () {
this.observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
const { oldValue } = mutation
const newValue = this.getValue()
if (oldValue !== newValue) {
return
}
this.handleMutation(oldValue, newValue)
}
})
const params = {
attributes: true,
attributeOldValue: true,
attributeFilter: [this.name]
} }
// The purpose of this mutation observer is to catch assignment to the src this.observer.observe(this.webViewImpl.webviewNode, params)
// attribute without any changes to its value. This is useful in the case }
// where the webview guest has crashed and navigating to the same address
// spawns off a new process. parse () {
setupMutationObserver() { if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) {
this.observer = new MutationObserver((mutations) => { return
for (const mutation of mutations) {
const { oldValue } = mutation;
const newValue = this.getValue();
if (oldValue !== newValue) {
return;
}
this.handleMutation(oldValue, newValue);
}
});
const params = {
attributes: true,
attributeOldValue: true,
attributeFilter: [this.name]
};
this.observer.observe(this.webViewImpl.webviewNode, params);
} }
parse() { if (this.webViewImpl.guestInstanceId == null) {
if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes["partition" /* ATTRIBUTE_PARTITION */].validPartitionId || !this.getValue()) { if (this.webViewImpl.beforeFirstNavigation) {
return; this.webViewImpl.beforeFirstNavigation = false
} this.webViewImpl.createGuest()
if (this.webViewImpl.guestInstanceId == null) { }
if (this.webViewImpl.beforeFirstNavigation) { return
this.webViewImpl.beforeFirstNavigation = false;
this.webViewImpl.createGuest();
}
return;
}
// Navigate to |this.src|.
const opts = {};
const httpreferrer = this.webViewImpl.attributes["httpreferrer" /* ATTRIBUTE_HTTPREFERRER */].getValue();
if (httpreferrer) {
opts.httpReferrer = httpreferrer;
}
const useragent = this.webViewImpl.attributes["useragent" /* ATTRIBUTE_USERAGENT */].getValue();
if (useragent) {
opts.userAgent = useragent;
}
const guestInstanceId = this.webViewImpl.guestInstanceId;
const method = 'loadURL';
const args = [this.getValue(), opts];
ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args);
} }
// Navigate to |this.src|.
const opts = {}
const httpreferrer = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
if (httpreferrer) {
opts.httpReferrer = httpreferrer
}
const useragent = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_USERAGENT].getValue()
if (useragent) {
opts.userAgent = useragent
}
const guestInstanceId = this.webViewImpl.guestInstanceId
const method = 'loadURL'
const args = [this.getValue(), opts]
const [error] = ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', guestInstanceId, method, args)
if (error) {
throw errorUtils.deserialize(error)
}
}
} }
// Attribute specifies HTTP referrer. // Attribute specifies HTTP referrer.
class HttpReferrerAttribute extends WebViewAttribute { class HttpReferrerAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("httpreferrer" /* ATTRIBUTE_HTTPREFERRER */, webViewImpl); super(webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl)
} }
} }
// Attribute specifies user agent // Attribute specifies user agent
class UserAgentAttribute extends WebViewAttribute { class UserAgentAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("useragent" /* ATTRIBUTE_USERAGENT */, webViewImpl); super(webViewConstants.ATTRIBUTE_USERAGENT, webViewImpl)
} }
} }
// Attribute that set preload script. // Attribute that set preload script.
class PreloadAttribute extends WebViewAttribute { class PreloadAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("preload" /* ATTRIBUTE_PRELOAD */, webViewImpl); super(webViewConstants.ATTRIBUTE_PRELOAD, webViewImpl)
}
getValue () {
if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) {
return this.value
} }
getValue() { let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name))
if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) { const protocol = preload.substr(0, 5)
return this.value; if (protocol !== 'file:') {
} console.error(webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE)
let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)); preload = ''
const protocol = preload.substr(0, 5);
if (protocol !== 'file:') {
console.error("Only \"file:\" protocol is supported in \"preload\" attribute." /* ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE */);
preload = '';
}
return preload;
} }
return preload
}
} }
// Attribute that specifies the blink features to be enabled. // Attribute that specifies the blink features to be enabled.
class BlinkFeaturesAttribute extends WebViewAttribute { class BlinkFeaturesAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("blinkfeatures" /* ATTRIBUTE_BLINKFEATURES */, webViewImpl); super(webViewConstants.ATTRIBUTE_BLINKFEATURES, webViewImpl)
} }
} }
// Attribute that specifies the blink features to be disabled. // Attribute that specifies the blink features to be disabled.
class DisableBlinkFeaturesAttribute extends WebViewAttribute { class DisableBlinkFeaturesAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("disableblinkfeatures" /* ATTRIBUTE_DISABLEBLINKFEATURES */, webViewImpl); super(webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl)
} }
} }
// Attribute that specifies the web preferences to be enabled. // Attribute that specifies the web preferences to be enabled.
class WebPreferencesAttribute extends WebViewAttribute { class WebPreferencesAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("webpreferences" /* ATTRIBUTE_WEBPREFERENCES */, webViewImpl); super(webViewConstants.ATTRIBUTE_WEBPREFERENCES, webViewImpl)
} }
} }
class EnableRemoteModuleAttribute extends WebViewAttribute { class EnableRemoteModuleAttribute extends WebViewAttribute {
constructor(webViewImpl) { constructor (webViewImpl) {
super("enableremotemodule" /* ATTRIBUTE_ENABLEREMOTEMODULE */, webViewImpl); super(webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl)
} }
getValue() {
return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false'; getValue () {
} return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false'
setValue(value) { }
this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false');
} setValue (value) {
this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false')
}
} }
// Sets up all of the webview attributes. // Sets up all of the webview attributes.
web_view_impl_1.WebViewImpl.prototype.setupWebViewAttributes = function () { WebViewImpl.prototype.setupWebViewAttributes = function () {
this.attributes = {}; this.attributes = {}
this.attributes["partition" /* ATTRIBUTE_PARTITION */] = new PartitionAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this)
this.attributes["src" /* ATTRIBUTE_SRC */] = new SrcAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this)
this.attributes["httpreferrer" /* ATTRIBUTE_HTTPREFERRER */] = new HttpReferrerAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
this.attributes["useragent" /* ATTRIBUTE_USERAGENT */] = new UserAgentAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this)
this.attributes["nodeintegration" /* ATTRIBUTE_NODEINTEGRATION */] = new BooleanAttribute("nodeintegration" /* ATTRIBUTE_NODEINTEGRATION */, this); this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this)
this.attributes["nodeintegrationinsubframes" /* ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES */] = new BooleanAttribute("nodeintegrationinsubframes" /* ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES */, this); this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this)
this.attributes["plugins" /* ATTRIBUTE_PLUGINS */] = new BooleanAttribute("plugins" /* ATTRIBUTE_PLUGINS */, this); this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this)
this.attributes["disablewebsecurity" /* ATTRIBUTE_DISABLEWEBSECURITY */] = new BooleanAttribute("disablewebsecurity" /* ATTRIBUTE_DISABLEWEBSECURITY */, this); this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
this.attributes["allowpopups" /* ATTRIBUTE_ALLOWPOPUPS */] = new BooleanAttribute("allowpopups" /* ATTRIBUTE_ALLOWPOPUPS */, this); this.attributes[webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this)
this.attributes["enableremotemodule" /* ATTRIBUTE_ENABLEREMOTEMODULE */] = new EnableRemoteModuleAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
this.attributes["preload" /* ATTRIBUTE_PRELOAD */] = new PreloadAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this)
this.attributes["blinkfeatures" /* ATTRIBUTE_BLINKFEATURES */] = new BlinkFeaturesAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this)
this.attributes["disableblinkfeatures" /* ATTRIBUTE_DISABLEBLINKFEATURES */] = new DisableBlinkFeaturesAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this)
this.attributes["webpreferences" /* ATTRIBUTE_WEBPREFERENCES */] = new WebPreferencesAttribute(this); }
};
//# sourceMappingURL=web-view-attributes.js.map

View File

@ -1,3 +1,28 @@
"use strict"; 'use strict'
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=web-view-constants.js.map module.exports = {
// Attributes.
ATTRIBUTE_NAME: 'name',
ATTRIBUTE_PARTITION: 'partition',
ATTRIBUTE_SRC: 'src',
ATTRIBUTE_HTTPREFERRER: 'httpreferrer',
ATTRIBUTE_NODEINTEGRATION: 'nodeintegration',
ATTRIBUTE_ENABLEREMOTEMODULE: 'enableremotemodule',
ATTRIBUTE_PLUGINS: 'plugins',
ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity',
ATTRIBUTE_ALLOWPOPUPS: 'allowpopups',
ATTRIBUTE_PRELOAD: 'preload',
ATTRIBUTE_USERAGENT: 'useragent',
ATTRIBUTE_BLINKFEATURES: 'blinkfeatures',
ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures',
ATTRIBUTE_WEBPREFERENCES: 'webpreferences',
// Internal attribute.
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',
// Error messages.
ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.',
ERROR_MSG_CANNOT_INJECT_SCRIPT: '<webview>: ' + 'Script cannot be injected into content until the page has loaded.',
ERROR_MSG_INVALID_PARTITION_ATTRIBUTE: 'Invalid partition attribute.',
ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE: 'Only "file:" protocol is supported in "preload" attribute.'
}

View File

@ -1,102 +0,0 @@
"use strict";
// When using context isolation, the WebViewElement and the custom element
// methods have to be defined in the main world to be able to be registered.
//
// Note: The hidden values can only be read/set inside the same context, all
// methods that access the "internal" hidden value must be put in this file.
//
// Note: This file could be loaded in the main world of contextIsolation page,
// which runs in browserify environment instead of Node environment, all native
// modules must be passed from outside, all included files must be plain JS.
Object.defineProperty(exports, "__esModule", { value: true });
// 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 [
"partition" /* ATTRIBUTE_PARTITION */,
"src" /* ATTRIBUTE_SRC */,
"httpreferrer" /* ATTRIBUTE_HTTPREFERRER */,
"useragent" /* ATTRIBUTE_USERAGENT */,
"nodeintegration" /* ATTRIBUTE_NODEINTEGRATION */,
"nodeintegrationinsubframes" /* ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES */,
"plugins" /* ATTRIBUTE_PLUGINS */,
"disablewebsecurity" /* ATTRIBUTE_DISABLEWEBSECURITY */,
"allowpopups" /* ATTRIBUTE_ALLOWPOPUPS */,
"enableremotemodule" /* ATTRIBUTE_ENABLEREMOTEMODULE */,
"preload" /* ATTRIBUTE_PRELOAD */,
"blinkfeatures" /* ATTRIBUTE_BLINKFEATURES */,
"disableblinkfeatures" /* ATTRIBUTE_DISABLEBLINKFEATURES */,
"webpreferences" /* 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["src" /* 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) => {
// I wish eslint wasn't so stupid, but it is
// eslint-disable-next-line
const WebViewElement = defineWebViewElement(v8Util, webViewImpl);
webViewImpl.setupMethods(WebViewElement);
// The customElements.define has to be called in a special scope.
const webFrame = webViewImpl.webFrame;
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.
// TypeScript is concerned that we're deleting a read-only attribute
delete WebViewElement.observedAttributes;
});
};
// Prepare to register the <webview> element.
exports.setupWebView = (v8Util, webViewImpl) => {
const useCapture = true;
const listener = (event) => {
if (document.readyState === 'loading') {
return;
}
webViewImpl.setupAttributes();
registerWebViewElement(v8Util, webViewImpl);
window.removeEventListener(event.type, listener, useCapture);
};
window.addEventListener('readystatechange', listener, useCapture);
};
//# sourceMappingURL=web-view-element.js.map

View File

@ -1,233 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron");
const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils");
const guestViewInternal = require("@electron/internal/renderer/web-view/guest-view-internal");
const web_view_methods_1 = require("@electron/internal/common/web-view-methods");
const v8Util = process.electronBinding('v8_util');
// ID generator.
let nextId = 0;
const getNextId = function () {
return ++nextId;
};
// Represents the internal state of the WebView node.
class WebViewImpl {
constructor(webviewNode) {
this.webviewNode = webviewNode;
this.beforeFirstNavigation = true;
this.elementAttached = false;
this.hasFocus = false;
// on* Event handlers.
this.on = {};
// Replaced in web-view-attributes
this.attributes = {};
// Create internal iframe element.
this.internalElement = this.createInternalElement();
const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = '<!DOCTYPE html><style type="text/css">:host { display: flex; }</style>';
this.setupWebViewAttributes();
this.viewInstanceId = getNextId();
shadowRoot.appendChild(this.internalElement);
// Provide access to contentWindow.
Object.defineProperty(this.webviewNode, 'contentWindow', {
get: () => {
return this.internalElement.contentWindow;
},
enumerable: true
});
}
setupWebViewAttributes() { }
createInternalElement() {
const iframeElement = document.createElement('iframe');
iframeElement.style.flex = '1 1 auto';
iframeElement.style.width = '100%';
iframeElement.style.border = '0';
v8Util.setHiddenValue(iframeElement, 'internal', this);
return iframeElement;
}
// Resets some state upon reattaching <webview> element to the DOM.
reset() {
// If guestInstanceId is defined then the <webview> has navigated and has
// already picked up a partition ID. Thus, we need to reset the initialization
// state. However, it may be the case that beforeFirstNavigation is false BUT
// guestInstanceId has yet to be initialized. This means that we have not
// heard back from createGuest yet. We will not reset the flag in this case so
// that we don't end up allocating a second guest.
if (this.guestInstanceId) {
guestViewInternal.destroyGuest(this.guestInstanceId);
this.guestInstanceId = void 0;
}
this.beforeFirstNavigation = true;
this.attributes["partition" /* ATTRIBUTE_PARTITION */].validPartitionId = true;
// Since attachment swaps a local frame for a remote frame, we need our
// internal iframe element to be local again before we can reattach.
const newFrame = this.createInternalElement();
const oldFrame = this.internalElement;
this.internalElement = newFrame;
if (oldFrame && oldFrame.parentNode) {
oldFrame.parentNode.replaceChild(newFrame, oldFrame);
}
}
// This observer monitors mutations to attributes of the <webview> and
// updates the BrowserPlugin properties accordingly. In turn, updating
// a BrowserPlugin property will update the corresponding BrowserPlugin
// attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
// details.
handleWebviewAttributeMutation(attributeName, oldValue, newValue) {
if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) {
return;
}
// Let the changed attribute handle its own mutation
this.attributes[attributeName].handleMutation(oldValue, newValue);
}
onElementResize() {
const resizeEvent = new Event('resize');
resizeEvent.newWidth = this.webviewNode.clientWidth;
resizeEvent.newHeight = this.webviewNode.clientHeight;
this.dispatchEvent(resizeEvent);
}
createGuest() {
guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => {
this.attachGuestInstance(guestInstanceId);
});
}
createGuestSync() {
this.beforeFirstNavigation = false;
this.attachGuestInstance(guestViewInternal.createGuestSync(this.buildParams()));
}
dispatchEvent(webViewEvent) {
this.webviewNode.dispatchEvent(webViewEvent);
}
// Adds an 'on<event>' property on the webview, which can be used to set/unset
// an event handler.
setupEventProperty(eventName) {
const propertyName = `on${eventName.toLowerCase()}`;
return Object.defineProperty(this.webviewNode, propertyName, {
get: () => {
return this.on[propertyName];
},
set: (value) => {
if (this.on[propertyName]) {
this.webviewNode.removeEventListener(eventName, this.on[propertyName]);
}
this.on[propertyName] = value;
if (value) {
return this.webviewNode.addEventListener(eventName, value);
}
},
enumerable: true
});
}
// Updates state upon loadcommit.
onLoadCommit(webViewEvent) {
const oldValue = this.webviewNode.getAttribute("src" /* ATTRIBUTE_SRC */);
const newValue = webViewEvent.url;
if (webViewEvent.isMainFrame && (oldValue !== newValue)) {
// Touching the src attribute triggers a navigation. To avoid
// triggering a page reload on every guest-initiated navigation,
// we do not handle this mutation.
this.attributes["src" /* ATTRIBUTE_SRC */].setValueIgnoreMutation(newValue);
}
}
// Emits focus/blur events.
onFocusChange() {
const hasFocus = document.activeElement === this.webviewNode;
if (hasFocus !== this.hasFocus) {
this.hasFocus = hasFocus;
this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur'));
}
}
onAttach(storagePartitionId) {
return this.attributes["partition" /* ATTRIBUTE_PARTITION */].setValue(storagePartitionId);
}
buildParams() {
const params = {
instanceId: this.viewInstanceId,
userAgentOverride: this.userAgentOverride
};
for (const attributeName in this.attributes) {
if (this.attributes.hasOwnProperty(attributeName)) {
params[attributeName] = this.attributes[attributeName].getValue();
}
}
return params;
}
attachGuestInstance(guestInstanceId) {
if (!this.elementAttached) {
// The element could be detached before we got response from browser.
return;
}
this.internalInstanceId = getNextId();
this.guestInstanceId = guestInstanceId;
guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams(), this.internalElement.contentWindow);
// ResizeObserver is a browser global not recognized by "standard".
/* globals ResizeObserver */
// TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not
// even documented.
this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this));
this.resizeObserver.observe(this.internalElement);
}
}
exports.WebViewImpl = WebViewImpl;
exports.setupAttributes = () => {
require('@electron/internal/renderer/web-view/web-view-attributes');
};
// I wish eslint wasn't so stupid, but it is
// eslint-disable-next-line
exports.setupMethods = (WebViewElement) => {
WebViewElement.prototype.getWebContentsId = function () {
const internal = v8Util.getHiddenValue(this, 'internal');
if (!internal.guestInstanceId) {
throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.');
}
return internal.guestInstanceId;
};
// WebContents associated with this webview.
WebViewElement.prototype.getWebContents = function () {
if (!electron_1.remote) {
throw new Error('getGuestWebContents requires remote, which is not enabled');
}
const internal = v8Util.getHiddenValue(this, 'internal');
if (!internal.guestInstanceId) {
internal.createGuestSync();
}
return electron_1.remote.getGuestWebContents(internal.guestInstanceId);
};
// Focusing the webview should move page focus to the underlying iframe.
WebViewElement.prototype.focus = function () {
this.contentWindow.focus();
};
// Forward proto.foo* method calls to WebViewImpl.foo*.
const createBlockHandler = function (method) {
return function (...args) {
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args);
};
};
for (const method of web_view_methods_1.syncMethods) {
WebViewElement.prototype[method] = createBlockHandler(method);
}
const createNonBlockHandler = function (method) {
return function (...args) {
ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args);
};
};
for (const method of web_view_methods_1.asyncCallbackMethods) {
WebViewElement.prototype[method] = createNonBlockHandler(method);
}
const createPromiseHandler = function (method) {
return function (...args) {
return ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args);
};
};
for (const method of web_view_methods_1.asyncPromiseMethods) {
WebViewElement.prototype[method] = electron_1.deprecate.promisify(createPromiseHandler(method));
}
};
exports.webViewImplModule = {
setupAttributes: exports.setupAttributes,
setupMethods: exports.setupMethods,
guestViewInternal,
webFrame: electron_1.webFrame,
WebViewImpl
};
//# sourceMappingURL=web-view-impl.js.map

View File

@ -1,33 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-renderer-internal");
const v8Util = process.electronBinding('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', () => {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, guestInstanceId);
});
window.addEventListener('blur', () => {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, guestInstanceId);
});
}
function webViewInit(contextIsolation, webviewTag, guestInstanceId) {
// Don't allow recursive `<webview>`.
if (webviewTag && guestInstanceId == null) {
const { webViewImplModule } = require('@electron/internal/renderer/web-view/web-view-impl');
if (contextIsolation) {
v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule);
}
else {
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element');
setupWebView(v8Util, webViewImplModule);
}
}
if (guestInstanceId) {
// Report focus/blur events of webview to browser.
handleFocusBlur(guestInstanceId);
}
}
exports.webViewInit = webViewInit;
//# sourceMappingURL=web-view-init.js.map

View File

@ -0,0 +1,346 @@
'use strict'
const { webFrame } = require('electron')
const v8Util = process.atomBinding('v8_util')
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal')
const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
const errorUtils = require('@electron/internal/common/error-utils')
const {
syncMethods,
asyncCallbackMethods,
asyncPromiseMethods
} = require('@electron/internal/common/web-view-methods')
// ID generator.
let nextId = 0
const getNextId = function () {
return ++nextId
}
// Represents the internal state of the WebView node.
class WebViewImpl {
constructor (webviewNode) {
this.webviewNode = webviewNode
v8Util.setHiddenValue(this.webviewNode, 'internal', this)
this.elementAttached = false
this.beforeFirstNavigation = true
this.hasFocus = false
// on* Event handlers.
this.on = {}
// Create internal iframe element.
this.internalElement = this.createInternalElement()
const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' })
shadowRoot.innerHTML = '<!DOCTYPE html><style type="text/css">:host { display: flex; }</style>'
this.setupWebViewAttributes()
this.viewInstanceId = getNextId()
shadowRoot.appendChild(this.internalElement)
// Provide access to contentWindow.
Object.defineProperty(this.webviewNode, 'contentWindow', {
get: () => {
return this.internalElement.contentWindow
},
enumerable: true
})
}
createInternalElement () {
const iframeElement = document.createElement('iframe')
iframeElement.style.flex = '1 1 auto'
iframeElement.style.width = '100%'
iframeElement.style.border = '0'
v8Util.setHiddenValue(iframeElement, 'internal', this)
return iframeElement
}
// Resets some state upon reattaching <webview> element to the DOM.
reset () {
// If guestInstanceId is defined then the <webview> has navigated and has
// already picked up a partition ID. Thus, we need to reset the initialization
// state. However, it may be the case that beforeFirstNavigation is false BUT
// guestInstanceId has yet to be initialized. This means that we have not
// heard back from createGuest yet. We will not reset the flag in this case so
// that we don't end up allocating a second guest.
if (this.guestInstanceId) {
guestViewInternal.destroyGuest(this.guestInstanceId)
this.guestInstanceId = void 0
}
this.beforeFirstNavigation = true
this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true
// Since attachment swaps a local frame for a remote frame, we need our
// internal iframe element to be local again before we can reattach.
const newFrame = this.createInternalElement()
const oldFrame = this.internalElement
this.internalElement = newFrame
oldFrame.parentNode.replaceChild(newFrame, oldFrame)
}
// Sets the <webview>.request property.
setRequestPropertyOnWebViewNode (request) {
Object.defineProperty(this.webviewNode, 'request', {
value: request,
enumerable: true
})
}
// This observer monitors mutations to attributes of the <webview> and
// updates the BrowserPlugin properties accordingly. In turn, updating
// a BrowserPlugin property will update the corresponding BrowserPlugin
// attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
// details.
handleWebviewAttributeMutation (attributeName, oldValue, newValue) {
if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) {
return
}
// Let the changed attribute handle its own mutation
this.attributes[attributeName].handleMutation(oldValue, newValue)
}
onElementResize () {
const resizeEvent = new Event('resize')
resizeEvent.newWidth = this.webviewNode.clientWidth
resizeEvent.newHeight = this.webviewNode.clientHeight
this.dispatchEvent(resizeEvent)
}
createGuest () {
return guestViewInternal.createGuest(this.buildParams(), (event, guestInstanceId) => {
this.attachGuestInstance(guestInstanceId)
})
}
createGuestSync () {
this.beforeFirstNavigation = false
this.attachGuestInstance(guestViewInternal.createGuestSync(this.buildParams()))
}
dispatchEvent (webViewEvent) {
this.webviewNode.dispatchEvent(webViewEvent)
}
// Adds an 'on<event>' property on the webview, which can be used to set/unset
// an event handler.
setupEventProperty (eventName) {
const propertyName = `on${eventName.toLowerCase()}`
return Object.defineProperty(this.webviewNode, propertyName, {
get: () => {
return this.on[propertyName]
},
set: (value) => {
if (this.on[propertyName]) {
this.webviewNode.removeEventListener(eventName, this.on[propertyName])
}
this.on[propertyName] = value
if (value) {
return this.webviewNode.addEventListener(eventName, value)
}
},
enumerable: true
})
}
// Updates state upon loadcommit.
onLoadCommit (webViewEvent) {
const oldValue = this.webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC)
const newValue = webViewEvent.url
if (webViewEvent.isMainFrame && (oldValue !== newValue)) {
// Touching the src attribute triggers a navigation. To avoid
// triggering a page reload on every guest-initiated navigation,
// we do not handle this mutation.
this.attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue)
}
}
// Emits focus/blur events.
onFocusChange () {
const hasFocus = document.activeElement === this.webviewNode
if (hasFocus !== this.hasFocus) {
this.hasFocus = hasFocus
this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur'))
}
}
onAttach (storagePartitionId) {
return this.attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue(storagePartitionId)
}
buildParams () {
const params = {
instanceId: this.viewInstanceId,
userAgentOverride: this.userAgentOverride
}
for (const attributeName in this.attributes) {
if (this.attributes.hasOwnProperty(attributeName)) {
params[attributeName] = this.attributes[attributeName].getValue()
}
}
return params
}
attachGuestInstance (guestInstanceId) {
if (!this.elementAttached) {
// The element could be detached before we got response from browser.
return
}
this.internalInstanceId = getNextId()
this.guestInstanceId = guestInstanceId
guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams(), this.internalElement.contentWindow)
// ResizeObserver is a browser global not recognized by "standard".
/* globals ResizeObserver */
// TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not
// even documented.
this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this)).observe(this.internalElement)
}
}
// Registers <webview> custom element.
const registerWebViewElement = (window) => {
const proto = Object.create(window.HTMLObjectElement.prototype)
proto.createdCallback = function () {
return new WebViewImpl(this)
}
proto.attributeChangedCallback = function (name, oldValue, newValue) {
const internal = v8Util.getHiddenValue(this, 'internal')
if (internal) {
internal.handleWebviewAttributeMutation(name, oldValue, newValue)
}
}
proto.detachedCallback = function () {
const internal = v8Util.getHiddenValue(this, 'internal')
if (!internal) {
return
}
guestViewInternal.deregisterEvents(internal.viewInstanceId)
internal.elementAttached = false
this.internalInstanceId = 0
internal.reset()
}
proto.attachedCallback = function () {
const internal = v8Util.getHiddenValue(this, 'internal')
if (!internal) {
return
}
if (!internal.elementAttached) {
guestViewInternal.registerEvents(internal, internal.viewInstanceId)
internal.elementAttached = true
internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse()
}
}
const getGuestInstanceId = function (self) {
const internal = v8Util.getHiddenValue(self, 'internal')
if (!internal.guestInstanceId) {
throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.')
}
return internal.guestInstanceId
}
// Forward proto.foo* method calls to WebViewImpl.foo*.
const createBlockHandler = function (method) {
return function (...args) {
const [error, result] = ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', getGuestInstanceId(this), method, args)
if (error) {
throw errorUtils.deserialize(error)
} else {
return result
}
}
}
for (const method of syncMethods) {
proto[method] = createBlockHandler(method)
}
const createNonBlockHandler = function (method) {
return function (...args) {
const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
const requestId = getNextId()
ipcRenderer.once(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, function (event, error, result) {
if (error == null) {
if (callback) callback(result)
} else {
throw errorUtils.deserialize(error)
}
})
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
}
}
for (const method of asyncCallbackMethods) {
proto[method] = createNonBlockHandler(method)
}
const createPromiseHandler = function (method) {
return function (...args) {
return new Promise((resolve, reject) => {
const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
const requestId = getNextId()
ipcRenderer.once(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, function (event, error, result) {
if (error == null) {
if (callback) {
callback(result)
}
resolve(result)
} else {
reject(errorUtils.deserialize(error))
}
})
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
})
}
}
for (const method of asyncPromiseMethods) {
proto[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 }

View File

@ -1,13 +1,12 @@
"use strict"; 'use strict'
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; // This file should have no requires since it is used by the isolated context
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); // preload bundle. Instead arguments should be passed in for everything it
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; // needs.
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-renderer-internal");
// This file implements the following APIs: // This file implements the following APIs:
// - window.alert()
// - window.confirm()
// - window.history.back() // - window.history.back()
// - window.history.forward() // - window.history.forward()
// - window.history.go() // - window.history.go()
@ -23,225 +22,171 @@ const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-rendere
// - window.prompt() // - window.prompt()
// - document.hidden // - document.hidden
// - document.visibilityState // - document.visibilityState
const { defineProperty } = Object;
const { defineProperty } = Object
// Helper function to resolve relative url. // Helper function to resolve relative url.
const a = window.document.createElement('a'); const a = window.top.document.createElement('a')
const resolveURL = function (url) { const resolveURL = function (url) {
a.href = url; a.href = url
return a.href; return a.href
}; }
// Use this method to ensure values expected as strings in the main process // Use this method to ensure values expected as strings in the main process
// are convertible to strings in the renderer process. This ensures exceptions // are convertible to strings in the renderer process. This ensures exceptions
// converting values to strings are thrown in this process. // converting values to strings are thrown in this process.
const toString = (value) => { const toString = (value) => {
return value != null ? `${value}` : value; return value != null ? `${value}` : value
}; }
const windowProxies = {};
const getOrCreateProxy = (guestId) => { const windowProxies = {}
let proxy = windowProxies[guestId];
if (proxy == null) { const getOrCreateProxy = (ipcRenderer, guestId) => {
proxy = new BrowserWindowProxy(guestId); let proxy = windowProxies[guestId]
windowProxies[guestId] = proxy; if (proxy == null) {
} proxy = new BrowserWindowProxy(ipcRenderer, guestId)
return proxy; windowProxies[guestId] = proxy
}; }
return proxy
}
const removeProxy = (guestId) => { const removeProxy = (guestId) => {
delete windowProxies[guestId]; delete windowProxies[guestId]
};
class LocationProxy {
constructor(guestId) {
// eslint will consider the constructor "useless"
// unless we assign them in the body. It's fine, that's what
// TS would do anyway.
this.guestId = guestId;
this.getGuestURL = this.getGuestURL.bind(this);
}
/**
* Beware: This decorator will have the _prototype_ as the `target`. It defines properties
* commonly found in URL on the LocationProxy.
*/
static ProxyProperty(target, propertyKey) {
Object.defineProperty(target, propertyKey, {
get: function () {
const guestURL = this.getGuestURL();
const value = guestURL ? guestURL[propertyKey] : '';
return value === undefined ? '' : value;
},
set: function (newVal) {
const guestURL = this.getGuestURL();
if (guestURL) {
// TypeScript doesn't want us to assign to read-only variables.
// It's right, that's bad, but we're doing it anway.
guestURL[propertyKey] = newVal;
return this.ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'loadURL', guestURL.toString());
}
}
});
}
toString() {
return this.href;
}
getGuestURL() {
const urlString = ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'getURL');
try {
return new URL(urlString);
}
catch (e) {
console.error('LocationProxy: failed to parse string', urlString, e);
}
return null;
}
} }
__decorate([
LocationProxy.ProxyProperty function BrowserWindowProxy (ipcRenderer, guestId) {
], LocationProxy.prototype, "hash", void 0); this.closed = false
__decorate([
LocationProxy.ProxyProperty defineProperty(this, 'location', {
], LocationProxy.prototype, "href", void 0); get: function () {
__decorate([ return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'getURL')
LocationProxy.ProxyProperty },
], LocationProxy.prototype, "host", void 0); set: function (url) {
__decorate([ url = resolveURL(url)
LocationProxy.ProxyProperty return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'loadURL', url)
], LocationProxy.prototype, "hostname", void 0);
__decorate([
LocationProxy.ProxyProperty
], LocationProxy.prototype, "origin", void 0);
__decorate([
LocationProxy.ProxyProperty
], LocationProxy.prototype, "pathname", void 0);
__decorate([
LocationProxy.ProxyProperty
], LocationProxy.prototype, "port", void 0);
__decorate([
LocationProxy.ProxyProperty
], LocationProxy.prototype, "protocol", void 0);
__decorate([
LocationProxy.ProxyProperty
], LocationProxy.prototype, "search", void 0);
class BrowserWindowProxy {
constructor(guestId) {
this.closed = false;
this.guestId = guestId;
this._location = new LocationProxy(guestId);
ipc_renderer_internal_1.ipcRendererInternal.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => {
removeProxy(guestId);
this.closed = true;
});
}
// TypeScript doesn't allow getters/accessors with different types,
// so for now, we'll have to make do with an "any" in the mix.
// https://github.com/Microsoft/TypeScript/issues/2521
get location() {
return this._location;
}
set location(url) {
url = resolveURL(url);
ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'loadURL', url);
}
close() {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', this.guestId);
}
focus() {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'focus');
}
blur() {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'blur');
}
print() {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'print');
}
postMessage(message, targetOrigin) {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin);
}
eval(...args) {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'executeJavaScript', ...args);
} }
})
ipcRenderer.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => {
removeProxy(guestId)
this.closed = true
})
this.close = () => {
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', guestId)
}
this.focus = () => {
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', guestId, 'focus')
}
this.blur = () => {
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', guestId, 'blur')
}
this.print = () => {
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', guestId, 'print')
}
this.postMessage = (message, targetOrigin) => {
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', guestId, message, toString(targetOrigin), window.location.origin)
}
this.eval = (...args) => {
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', guestId, 'executeJavaScript', ...args)
}
} }
exports.windowSetup = (guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen) => {
if (guestInstanceId == null) { module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen) => {
// Override default window.close. if (guestInstanceId == null) {
window.close = function () { // Override default window.close.
ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE'); window.close = function () {
}; ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE')
} }
if (!usesNativeWindowOpen) { }
// Make the browser window or guest view emit "new-window" event.
window.open = function (url, frameName, features) { if (!usesNativeWindowOpen) {
if (url != null && url !== '') { // Make the browser window or guest view emit "new-window" event.
url = resolveURL(url); window.open = function (url, frameName, features) {
} if (url != null && url !== '') {
const guestId = ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features)); url = resolveURL(url)
if (guestId != null) { }
return getOrCreateProxy(guestId); const guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features))
} if (guestId != null) {
else { return getOrCreateProxy(ipcRenderer, guestId)
return null; } else {
} return null
}; }
if (openerId != null) {
window.opener = getOrCreateProxy(openerId);
}
} }
// But we do not support prompt().
window.prompt = function () { if (openerId != null) {
throw new Error('prompt() is and will not be supported.'); window.opener = getOrCreateProxy(ipcRenderer, openerId)
};
ipc_renderer_internal_1.ipcRendererInternal.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (_event, sourceId, message, sourceOrigin) {
// Manually dispatch event instead of using postMessage because we also need to
// set event.source.
//
// Why any? We can't construct a MessageEvent and we can't
// use `as MessageEvent` because you're not supposed to override
// data, origin, and source
const event = document.createEvent('Event');
event.initEvent('message', false, false);
event.data = message;
event.origin = sourceOrigin;
event.source = getOrCreateProxy(sourceId);
window.dispatchEvent(event);
});
window.history.back = function () {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK');
};
window.history.forward = function () {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD');
};
window.history.go = function (offset) {
ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset);
};
defineProperty(window.history, 'length', {
get: function () {
return ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH');
}
});
if (guestInstanceId != null) {
// Webview `document.visibilityState` tracks window visibility (and ignores
// the actual <webview> element visibility) for backwards compatibility.
// See discussion in #9178.
//
// Note that this results in duplicate visibilitychange events (since
// Chromium also fires them) and potentially incorrect visibility change.
// We should reconsider this decision for Electron 2.0.
let cachedVisibilityState = isHiddenPage ? 'hidden' : 'visible';
// Subscribe to visibilityState changes.
ipc_renderer_internal_1.ipcRendererInternal.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (_event, visibilityState) {
if (cachedVisibilityState !== visibilityState) {
cachedVisibilityState = visibilityState;
document.dispatchEvent(new Event('visibilitychange'));
}
});
// Make document.hidden and document.visibilityState return the correct value.
defineProperty(document, 'hidden', {
get: function () {
return cachedVisibilityState !== 'visible';
}
});
defineProperty(document, 'visibilityState', {
get: function () {
return cachedVisibilityState;
}
});
} }
}; }
//# sourceMappingURL=window-setup.js.map
// But we do not support prompt().
window.prompt = function () {
throw new Error('prompt() is and will not be supported.')
}
ipcRenderer.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (event, sourceId, message, sourceOrigin) {
// Manually dispatch event instead of using postMessage because we also need to
// set event.source.
event = document.createEvent('Event')
event.initEvent('message', false, false)
event.data = message
event.origin = sourceOrigin
event.source = getOrCreateProxy(ipcRenderer, sourceId)
window.dispatchEvent(event)
})
window.history.back = function () {
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK')
}
window.history.forward = function () {
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD')
}
window.history.go = function (offset) {
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset)
}
defineProperty(window.history, 'length', {
get: function () {
return ipcRenderer.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH')
}
})
if (guestInstanceId != null) {
// Webview `document.visibilityState` tracks window visibility (and ignores
// the actual <webview> element visibility) for backwards compatibility.
// See discussion in #9178.
//
// Note that this results in duplicate visibilitychange events (since
// Chromium also fires them) and potentially incorrect visibility change.
// We should reconsider this decision for Electron 2.0.
let cachedVisibilityState = hiddenPage ? 'hidden' : 'visible'
// Subscribe to visibilityState changes.
ipcRenderer.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (event, visibilityState) {
if (cachedVisibilityState !== visibilityState) {
cachedVisibilityState = visibilityState
document.dispatchEvent(new Event('visibilitychange'))
}
})
// Make document.hidden and document.visibilityState return the correct value.
defineProperty(document, 'hidden', {
get: function () {
return cachedVisibilityState !== 'visible'
}
})
defineProperty(document, 'visibilityState', {
get: function () {
return cachedVisibilityState
}
})
}
}

View File

@ -1,30 +1,37 @@
'use strict'; 'use strict'
const path = require('path');
const Module = require('module'); const path = require('path')
const Module = require('module')
// We modified the original process.argv to let node.js load the // We modified the original process.argv to let node.js load the
// init.js, we need to restore it here. // init.js, we need to restore it here.
process.argv.splice(1, 1); process.argv.splice(1, 1)
// Clear search paths. // Clear search paths.
require('../common/reset-search-paths'); require('../common/reset-search-paths')
// Import common settings. // Import common settings.
require('@electron/internal/common/init'); require('@electron/internal/common/init')
// Expose public APIs. // Expose public APIs.
Module.globalPaths.push(path.join(__dirname, 'api', 'exports')); Module.globalPaths.push(path.join(__dirname, 'api', 'exports'))
// Export node bindings to global. // Export node bindings to global.
global.require = require; global.require = require
global.module = module; global.module = module
// Set the __filename to the path of html file if it is file: protocol. // Set the __filename to the path of html file if it is file: protocol.
if (self.location.protocol === 'file:') { if (self.location.protocol === 'file:') {
const pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.pathname; const pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.pathname
global.__filename = path.normalize(decodeURIComponent(pathname)); global.__filename = path.normalize(decodeURIComponent(pathname))
global.__dirname = path.dirname(global.__filename); global.__dirname = path.dirname(global.__filename)
// Set module's filename so relative require can work as expected.
module.filename = global.__filename; // Set module's filename so relative require can work as expected.
// Also search for module under the html file. module.filename = global.__filename
module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname));
// Also search for module under the html file.
module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
} else {
global.__filename = __filename
global.__dirname = __dirname
} }
else {
global.__filename = __filename;
global.__dirname = __dirname;
}
//# sourceMappingURL=init.js.map