diff --git a/appasar/development/app_bootstrap/bootstrap.js b/appasar/development/app_bootstrap/bootstrap.js
index 252b489..2083fb4 100644
--- a/appasar/development/app_bootstrap/bootstrap.js
+++ b/appasar/development/app_bootstrap/bootstrap.js
@@ -37,8 +37,10 @@ const GPUSettings = require('./GPUSettings');
function setupHardwareAcceleration() {
const settings = appSettings.getSettings();
+ const electronMajor = parseInt(process.versions.electron.split('.')[0]);
+ const allowed = process.env.DISCORD_ENABLE_HARDWARE_ACCELERATION || buildInfo.releaseChannel === 'development' || !(electronMajor === 7 && process.platform === 'darwin');
// TODO: this is a copy of gpuSettings.getEnableHardwareAcceleration
- if (!settings.get('enableHardwareAcceleration', true)) {
+ if (!allowed || !settings.get('enableHardwareAcceleration', true)) {
app.disableHardwareAcceleration();
}
}
diff --git a/electronasar/development/browser/api/app.js b/electronasar/development/browser/api/app.js
new file mode 100644
index 0000000..e7d2017
--- /dev/null
+++ b/electronasar/development/browser/api/app.js
@@ -0,0 +1,88 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const path = require("path");
+const electron = require("electron");
+const events_1 = require("events");
+const bindings = process.electronBinding('app');
+const commandLine = process.electronBinding('command_line');
+const { app, App } = bindings;
+// Only one app object permitted.
+exports.default = app;
+const { deprecate, Menu } = electron;
+let dockMenu = null;
+// App is an EventEmitter.
+Object.setPrototypeOf(App.prototype, events_1.EventEmitter.prototype);
+events_1.EventEmitter.call(app);
+Object.assign(app, {
+ // TODO(codebytere): remove in 7.0
+ setApplicationMenu(menu) {
+ return Menu.setApplicationMenu(menu);
+ },
+ // TODO(codebytere): remove in 7.0
+ getApplicationMenu() {
+ return Menu.getApplicationMenu();
+ },
+ commandLine: {
+ 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', {
+ get() {
+ return Menu.getApplicationMenu();
+ },
+ set(menu) {
+ return Menu.setApplicationMenu(menu);
+ }
+});
+app.isPackaged = (() => {
+ const execFile = path.basename(process.execPath).toLowerCase();
+ if (process.platform === 'win32') {
+ return execFile !== 'electron.exe';
+ }
+ 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') {
+ const setDockMenu = app.dock.setMenu;
+ app.dock.setMenu = (menu) => {
+ dockMenu = menu;
+ setDockMenu(menu);
+ };
+ app.dock.getMenu = () => dockMenu;
+}
+// Routes the events to webContents.
+const events = ['login', 'certificate-error', 'select-client-certificate'];
+for (const name of events) {
+ app.on(name, (event, webContents, ...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.
+const { DownloadItem } = process.electronBinding('download_item');
+Object.setPrototypeOf(DownloadItem.prototype, events_1.EventEmitter.prototype);
+//# sourceMappingURL=app.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/auto-updater.js b/electronasar/development/browser/api/auto-updater.js
new file mode 100644
index 0000000..cbd3003
--- /dev/null
+++ b/electronasar/development/browser/api/auto-updater.js
@@ -0,0 +1,8 @@
+'use strict';
+if (process.platform === 'win32') {
+ 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');
+}
+//# sourceMappingURL=auto-updater.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/auto-updater/auto-updater-native.js b/electronasar/development/browser/api/auto-updater/auto-updater-native.js
new file mode 100644
index 0000000..4b34107
--- /dev/null
+++ b/electronasar/development/browser/api/auto-updater/auto-updater-native.js
@@ -0,0 +1,8 @@
+'use strict';
+const EventEmitter = require('events').EventEmitter;
+const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater');
+// AutoUpdater is an EventEmitter.
+Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
+EventEmitter.call(autoUpdater);
+module.exports = autoUpdater;
+//# sourceMappingURL=auto-updater-native.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/auto-updater/auto-updater-win.js b/electronasar/development/browser/api/auto-updater/auto-updater-win.js
new file mode 100644
index 0000000..177b630
--- /dev/null
+++ b/electronasar/development/browser/api/auto-updater/auto-updater-win.js
@@ -0,0 +1,71 @@
+'use strict';
+const { app } = require('electron');
+const { EventEmitter } = require('events');
+const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win');
+class AutoUpdater extends EventEmitter {
+ quitAndInstall() {
+ if (!this.updateAvailable) {
+ return this.emitError('No update available, can\'t quit and install');
+ }
+ squirrelUpdate.processStart();
+ 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');
+ }
+ this.updateURL = updateURL;
+ }
+ checkForUpdates() {
+ if (!this.updateURL) {
+ 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);
+ }
+}
+module.exports = new AutoUpdater();
+//# sourceMappingURL=auto-updater-win.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/auto-updater/squirrel-update-win.js b/electronasar/development/browser/api/auto-updater/squirrel-update-win.js
new file mode 100644
index 0000000..97a3230
--- /dev/null
+++ b/electronasar/development/browser/api/auto-updater/squirrel-update-win.js
@@ -0,0 +1,107 @@
+'use strict';
+const fs = require('fs');
+const path = require('path');
+const spawn = require('child_process').spawn;
+// i.e. my-app/app-0.1.13/
+const appFolder = path.dirname(process.execPath);
+// i.e. my-app/Update.exe
+const updateExe = path.resolve(appFolder, '..', 'Update.exe');
+const exeName = path.basename(process.execPath);
+let spawnedArgs = [];
+let spawnedProcess;
+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
+// and the output from standard out.
+const spawnUpdate = function (args, detached, callback) {
+ let error, errorEmitted, stderr, stdout;
+ try {
+ // Ensure we don't spawn multiple squirrel processes
+ // Process spawned, same args: Attach events to alread running process
+ // Process spawned, different args: Return with error
+ // No process spawned: Spawn new process
+ if (spawnedProcess && !isSameArgs(args)) {
+ // Disabled for backwards compatibility:
+ // eslint-disable-next-line standard/no-callback-literal
+ return callback(`AutoUpdater process with arguments ${args} is already running`);
+ }
+ else if (!spawnedProcess) {
+ spawnedProcess = spawn(updateExe, args, {
+ detached: detached,
+ windowsHide: true
+ });
+ spawnedArgs = args || [];
+ }
+ }
+ catch (error1) {
+ error = error1;
+ // Shouldn't happen, but still guard it.
+ process.nextTick(function () {
+ return callback(error);
+ });
+ 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;
+ }
+ // 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.
+exports.processStart = function () {
+ return spawnUpdate(['--processStartAndWait', exeName], true, function () { });
+};
+// Download the releases specified by the URL and write new results to stdout.
+exports.checkForUpdate = function (updateURL, callback) {
+ return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) {
+ let ref, ref1, update;
+ if (error != null) {
+ return callback(error);
+ }
+ try {
+ // Last line of output is the JSON details about the releases
+ 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;
+ }
+ catch (_a) {
+ // Disabled for backwards compatibility:
+ // eslint-disable-next-line standard/no-callback-literal
+ return callback(`Invalid result:\n${stdout}`);
+ }
+ return callback(null, update);
+ });
+};
+// Update the application to the latest remote version specified by URL.
+exports.update = function (updateURL, callback) {
+ return spawnUpdate(['--update', updateURL], false, callback);
+};
+// Is the Update.exe installed with the current application?
+exports.supported = function () {
+ try {
+ fs.accessSync(updateExe, fs.R_OK);
+ return true;
+ }
+ catch (_a) {
+ return false;
+ }
+};
+//# sourceMappingURL=squirrel-update-win.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/browser-view.js b/electronasar/development/browser/api/browser-view.js
new file mode 100644
index 0000000..56f62c6
--- /dev/null
+++ b/electronasar/development/browser/api/browser-view.js
@@ -0,0 +1,13 @@
+'use strict';
+const { EventEmitter } = require('events');
+const { BrowserView } = process.electronBinding('browser_view');
+Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
+BrowserView.fromWebContents = (webContents) => {
+ for (const view of BrowserView.getAllViews()) {
+ if (view.webContents.equal(webContents))
+ return view;
+ }
+ return null;
+};
+module.exports = BrowserView;
+//# sourceMappingURL=browser-view.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/browser-window.js b/electronasar/development/browser/api/browser-window.js
new file mode 100644
index 0000000..3927adc
--- /dev/null
+++ b/electronasar/development/browser/api/browser-window.js
@@ -0,0 +1,171 @@
+'use strict';
+const electron = require('electron');
+const { WebContentsView, TopLevelWindow } = electron;
+const { BrowserWindow } = process.electronBinding('window');
+Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype);
+BrowserWindow.prototype._init = function () {
+ // Call parent class's _init.
+ TopLevelWindow.prototype._init.call(this);
+ // Avoid recursive require.
+ const { app } = electron;
+ // Create WebContentsView.
+ this.setContentView(new WebContentsView(this.webContents));
+ const nativeSetBounds = this.setBounds;
+ this.setBounds = (bounds, ...opts) => {
+ bounds = Object.assign({}, this.getBounds(), bounds);
+ nativeSetBounds.call(this, bounds, ...opts);
+ };
+ // window.resizeTo(...)
+ // window.moveTo(...)
+ this.webContents.on('move', (event, size) => {
+ 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.
+ app.emit('browser-window-created', {}, this);
+ Object.defineProperty(this, 'devToolsWebContents', {
+ enumerable: true,
+ configurable: false,
+ get() {
+ return this.webContents.devToolsWebContents;
+ }
+ });
+};
+const isBrowserWindow = (win) => {
+ return win && win.constructor.name === 'BrowserWindow';
+};
+BrowserWindow.fromId = (id) => {
+ const win = TopLevelWindow.fromId(id);
+ return isBrowserWindow(win) ? win : null;
+};
+BrowserWindow.getAllWindows = () => {
+ return TopLevelWindow.getAllWindows().filter(isBrowserWindow);
+};
+BrowserWindow.getFocusedWindow = () => {
+ for (const window of BrowserWindow.getAllWindows()) {
+ if (window.isFocused() || window.isDevToolsFocused())
+ return window;
+ }
+ return null;
+};
+BrowserWindow.fromWebContents = (webContents) => {
+ for (const window of BrowserWindow.getAllWindows()) {
+ if (window.webContents.equal(webContents))
+ return window;
+ }
+};
+BrowserWindow.fromBrowserView = (browserView) => {
+ for (const window of BrowserWindow.getAllWindows()) {
+ if (window.getBrowserView() === browserView)
+ return window;
+ }
+ return null;
+};
+BrowserWindow.fromDevToolsWebContents = (webContents) => {
+ for (const window of BrowserWindow.getAllWindows()) {
+ const { devToolsWebContents } = window;
+ if (devToolsWebContents != null && devToolsWebContents.equal(webContents)) {
+ return window;
+ }
+ }
+};
+// Helpers.
+Object.assign(BrowserWindow.prototype, {
+ loadURL(...args) {
+ return this.webContents.loadURL(...args);
+ },
+ getURL(...args) {
+ return this.webContents.getURL();
+ },
+ loadFile(...args) {
+ return this.webContents.loadFile(...args);
+ },
+ reload(...args) {
+ return this.webContents.reload(...args);
+ },
+ send(...args) {
+ return this.webContents.send(...args);
+ },
+ openDevTools(...args) {
+ return this.webContents.openDevTools(...args);
+ },
+ closeDevTools() {
+ return this.webContents.closeDevTools();
+ },
+ isDevToolsOpened() {
+ return this.webContents.isDevToolsOpened();
+ },
+ isDevToolsFocused() {
+ return this.webContents.isDevToolsFocused();
+ },
+ toggleDevTools() {
+ return this.webContents.toggleDevTools();
+ },
+ inspectElement(...args) {
+ return this.webContents.inspectElement(...args);
+ },
+ inspectSharedWorker() {
+ return this.webContents.inspectSharedWorker();
+ },
+ inspectServiceWorker() {
+ return this.webContents.inspectServiceWorker();
+ },
+ showDefinitionForSelection() {
+ return this.webContents.showDefinitionForSelection();
+ },
+ capturePage(...args) {
+ return this.webContents.capturePage(...args);
+ },
+ setTouchBar(touchBar) {
+ electron.TouchBar._setOnWindow(touchBar, this);
+ },
+ setBackgroundThrottling(allowed) {
+ this.webContents.setBackgroundThrottling(allowed);
+ }
+});
+module.exports = BrowserWindow;
+//# sourceMappingURL=browser-window.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/content-tracing.js b/electronasar/development/browser/api/content-tracing.js
new file mode 100644
index 0000000..9e1682c
--- /dev/null
+++ b/electronasar/development/browser/api/content-tracing.js
@@ -0,0 +1,12 @@
+'use strict';
+const { deprecate } = require('electron');
+const contentTracing = process.electronBinding('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
\ No newline at end of file
diff --git a/electronasar/development/browser/api/crash-reporter.js b/electronasar/development/browser/api/crash-reporter.js
new file mode 100644
index 0000000..988c0d1
--- /dev/null
+++ b/electronasar/development/browser/api/crash-reporter.js
@@ -0,0 +1,10 @@
+'use strict';
+const CrashReporter = require('@electron/internal/common/crash-reporter');
+const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
+class CrashReporterMain extends CrashReporter {
+ init(options) {
+ return crashReporterInit(options);
+ }
+}
+module.exports = new CrashReporterMain();
+//# sourceMappingURL=crash-reporter.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/dialog.js b/electronasar/development/browser/api/dialog.js
new file mode 100644
index 0000000..3e5d83d
--- /dev/null
+++ b/electronasar/development/browser/api/dialog.js
@@ -0,0 +1,184 @@
+'use strict';
+const { app, BrowserWindow, deprecate } = require('electron');
+const binding = process.electronBinding('dialog');
+const v8Util = process.electronBinding('v8_util');
+const fileDialogProperties = {
+ openFile: 1 << 0,
+ openDirectory: 1 << 1,
+ multiSelections: 1 << 2,
+ createDirectory: 1 << 3,
+ showHiddenFiles: 1 << 4,
+ promptToCreate: 1 << 5,
+ noResolveAliases: 1 << 6,
+ treatPackageAsDirectory: 1 << 7
+};
+const normalizeAccessKey = (text) => {
+ if (typeof text !== 'string')
+ return text;
+ // macOS does not have access keys so remove single ampersands
+ // and replace double ampersands with a single ampersand
+ if (process.platform === 'darwin') {
+ 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
+ // ampersands with a single ampersand, and replace a single ampersand with
+ // a single underscore
+ if (process.platform === 'linux') {
+ return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
+ if (after === '&')
+ return after;
+ return `_${after}`;
+ });
+ }
+ return text;
+};
+const checkAppInitialized = function () {
+ if (!app.isReady()) {
+ throw new Error('dialog module can only be used after app is ready');
+ }
+};
+const saveDialog = (sync, window, options) => {
+ checkAppInitialized();
+ if (window && window.constructor !== BrowserWindow) {
+ options = window;
+ window = null;
+ }
+ 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) {
+ options = {
+ title: 'Open',
+ properties: ['openFile']
+ };
+ }
+ const { buttonLabel = '', defaultPath = '', filters = [], properties = ['openFile'], title = '', message = '', securityScopedBookmarks = false } = options;
+ if (!Array.isArray(properties))
+ throw new TypeError('Properties must be an array');
+ let dialogProperties = 0;
+ for (const prop in fileDialogProperties) {
+ if (properties.includes(prop)) {
+ dialogProperties |= fileDialogProperties[prop];
+ }
+ }
+ 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');
+ 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' };
+ const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
+ const messageBoxOptions = { noLink: 1 << 0 };
+ let { buttons = [], cancelId, checkboxLabel = '', checkboxChecked, defaultId = -1, detail = '', icon = null, message = '', title = '', type = 'none' } = options;
+ const messageBoxType = messageBoxTypes.indexOf(type);
+ if (messageBoxType === -1)
+ throw new TypeError('Invalid message box type');
+ if (!Array.isArray(buttons))
+ throw new TypeError('Buttons must be an array');
+ if (options.normalizeAccessKeys)
+ buttons = buttons.map(normalizeAccessKey);
+ if (typeof title !== 'string')
+ throw new TypeError('Title must be a string');
+ if (typeof message !== 'string')
+ throw new TypeError('Message must be a string');
+ if (typeof detail !== 'string')
+ throw new TypeError('Detail must be a string');
+ if (typeof checkboxLabel !== 'string')
+ throw new TypeError('checkboxLabel must be a string');
+ checkboxChecked = !!checkboxChecked;
+ // Choose a default button to get selected when dialog is cancelled.
+ if (cancelId == null) {
+ // If the defaultId is set to 0, ensure the cancel button is a different index (1)
+ cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
+ for (let i = 0; i < buttons.length; i++) {
+ const text = buttons[i].toLowerCase();
+ if (text === 'cancel' || text === 'no') {
+ cancelId = i;
+ break;
+ }
+ }
+ }
+ const flags = options.noLink ? messageBoxOptions.noLink : 0;
+ if (sync) {
+ return binding.showMessageBoxSync(messageBoxType, buttons, defaultId, cancelId, flags, title, message, detail, checkboxLabel, checkboxChecked, icon, window);
+ }
+ else {
+ return binding.showMessageBox(messageBoxType, buttons, defaultId, cancelId, flags, title, message, detail, checkboxLabel, checkboxChecked, icon, window);
+ }
+};
+module.exports = {
+ showOpenDialog: function (window, options) {
+ return openDialog(false, window, options);
+ },
+ 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]);
+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
\ No newline at end of file
diff --git a/electronasar/development/browser/api/exports/electron.js b/electronasar/development/browser/api/exports/electron.js
new file mode 100644
index 0000000..8c94396
--- /dev/null
+++ b/electronasar/development/browser/api/exports/electron.js
@@ -0,0 +1,19 @@
+'use strict';
+const common = require('@electron/internal/common/api/exports/electron');
+// since browser module list is also used in renderer, keep it separate.
+const moduleList = require('@electron/internal/browser/api/module-list');
+// Import common modules.
+common.defineProperties(exports);
+for (const module of moduleList) {
+ Object.defineProperty(exports, module.name, {
+ enumerable: !module.private,
+ get: common.memoizedGetter(() => {
+ 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
\ No newline at end of file
diff --git a/electronasar/development/browser/api/global-shortcut.js b/electronasar/development/browser/api/global-shortcut.js
new file mode 100644
index 0000000..834e35b
--- /dev/null
+++ b/electronasar/development/browser/api/global-shortcut.js
@@ -0,0 +1,3 @@
+'use strict';
+module.exports = process.electronBinding('global_shortcut').globalShortcut;
+//# sourceMappingURL=global-shortcut.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/in-app-purchase.js b/electronasar/development/browser/api/in-app-purchase.js
new file mode 100644
index 0000000..8a6d71e
--- /dev/null
+++ b/electronasar/development/browser/api/in-app-purchase.js
@@ -0,0 +1,22 @@
+'use strict';
+const { deprecate } = require('electron');
+if (process.platform === 'darwin') {
+ const { EventEmitter } = require('events');
+ const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase');
+ // inAppPurchase is an EventEmitter.
+ Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype);
+ 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: () => ''
+ };
+}
+module.exports.purchaseProduct = deprecate.promisify(module.exports.purchaseProduct);
+module.exports.getProducts = deprecate.promisify(module.exports.getProducts);
+//# sourceMappingURL=in-app-purchase.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/ipc-main.js b/electronasar/development/browser/api/ipc-main.js
new file mode 100644
index 0000000..b82b602
--- /dev/null
+++ b/electronasar/development/browser/api/ipc-main.js
@@ -0,0 +1,8 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const events_1 = require("events");
+const emitter = new events_1.EventEmitter();
+// Do not throw exception when channel name is "error".
+emitter.on('error', () => { });
+exports.default = emitter;
+//# sourceMappingURL=ipc-main.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/menu-item-roles.js b/electronasar/development/browser/api/menu-item-roles.js
new file mode 100644
index 0000000..b9ea0ce
--- /dev/null
+++ b/electronasar/development/browser/api/menu-item-roles.js
@@ -0,0 +1,309 @@
+'use strict';
+const { app } = require('electron');
+const isMac = process.platform === 'darwin';
+const isWindows = process.platform === 'win32';
+const isLinux = process.platform === 'linux';
+const roles = {
+ about: {
+ get label() {
+ return isLinux ? 'About' : `About ${app.getName()}`;
+ }
+ },
+ 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' }
+ ])
+ ]
+ }
+};
+const canExecuteRole = (role) => {
+ if (!roles.hasOwnProperty(role))
+ return false;
+ if (!isMac)
+ return true;
+ // macOS handles all roles natively except for a few
+ return roles[role].nonNativeMacOSRole;
+};
+exports.getDefaultLabel = (role) => {
+ return roles.hasOwnProperty(role) ? roles[role].label : '';
+};
+exports.getDefaultAccelerator = (role) => {
+ if (roles.hasOwnProperty(role))
+ return roles[role].accelerator;
+};
+exports.shouldRegisterAccelerator = (role) => {
+ const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined;
+ return hasRoleRegister ? roles[role].registerAccelerator : true;
+};
+exports.getDefaultSubmenu = (role) => {
+ if (!roles.hasOwnProperty(role))
+ return;
+ let { submenu } = roles[role];
+ // remove null items from within the submenu
+ if (Array.isArray(submenu)) {
+ submenu = submenu.filter((item) => item != null);
+ }
+ return submenu;
+};
+exports.execute = (role, focusedWindow, focusedWebContents) => {
+ if (!canExecuteRole(role))
+ return false;
+ const { appMethod, webContentsMethod, windowMethod } = roles[role];
+ if (appMethod) {
+ app[appMethod]();
+ return true;
+ }
+ if (windowMethod && focusedWindow != null) {
+ if (typeof windowMethod === 'function') {
+ windowMethod(focusedWindow);
+ }
+ else {
+ focusedWindow[windowMethod]();
+ }
+ return true;
+ }
+ if (webContentsMethod && focusedWebContents != null) {
+ if (typeof webContentsMethod === 'function') {
+ webContentsMethod(focusedWebContents);
+ }
+ else {
+ focusedWebContents[webContentsMethod]();
+ }
+ return true;
+ }
+ return false;
+};
+//# sourceMappingURL=menu-item-roles.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/menu-item.js b/electronasar/development/browser/api/menu-item.js
new file mode 100644
index 0000000..244621f
--- /dev/null
+++ b/electronasar/development/browser/api/menu-item.js
@@ -0,0 +1,74 @@
+'use strict';
+const roles = require('@electron/internal/browser/api/menu-item-roles');
+let nextCommandId = 0;
+const MenuItem = function (options) {
+ const { Menu } = require('electron');
+ // Preserve extra fields specified by user
+ for (const key in options) {
+ 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('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 () {
+ return roles.getDefaultAccelerator(this.role);
+};
+MenuItem.prototype.overrideProperty = function (name, defaultValue = null) {
+ if (this[name] == null) {
+ this[name] = defaultValue;
+ }
+};
+MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
+ this.overrideProperty(name, defaultValue);
+ Object.defineProperty(this, name, {
+ enumerable: true,
+ writable: false,
+ value: this[name]
+ });
+};
+module.exports = MenuItem;
+//# sourceMappingURL=menu-item.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/menu-utils.js b/electronasar/development/browser/api/menu-utils.js
new file mode 100644
index 0000000..67f856e
--- /dev/null
+++ b/electronasar/development/browser/api/menu-utils.js
@@ -0,0 +1,155 @@
+'use strict';
+function splitArray(arr, predicate) {
+ const result = arr.reduce((multi, item) => {
+ const current = multi[multi.length - 1];
+ if (predicate(item)) {
+ if (current.length > 0)
+ multi.push([]);
+ }
+ else {
+ current.push(item);
+ }
+ 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) => {
+ if (i > 0 && arr.length) {
+ if (joinIDs.length > 0) {
+ joined.push(joinIDs[0]);
+ joinIDs.splice(0, 1);
+ }
+ else {
+ 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);
+}
+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
+// are broken.
+function sortTopologically(originalOrder, edgesById) {
+ const sorted = [];
+ const marked = new Set();
+ const visit = (mark) => {
+ if (marked.has(mark))
+ return;
+ marked.add(mark);
+ const edges = edgesById.get(mark);
+ if (edges != null) {
+ 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;
+}
+function mergeGroups(groups) {
+ let merged = true;
+ while (merged) {
+ 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);
+ }
+ });
+ }
+ 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) {
+ if (item.beforeGroupContaining) {
+ for (const id of item.beforeGroupContaining) {
+ const to = indexOfGroupContainingID(groups, id, group);
+ if (to !== -1) {
+ pushOntoMultiMap(edges, to, i);
+ return;
+ }
+ }
+ }
+ if (item.afterGroupContaining) {
+ for (const id of item.afterGroupContaining) {
+ const to = indexOfGroupContainingID(groups, id, group);
+ if (to !== -1) {
+ pushOntoMultiMap(edges, i, to);
+ return;
+ }
+ }
+ }
+ }
+}
+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 };
+//# sourceMappingURL=menu-utils.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/menu.js b/electronasar/development/browser/api/menu.js
new file mode 100644
index 0000000..ad0b387
--- /dev/null
+++ b/electronasar/development/browser/api/menu.js
@@ -0,0 +1,263 @@
+'use strict';
+const { TopLevelWindow, MenuItem, webContents } = require('electron');
+const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils');
+const EventEmitter = require('events').EventEmitter;
+const v8Util = process.electronBinding('v8_util');
+const bindings = process.electronBinding('menu');
+const { Menu } = bindings;
+let applicationMenu = null;
+let groupIdIndex = 0;
+Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype);
+// Menu Delegate.
+// This object should hold no reference to |Menu| to avoid cyclic reference.
+const delegate = {
+ isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : 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,
+ getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
+ const command = menu.commandsMap[id];
+ if (!command)
+ return;
+ if (command.accelerator != null)
+ return command.accelerator;
+ if (useDefaultAccelerator)
+ return command.getDefaultRoleAccelerator();
+ },
+ shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined,
+ executeCommand: (menu, event, id) => {
+ const command = menu.commandsMap[id];
+ if (!command)
+ return;
+ command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents());
+ },
+ 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 */
+Menu.prototype._init = function () {
+ this.commandsMap = {};
+ this.groupsMap = {};
+ this.items = [];
+ this.delegate = delegate;
+};
+Menu.prototype.popup = function (options = {}) {
+ if (options == null || typeof options !== '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];
+ }
+ 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 };
+};
+Menu.prototype.closePopup = function (window) {
+ if (window instanceof TopLevelWindow) {
+ this.closePopupAt(window.id);
+ }
+ else {
+ // Passing -1 (invalid) would make closePopupAt close the all menu runners
+ // belong to this menu.
+ this.closePopupAt(-1);
+ }
+};
+Menu.prototype.getMenuItemById = function (id) {
+ const items = this.items;
+ let found = items.find(item => item.id === id) || null;
+ for (let i = 0; !found && i < items.length; i++) {
+ if (items[i].submenu) {
+ found = items[i].submenu.getMenuItemById(id);
+ }
+ }
+ return found;
+};
+Menu.prototype.append = function (item) {
+ return this.insert(this.getItemCount(), item);
+};
+Menu.prototype.insert = function (pos, item) {
+ if ((item ? item.constructor : void 0) !== MenuItem) {
+ throw new TypeError('Invalid item');
+ }
+ if (pos < 0) {
+ throw new RangeError(`Position ${pos} cannot be less than 0`);
+ }
+ else if (pos > this.getItemCount()) {
+ throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`);
+ }
+ // insert item depending on its type
+ insertItemByType.call(this, item, pos);
+ // set item properties
+ if (item.sublabel)
+ this.setSublabel(pos, item.sublabel);
+ if (item.icon)
+ this.setIcon(pos, item.icon);
+ if (item.role)
+ this.setRole(pos, item.role);
+ // 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 () {
+ if (this.delegate)
+ this.delegate.menuWillShow(this);
+ this.items.forEach(item => {
+ if (item.submenu)
+ item.submenu._callMenuWillShow();
+ });
+};
+/* Static Methods */
+Menu.getApplicationMenu = () => applicationMenu;
+Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
+// set application menu with a preexisting menu
+Menu.setApplicationMenu = function (menu) {
+ if (menu && menu.constructor !== Menu) {
+ throw new TypeError('Invalid menu');
+ }
+ applicationMenu = menu;
+ v8Util.setHiddenValue(global, 'applicationMenuSet', true);
+ if (process.platform === 'darwin') {
+ if (!menu)
+ return;
+ menu._callMenuWillShow();
+ bindings.setApplicationMenu(menu);
+ }
+ else {
+ const windows = TopLevelWindow.getAllWindows();
+ return windows.map(w => w.setMenu(menu));
+ }
+};
+Menu.buildFromTemplate = function (template) {
+ if (!Array.isArray(template)) {
+ throw new TypeError('Invalid template for Menu: Menu template must be an array');
+ }
+ if (!areValidTemplateItems(template)) {
+ throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type');
+ }
+ const filtered = removeExtraSeparators(template);
+ const sorted = sortTemplate(filtered);
+ const menu = new Menu();
+ sorted.forEach(item => {
+ if (item instanceof MenuItem) {
+ menu.append(item);
+ }
+ else {
+ menu.append(new MenuItem(item));
+ }
+ });
+ return menu;
+};
+/* Helper Functions */
+// validate the template against having the wrong attribute
+function areValidTemplateItems(template) {
+ return template.every(item => item != null &&
+ typeof item === 'object' &&
+ (item.hasOwnProperty('label') ||
+ item.hasOwnProperty('role') ||
+ item.type === 'separator'));
+}
+function sortTemplate(template) {
+ const sorted = sortMenuItems(template);
+ for (const id in sorted) {
+ const item = sorted[id];
+ if (Array.isArray(item.submenu)) {
+ item.submenu = sortTemplate(item.submenu);
+ }
+ }
+ return sorted;
+}
+// Search between separators to find a radio menu item and return its group id
+function generateGroupId(items, pos) {
+ if (pos > 0) {
+ for (let idx = pos - 1; idx >= 0; idx--) {
+ if (items[idx].type === 'radio')
+ return items[idx].groupId;
+ if (items[idx].type === 'separator')
+ break;
+ }
+ }
+ else if (pos < items.length) {
+ for (let idx = pos; idx <= items.length - 1; idx++) {
+ if (items[idx].type === 'radio')
+ return items[idx].groupId;
+ if (items[idx].type === 'separator')
+ break;
+ }
+ }
+ groupIdIndex += 1;
+ return groupIdIndex;
+}
+function removeExtraSeparators(items) {
+ // fold adjacent separators together
+ let ret = items.filter((e, idx, arr) => {
+ if (e.visible === false)
+ return true;
+ return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator';
+ });
+ // remove edge separators
+ ret = ret.filter((e, idx, arr) => {
+ if (e.visible === false)
+ return true;
+ return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1);
+ });
+ return ret;
+}
+function insertItemByType(item, pos) {
+ const types = {
+ normal: () => this.insertItem(pos, item.commandId, item.label),
+ checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),
+ separator: () => this.insertSeparator(pos),
+ submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
+ radio: () => {
+ // Grouping radio menu items
+ item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos));
+ 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.
+ v8Util.setHiddenValue(item, 'checked', item.checked);
+ Object.defineProperty(item, 'checked', {
+ enumerable: true,
+ get: () => v8Util.getHiddenValue(item, 'checked'),
+ set: () => {
+ this.groupsMap[item.groupId].forEach(other => {
+ if (other !== item)
+ v8Util.setHiddenValue(other, 'checked', false);
+ });
+ v8Util.setHiddenValue(item, 'checked', true);
+ }
+ });
+ this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
+ }
+ };
+ types[item.type]();
+}
+module.exports = Menu;
+//# sourceMappingURL=menu.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/module-list.js b/electronasar/development/browser/api/module-list.js
new file mode 100644
index 0000000..d54bbf7
--- /dev/null
+++ b/electronasar/development/browser/api/module-list.js
@@ -0,0 +1,36 @@
+'use strict';
+const features = process.electronBinding('features');
+// Browser side modules, please sort alphabetically.
+module.exports = [
+ { name: 'app', file: 'app' },
+ { name: 'autoUpdater', file: 'auto-updater' },
+ { name: 'BrowserView', file: 'browser-view' },
+ { name: 'BrowserWindow', file: 'browser-window' },
+ { name: 'contentTracing', file: 'content-tracing' },
+ { name: 'crashReporter', file: 'crash-reporter' },
+ { name: 'dialog', file: 'dialog' },
+ { name: 'globalShortcut', file: 'global-shortcut' },
+ { name: 'ipcMain', file: 'ipc-main' },
+ { name: 'inAppPurchase', file: 'in-app-purchase' },
+ { name: 'Menu', file: 'menu' },
+ { name: 'MenuItem', file: 'menu-item' },
+ { name: 'net', file: 'net' },
+ { name: 'netLog', file: 'net-log' },
+ { name: 'Notification', file: 'notification' },
+ { name: 'powerMonitor', file: 'power-monitor' },
+ { name: 'powerSaveBlocker', file: 'power-save-blocker' },
+ { name: 'protocol', file: 'protocol' },
+ { name: 'screen', file: 'screen' },
+ { name: 'session', file: 'session' },
+ { name: 'systemPreferences', file: 'system-preferences' },
+ { name: 'TopLevelWindow', file: 'top-level-window' },
+ { name: 'TouchBar', file: 'touch-bar' },
+ { name: 'Tray', file: 'tray' },
+ { name: 'View', file: 'view' },
+ { name: 'webContents', file: 'web-contents' },
+ { name: 'WebContentsView', file: 'web-contents-view' }
+];
+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' });
+}
+//# sourceMappingURL=module-list.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/net-log.js b/electronasar/development/browser/api/net-log.js
new file mode 100644
index 0000000..5f9f73d
--- /dev/null
+++ b/electronasar/development/browser/api/net-log.js
@@ -0,0 +1,28 @@
+'use strict';
+// TODO(deepak1556): Deprecate and remove standalone netLog module,
+// it is now a property of session module.
+const { app, session } = require('electron');
+// Fallback to default session.
+Object.setPrototypeOf(module.exports, new Proxy({}, {
+ get(target, property) {
+ if (!app.isReady())
+ return;
+ const netLog = session.defaultSession.netLog;
+ if (!Object.getPrototypeOf(netLog).hasOwnProperty(property))
+ return;
+ // check for properties on the prototype chain that aren't functions
+ if (typeof netLog[property] !== 'function')
+ return netLog[property];
+ // Returning a native function directly would throw error.
+ return (...args) => netLog[property](...args);
+ },
+ ownKeys() {
+ if (!app.isReady())
+ return [];
+ return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog));
+ },
+ getOwnPropertyDescriptor(target) {
+ return { configurable: true, enumerable: true };
+ }
+}));
+//# sourceMappingURL=net-log.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/net.js b/electronasar/development/browser/api/net.js
new file mode 100644
index 0000000..6f2dbb1
--- /dev/null
+++ b/electronasar/development/browser/api/net.js
@@ -0,0 +1,358 @@
+'use strict';
+const url = require('url');
+const { EventEmitter } = require('events');
+const { Readable } = require('stream');
+const { app } = require('electron');
+const { Session } = process.electronBinding('session');
+const { net, Net } = process.electronBinding('net');
+const { URLRequest } = net;
+// Net is an EventEmitter.
+Object.setPrototypeOf(Net.prototype, EventEmitter.prototype);
+EventEmitter.call(net);
+Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype);
+const kSupportedProtocols = new Set(['http:', 'https:']);
+// set of headers that Node.js discards duplicates for
+// see https://nodejs.org/api/http.html#http_message_headers
+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 {
+ constructor(urlRequest) {
+ super();
+ this.urlRequest = urlRequest;
+ this.shouldPush = false;
+ this.data = [];
+ this.urlRequest.on('data', (event, chunk) => {
+ this._storeInternalData(chunk);
+ this._pushInternalData();
+ });
+ this.urlRequest.on('end', () => {
+ this._storeInternalData(null);
+ this._pushInternalData();
+ });
+ }
+ get statusCode() {
+ return this.urlRequest.statusCode;
+ }
+ get statusMessage() {
+ return this.urlRequest.statusMessage;
+ }
+ get headers() {
+ const filteredHeaders = {};
+ const rawHeaders = this.urlRequest.rawResponseHeaders;
+ Object.keys(rawHeaders).forEach(header => {
+ if (header in filteredHeaders && discardableDuplicateHeaders.has(header)) {
+ // do nothing with discardable duplicate headers
+ }
+ else {
+ if (header === 'set-cookie') {
+ // keep set-cookie as an array per Node.js rules
+ // see https://nodejs.org/api/http.html#http_message_headers
+ filteredHeaders[header] = rawHeaders[header];
+ }
+ else {
+ // for non-cookie headers, the values are joined together with ', '
+ filteredHeaders[header] = rawHeaders[header].join(', ');
+ }
+ }
+ });
+ return filteredHeaders;
+ }
+ get httpVersion() {
+ return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
+ }
+ get httpVersionMajor() {
+ return this.urlRequest.httpVersionMajor;
+ }
+ get httpVersionMinor() {
+ return this.urlRequest.httpVersionMinor;
+ }
+ get rawTrailers() {
+ throw new Error('HTTP trailers are not supported.');
+ }
+ get trailers() {
+ throw new Error('HTTP trailers are not supported.');
+ }
+ _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();
+ }
+}
+URLRequest.prototype._emitRequestEvent = function (isAsync, ...rest) {
+ if (isAsync) {
+ process.nextTick(() => {
+ this.clientRequest.emit(...rest);
+ });
+ }
+ else {
+ this.clientRequest.emit(...rest);
+ }
+};
+URLRequest.prototype._emitResponseEvent = function (isAsync, ...rest) {
+ if (isAsync) {
+ process.nextTick(() => {
+ this._response.emit(...rest);
+ });
+ }
+ else {
+ this._response.emit(...rest);
+ }
+};
+class ClientRequest extends EventEmitter {
+ constructor(options, callback) {
+ super();
+ 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;
+ }
+ set chunkedEncoding(value) {
+ if (!this.urlRequest.notStarted) {
+ throw new Error('Can\'t set the transfer encoding, headers have been sent.');
+ }
+ this.chunkedEncodingEnabled = value;
+ }
+ setHeader(name, value) {
+ if (typeof name !== 'string') {
+ throw new TypeError('`name` should be a string in setHeader(name, value).');
+ }
+ 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) {
+ 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);
+ if (callback)
+ callback(error);
+}
+Net.prototype.request = function (options, callback) {
+ return new ClientRequest(options, callback);
+};
+net.ClientRequest = ClientRequest;
+module.exports = net;
+//# sourceMappingURL=net.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/notification.js b/electronasar/development/browser/api/notification.js
new file mode 100644
index 0000000..a6bb64b
--- /dev/null
+++ b/electronasar/development/browser/api/notification.js
@@ -0,0 +1,7 @@
+'use strict';
+const { EventEmitter } = require('events');
+const { Notification, isSupported } = process.electronBinding('notification');
+Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype);
+Notification.isSupported = isSupported;
+module.exports = Notification;
+//# sourceMappingURL=notification.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/power-monitor.js b/electronasar/development/browser/api/power-monitor.js
new file mode 100644
index 0000000..428d427
--- /dev/null
+++ b/electronasar/development/browser/api/power-monitor.js
@@ -0,0 +1,43 @@
+'use strict';
+const { EventEmitter } = require('events');
+const { powerMonitor, PowerMonitor } = process.electronBinding('power_monitor');
+const { deprecate } = require('electron');
+// PowerMonitor is an EventEmitter.
+Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype);
+EventEmitter.call(powerMonitor);
+// On Linux we need to call blockShutdown() to subscribe to shutdown event.
+if (process.platform === 'linux') {
+ powerMonitor.on('newListener', (event) => {
+ if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
+ powerMonitor.blockShutdown();
+ }
+ });
+ powerMonitor.on('removeListener', (event) => {
+ if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
+ powerMonitor.unblockShutdown();
+ }
+ });
+}
+// TODO(nitsakh): Remove in 7.0
+powerMonitor.querySystemIdleState = function (threshold, callback) {
+ 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
\ No newline at end of file
diff --git a/electronasar/development/browser/api/power-save-blocker.js b/electronasar/development/browser/api/power-save-blocker.js
new file mode 100644
index 0000000..648ee3b
--- /dev/null
+++ b/electronasar/development/browser/api/power-save-blocker.js
@@ -0,0 +1,3 @@
+'use strict';
+module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker;
+//# sourceMappingURL=power-save-blocker.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/protocol.js b/electronasar/development/browser/api/protocol.js
new file mode 100644
index 0000000..d7d8c58
--- /dev/null
+++ b/electronasar/development/browser/api/protocol.js
@@ -0,0 +1,27 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const electron_1 = require("electron");
+// Global protocol APIs.
+const protocol = process.electronBinding('protocol');
+// Fallback protocol APIs of default session.
+Object.setPrototypeOf(protocol, new Proxy({}, {
+ get(_target, property) {
+ if (!electron_1.app.isReady())
+ return;
+ const protocol = electron_1.session.defaultSession.protocol;
+ if (!Object.getPrototypeOf(protocol).hasOwnProperty(property))
+ return;
+ // Returning a native function directly would throw error.
+ return (...args) => protocol[property](...args);
+ },
+ ownKeys() {
+ if (!electron_1.app.isReady())
+ return [];
+ return Object.getOwnPropertyNames(Object.getPrototypeOf(electron_1.session.defaultSession.protocol));
+ },
+ getOwnPropertyDescriptor() {
+ return { configurable: true, enumerable: true };
+ }
+}));
+exports.default = protocol;
+//# sourceMappingURL=protocol.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/screen.js b/electronasar/development/browser/api/screen.js
new file mode 100644
index 0000000..7ea700f
--- /dev/null
+++ b/electronasar/development/browser/api/screen.js
@@ -0,0 +1,8 @@
+'use strict';
+const { EventEmitter } = require('events');
+const { screen, Screen } = process.electronBinding('screen');
+// Screen is an EventEmitter.
+Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
+EventEmitter.call(screen);
+module.exports = screen;
+//# sourceMappingURL=screen.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/session.js b/electronasar/development/browser/api/session.js
new file mode 100644
index 0000000..62eb8f1
--- /dev/null
+++ b/electronasar/development/browser/api/session.js
@@ -0,0 +1,41 @@
+'use strict';
+const { EventEmitter } = require('events');
+const { app, deprecate } = require('electron');
+const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session');
+// Public API.
+Object.defineProperties(exports, {
+ defaultSession: {
+ enumerable: true,
+ get() { return fromPartition(''); }
+ },
+ fromPartition: {
+ enumerable: true,
+ value: fromPartition
+ }
+});
+Object.setPrototypeOf(Session.prototype, EventEmitter.prototype);
+Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype);
+Session.prototype._init = function () {
+ 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
\ No newline at end of file
diff --git a/electronasar/development/browser/api/system-preferences.js b/electronasar/development/browser/api/system-preferences.js
new file mode 100644
index 0000000..40068f6
--- /dev/null
+++ b/electronasar/development/browser/api/system-preferences.js
@@ -0,0 +1,8 @@
+'use strict';
+const { EventEmitter } = require('events');
+const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences');
+// SystemPreferences is an EventEmitter.
+Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
+EventEmitter.call(systemPreferences);
+module.exports = systemPreferences;
+//# sourceMappingURL=system-preferences.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/top-level-window.js b/electronasar/development/browser/api/top-level-window.js
new file mode 100644
index 0000000..9b73c1c
--- /dev/null
+++ b/electronasar/development/browser/api/top-level-window.js
@@ -0,0 +1,20 @@
+'use strict';
+const electron = require('electron');
+const { EventEmitter } = require('events');
+const { TopLevelWindow } = process.electronBinding('top_level_window');
+Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype);
+TopLevelWindow.prototype._init = function () {
+ // Avoid recursive require.
+ const { app } = electron;
+ // Simulate the application menu on platforms other than macOS.
+ if (process.platform !== 'darwin') {
+ const menu = app.applicationMenu;
+ if (menu)
+ this.setMenu(menu);
+ }
+};
+TopLevelWindow.getFocusedWindow = () => {
+ return TopLevelWindow.getAllWindows().find((win) => win.isFocused());
+};
+module.exports = TopLevelWindow;
+//# sourceMappingURL=top-level-window.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/touch-bar.js b/electronasar/development/browser/api/touch-bar.js
new file mode 100644
index 0000000..1fd3f8c
--- /dev/null
+++ b/electronasar/development/browser/api/touch-bar.js
@@ -0,0 +1,311 @@
+'use strict';
+const { EventEmitter } = require('events');
+let nextItemID = 1;
+class TouchBar extends EventEmitter {
+ // Bind a touch bar to a window
+ static _setOnWindow(touchBar, window) {
+ if (window._touchBar != null) {
+ 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 (options == null) {
+ throw new Error('Must specify options object as first argument');
+ }
+ 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)) {
+ 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);
+ }
+ }
+ };
+ 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();
+ }
+}
+class TouchBarItem extends EventEmitter {
+ constructor() {
+ super();
+ this._addImmutableProperty('id', `${nextItemID++}`);
+ this._parents = [];
+ }
+ _addImmutableProperty(name, value) {
+ Object.defineProperty(this, name, {
+ get: function () {
+ return value;
+ },
+ set: function () {
+ throw new Error(`Cannot override property ${name}`);
+ },
+ enumerable: true,
+ configurable: false
+ });
+ }
+ _addLiveProperty(name, initialValue) {
+ const privateName = `_${name}`;
+ this[privateName] = initialValue;
+ Object.defineProperty(this, name, {
+ get: function () {
+ return this[privateName];
+ },
+ set: function (value) {
+ this[privateName] = value;
+ this.emit('change', this);
+ },
+ enumerable: true
+ });
+ }
+ _addParent(item) {
+ const existing = this._parents.some(test => test.id === item.id);
+ if (!existing) {
+ this._parents.push({
+ id: item.id,
+ type: item.type
+ });
+ }
+ }
+}
+TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ this._addImmutableProperty('type', 'button');
+ const { click, icon, iconPosition, label, backgroundColor } = config;
+ this._addLiveProperty('label', label);
+ this._addLiveProperty('backgroundColor', backgroundColor);
+ this._addLiveProperty('icon', icon);
+ this._addLiveProperty('iconPosition', iconPosition);
+ if (typeof click === 'function') {
+ this._addImmutableProperty('onInteraction', () => {
+ config.click();
+ });
+ }
+ }
+};
+TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ this._addImmutableProperty('type', 'colorpicker');
+ const { availableColors, change, selectedColor } = config;
+ this._addLiveProperty('availableColors', availableColors);
+ this._addLiveProperty('selectedColor', selectedColor);
+ if (typeof change === 'function') {
+ this._addImmutableProperty('onInteraction', (details) => {
+ this._selectedColor = details.color;
+ change(details.color);
+ });
+ }
+ }
+};
+TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ this._addImmutableProperty('type', 'group');
+ const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
+ this._addLiveProperty('child', defaultChild);
+ this.child.ordereredItems.forEach((item) => item._addParent(this));
+ }
+};
+TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ this._addImmutableProperty('type', 'label');
+ this._addLiveProperty('label', config.label);
+ this._addLiveProperty('textColor', config.textColor);
+ }
+};
+TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ this._addImmutableProperty('type', 'popover');
+ this._addLiveProperty('label', config.label);
+ this._addLiveProperty('icon', config.icon);
+ this._addLiveProperty('showCloseButton', config.showCloseButton);
+ const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
+ this._addLiveProperty('child', defaultChild);
+ this.child.ordereredItems.forEach((item) => item._addParent(this));
+ }
+};
+TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ this._addImmutableProperty('type', 'slider');
+ const { change, label, minValue, maxValue, value } = config;
+ this._addLiveProperty('label', label);
+ this._addLiveProperty('minValue', minValue);
+ this._addLiveProperty('maxValue', maxValue);
+ this._addLiveProperty('value', value);
+ if (typeof change === 'function') {
+ this._addImmutableProperty('onInteraction', (details) => {
+ this._value = details.value;
+ change(details.value);
+ });
+ }
+ }
+};
+TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ this._addImmutableProperty('type', 'spacer');
+ this._addImmutableProperty('size', config.size);
+ }
+};
+TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ const { segmentStyle, segments, selectedIndex, change, mode } = config;
+ this._addImmutableProperty('type', 'segmented_control');
+ this._addLiveProperty('segmentStyle', segmentStyle);
+ this._addLiveProperty('segments', segments || []);
+ this._addLiveProperty('selectedIndex', selectedIndex);
+ this._addLiveProperty('mode', mode);
+ if (typeof change === 'function') {
+ this._addImmutableProperty('onInteraction', (details) => {
+ this._selectedIndex = details.selectedIndex;
+ change(details.selectedIndex, details.isSelected);
+ });
+ }
+ }
+};
+TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem {
+ constructor(config) {
+ super();
+ if (config == null)
+ config = {};
+ const { items, selectedStyle, overlayStyle, showArrowButtons, continuous, mode } = config;
+ let { select, highlight } = config;
+ this._addImmutableProperty('type', 'scrubber');
+ this._addLiveProperty('items', items);
+ this._addLiveProperty('selectedStyle', selectedStyle || null);
+ this._addLiveProperty('overlayStyle', overlayStyle || null);
+ this._addLiveProperty('showArrowButtons', showArrowButtons || false);
+ this._addLiveProperty('mode', mode || 'free');
+ this._addLiveProperty('continuous', typeof continuous === 'undefined' ? true : continuous);
+ if (typeof select === 'function' || typeof highlight === 'function') {
+ if (select == null)
+ select = () => { };
+ if (highlight == null)
+ highlight = () => { };
+ this._addImmutableProperty('onInteraction', (details) => {
+ if (details.type === 'select' && typeof select === 'function') {
+ select(details.selectedIndex);
+ }
+ else if (details.type === 'highlight' && typeof highlight === 'function') {
+ highlight(details.highlightedIndex);
+ }
+ });
+ }
+ }
+};
+module.exports = TouchBar;
+//# sourceMappingURL=touch-bar.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/tray.js b/electronasar/development/browser/api/tray.js
new file mode 100644
index 0000000..7d053dc
--- /dev/null
+++ b/electronasar/development/browser/api/tray.js
@@ -0,0 +1,9 @@
+'use strict';
+const { EventEmitter } = require('events');
+const { deprecate } = require('electron');
+const { Tray } = process.electronBinding('tray');
+Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype);
+// Deprecations
+Tray.prototype.setHighlightMode = deprecate.removeFunction(Tray.prototype.setHighlightMode, 'setHighlightMode');
+module.exports = Tray;
+//# sourceMappingURL=tray.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/view.js b/electronasar/development/browser/api/view.js
new file mode 100644
index 0000000..41032f1
--- /dev/null
+++ b/electronasar/development/browser/api/view.js
@@ -0,0 +1,8 @@
+'use strict';
+const { EventEmitter } = require('events');
+const { View } = process.electronBinding('view');
+Object.setPrototypeOf(View.prototype, EventEmitter.prototype);
+View.prototype._init = function () {
+};
+module.exports = View;
+//# sourceMappingURL=view.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/web-contents-view.js b/electronasar/development/browser/api/web-contents-view.js
new file mode 100644
index 0000000..726d6fc
--- /dev/null
+++ b/electronasar/development/browser/api/web-contents-view.js
@@ -0,0 +1,11 @@
+'use strict';
+const electron = require('electron');
+const { View } = electron;
+const { WebContentsView } = process.electronBinding('web_contents_view');
+Object.setPrototypeOf(WebContentsView.prototype, View.prototype);
+WebContentsView.prototype._init = function () {
+ // Call parent class's _init.
+ View.prototype._init.call(this);
+};
+module.exports = WebContentsView;
+//# sourceMappingURL=web-contents-view.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/api/web-contents.js b/electronasar/development/browser/api/web-contents.js
new file mode 100644
index 0000000..898fa56
--- /dev/null
+++ b/electronasar/development/browser/api/web-contents.js
@@ -0,0 +1,447 @@
+'use strict';
+const features = process.electronBinding('features');
+const { EventEmitter } = require('events');
+const electron = require('electron');
+const path = require('path');
+const url = require('url');
+const { app, ipcMain, session, deprecate } = electron;
+const NavigationController = require('@electron/internal/browser/navigation-controller');
+const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
+const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
+const errorUtils = require('@electron/internal/common/error-utils');
+// session is not used here, the purpose is to make sure session is initalized
+// before the webContents module.
+// eslint-disable-next-line
+session;
+let nextId = 0;
+const getNextId = function () {
+ return ++nextId;
+};
+// Stock page sizes
+const PDFPageSizes = {
+ A5: {
+ custom_display_name: 'A5',
+ height_microns: 210000,
+ name: 'ISO_A5',
+ width_microns: 148000
+ },
+ A4: {
+ custom_display_name: 'A4',
+ height_microns: 297000,
+ name: 'ISO_A4',
+ is_default: 'true',
+ width_microns: 210000
+ },
+ A3: {
+ custom_display_name: 'A3',
+ height_microns: 420000,
+ name: 'ISO_A3',
+ width_microns: 297000
+ },
+ Legal: {
+ custom_display_name: 'Legal',
+ height_microns: 355600,
+ name: 'NA_LEGAL',
+ width_microns: 215900
+ },
+ Letter: {
+ custom_display_name: 'Letter',
+ height_microns: 279400,
+ name: 'NA_LETTER',
+ width_microns: 215900
+ },
+ Tabloid: {
+ height_microns: 431800,
+ name: 'NA_LEDGER',
+ width_microns: 279400,
+ custom_display_name: 'Tabloid'
+ }
+};
+// Default printing setting
+const defaultPrintingSetting = {
+ pageRage: [],
+ mediaSize: {},
+ landscape: false,
+ color: 2,
+ headerFooterEnabled: false,
+ marginsType: 0,
+ isFirstRequest: false,
+ requestID: getNextId(),
+ previewUIID: 0,
+ previewModifiable: true,
+ printToPDF: true,
+ printWithCloudPrint: false,
+ printWithPrivet: false,
+ printWithExtension: false,
+ pagesPerSheet: 1,
+ deviceName: 'Save as PDF',
+ generateDraftData: true,
+ fitToPageEnabled: false,
+ scaleFactor: 1,
+ dpiHorizontal: 72,
+ dpiVertical: 72,
+ rasterizePDF: false,
+ duplex: 0,
+ copies: 1,
+ collate: true,
+ shouldPrintBackgrounds: false,
+ shouldPrintSelectionOnly: false
+};
+// JavaScript implementations of WebContents.
+const binding = process.electronBinding('web_contents');
+const { WebContents } = binding;
+Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype);
+Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype);
+// WebContents::send(channel, args..)
+// WebContents::sendToAll(channel, args..)
+WebContents.prototype.send = function (channel, ...args) {
+ if (typeof channel !== 'string') {
+ throw new Error('Missing required channel argument');
+ }
+ const internal = false;
+ const sendToAll = false;
+ return this._send(internal, sendToAll, channel, args);
+};
+WebContents.prototype.sendToAll = function (channel, ...args) {
+ if (typeof channel !== 'string') {
+ throw new Error('Missing required channel argument');
+ }
+ const internal = false;
+ const sendToAll = true;
+ return this._send(internal, sendToAll, channel, args);
+};
+WebContents.prototype._sendInternal = function (channel, ...args) {
+ if (typeof channel !== 'string') {
+ throw new Error('Missing required channel argument');
+ }
+ const internal = true;
+ const sendToAll = false;
+ return this._send(internal, sendToAll, channel, args);
+};
+WebContents.prototype._sendInternalToAll = function (channel, ...args) {
+ if (typeof channel !== 'string') {
+ throw new Error('Missing required channel argument');
+ }
+ const internal = true;
+ const sendToAll = true;
+ return this._send(internal, sendToAll, channel, args);
+};
+WebContents.prototype.sendToFrame = function (frameId, channel, ...args) {
+ if (typeof channel !== 'string') {
+ throw new Error('Missing required channel argument');
+ }
+ else if (typeof frameId !== 'number') {
+ throw new Error('Missing required frameId argument');
+ }
+ const internal = false;
+ const sendToAll = false;
+ return this._sendToFrame(internal, sendToAll, frameId, channel, args);
+};
+WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) {
+ if (typeof channel !== 'string') {
+ throw new Error('Missing required channel argument');
+ }
+ else if (typeof frameId !== 'number') {
+ throw new Error('Missing required frameId argument');
+ }
+ const internal = true;
+ const sendToAll = false;
+ return this._sendToFrame(internal, sendToAll, frameId, channel, args);
+};
+// Following methods are mapped to webFrame.
+const webFrameMethods = [
+ 'insertCSS',
+ 'insertText',
+ 'setLayoutZoomLevelLimits',
+ 'setVisualZoomLevelLimits'
+];
+for (const method of webFrameMethods) {
+ WebContents.prototype[method] = function (...args) {
+ ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args);
+ };
+}
+const executeJavaScript = (contents, code, hasUserGesture) => {
+ return ipcMainUtils.invokeInWebContents(contents, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture);
+};
+// Make sure WebContents::executeJavaScript would run the code only when the
+// WebContents has been loaded.
+WebContents.prototype.executeJavaScript = function (code, hasUserGesture) {
+ if (this.getURL() && !this.isLoadingMainFrame()) {
+ return executeJavaScript(this, code, hasUserGesture);
+ }
+ else {
+ return new Promise((resolve, reject) => {
+ this.once('did-stop-loading', () => {
+ executeJavaScript(this, code, hasUserGesture).then(resolve, reject);
+ });
+ });
+ }
+};
+// TODO(codebytere): remove when promisifications is complete
+const nativeZoomLevel = WebContents.prototype.getZoomLevel;
+WebContents.prototype.getZoomLevel = function (callback) {
+ if (callback == null) {
+ return nativeZoomLevel.call(this);
+ }
+ else {
+ process.nextTick(() => {
+ callback(nativeZoomLevel.call(this));
+ });
+ }
+};
+// TODO(codebytere): remove when promisifications is complete
+const nativeZoomFactor = WebContents.prototype.getZoomFactor;
+WebContents.prototype.getZoomFactor = function (callback) {
+ if (callback == null) {
+ return nativeZoomFactor.call(this);
+ }
+ else {
+ process.nextTick(() => {
+ callback(nativeZoomFactor.call(this));
+ });
+ }
+};
+WebContents.prototype.takeHeapSnapshot = function (filePath) {
+ return new Promise((resolve, reject) => {
+ this._takeHeapSnapshot(filePath, (success) => {
+ if (success) {
+ resolve();
+ }
+ else {
+ reject(new Error('takeHeapSnapshot failed'));
+ }
+ });
+ });
+};
+// Translate the options of printToPDF.
+WebContents.prototype.printToPDF = function (options) {
+ const printingSetting = Object.assign({}, defaultPrintingSetting);
+ if (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 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) {
+ if (features.isPrintingEnabled()) {
+ this._print(...args);
+ }
+ else {
+ console.error('Error: Printing feature is disabled.');
+ }
+};
+WebContents.prototype.getPrinters = function () {
+ if (features.isPrintingEnabled()) {
+ return this._getPrinters();
+ }
+ else {
+ console.error('Error: Printing feature is disabled.');
+ }
+};
+WebContents.prototype.loadFile = function (filePath, options = {}) {
+ if (typeof filePath !== 'string') {
+ throw new Error('Must pass filePath as a string');
+ }
+ const { query, search, hash } = options;
+ return this.loadURL(url.format({
+ protocol: 'file',
+ slashes: true,
+ pathname: path.resolve(app.getAppPath(), filePath),
+ query,
+ 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.savePage = deprecate.promisify(WebContents.prototype.savePage);
+const addReplyToEvent = (event) => {
+ event.reply = (...args) => {
+ event.sender.sendToFrame(event.frameId, ...args);
+ };
+};
+const addReplyInternalToEvent = (event) => {
+ Object.defineProperty(event, '_replyInternal', {
+ configurable: false,
+ enumerable: false,
+ value: (...args) => {
+ event.sender._sendToFrameInternal(event.frameId, ...args);
+ }
+ });
+};
+const addReturnValueToEvent = (event) => {
+ Object.defineProperty(event, 'returnValue', {
+ set: (value) => event.sendReply([value]),
+ get: () => { }
+ });
+};
+// Add JavaScript wrappers for WebContents class.
+WebContents.prototype._init = function () {
+ // The navigation controller.
+ NavigationController.call(this, this);
+ // Every remote callback from renderer process would add a listener to the
+ // render-view-deleted event, so ignore the listeners warning.
+ this.setMaxListeners(0);
+ // Dispatch IPC messages to the ipc module.
+ this.on('-ipc-message', function (event, internal, channel, args) {
+ if (internal) {
+ addReplyInternalToEvent(event);
+ ipcMainInternal.emit(channel, event, ...args);
+ }
+ else {
+ addReplyToEvent(event);
+ this.emit('ipc-message', event, channel, ...args);
+ ipcMain.emit(channel, event, ...args);
+ }
+ });
+ this.on('-ipc-message-sync', function (event, internal, channel, args) {
+ addReturnValueToEvent(event);
+ if (internal) {
+ addReplyInternalToEvent(event);
+ ipcMainInternal.emit(channel, event, ...args);
+ }
+ else {
+ addReplyToEvent(event);
+ this.emit('ipc-message-sync', event, channel, ...args);
+ ipcMain.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 = [
+ '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.
+const { Debugger } = process.electronBinding('debugger');
+Debugger.prototype.sendCommand = deprecate.promisify(Debugger.prototype.sendCommand);
+Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype);
+// Public APIs.
+module.exports = {
+ create(options = {}) {
+ return binding.create(options);
+ },
+ fromId(id) {
+ return binding.fromId(id);
+ },
+ getFocusedWebContents() {
+ let focused = null;
+ for (const contents of binding.getAllWebContents()) {
+ if (!contents.isFocused())
+ continue;
+ if (focused == null)
+ focused = contents;
+ // Return webview web contents which may be embedded inside another
+ // web contents that is also reporting as focused
+ if (contents.getType() === 'webview')
+ return contents;
+ }
+ return focused;
+ },
+ getAllWebContents() {
+ return binding.getAllWebContents();
+ }
+};
+//# sourceMappingURL=web-contents.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/chrome-extension.js b/electronasar/development/browser/chrome-extension.js
new file mode 100644
index 0000000..2736389
--- /dev/null
+++ b/electronasar/development/browser/chrome-extension.js
@@ -0,0 +1,490 @@
+'use strict';
+const { app, webContents, BrowserWindow } = require('electron');
+const { getAllWebContents } = process.electronBinding('web_contents');
+const renderProcessPreferences = process.electronBinding('render_process_preferences').forAllWebContents();
+const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
+const { Buffer } = require('buffer');
+const fs = require('fs');
+const path = require('path');
+const url = require('url');
+const util = require('util');
+const readFile = util.promisify(fs.readFile);
+const writeFile = util.promisify(fs.writeFile);
+// Mapping between extensionId(hostname) and manifest.
+const manifestMap = {}; // extensionId => manifest
+const manifestNameMap = {}; // name => manifest
+const devToolsExtensionNames = new Set();
+const generateExtensionIdFromName = function (name) {
+ return name.replace(/[\W_]+/g, '-').toLowerCase();
+};
+const isWindowOrWebView = function (webContents) {
+ const type = webContents.getType();
+ return type === 'window' || type === 'webview';
+};
+const isBackgroundPage = function (webContents) {
+ return webContents.getType() === 'backgroundPage';
+};
+// Create or get manifest object from |srcDirectory|.
+const getManifestFromPath = function (srcDirectory) {
+ let manifest;
+ let manifestContent;
+ try {
+ manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json'));
+ }
+ catch (readError) {
+ console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`);
+ console.warn(readError.stack || readError);
+ throw readError;
+ }
+ try {
+ manifest = JSON.parse(manifestContent);
+ }
+ catch (parseError) {
+ console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`);
+ console.warn(parseError.stack || parseError);
+ throw parseError;
+ }
+ if (!manifestNameMap[manifest.name]) {
+ const extensionId = generateExtensionIdFromName(manifest.name);
+ manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest;
+ Object.assign(manifest, {
+ srcDirectory: srcDirectory,
+ extensionId: extensionId,
+ // We can not use 'file://' directly because all resources in the extension
+ // will be treated as relative to the root in Chrome.
+ startPage: url.format({
+ protocol: 'chrome-extension',
+ 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 ``;
+ }).join('');
+ html = Buffer.from(`
${scripts}`);
+ }
+ 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',
+ slashes: true,
+ hostname: manifest.extensionId,
+ pathname: name
+ }));
+};
+const removeBackgroundPages = function (manifest) {
+ if (!backgroundPages[manifest.extensionId])
+ return;
+ backgroundPages[manifest.extensionId].webContents.destroy();
+ delete backgroundPages[manifest.extensionId];
+};
+const sendToBackgroundPages = function (...args) {
+ for (const page of Object.values(backgroundPages)) {
+ if (!page.webContents.isDestroyed()) {
+ page.webContents._sendInternalToAll(...args);
+ }
+ }
+};
+// Dispatch web contents events to Chrome APIs
+const hookWebContentsEvents = function (webContents) {
+ const tabId = webContents.id;
+ sendToBackgroundPages('CHROME_TABS_ONCREATED');
+ webContents.on('will-navigate', (event, url) => {
+ sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
+ frameId: 0,
+ parentFrameId: -1,
+ processId: webContents.getProcessId(),
+ tabId: tabId,
+ timeStamp: Date.now(),
+ url: url
+ });
+ });
+ webContents.on('did-navigate', (event, url) => {
+ sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', {
+ frameId: 0,
+ parentFrameId: -1,
+ processId: webContents.getProcessId(),
+ tabId: tabId,
+ timeStamp: Date.now(),
+ url: url
+ });
+ });
+ webContents.once('destroyed', () => {
+ sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId);
+ });
+};
+// Handle the chrome.* API messages.
+let nextId = 0;
+ipcMainUtils.handle('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
+ if (isBackgroundPage(event.sender)) {
+ throw new Error('chrome.runtime.connect is not supported in background page');
+ }
+ const page = backgroundPages[extensionId];
+ if (!page || page.webContents.isDestroyed()) {
+ throw new Error(`Connect to unknown extension ${extensionId}`);
+ }
+ const tabId = page.webContents.id;
+ const portId = ++nextId;
+ event.sender.once('render-view-deleted', () => {
+ if (page.webContents.isDestroyed())
+ return;
+ page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`);
+ });
+ page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo);
+ return { tabId, portId };
+});
+ipcMainUtils.handle('CHROME_EXTENSION_MANIFEST', function (event, extensionId) {
+ const manifest = manifestMap[extensionId];
+ if (!manifest) {
+ throw new Error(`Invalid extensionId: ${extensionId}`);
+ }
+ return manifest;
+});
+ipcMainUtils.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) {
+ if (isBackgroundPage(event.sender)) {
+ throw new Error('chrome.runtime.sendMessage is not supported in background page');
+ }
+ const page = backgroundPages[extensionId];
+ if (!page || page.webContents.isDestroyed()) {
+ throw new Error(`Connect to unknown extension ${extensionId}`);
+ }
+ return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message);
+});
+ipcMainUtils.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) {
+ const contents = webContents.fromId(tabId);
+ if (!contents) {
+ throw new Error(`Sending message to unknown tab ${tabId}`);
+ }
+ const senderTabId = isBackgroundPage(event.sender) ? null : event.sender.id;
+ return ipcMainUtils.invokeInWebContents(contents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message);
+});
+const getLanguage = () => {
+ return app.getLocale().replace(/-.*$/, '').toLowerCase();
+};
+const getMessagesPath = (extensionId) => {
+ const metadata = manifestMap[extensionId];
+ if (!metadata) {
+ throw new Error(`Invalid extensionId: ${extensionId}`);
+ }
+ 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 { protocol } = url.parse(pageURL);
+ return protocol === 'chrome-extension:';
+};
+const assertChromeExtension = function (contents, api) {
+ const pageURL = contents._getURL();
+ if (!isChromeExtension(pageURL)) {
+ console.error(`Blocked ${pageURL} from calling ${api}`);
+ throw new Error(`Blocked ${api}`);
+ }
+};
+ipcMainUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) {
+ assertChromeExtension(event.sender, 'chrome.tabs.executeScript()');
+ const contents = webContents.fromId(tabId);
+ if (!contents) {
+ throw new Error(`Sending message to unknown tab ${tabId}`);
+ }
+ let code, url;
+ if (details.file) {
+ const manifest = manifestMap[extensionId];
+ code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file)));
+ url = `chrome-extension://${extensionId}${details.file}`;
+ }
+ else {
+ code = details.code;
+ url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`;
+ }
+ return ipcMainUtils.invokeInWebContents(contents, false, 'CHROME_TABS_EXECUTE_SCRIPT', extensionId, url, code);
+});
+// Transfer the content scripts to renderer.
+const contentScripts = {};
+const injectContentScripts = function (manifest) {
+ if (contentScripts[manifest.name] || !manifest.content_scripts)
+ return;
+ const readArrayOfFiles = function (relativePath) {
+ return {
+ url: `chrome-extension://${manifest.extensionId}/${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 removeContentScripts = function (manifest) {
+ if (!contentScripts[manifest.name])
+ return;
+ renderProcessPreferences.removeEntry(contentScripts[manifest.name]);
+ delete contentScripts[manifest.name];
+};
+// Transfer the |manifest| to a format that can be recognized by the
+// |DevToolsAPI.addExtensions|.
+const manifestToExtensionInfo = function (manifest) {
+ return {
+ startPage: manifest.startPage,
+ srcDirectory: manifest.srcDirectory,
+ name: manifest.name,
+ exposeExperimentalAPIs: true
+ };
+};
+// Load the extensions for the window.
+const loadExtension = function (manifest) {
+ startBackgroundPages(manifest);
+ injectContentScripts(manifest);
+};
+const loadDevToolsExtensions = function (win, manifests) {
+ if (!win.devToolsWebContents)
+ return;
+ manifests.forEach(loadExtension);
+ const extensionInfoArray = manifests.map(manifestToExtensionInfo);
+ extensionInfoArray.forEach((extension) => {
+ win.devToolsWebContents._grantOriginAccess(extension.startPage);
+ });
+ win.devToolsWebContents.executeJavaScript(`InspectorFrontendAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`);
+};
+app.on('web-contents-created', function (event, webContents) {
+ if (!isWindowOrWebView(webContents))
+ return;
+ hookWebContentsEvents(webContents);
+ webContents.on('devtools-opened', function () {
+ loadDevToolsExtensions(webContents, Object.values(manifestMap));
+ });
+});
+// The chrome-extension: can map a extension URL request to real file path.
+const chromeExtensionHandler = function (request, callback) {
+ const parsed = url.parse(request.url);
+ if (!parsed.hostname || !parsed.path)
+ return callback();
+ const manifest = manifestMap[parsed.hostname];
+ if (!manifest)
+ return callback();
+ const page = backgroundPages[parsed.hostname];
+ if (page && parsed.path === `/${page.name}`) {
+ // Disabled due to false positive in StandardJS
+ // eslint-disable-next-line standard/no-callback-literal
+ return callback({
+ mimeType: 'text/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);
+ }
+ });
+};
+app.on('session-created', function (ses) {
+ ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
+ if (error) {
+ console.error(`Unable to register chrome-extension protocol: ${error}`);
+ }
+ });
+});
+// The persistent path of "DevTools Extensions" preference file.
+let loadedDevToolsExtensionsPath = null;
+app.on('will-quit', function () {
+ try {
+ const loadedDevToolsExtensions = Array.from(devToolsExtensionNames)
+ .map(name => manifestNameMap[name].srcDirectory);
+ if (loadedDevToolsExtensions.length > 0) {
+ try {
+ fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath));
+ }
+ catch (_a) {
+ // Ignore error
+ }
+ fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions));
+ }
+ else {
+ fs.unlinkSync(loadedDevToolsExtensionsPath);
+ }
+ }
+ catch (_b) {
+ // Ignore error
+ }
+});
+// We can not use protocol or BrowserWindow until app is ready.
+app.once('ready', function () {
+ // The public API to add/remove extensions.
+ BrowserWindow.addExtension = function (srcDirectory) {
+ const manifest = getManifestFromPath(srcDirectory);
+ if (manifest) {
+ loadExtension(manifest);
+ for (const webContents of getAllWebContents()) {
+ if (isWindowOrWebView(webContents)) {
+ 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);
+ }
+ }
+ }
+ catch (error) {
+ if (process.env.ELECTRON_ENABLE_LOGGING && error.code !== 'ENOENT') {
+ console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath);
+ console.error(error);
+ }
+ }
+});
+//# sourceMappingURL=chrome-extension.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/crash-reporter-init.js b/electronasar/development/browser/crash-reporter-init.js
new file mode 100644
index 0000000..917ae67
--- /dev/null
+++ b/electronasar/development/browser/crash-reporter-init.js
@@ -0,0 +1,23 @@
+'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
\ No newline at end of file
diff --git a/electronasar/development/browser/default-menu.js b/electronasar/development/browser/default-menu.js
new file mode 100644
index 0000000..8e7425c
--- /dev/null
+++ b/electronasar/development/browser/default-menu.js
@@ -0,0 +1,50 @@
+"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
\ No newline at end of file
diff --git a/electronasar/development/browser/desktop-capturer.js b/electronasar/development/browser/desktop-capturer.js
new file mode 100644
index 0000000..b012a0c
--- /dev/null
+++ b/electronasar/development/browser/desktop-capturer.js
@@ -0,0 +1,86 @@
+'use strict';
+const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
+const { createDesktopCapturer } = process.electronBinding('desktop_capturer');
+const eventBinding = process.electronBinding('event');
+const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
+let currentlyRunning = [];
+ipcMainUtils.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, fetchWindowIcons) => {
+ const customEvent = eventBinding.createWithSender(event.sender);
+ event.sender.emit('desktop-capturer-get-sources', customEvent);
+ if (customEvent.defaultPrevented) {
+ return [];
+ }
+ const options = {
+ captureWindow,
+ captureScreen,
+ thumbnailSize,
+ fetchWindowIcons
+ };
+ for (const running of currentlyRunning) {
+ if (deepEqual(running.options, options)) {
+ // If a request is currently running for the same options
+ // return that promise
+ return running.getSources;
+ }
+ }
+ const getSources = new Promise((resolve, reject) => {
+ const stopRunning = () => {
+ // Remove from currentlyRunning once we resolve or reject
+ currentlyRunning = currentlyRunning.filter(running => running.options !== options);
+ };
+ const request = {
+ options,
+ 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
\ No newline at end of file
diff --git a/electronasar/development/browser/devtools.js b/electronasar/development/browser/devtools.js
new file mode 100644
index 0000000..fde2e96
--- /dev/null
+++ b/electronasar/development/browser/devtools.js
@@ -0,0 +1,93 @@
+'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
\ No newline at end of file
diff --git a/electronasar/development/browser/guest-view-manager.js b/electronasar/development/browser/guest-view-manager.js
new file mode 100644
index 0000000..02b6290
--- /dev/null
+++ b/electronasar/development/browser/guest-view-manager.js
@@ -0,0 +1,365 @@
+'use strict';
+const { webContents } = require('electron');
+const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
+const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
+const parseFeaturesString = require('@electron/internal/common/parse-features-string');
+const { syncMethods, asyncCallbackMethods, asyncPromiseMethods } = require('@electron/internal/common/web-view-methods');
+// Doesn't exist in early initialization.
+let webViewManager = null;
+const supportedWebViewEvents = [
+ 'load-commit',
+ 'did-attach',
+ 'did-finish-load',
+ 'did-fail-load',
+ 'did-frame-finish-load',
+ 'did-start-loading',
+ 'did-stop-loading',
+ 'dom-ready',
+ 'console-message',
+ 'context-menu',
+ 'devtools-opened',
+ 'devtools-closed',
+ 'devtools-focused',
+ 'new-window',
+ 'will-navigate',
+ 'did-start-navigation',
+ 'did-navigate',
+ 'did-frame-navigate',
+ 'did-navigate-in-page',
+ 'focus-change',
+ 'close',
+ 'crashed',
+ 'plugin-crashed',
+ 'destroyed',
+ 'page-title-updated',
+ 'page-favicon-updated',
+ 'enter-html-full-screen',
+ 'leave-html-full-screen',
+ 'media-started-playing',
+ 'media-paused',
+ 'found-in-page',
+ 'did-change-theme-color',
+ 'update-target-url'
+];
+const guestInstances = {};
+const embedderElementsMap = {};
+// Create a new guest instance.
+const createGuest = function (embedder, params) {
+ if (webViewManager == null) {
+ webViewManager = process.electronBinding('web_view_manager');
+ }
+ const guest = webContents.create({
+ isGuest: true,
+ partition: params.partition,
+ embedder: embedder
+ });
+ const guestInstanceId = guest.id;
+ 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);
+ }
+ });
+ // 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) {
+ 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);
+ }
+ }
+ });
+ return guestInstanceId;
+};
+// Attach the guest to an element of embedder.
+const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
+ const embedder = event.sender;
+ // Destroy the old guest when attaching.
+ const key = `${embedder.id}-${elementInstanceId}`;
+ const oldGuestInstanceId = embedderElementsMap[key];
+ if (oldGuestInstanceId != null) {
+ // Reattachment to the same guest is just a no-op.
+ if (oldGuestInstanceId === guestInstanceId) {
+ 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.
+ 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}`);
+ }
+ }
+ const webPreferences = {
+ guestInstanceId: guestInstanceId,
+ nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
+ nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
+ enableRemoteModule: params.enableremotemodule,
+ plugins: params.plugins,
+ zoomFactor: embedder.getZoomFactor(),
+ disablePopups: !params.allowpopups,
+ 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;
+ }
+ // 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.
+const detachGuest = function (embedder, guestInstanceId) {
+ const guestInstance = guestInstances[guestInstanceId];
+ if (embedder !== guestInstance.embedder) {
+ return;
+ }
+ webViewManager.removeGuest(embedder, guestInstanceId);
+ delete guestInstances[guestInstanceId];
+ const key = `${embedder.id}-${guestInstance.elementInstanceId}`;
+ delete embedderElementsMap[key];
+};
+// Once an embedder has had a guest attached we watch it for destruction to
+// destroy any remaining guests.
+const watchedEmbedders = new Set();
+const watchEmbedder = function (embedder) {
+ if (watchedEmbedders.has(embedder)) {
+ 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);
+ }
+ }
+ };
+ embedder.on('-window-visibility-change', onVisibilityChange);
+ embedder.once('will-destroy', () => {
+ // Usually the guestInstances is cleared when guest is destroyed, but it
+ // may happen that the embedder gets manually destroyed earlier than guest,
+ // and the embedder will be invalid in the usual code path.
+ for (const guestInstanceId in guestInstances) {
+ const guestInstance = guestInstances[guestInstanceId];
+ if (guestInstance.embedder === embedder) {
+ detachGuest(embedder, parseInt(guestInstanceId));
+ }
+ }
+ // Clear the listeners.
+ embedder.removeListener('-window-visibility-change', onVisibilityChange);
+ watchedEmbedders.delete(embedder);
+ });
+};
+const isWebViewTagEnabledCache = new WeakMap();
+const isWebViewTagEnabled = function (contents) {
+ if (!isWebViewTagEnabledCache.has(contents)) {
+ const webPreferences = contents.getLastWebPreferences() || {};
+ isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag);
+ }
+ return isWebViewTagEnabledCache.get(contents);
+};
+const handleMessage = function (channel, handler) {
+ ipcMainUtils.handle(channel, (event, ...args) => {
+ if (isWebViewTagEnabled(event.sender)) {
+ return handler(event, ...args);
+ }
+ else {
+ console.error(` IPC message ${channel} sent by WebContents with disabled (${event.sender.id})`);
+ throw new Error(' disabled');
+ }
+ });
+};
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) {
+ return createGuest(event.sender, params);
+});
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) {
+ try {
+ const guest = getGuestForWebContents(guestInstanceId, event.sender);
+ guest.detachFromOuterFrame();
+ }
+ catch (error) {
+ console.error(`Guest destroy failed: ${error}`);
+ }
+});
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
+ try {
+ attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params);
+ }
+ catch (error) {
+ console.error(`Guest attach failed: ${error}`);
+ }
+});
+// this message is sent by the actual
+ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
+ const guest = getGuest(guestInstanceId);
+ if (guest === event.sender) {
+ event.sender.emit('focus-change', {}, focus, guestInstanceId);
+ }
+ else {
+ console.error(`focus-change for guestInstanceId: ${guestInstanceId}`);
+ }
+});
+const allMethods = new Set([
+ ...syncMethods,
+ ...asyncCallbackMethods,
+ ...asyncPromiseMethods
+]);
+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}`);
+ }
+ return guest[method](...args);
+});
+// Returns WebContents from its guest id hosted in given webContents.
+const getGuestForWebContents = function (guestInstanceId, contents) {
+ const guest = getGuest(guestInstanceId);
+ if (!guest) {
+ throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`);
+ }
+ if (guest.hostWebContents !== contents) {
+ throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`);
+ }
+ return guest;
+};
+// Returns WebContents from its guest id.
+const getGuest = function (guestInstanceId) {
+ const guestInstance = guestInstances[guestInstanceId];
+ if (guestInstance != null)
+ return guestInstance.guest;
+};
+// Returns the embedder of the guest.
+const getEmbedder = function (guestInstanceId) {
+ const guestInstance = guestInstances[guestInstanceId];
+ if (guestInstance != null)
+ return guestInstance.embedder;
+};
+exports.getGuestForWebContents = getGuestForWebContents;
+exports.isWebViewTagEnabled = isWebViewTagEnabled;
+//# sourceMappingURL=guest-view-manager.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/guest-window-manager.js b/electronasar/development/browser/guest-window-manager.js
new file mode 100644
index 0000000..6cb2fc8
--- /dev/null
+++ b/electronasar/development/browser/guest-window-manager.js
@@ -0,0 +1,331 @@
+'use strict';
+const { BrowserWindow, webContents } = require('electron');
+const { isSameOrigin } = process.electronBinding('v8_util');
+const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
+const parseFeaturesString = require('@electron/internal/common/parse-features-string');
+const hasProp = {}.hasOwnProperty;
+const frameToGuest = new Map();
+// Security options that child windows will always inherit from parent windows
+const inheritedWebPreferences = new Map([
+ ['contextIsolation', true],
+ ['javascript', false],
+ ['nativeWindowOpen', true],
+ ['nodeIntegration', false],
+ ['enableRemoteModule', false],
+ ['sandbox', true],
+ ['webviewTag', false],
+ ['nodeIntegrationInSubFrames', false]
+]);
+// Copy attribute of |parent| to |child| if it is not defined in |child|.
+const mergeOptions = function (child, parent, visited) {
+ // Check for circular reference.
+ if (visited == null)
+ visited = new Set();
+ if (visited.has(parent))
+ return;
+ visited.add(parent);
+ for (const key in parent) {
+ if (key === 'isBrowserView')
+ continue;
+ if (!hasProp.call(parent, key))
+ continue;
+ if (key in child && key !== 'webPreferences')
+ continue;
+ const value = parent[key];
+ if (typeof value === 'object' && !Array.isArray(value)) {
+ child[key] = mergeOptions(child[key] || {}, value, visited);
+ }
+ else {
+ child[key] = value;
+ }
+ }
+ visited.delete(parent);
+ return child;
+};
+// Merge |options| with the |embedder|'s window's options.
+const mergeBrowserWindowOptions = function (embedder, options) {
+ if (options.webPreferences == null) {
+ 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 = Object.assign({}, embedder.browserWindowOptions, { show: win.isVisible() });
+ }
+ // Inherit the original options if it is a BrowserWindow.
+ mergeOptions(options, parentOptions);
+ }
+ else {
+ // Or only inherit webPreferences if it is a webview.
+ mergeOptions(options.webPreferences, embedder.getLastWebPreferences());
+ }
+ // 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;
+ }
+ }
+ // 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|
+const setupGuest = function (embedder, frameName, guest, options) {
+ // When |embedder| is destroyed we should also destroy attached guest, and if
+ // guest is closed by user then we should prevent |embedder| from double
+ // closing guest.
+ const guestId = guest.webContents.id;
+ const closedByEmbedder = function () {
+ guest.removeListener('closed', closedByUser);
+ guest.destroy();
+ };
+ const closedByUser = function () {
+ embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId);
+ embedder.removeListener('current-render-view-deleted', closedByEmbedder);
+ };
+ embedder.once('current-render-view-deleted', closedByEmbedder);
+ guest.once('closed', closedByUser);
+ if (frameName) {
+ frameToGuest.set(frameName, guest);
+ guest.frameName = frameName;
+ guest.once('closed', function () {
+ frameToGuest.delete(frameName);
+ });
+ }
+ return guestId;
+};
+// Create a new guest created by |embedder| with |options|.
+const createGuest = function (embedder, url, referrer, frameName, options, postData) {
+ let guest = frameToGuest.get(frameName);
+ if (frameName && (guest != null)) {
+ guest.loadURL(url);
+ return guest.webContents.id;
+ }
+ // Remember the embedder window's id.
+ if (options.webPreferences == null) {
+ options.webPreferences = {};
+ }
+ guest = new BrowserWindow(options);
+ if (!options.webContents) {
+ // We should not call `loadURL` if the window was constructed from an
+ // existing webContents (window.open in a sandboxed renderer).
+ //
+ // 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);
+};
+const getGuestWindow = function (guestContents) {
+ let guestWindow = BrowserWindow.fromWebContents(guestContents);
+ if (guestWindow == null) {
+ const hostContents = guestContents.hostWebContents;
+ if (hostContents != null) {
+ guestWindow = BrowserWindow.fromWebContents(hostContents);
+ }
+ }
+ return guestWindow;
+};
+const isChildWindow = function (sender, target) {
+ return target.getLastWebPreferences().openerId === sender.id;
+};
+const isRelatedWindow = function (sender, target) {
+ return isChildWindow(sender, target) || isChildWindow(target, sender);
+};
+const isScriptableWindow = function (sender, target) {
+ return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL());
+};
+const isNodeIntegrationEnabled = function (sender) {
+ return sender.getLastWebPreferences().nodeIntegration === true;
+};
+// Checks whether |sender| can access the |target|:
+const canAccessWindow = function (sender, target) {
+ return isChildWindow(sender, target) ||
+ isScriptableWindow(sender, target) ||
+ isNodeIntegrationEnabled(sender);
+};
+// Routed window.open messages with raw options
+ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
+ if (url == null || url === '')
+ url = 'about:blank';
+ if (frameName == null)
+ frameName = '';
+ if (features == null)
+ features = '';
+ const options = {};
+ const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor'];
+ const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag'];
+ const disposition = 'new-window';
+ // Used to store additional features
+ const additionalFeatures = [];
+ // Parse the features
+ parseFeaturesString(features, function (key, value) {
+ if (value === undefined) {
+ additionalFeatures.push(key);
+ }
+ else {
+ // Don't allow webPreferences to be set since it must be an object
+ // that cannot be directly overridden
+ if (key === 'webPreferences')
+ return;
+ if (webPreferences.includes(key)) {
+ if (options.webPreferences == null) {
+ options.webPreferences = {};
+ }
+ options.webPreferences[key] = value;
+ }
+ else {
+ options[key] = value;
+ }
+ }
+ });
+ if (options.left) {
+ if (options.x == null) {
+ options.x = options.left;
+ }
+ }
+ if (options.top) {
+ if (options.y == null) {
+ options.y = options.top;
+ }
+ }
+ 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);
+ }
+ }
+ 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
+ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer, frameName, disposition, options, additionalFeatures, postData) {
+ options = mergeBrowserWindowOptions(event.sender, options);
+ event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer);
+ const { newGuest } = event;
+ if ((event.sender.isGuest() && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) {
+ if (newGuest != null) {
+ if (options.webContents === newGuest.webContents) {
+ // the webContents is not changed, so set defaultPrevented to false to
+ // stop the callers of this event from destroying the webContents.
+ event.defaultPrevented = false;
+ }
+ event.returnValue = setupGuest(event.sender, frameName, newGuest, options);
+ }
+ else {
+ event.returnValue = null;
+ }
+ }
+ else {
+ 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);
+ if (guestContents == null)
+ return;
+ if (!canAccessWindow(event.sender, guestContents)) {
+ console.error(`Blocked ${event.sender.getURL()} from closing its opener.`);
+ return;
+ }
+ const guestWindow = getGuestWindow(guestContents);
+ if (guestWindow != null)
+ guestWindow.destroy();
+});
+const windowMethods = new Set([
+ 'focus',
+ 'blur'
+]);
+ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
+ const guestContents = webContents.fromId(guestId);
+ if (guestContents == null) {
+ 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;
+ return;
+ }
+ const guestWindow = getGuestWindow(guestContents);
+ if (guestWindow != 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 = '*';
+ }
+ const guestContents = webContents.fromId(guestId);
+ if (guestContents == null)
+ 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
+ // 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([
+ 'print',
+ 'executeJavaScript'
+]);
+ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
+ const guestContents = webContents.fromId(guestId);
+ if (guestContents == null)
+ return;
+ if (canAccessWindow(event.sender, guestContents) && webContentsMethods.has(method)) {
+ guestContents[method](...args);
+ }
+ else {
+ console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`);
+ }
+});
+const webContentsSyncMethods = new Set([
+ 'getURL',
+ 'loadURL'
+]);
+ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
+ const guestContents = webContents.fromId(guestId);
+ if (guestContents == null) {
+ event.returnValue = null;
+ return;
+ }
+ if (canAccessWindow(event.sender, guestContents) && webContentsSyncMethods.has(method)) {
+ event.returnValue = guestContents[method](...args);
+ }
+ else {
+ console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`);
+ event.returnValue = null;
+ }
+});
+//# sourceMappingURL=guest-window-manager.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/init.js b/electronasar/development/browser/init.js
new file mode 100644
index 0000000..7d92c1c
--- /dev/null
+++ b/electronasar/development/browser/init.js
@@ -0,0 +1,187 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const buffer_1 = require("buffer");
+const fs = require("fs");
+const path = require("path");
+const util = require("util");
+const v8 = require("v8");
+const Module = require('module');
+// We modified the original process.argv to let node.js load the init.js,
+// we need to restore it here.
+process.argv.splice(1, 1);
+// Clear search paths.
+require('../common/reset-search-paths');
+// Import common settings.
+require('@electron/internal/common/init');
+const globalPaths = Module.globalPaths;
+// Expose public APIs.
+globalPaths.push(path.join(__dirname, 'api', 'exports'));
+if (process.platform === 'win32') {
+ // Redirect node's console to use our own implementations, since node can not
+ // handle console output when running as GUI program.
+ const consoleLog = (format, ...args) => {
+ return process.log(util.format(format, ...args) + '\n');
+ };
+ const streamWrite = function (chunk, encoding, callback) {
+ if (buffer_1.Buffer.isBuffer(chunk)) {
+ chunk = chunk.toString(encoding);
+ }
+ process.log(chunk);
+ if (callback) {
+ callback();
+ }
+ return true;
+ };
+ console.log = console.error = console.warn = consoleLog;
+ process.stdout.write = process.stderr.write = streamWrite;
+}
+// Don't quit on fatal error.
+process.on('uncaughtException', function (error) {
+ // Do nothing if the user has a custom uncaught exception handler.
+ if (process.listeners('uncaughtException').length > 1) {
+ return;
+ }
+ // Show error in GUI.
+ // We can't import { dialog } at the top of this file as this file is
+ // responsible for setting up the require hook for the "electron" module
+ // so we import it inside the handler down here
+ Promise.resolve().then(() => require('electron')).then(({ dialog }) => {
+ const stack = error.stack ? error.stack : `${error.name}: ${error.message}`;
+ const message = 'Uncaught Exception:\n' + stack;
+ dialog.showErrorBox('A JavaScript error occurred in the main process', message);
+ });
+});
+// Emit 'exit' event on quit.
+const { app } = require('electron');
+app.on('quit', function (event, exitCode) {
+ process.emit('exit', exitCode);
+});
+if (process.platform === 'win32') {
+ // If we are a Squirrel.Windows-installed app, set app user model ID
+ // so that users don't have to do this.
+ //
+ // Squirrel packages are always of the form:
+ //
+ // PACKAGE-NAME
+ // - Update.exe
+ // - app-VERSION
+ // - OUREXE.exe
+ //
+ // Squirrel itself will always set the shortcut's App User Model ID to the
+ // form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
+ // app.setAppUserModelId with a matching identifier so that renderer processes
+ // will inherit this value.
+ const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe');
+ if (fs.existsSync(updateDotExe)) {
+ const packageDir = path.dirname(path.resolve(updateDotExe));
+ const packageName = path.basename(packageDir).replace(/\s/g, '');
+ 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.
+process.exit = app.exit;
+// Load the RPC server.
+require('@electron/internal/browser/rpc-server');
+// Load the guest view manager.
+require('@electron/internal/browser/guest-view-manager');
+require('@electron/internal/browser/guest-window-manager');
+// Now we try to load app's package.json.
+let packagePath = null;
+let packageJson = null;
+const searchPaths = ['app', 'app.asar', 'default_app.asar'];
+if (process.resourcesPath) {
+ for (packagePath of searchPaths) {
+ try {
+ packagePath = path.join(process.resourcesPath, packagePath);
+ packageJson = require(path.join(packagePath, 'package.json'));
+ break;
+ }
+ catch (_a) {
+ continue;
+ }
+ }
+}
+if (packageJson == null) {
+ process.nextTick(function () {
+ return process.exit(1);
+ });
+ throw new Error('Unable to find a valid app');
+}
+// Set application's version.
+if (packageJson.version != null) {
+ app.setVersion(packageJson.version);
+}
+// Set application's name.
+if (packageJson.productName != null) {
+ app.setName(`${packageJson.productName}`.trim());
+}
+else if (packageJson.name != null) {
+ app.setName(`${packageJson.name}`.trim());
+}
+// Set application's desktop name.
+if (packageJson.desktopName != null) {
+ app.setDesktopName(packageJson.desktopName);
+}
+else {
+ app.setDesktopName((app.getName()) + '.desktop');
+}
+// Set v8 flags
+if (packageJson.v8Flags != null) {
+ v8.setFlagsFromString(packageJson.v8Flags);
+}
+app._setDefaultAppPaths(packagePath);
+// Load the chrome devtools support.
+require('@electron/internal/browser/devtools');
+// Load the chrome extension support.
+require('@electron/internal/browser/chrome-extension');
+const features = process.electronBinding('features');
+if (features.isDesktopCapturerEnabled()) {
+ // Load internal desktop-capturer module.
+ require('@electron/internal/browser/desktop-capturer');
+}
+// Load protocol module to ensure it is populated on app ready
+require('@electron/internal/browser/api/protocol');
+// Set main startup script of the app.
+const mainStartupScript = packageJson.main || 'index.js';
+const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME'];
+function currentPlatformSupportsAppIndicator() {
+ if (process.platform !== 'linux')
+ return false;
+ const currentDesktop = process.env.XDG_CURRENT_DESKTOP;
+ if (!currentDesktop)
+ return false;
+ if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop))
+ return true;
+ // ubuntu based or derived session (default ubuntu one, communitheme…) supports
+ // indicator too.
+ if (/ubuntu/ig.test(currentDesktop))
+ return true;
+ return false;
+}
+// Workaround for electron/electron#5050 and electron/electron#9046
+if (currentPlatformSupportsAppIndicator()) {
+ process.env.XDG_CURRENT_DESKTOP = 'Unity';
+}
+// Quit when all windows are closed and no other one is listening to this.
+app.on('window-all-closed', () => {
+ if (app.listenerCount('window-all-closed') === 1) {
+ app.quit();
+ }
+});
+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
\ No newline at end of file
diff --git a/electronasar/development/browser/ipc-main-internal-utils.js b/electronasar/development/browser/ipc-main-internal-utils.js
new file mode 100644
index 0000000..6c57a46
--- /dev/null
+++ b/electronasar/development/browser/ipc-main-internal-utils.js
@@ -0,0 +1,53 @@
+"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
\ No newline at end of file
diff --git a/electronasar/development/browser/ipc-main-internal.js b/electronasar/development/browser/ipc-main-internal.js
new file mode 100644
index 0000000..9ec98d1
--- /dev/null
+++ b/electronasar/development/browser/ipc-main-internal.js
@@ -0,0 +1,8 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const events_1 = require("events");
+const emitter = new events_1.EventEmitter();
+// Do not throw exception when channel name is "error".
+emitter.on('error', () => { });
+exports.ipcMainInternal = emitter;
+//# sourceMappingURL=ipc-main-internal.js.map
\ No newline at end of file
diff --git a/electronasar/development/browser/navigation-controller.js b/electronasar/development/browser/navigation-controller.js
new file mode 100644
index 0000000..a76da41
--- /dev/null
+++ b/electronasar/development/browser/navigation-controller.js
@@ -0,0 +1,225 @@
+'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
\ No newline at end of file
diff --git a/electronasar/development/browser/objects-registry.js b/electronasar/development/browser/objects-registry.js
new file mode 100644
index 0000000..5e28043
--- /dev/null
+++ b/electronasar/development/browser/objects-registry.js
@@ -0,0 +1,121 @@
+'use strict';
+const v8Util = process.electronBinding('v8_util');
+const getOwnerKey = (webContents, 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
\ No newline at end of file
diff --git a/electronasar/development/browser/rpc-server.js b/electronasar/development/browser/rpc-server.js
new file mode 100644
index 0000000..1886c7c
--- /dev/null
+++ b/electronasar/development/browser/rpc-server.js
@@ -0,0 +1,482 @@
+'use strict';
+const electron = require('electron');
+const { EventEmitter } = require('events');
+const fs = require('fs');
+const util = require('util');
+const v8Util = process.electronBinding('v8_util');
+const eventBinding = process.electronBinding('event');
+const clipboard = process.electronBinding('clipboard');
+const { isPromise } = electron;
+const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
+const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
+const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
+const objectsRegistry = require('@electron/internal/browser/objects-registry');
+const guestViewManager = require('@electron/internal/browser/guest-view-manager');
+const bufferUtils = require('@electron/internal/common/buffer-utils');
+const errorUtils = require('@electron/internal/common/error-utils');
+const clipboardUtils = require('@electron/internal/common/clipboard-utils');
+const hasProp = {}.hasOwnProperty;
+// The internal properties of Function.
+const FUNCTION_PROPERTIES = [
+ 'length', 'name', 'arguments', 'caller', 'prototype'
+];
+// The remote functions in renderer processes.
+// id => Function
+const rendererFunctions = v8Util.createDoubleIDWeakMap();
+// Return the description of object's members:
+const getObjectMembers = function (object) {
+ let names = Object.getOwnPropertyNames(object);
+ // For Function, we should not override following properties even though they
+ // are "own" properties.
+ if (typeof object === 'function') {
+ names = names.filter((name) => {
+ return !FUNCTION_PROPERTIES.includes(name);
+ });
+ }
+ // Map properties to descriptors.
+ return names.map((name) => {
+ const descriptor = Object.getOwnPropertyDescriptor(object, name);
+ const member = { name, enumerable: descriptor.enumerable, writable: false };
+ if (descriptor.get === undefined && typeof object[name] === 'function') {
+ member.type = 'method';
+ }
+ else {
+ if (descriptor.set || descriptor.writable)
+ member.writable = true;
+ member.type = 'get';
+ }
+ return member;
+ });
+};
+// Return the description of object's prototype.
+const getObjectPrototype = function (object) {
+ const proto = Object.getPrototypeOf(object);
+ if (proto === null || proto === Object.prototype)
+ return null;
+ return {
+ members: getObjectMembers(proto),
+ proto: getObjectPrototype(proto)
+ };
+};
+// Convert a real value into meta data.
+const valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
+ // Determine the type of value.
+ const meta = { type: typeof value };
+ if (meta.type === 'object') {
+ // Recognize certain types of objects.
+ if (value === null) {
+ meta.type = 'value';
+ }
+ else if (bufferUtils.isBuffer(value)) {
+ meta.type = 'buffer';
+ }
+ else if (Array.isArray(value)) {
+ meta.type = 'array';
+ }
+ else if (value instanceof Error) {
+ meta.type = 'error';
+ }
+ else if (value instanceof Date) {
+ meta.type = 'date';
+ }
+ else if (isPromise(value)) {
+ meta.type = 'promise';
+ }
+ else if (hasProp.call(value, 'callee') && value.length != null) {
+ // Treat the arguments object as array.
+ meta.type = 'array';
+ }
+ else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
+ // Treat simple objects as value.
+ meta.type = 'value';
+ }
+ }
+ // Fill the meta object according to value's type.
+ if (meta.type === 'array') {
+ meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject));
+ }
+ else if (meta.type === 'object' || meta.type === 'function') {
+ meta.name = value.constructor ? value.constructor.name : '';
+ // Reference the original value if it's an object, because when it's
+ // passed to renderer we would assume the renderer keeps a reference of
+ // it.
+ meta.id = objectsRegistry.add(sender, contextId, value);
+ meta.members = getObjectMembers(value);
+ meta.proto = getObjectPrototype(value);
+ }
+ else if (meta.type === 'buffer') {
+ meta.value = bufferUtils.bufferToMeta(value);
+ }
+ else if (meta.type === 'promise') {
+ // Add default handler to prevent unhandled rejections in main process
+ // Instead they should appear in the renderer process
+ value.then(function () { }, function () { });
+ meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
+ value.then(onFulfilled, onRejected);
+ });
+ }
+ else if (meta.type === 'error') {
+ meta.members = plainObjectToMeta(value);
+ // Error.name is not part of own properties.
+ meta.members.push({
+ name: 'name',
+ value: value.name
+ });
+ }
+ else if (meta.type === 'date') {
+ meta.value = value.getTime();
+ }
+ else {
+ meta.type = 'value';
+ meta.value = value;
+ }
+ return meta;
+};
+// Convert object to meta by value.
+const plainObjectToMeta = function (obj) {
+ return Object.getOwnPropertyNames(obj).map(function (name) {
+ return {
+ name: name,
+ value: obj[name]
+ };
+ });
+};
+// Convert Error into meta data.
+const exceptionToMeta = function (error) {
+ return {
+ type: 'exception',
+ value: errorUtils.serialize(error)
+ };
+};
+const throwRPCError = function (message) {
+ const error = new Error(message);
+ error.code = 'EBADRPC';
+ error.errno = -72;
+ throw error;
+};
+const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => {
+ const location = v8Util.getHiddenValue(callIntoRenderer, 'location');
+ let message = `Attempting to call a function in a renderer window that has been closed or released.` +
+ `\nFunction provided here: ${location}`;
+ if (sender instanceof EventEmitter) {
+ const remoteEvents = sender.eventNames().filter((eventName) => {
+ return sender.listeners(eventName).includes(callIntoRenderer);
+ });
+ if (remoteEvents.length > 0) {
+ message += `\nRemote event names: ${remoteEvents.join(', ')}`;
+ remoteEvents.forEach((eventName) => {
+ sender.removeListener(eventName, callIntoRenderer);
+ });
+ }
+ }
+ console.warn(message);
+};
+// Convert array of meta data from renderer into array of real values.
+const unwrapArgs = function (sender, frameId, contextId, args) {
+ const metaToValue = function (meta) {
+ switch (meta.type) {
+ case 'value':
+ return meta.value;
+ case 'remote-object':
+ return objectsRegistry.get(meta.id);
+ case 'array':
+ return unwrapArgs(sender, frameId, contextId, meta.value);
+ case 'buffer':
+ return bufferUtils.metaToBuffer(meta.value);
+ case 'date':
+ return new Date(meta.value);
+ case 'promise':
+ return Promise.resolve({
+ then: metaToValue(meta.then)
+ });
+ case 'object': {
+ const ret = {};
+ Object.defineProperty(ret.constructor, 'name', { value: meta.name });
+ for (const { name, value } of meta.members) {
+ ret[name] = metaToValue(value);
+ }
+ return ret;
+ }
+ case 'function-with-return-value':
+ const returnValue = metaToValue(meta.value);
+ return function () {
+ return returnValue;
+ };
+ case 'function': {
+ // Merge contextId and meta.id, since meta.id can be the same in
+ // different webContents.
+ const objectId = [contextId, meta.id];
+ // Cache the callbacks in renderer.
+ if (rendererFunctions.has(objectId)) {
+ return rendererFunctions.get(objectId);
+ }
+ const callIntoRenderer = function (...args) {
+ let succeed = false;
+ if (!sender.isDestroyed()) {
+ succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args));
+ }
+ if (!succeed) {
+ removeRemoteListenersAndLogWarning(this, callIntoRenderer);
+ }
+ };
+ v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location);
+ Object.defineProperty(callIntoRenderer, 'length', { value: meta.length });
+ v8Util.setRemoteCallbackFreer(callIntoRenderer, contextId, meta.id, sender);
+ rendererFunctions.set(objectId, callIntoRenderer);
+ return callIntoRenderer;
+ }
+ default:
+ throw new TypeError(`Unknown type: ${meta.type}`);
+ }
+ };
+ return args.map(metaToValue);
+};
+const isRemoteModuleEnabledCache = new WeakMap();
+const isRemoteModuleEnabled = function (contents) {
+ if (!isRemoteModuleEnabledCache.has(contents)) {
+ isRemoteModuleEnabledCache.set(contents, contents._isRemoteModuleEnabled());
+ }
+ return isRemoteModuleEnabledCache.get(contents);
+};
+const handleRemoteCommand = function (channel, handler) {
+ ipcMainInternal.on(channel, (event, contextId, ...args) => {
+ let returnValue;
+ if (!isRemoteModuleEnabled(event.sender)) {
+ event.returnValue = null;
+ return;
+ }
+ try {
+ returnValue = handler(event, contextId, ...args);
+ }
+ catch (error) {
+ returnValue = exceptionToMeta(error);
+ }
+ if (returnValue !== undefined) {
+ event.returnValue = returnValue;
+ }
+ });
+};
+handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) {
+ const objectId = [passedContextId, id];
+ if (!rendererFunctions.has(objectId)) {
+ // Do nothing if the error has already been reported before.
+ return;
+ }
+ removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId));
+});
+handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName) {
+ const customEvent = eventBinding.createWithSender(event.sender);
+ event.sender.emit('remote-require', customEvent, moduleName);
+ if (customEvent.returnValue === undefined) {
+ if (customEvent.defaultPrevented) {
+ throw new Error(`Blocked remote.require('${moduleName}')`);
+ }
+ else {
+ customEvent.returnValue = process.mainModule.require(moduleName);
+ }
+ }
+ return valueToMeta(event.sender, contextId, customEvent.returnValue);
+});
+handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) {
+ const customEvent = eventBinding.createWithSender(event.sender);
+ event.sender.emit('remote-get-builtin', customEvent, moduleName);
+ if (customEvent.returnValue === undefined) {
+ if (customEvent.defaultPrevented) {
+ throw new Error(`Blocked remote.getBuiltin('${moduleName}')`);
+ }
+ else {
+ customEvent.returnValue = electron[moduleName];
+ }
+ }
+ return valueToMeta(event.sender, contextId, customEvent.returnValue);
+});
+handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) {
+ const customEvent = eventBinding.createWithSender(event.sender);
+ event.sender.emit('remote-get-global', customEvent, globalName);
+ if (customEvent.returnValue === undefined) {
+ if (customEvent.defaultPrevented) {
+ throw new Error(`Blocked remote.getGlobal('${globalName}')`);
+ }
+ else {
+ customEvent.returnValue = global[globalName];
+ }
+ }
+ return valueToMeta(event.sender, contextId, customEvent.returnValue);
+});
+handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
+ const customEvent = eventBinding.createWithSender(event.sender);
+ event.sender.emit('remote-get-current-window', customEvent);
+ if (customEvent.returnValue === undefined) {
+ if (customEvent.defaultPrevented) {
+ throw new Error('Blocked remote.getCurrentWindow()');
+ }
+ else {
+ customEvent.returnValue = event.sender.getOwnerBrowserWindow();
+ }
+ }
+ return valueToMeta(event.sender, contextId, customEvent.returnValue);
+});
+handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
+ const customEvent = eventBinding.createWithSender(event.sender);
+ event.sender.emit('remote-get-current-web-contents', customEvent);
+ if (customEvent.returnValue === undefined) {
+ if (customEvent.defaultPrevented) {
+ throw new Error('Blocked remote.getCurrentWebContents()');
+ }
+ else {
+ customEvent.returnValue = event.sender;
+ }
+ }
+ return valueToMeta(event.sender, contextId, customEvent.returnValue);
+});
+handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
+ args = unwrapArgs(event.sender, event.frameId, contextId, args);
+ const constructor = objectsRegistry.get(id);
+ if (constructor == null) {
+ throwRPCError(`Cannot call constructor on missing remote object ${id}`);
+ }
+ return valueToMeta(event.sender, contextId, new constructor(...args));
+});
+handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
+ args = unwrapArgs(event.sender, event.frameId, contextId, args);
+ const func = objectsRegistry.get(id);
+ if (func == null) {
+ throwRPCError(`Cannot call function on missing remote object ${id}`);
+ }
+ try {
+ return valueToMeta(event.sender, contextId, func(...args), true);
+ }
+ catch (error) {
+ const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
+ err.cause = error;
+ throw err;
+ }
+});
+handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
+ args = unwrapArgs(event.sender, event.frameId, contextId, args);
+ const object = objectsRegistry.get(id);
+ if (object == null) {
+ throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`);
+ }
+ return valueToMeta(event.sender, contextId, new object[method](...args));
+});
+handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
+ args = unwrapArgs(event.sender, event.frameId, contextId, args);
+ const object = objectsRegistry.get(id);
+ if (object == null) {
+ throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`);
+ }
+ try {
+ return valueToMeta(event.sender, contextId, object[method](...args), true);
+ }
+ catch (error) {
+ const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
+ err.cause = error;
+ throw err;
+ }
+});
+handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
+ args = unwrapArgs(event.sender, event.frameId, contextId, args);
+ const obj = objectsRegistry.get(id);
+ if (obj == null) {
+ throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`);
+ }
+ obj[name] = args[0];
+ return null;
+});
+handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
+ const obj = objectsRegistry.get(id);
+ if (obj == null) {
+ throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`);
+ }
+ return valueToMeta(event.sender, contextId, obj[name]);
+});
+handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id, rendererSideRefCount) {
+ objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount);
+});
+handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
+ objectsRegistry.clear(event.sender, contextId);
+ return null;
+});
+handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
+ const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender);
+ const customEvent = eventBinding.createWithSender(event.sender);
+ event.sender.emit('remote-get-guest-web-contents', customEvent, guest);
+ if (customEvent.returnValue === undefined) {
+ if (customEvent.defaultPrevented) {
+ throw new Error(`Blocked remote.getGuestForWebContents()`);
+ }
+ else {
+ customEvent.returnValue = guest;
+ }
+ }
+ return valueToMeta(event.sender, contextId, customEvent.returnValue);
+});
+// Implements window.close()
+ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
+ const window = event.sender.getOwnerBrowserWindow();
+ if (window) {
+ window.close();
+ }
+ event.returnValue = null;
+});
+ipcMainUtils.handle('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
+ return crashReporterInit(options);
+});
+ipcMainUtils.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
+ return event.sender.getLastWebPreferences();
+});
+// Methods not listed in this set are called directly in the renderer process.
+const allowedClipboardMethods = (() => {
+ switch (process.platform) {
+ case 'darwin':
+ return new Set(['readFindText', 'writeFindText']);
+ case 'linux':
+ return new Set(Object.keys(clipboard));
+ default:
+ return new Set();
+ }
+})();
+ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) {
+ if (!allowedClipboardMethods.has(method)) {
+ throw new Error(`Invalid method: ${method}`);
+ }
+ return clipboardUtils.serialize(electron.clipboard[method](...clipboardUtils.deserialize(args)));
+});
+const readFile = util.promisify(fs.readFile);
+const getPreloadScript = async function (preloadPath) {
+ let preloadSrc = null;
+ let preloadError = null;
+ if (preloadPath) {
+ try {
+ preloadSrc = (await readFile(preloadPath)).toString();
+ }
+ catch (err) {
+ preloadError = errorUtils.serialize(err);
+ }
+ }
+ return { preloadPath, preloadSrc, preloadError };
+};
+ipcMainUtils.handle('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
+ const preloadPaths = [
+ ...(event.sender.session ? event.sender.session.getPreloads() : []),
+ event.sender._getPreloadPath()
+ ];
+ return {
+ preloadScripts: await Promise.all(preloadPaths.map(path => getPreloadScript(path))),
+ isRemoteModuleEnabled: isRemoteModuleEnabled(event.sender),
+ isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender),
+ process: {
+ arch: process.arch,
+ platform: process.platform,
+ env: process.env,
+ version: process.version,
+ versions: process.versions,
+ execPath: process.helperExecPath
+ }
+ };
+});
+ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) {
+ event.sender.emit('preload-error', event, preloadPath, errorUtils.deserialize(error));
+});
+//# sourceMappingURL=rpc-server.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/api/clipboard.js b/electronasar/development/common/api/clipboard.js
new file mode 100644
index 0000000..21a5197
--- /dev/null
+++ b/electronasar/development/common/api/clipboard.js
@@ -0,0 +1,26 @@
+'use strict';
+const clipboard = process.electronBinding('clipboard');
+if (process.type === 'renderer') {
+ const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
+ const clipboardUtils = require('@electron/internal/common/clipboard-utils');
+ const makeRemoteMethod = function (method) {
+ return (...args) => {
+ args = clipboardUtils.serialize(args);
+ const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args);
+ return clipboardUtils.deserialize(result);
+ };
+ };
+ if (process.platform === 'linux') {
+ // On Linux we could not access clipboard in renderer process.
+ for (const method of Object.keys(clipboard)) {
+ clipboard[method] = makeRemoteMethod(method);
+ }
+ }
+ else if (process.platform === 'darwin') {
+ // Read/write to find pasteboard over IPC since only main process is notified of changes
+ clipboard.readFindText = makeRemoteMethod('readFindText');
+ clipboard.writeFindText = makeRemoteMethod('writeFindText');
+ }
+}
+module.exports = clipboard;
+//# sourceMappingURL=clipboard.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/api/deprecate.js b/electronasar/development/common/api/deprecate.js
new file mode 100644
index 0000000..986c472
--- /dev/null
+++ b/electronasar/development/common/api/deprecate.js
@@ -0,0 +1,187 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+let deprecationHandler = null;
+function warnOnce(oldName, newName) {
+ let warned = false;
+ const msg = newName
+ ? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.`
+ : `'${oldName}' is deprecated and will be removed.`;
+ return () => {
+ if (!warned && !process.noDeprecation) {
+ 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}`);
+ }
+ },
+ // remove a function with no replacement
+ removeFunction: (fn, removedName) => {
+ if (!fn) {
+ throw Error(`'${removedName} function' is invalid or does not exist.`);
+ }
+ // wrap the deprecated function to warn user
+ const warn = warnOnce(`${fn.name} function`);
+ return function () {
+ warn();
+ fn.apply(this, arguments);
+ };
+ },
+ // change the name of a function
+ renameFunction: (fn, newName) => {
+ const warn = warnOnce(`${fn.name} function`, `${newName} function`);
+ 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
\ No newline at end of file
diff --git a/electronasar/development/common/api/deprecations.js b/electronasar/development/common/api/deprecations.js
new file mode 100644
index 0000000..cf54b25
--- /dev/null
+++ b/electronasar/development/common/api/deprecations.js
@@ -0,0 +1,9 @@
+'use strict';
+const deprecate = require('electron').deprecate;
+exports.setHandler = function (deprecationHandler) {
+ deprecate.setHandler(deprecationHandler);
+};
+exports.getHandler = function () {
+ return deprecate.getHandler();
+};
+//# sourceMappingURL=deprecations.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/api/exports/electron.js b/electronasar/development/common/api/exports/electron.js
new file mode 100644
index 0000000..6ed49e0
--- /dev/null
+++ b/electronasar/development/common/api/exports/electron.js
@@ -0,0 +1,34 @@
+'use strict';
+const moduleList = require('@electron/internal/common/api/module-list');
+exports.memoizedGetter = (getter) => {
+ /*
+ * It's ok to leak this value as it would be leaked by the global
+ * node module cache anyway at `Module._cache`. This memoization
+ * is dramatically faster than relying on nodes module cache however
+ */
+ let memoizedValue = null;
+ return () => {
+ if (memoizedValue === null) {
+ memoizedValue = getter();
+ }
+ return memoizedValue;
+ };
+};
+// Attaches properties to |targetExports|.
+exports.defineProperties = function (targetExports) {
+ const descriptors = {};
+ for (const module of moduleList) {
+ descriptors[module.name] = {
+ enumerable: !module.private,
+ get: exports.memoizedGetter(() => {
+ 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);
+};
+//# sourceMappingURL=electron.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/api/is-promise.js b/electronasar/development/common/api/is-promise.js
new file mode 100644
index 0000000..df119d4
--- /dev/null
+++ b/electronasar/development/common/api/is-promise.js
@@ -0,0 +1,12 @@
+'use strict';
+module.exports = function isPromise(val) {
+ return (val &&
+ val.then &&
+ val.then instanceof Function &&
+ val.constructor &&
+ val.constructor.reject &&
+ val.constructor.reject instanceof Function &&
+ val.constructor.resolve &&
+ val.constructor.resolve instanceof Function);
+};
+//# sourceMappingURL=is-promise.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/api/module-list.js b/electronasar/development/common/api/module-list.js
new file mode 100644
index 0000000..f88500d
--- /dev/null
+++ b/electronasar/development/common/api/module-list.js
@@ -0,0 +1,12 @@
+'use strict';
+// Common modules, please sort alphabetically
+module.exports = [
+ { name: 'clipboard', file: 'clipboard' },
+ { name: 'nativeImage', file: 'native-image' },
+ { name: 'shell', file: 'shell' },
+ // The internal modules, invisible unless you know their names.
+ { name: 'deprecate', file: 'deprecate', private: true },
+ { name: 'deprecations', file: 'deprecations', private: true },
+ { name: 'isPromise', file: 'is-promise', private: true }
+];
+//# sourceMappingURL=module-list.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/api/native-image.js b/electronasar/development/common/api/native-image.js
new file mode 100644
index 0000000..8a373b0
--- /dev/null
+++ b/electronasar/development/common/api/native-image.js
@@ -0,0 +1,4 @@
+'use strict';
+const { nativeImage } = process.electronBinding('native_image');
+module.exports = nativeImage;
+//# sourceMappingURL=native-image.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/api/shell.js b/electronasar/development/common/api/shell.js
new file mode 100644
index 0000000..8889c22
--- /dev/null
+++ b/electronasar/development/common/api/shell.js
@@ -0,0 +1,3 @@
+'use strict';
+module.exports = process.electronBinding('shell');
+//# sourceMappingURL=shell.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/atom-binding-setup.js b/electronasar/development/common/atom-binding-setup.js
new file mode 100644
index 0000000..d074421
--- /dev/null
+++ b/electronasar/development/common/atom-binding-setup.js
@@ -0,0 +1,19 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+function electronBindingSetup(binding, processType) {
+ return function electronBinding(name) {
+ try {
+ return binding(`atom_${processType}_${name}`);
+ }
+ catch (error) {
+ if (/No such module/.test(error.message)) {
+ return binding(`atom_common_${name}`);
+ }
+ else {
+ throw error;
+ }
+ }
+ };
+}
+exports.electronBindingSetup = electronBindingSetup;
+//# sourceMappingURL=atom-binding-setup.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/buffer-utils.js b/electronasar/development/common/buffer-utils.js
new file mode 100644
index 0000000..7b08164
--- /dev/null
+++ b/electronasar/development/common/buffer-utils.js
@@ -0,0 +1,64 @@
+'use strict';
+// Note: Don't use destructuring assignment for `Buffer`, or we'll hit a
+// browserify bug that makes the statement invalid, throwing an error in
+// sandboxed renderer.
+const Buffer = require('buffer').Buffer;
+const typedArrays = {
+ Buffer,
+ ArrayBuffer,
+ Int8Array,
+ Uint8Array,
+ Uint8ClampedArray,
+ Int16Array,
+ Uint16Array,
+ Int32Array,
+ Uint32Array,
+ Float32Array,
+ 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) {
+ 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) {
+ return ArrayBuffer.isView(value) || value instanceof ArrayBuffer;
+};
+exports.bufferToMeta = function (value) {
+ return {
+ type: getType(value),
+ data: getBuffer(value),
+ length: value.length
+ };
+};
+exports.metaToBuffer = function (value) {
+ const constructor = typedArrays[value.type];
+ const data = getBuffer(value.data);
+ if (constructor === Buffer) {
+ return data;
+ }
+ else if (constructor === ArrayBuffer) {
+ return data.buffer;
+ }
+ else if (constructor) {
+ return new constructor(data.buffer, data.byteOffset, value.length);
+ }
+ else {
+ return data;
+ }
+};
+//# sourceMappingURL=buffer-utils.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/clipboard-utils.js b/electronasar/development/common/clipboard-utils.js
new file mode 100644
index 0000000..d6d89f0
--- /dev/null
+++ b/electronasar/development/common/clipboard-utils.js
@@ -0,0 +1,50 @@
+'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
\ No newline at end of file
diff --git a/electronasar/development/common/crash-reporter.js b/electronasar/development/common/crash-reporter.js
new file mode 100644
index 0000000..923feca
--- /dev/null
+++ b/electronasar/development/common/crash-reporter.js
@@ -0,0 +1,78 @@
+'use strict';
+const binding = process.electronBinding('crash_reporter');
+class CrashReporter {
+ contructor() {
+ this.productName = null;
+ this.crashesDirectory = null;
+ }
+ init(options) {
+ throw new Error('Not implemented');
+ }
+ start(options) {
+ if (options == null)
+ options = {};
+ 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()
+ .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');
+ }
+ }
+ 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();
+ }
+}
+module.exports = CrashReporter;
+//# sourceMappingURL=crash-reporter.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/error-utils.js b/electronasar/development/common/error-utils.js
new file mode 100644
index 0000000..ecfd055
--- /dev/null
+++ b/electronasar/development/common/error-utils.js
@@ -0,0 +1,37 @@
+'use strict';
+const constructors = new Map([
+ [Error.name, Error],
+ [EvalError.name, EvalError],
+ [RangeError.name, RangeError],
+ [ReferenceError.name, ReferenceError],
+ [SyntaxError.name, SyntaxError],
+ [TypeError.name, TypeError],
+ [URIError.name, URIError]
+]);
+exports.deserialize = function (error) {
+ if (error && error.__ELECTRON_SERIALIZED_ERROR__ && constructors.has(error.name)) {
+ const constructor = constructors.get(error.name);
+ const deserializedError = new constructor(error.message);
+ deserializedError.stack = error.stack;
+ deserializedError.from = error.from;
+ deserializedError.cause = exports.deserialize(error.cause);
+ return deserializedError;
+ }
+ return error;
+};
+exports.serialize = function (error) {
+ if (error instanceof Error) {
+ // Errors get lost, because: JSON.stringify(new Error('Message')) === {}
+ // Take the serializable properties and construct a generic object
+ return {
+ message: error.message,
+ stack: error.stack,
+ name: error.name,
+ from: process.type,
+ cause: exports.serialize(error.cause),
+ __ELECTRON_SERIALIZED_ERROR__: true
+ };
+ }
+ return error;
+};
+//# sourceMappingURL=error-utils.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/init.js b/electronasar/development/common/init.js
new file mode 100644
index 0000000..3c233ea
--- /dev/null
+++ b/electronasar/development/common/init.js
@@ -0,0 +1,61 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const util = require("util");
+const atom_binding_setup_1 = require("@electron/internal/common/atom-binding-setup");
+const timers = require('timers');
+process.electronBinding = atom_binding_setup_1.electronBindingSetup(process._linkedBinding, process.type);
+// 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
+// callbacks wouldn't be called until something else activated the uv loop,
+// which would delay the callbacks for arbitrary long time. So we should
+// initiatively activate the uv loop once setImmediate and process.nextTick is
+// called.
+const wrapWithActivateUvLoop = function (func) {
+ return wrap(func, function (func) {
+ return function (...args) {
+ process.activateUvLoop();
+ return func.apply(this, args);
+ };
+ });
+};
+/**
+ * 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 = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
+global.clearImmediate = timers.clearImmediate;
+// 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
+// to update the timeout, so we have to force the node's event loop to
+// recalculate the timeout in browser process.
+timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout);
+timers.setInterval = wrapWithActivateUvLoop(timers.setInterval);
+// Only override the global setTimeout/setInterval impls in the browser process
+if (process.type === 'browser') {
+ global.setTimeout = timers.setTimeout;
+ global.setInterval = timers.setInterval;
+}
+if (process.platform === 'win32') {
+ // Always returns EOF for stdin stream.
+ const { Readable } = require('stream');
+ const stdin = new Readable();
+ stdin.push(null);
+ Object.defineProperty(process, 'stdin', {
+ configurable: false,
+ enumerable: true,
+ get() {
+ return stdin;
+ }
+ });
+}
+//# sourceMappingURL=init.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/parse-features-string.js b/electronasar/development/common/parse-features-string.js
new file mode 100644
index 0000000..26ca6ff
--- /dev/null
+++ b/electronasar/development/common/parse-features-string.js
@@ -0,0 +1,20 @@
+'use strict';
+// parses a feature string that has the format used in window.open()
+// - `features` input string
+// - `emit` function(key, value) - called for each parsed KV
+module.exports = function parseFeaturesString(features, emit) {
+ features = `${features}`.trim();
+ // split the string by ','
+ features.split(/\s*,\s*/).forEach((feature) => {
+ // expected form is either a key by itself or a key/value pair in the form of
+ // 'key=value'
+ let [key, value] = feature.split(/\s*=\s*/);
+ if (!key)
+ return;
+ // interpret the value as a boolean, if possible
+ value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value;
+ // emit the parsed pair
+ emit(key, value);
+ });
+};
+//# sourceMappingURL=parse-features-string.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/reset-search-paths.js b/electronasar/development/common/reset-search-paths.js
new file mode 100644
index 0000000..acb19bb
--- /dev/null
+++ b/electronasar/development/common/reset-search-paths.js
@@ -0,0 +1,44 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const path = require("path");
+const Module = require('module');
+// Clear Node's global search paths.
+Module.globalPaths.length = 0;
+// Clear current and parent(init.js)'s search paths.
+module.paths = [];
+module.parent.paths = [];
+// Prevent Node from adding paths outside this app to search paths.
+const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep;
+const originalNodeModulePaths = Module._nodeModulePaths;
+Module._nodeModulePaths = function (from) {
+ const paths = originalNodeModulePaths(from);
+ const fromPath = path.resolve(from) + path.sep;
+ // If "from" is outside the app then we do nothing.
+ if (fromPath.startsWith(resourcesPathWithTrailingSlash)) {
+ return paths.filter(function (candidate) {
+ return candidate.startsWith(resourcesPathWithTrailingSlash);
+ });
+ }
+ else {
+ return paths;
+ }
+};
+const BASE_INTERNAL_PATH = path.resolve(__dirname, '..');
+const INTERNAL_MODULE_PREFIX = '@electron/internal/';
+// Patch Module._resolveFilename to always require the Electron API when
+// require('electron') is done.
+const electronPath = path.join(__dirname, '..', process.type, 'api', 'exports', 'electron.js');
+const originalResolveFilename = Module._resolveFilename;
+Module._resolveFilename = function (request, parent, isMain) {
+ if (request === 'electron') {
+ return electronPath;
+ }
+ else if (request.startsWith(INTERNAL_MODULE_PREFIX) && request.length > INTERNAL_MODULE_PREFIX.length) {
+ const slicedRequest = request.slice(INTERNAL_MODULE_PREFIX.length);
+ return path.resolve(BASE_INTERNAL_PATH, `${slicedRequest}${slicedRequest.endsWith('.js') ? '' : '.js'}`);
+ }
+ else {
+ return originalResolveFilename(request, parent, isMain);
+ }
+};
+//# sourceMappingURL=reset-search-paths.js.map
\ No newline at end of file
diff --git a/electronasar/development/common/web-view-methods.js b/electronasar/development/common/web-view-methods.js
new file mode 100644
index 0000000..b9f548f
--- /dev/null
+++ b/electronasar/development/common/web-view-methods.js
@@ -0,0 +1,69 @@
+'use strict';
+// Public-facing API methods.
+exports.syncMethods = new Set([
+ 'getURL',
+ 'getTitle',
+ 'isLoading',
+ 'isLoadingMainFrame',
+ 'isWaitingForResponse',
+ 'stop',
+ 'reload',
+ 'reloadIgnoringCache',
+ 'canGoBack',
+ 'canGoForward',
+ 'canGoToOffset',
+ 'clearHistory',
+ 'goBack',
+ 'goForward',
+ 'goToIndex',
+ 'goToOffset',
+ 'isCrashed',
+ 'setUserAgent',
+ 'getUserAgent',
+ 'openDevTools',
+ 'closeDevTools',
+ 'isDevToolsOpened',
+ 'isDevToolsFocused',
+ 'inspectElement',
+ 'setAudioMuted',
+ 'isAudioMuted',
+ 'isCurrentlyAudible',
+ 'undo',
+ 'redo',
+ 'cut',
+ 'copy',
+ 'paste',
+ 'pasteAndMatchStyle',
+ 'delete',
+ 'selectAll',
+ 'unselect',
+ 'replace',
+ 'replaceMisspelling',
+ 'findInPage',
+ 'stopFindInPage',
+ 'downloadURL',
+ 'inspectSharedWorker',
+ 'inspectServiceWorker',
+ 'showDefinitionForSelection',
+ 'getZoomFactor',
+ 'getZoomLevel',
+ 'setZoomFactor',
+ 'setZoomLevel',
+ 'sendImeEvent'
+]);
+exports.asyncCallbackMethods = new Set([
+ 'insertCSS',
+ 'insertText',
+ 'send',
+ 'sendInputEvent',
+ 'setLayoutZoomLevelLimits',
+ 'setVisualZoomLevelLimits',
+ 'print'
+]);
+exports.asyncPromiseMethods = new Set([
+ 'loadURL',
+ 'capturePage',
+ 'executeJavaScript',
+ 'printToPDF'
+]);
+//# sourceMappingURL=web-view-methods.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/api/crash-reporter.js b/electronasar/development/renderer/api/crash-reporter.js
new file mode 100644
index 0000000..59ea3b5
--- /dev/null
+++ b/electronasar/development/renderer/api/crash-reporter.js
@@ -0,0 +1,10 @@
+'use strict';
+const CrashReporter = require('@electron/internal/common/crash-reporter');
+const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
+class CrashReporterRenderer extends CrashReporter {
+ init(options) {
+ return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options);
+ }
+}
+module.exports = new CrashReporterRenderer();
+//# sourceMappingURL=crash-reporter.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/api/desktop-capturer.js b/electronasar/development/renderer/api/desktop-capturer.js
new file mode 100644
index 0000000..f77c21a
--- /dev/null
+++ b/electronasar/development/renderer/api/desktop-capturer.js
@@ -0,0 +1,38 @@
+'use strict';
+const { nativeImage, deprecate } = require('electron');
+const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
+// |options.types| can't be empty and must be an array
+function isValid(options) {
+ const types = options ? options.types : undefined;
+ return Array.isArray(types);
+}
+function mapSources(sources) {
+ return sources.map(source => ({
+ id: source.id,
+ name: source.name,
+ thumbnail: nativeImage.createFromDataURL(source.thumbnail),
+ display_id: source.display_id,
+ appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null
+ }));
+}
+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
\ No newline at end of file
diff --git a/electronasar/development/renderer/api/exports/electron.js b/electronasar/development/renderer/api/exports/electron.js
new file mode 100644
index 0000000..3702ebe
--- /dev/null
+++ b/electronasar/development/renderer/api/exports/electron.js
@@ -0,0 +1,21 @@
+'use strict';
+const common = require('@electron/internal/common/api/exports/electron');
+const moduleList = require('@electron/internal/renderer/api/module-list');
+// Import common modules.
+common.defineProperties(exports);
+for (const { name, file, enabled = true, private: isPrivate = false } of moduleList) {
+ if (!enabled) {
+ continue;
+ }
+ Object.defineProperty(exports, name, {
+ enumerable: !isPrivate,
+ get: common.memoizedGetter(() => {
+ const value = require(`@electron/internal/renderer/api/${file}.js`);
+ // Handle Typescript modules with an "export default X" statement
+ if (value.__esModule)
+ return value.default;
+ return value;
+ })
+ });
+}
+//# sourceMappingURL=electron.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/api/ipc-renderer.js b/electronasar/development/renderer/api/ipc-renderer.js
new file mode 100644
index 0000000..fa5e2b9
--- /dev/null
+++ b/electronasar/development/renderer/api/ipc-renderer.js
@@ -0,0 +1,23 @@
+'use strict';
+const { ipc } = process.electronBinding('ipc');
+const v8Util = process.electronBinding('v8_util');
+// Created by init.js.
+const ipcRenderer = v8Util.getHiddenValue(global, 'ipc');
+const internal = false;
+ipcRenderer.send = function (channel, ...args) {
+ return ipc.send(internal, channel, args);
+};
+ipcRenderer.sendSync = function (channel, ...args) {
+ return ipc.sendSync(internal, channel, args)[0];
+};
+ipcRenderer.sendToHost = function (channel, ...args) {
+ return ipc.sendToHost(channel, args);
+};
+ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
+ return ipc.sendTo(internal, false, webContentsId, channel, args);
+};
+ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
+ return ipc.sendTo(internal, true, webContentsId, channel, args);
+};
+module.exports = ipcRenderer;
+//# sourceMappingURL=ipc-renderer.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/api/module-list.js b/electronasar/development/renderer/api/module-list.js
new file mode 100644
index 0000000..c27561b
--- /dev/null
+++ b/electronasar/development/renderer/api/module-list.js
@@ -0,0 +1,18 @@
+'use strict';
+const features = process.electronBinding('features');
+const v8Util = process.electronBinding('v8_util');
+const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule');
+// Renderer side modules, please sort alphabetically.
+// A module is `enabled` if there is no explicit condition defined.
+module.exports = [
+ { name: 'crashReporter', file: 'crash-reporter', enabled: true },
+ {
+ name: 'desktopCapturer',
+ file: 'desktop-capturer',
+ enabled: features.isDesktopCapturerEnabled()
+ },
+ { name: 'ipcRenderer', file: 'ipc-renderer' },
+ { name: 'remote', file: 'remote', enabled: enableRemoteModule },
+ { name: 'webFrame', file: 'web-frame' }
+];
+//# sourceMappingURL=module-list.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/api/remote.js b/electronasar/development/renderer/api/remote.js
new file mode 100644
index 0000000..57a75d3
--- /dev/null
+++ b/electronasar/development/renderer/api/remote.js
@@ -0,0 +1,340 @@
+'use strict';
+const v8Util = process.electronBinding('v8_util');
+const { isPromise } = require('electron');
+const resolvePromise = Promise.resolve.bind(Promise);
+const CallbacksRegistry = require('@electron/internal/renderer/callbacks-registry');
+const bufferUtils = require('@electron/internal/common/buffer-utils');
+const errorUtils = require('@electron/internal/common/error-utils');
+const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
+const callbacksRegistry = new CallbacksRegistry();
+const remoteObjectCache = v8Util.createIDWeakMap();
+// An unique ID that can represent current context.
+const contextId = v8Util.getHiddenValue(global, 'contextId');
+// 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
+// sent, we also listen to the "render-view-deleted" event in the main process
+// to guard that situation.
+process.on('exit', () => {
+ const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE';
+ ipcRendererInternal.sendSync(command, contextId);
+});
+// Convert the arguments object into an array of meta data.
+function wrapArgs(args, visited = new Set()) {
+ const valueToMeta = (value) => {
+ // Check for circular reference.
+ if (visited.has(value)) {
+ return {
+ type: 'value',
+ 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);
+ })
+ };
+ }
+ else if (v8Util.getHiddenValue(value, 'atomId')) {
+ return {
+ type: 'remote-object',
+ id: v8Util.getHiddenValue(value, 'atomId')
+ };
+ }
+ const meta = {
+ type: 'object',
+ name: value.constructor ? value.constructor.name : '',
+ members: []
+ };
+ visited.add(value);
+ for (const prop in value) {
+ meta.members.push({
+ name: prop,
+ 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.
+// The |ref| will be kept referenced by |members|.
+// This matches |getObjectMemebers| in rpc-server.
+function setObjectMembers(ref, object, metaId, members) {
+ if (!Array.isArray(members))
+ return;
+ for (const member of members) {
+ if (object.hasOwnProperty(member.name))
+ continue;
+ const descriptor = { enumerable: member.enumerable };
+ if (member.type === 'method') {
+ const remoteMemberFunction = function (...args) {
+ let command;
+ if (this && this.constructor === remoteMemberFunction) {
+ command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR';
+ }
+ else {
+ 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') {
+ descriptor.get = () => {
+ const command = 'ELECTRON_BROWSER_MEMBER_GET';
+ const meta = ipcRendererInternal.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 = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args);
+ if (meta != null)
+ metaToValue(meta);
+ return value;
+ };
+ }
+ }
+ Object.defineProperty(object, member.name, descriptor);
+ }
+}
+// Populate object's prototype from descriptor.
+// This matches |getObjectPrototype| in rpc-server.
+function setObjectPrototype(ref, object, metaId, descriptor) {
+ if (descriptor === null)
+ return;
+ const proto = {};
+ setObjectMembers(ref, proto, metaId, descriptor.members);
+ setObjectPrototype(ref, proto, metaId, descriptor.proto);
+ Object.setPrototypeOf(object, proto);
+}
+// Wrap function in Proxy for accessing remote properties
+function proxyFunctionProperties(remoteMemberFunction, metaId, name) {
+ let loaded = false;
+ // Lazily load function properties
+ const loadRemoteProperties = () => {
+ if (loaded)
+ return;
+ loaded = true;
+ const command = 'ELECTRON_BROWSER_MEMBER_GET';
+ const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name);
+ setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members);
+ };
+ return new Proxy(remoteMemberFunction, {
+ set: (target, property, value, receiver) => {
+ if (property !== 'ref')
+ loadRemoteProperties();
+ target[property] = value;
+ return true;
+ },
+ get: (target, property, receiver) => {
+ if (!target.hasOwnProperty(property))
+ loadRemoteProperties();
+ const value = target[property];
+ if (property === 'toString' && typeof value === 'function') {
+ return value.bind(target);
+ }
+ return value;
+ },
+ ownKeys: (target) => {
+ loadRemoteProperties();
+ return Object.getOwnPropertyNames(target);
+ },
+ getOwnPropertyDescriptor: (target, property) => {
+ const descriptor = Object.getOwnPropertyDescriptor(target, property);
+ if (descriptor)
+ return descriptor;
+ loadRemoteProperties();
+ return Object.getOwnPropertyDescriptor(target, property);
+ }
+ });
+}
+// Convert meta data from browser into real value.
+function metaToValue(meta) {
+ const types = {
+ value: () => meta.value,
+ array: () => meta.members.map((member) => metaToValue(member)),
+ buffer: () => bufferUtils.metaToBuffer(meta.value),
+ promise: () => resolvePromise({ then: metaToValue(meta.then) }),
+ error: () => metaToPlainObject(meta),
+ date: () => new Date(meta.value),
+ exception: () => { throw errorUtils.deserialize(meta.value); }
+ };
+ if (meta.type in types) {
+ return types[meta.type]();
+ }
+ else {
+ let ret;
+ if (remoteObjectCache.has(meta.id)) {
+ v8Util.addRemoteObjectRef(contextId, meta.id);
+ return remoteObjectCache.get(meta.id);
+ }
+ // A shadow class to represent the remote function object.
+ if (meta.type === 'function') {
+ const remoteFunction = function (...args) {
+ let command;
+ if (this && this.constructor === remoteFunction) {
+ command = 'ELECTRON_BROWSER_CONSTRUCTOR';
+ }
+ 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;
+ }
+}
+// Construct a plain object from the meta.
+function metaToPlainObject(meta) {
+ const obj = (() => meta.type === 'error' ? new Error() : {})();
+ for (let i = 0; i < meta.members.length; i++) {
+ const { name, value } = meta.members[i];
+ obj[name] = value;
+ }
+ 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.
+handleMessage('ELECTRON_RENDERER_CALLBACK', (id, args) => {
+ callbacksRegistry.apply(id, metaToValue(args));
+});
+// A callback in browser is released.
+handleMessage('ELECTRON_RENDERER_RELEASE_CALLBACK', (id) => {
+ callbacksRegistry.remove(id);
+});
+exports.require = (module) => {
+ const command = 'ELECTRON_BROWSER_REQUIRE';
+ const meta = ipcRendererInternal.sendSync(command, contextId, module);
+ return metaToValue(meta);
+};
+// Alias to remote.require('electron').xxx.
+exports.getBuiltin = (module) => {
+ const command = 'ELECTRON_BROWSER_GET_BUILTIN';
+ const meta = ipcRendererInternal.sendSync(command, contextId, module);
+ return metaToValue(meta);
+};
+exports.getCurrentWindow = () => {
+ const command = 'ELECTRON_BROWSER_CURRENT_WINDOW';
+ const meta = ipcRendererInternal.sendSync(command, contextId);
+ return metaToValue(meta);
+};
+// Get current WebContents object.
+exports.getCurrentWebContents = () => {
+ const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS';
+ const meta = ipcRendererInternal.sendSync(command, contextId);
+ return metaToValue(meta);
+};
+// Get a global object in browser.
+exports.getGlobal = (name) => {
+ const command = 'ELECTRON_BROWSER_GLOBAL';
+ const meta = ipcRendererInternal.sendSync(command, contextId, name);
+ return metaToValue(meta);
+};
+// Get the process object in browser.
+exports.__defineGetter__('process', () => exports.getGlobal('process'));
+// Create a function that will return the specified value when called in browser.
+exports.createFunctionWithReturnValue = (returnValue) => {
+ const func = () => returnValue;
+ v8Util.setHiddenValue(func, 'returnValue', true);
+ return func;
+};
+// Get the guest WebContents from guestInstanceId.
+exports.getGuestWebContents = (guestInstanceId) => {
+ const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS';
+ const meta = ipcRendererInternal.sendSync(command, contextId, guestInstanceId);
+ return metaToValue(meta);
+};
+const addBuiltinProperty = (name) => {
+ Object.defineProperty(exports, name, {
+ get: () => exports.getBuiltin(name)
+ });
+};
+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.
+browserModules
+ .filter((m) => !m.private)
+ .map((m) => m.name)
+ .forEach(addBuiltinProperty);
+//# sourceMappingURL=remote.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/api/web-frame.js b/electronasar/development/renderer/api/web-frame.js
new file mode 100644
index 0000000..7f902cb
--- /dev/null
+++ b/electronasar/development/renderer/api/web-frame.js
@@ -0,0 +1,82 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const events_1 = require("events");
+const electron_1 = require("electron");
+const binding = process.electronBinding('web_frame');
+class WebFrame extends events_1.EventEmitter {
+ constructor(context) {
+ super();
+ this.context = context;
+ // Lots of webview would subscribe to webFrame's events.
+ this.setMaxListeners(0);
+ }
+ findFrameByRoutingId(...args) {
+ return getWebFrame(binding._findFrameByRoutingId(this.context, ...args));
+ }
+ getFrameForSelector(...args) {
+ return getWebFrame(binding._getFrameForSelector(this.context, ...args));
+ }
+ findFrameByName(...args) {
+ return getWebFrame(binding._findFrameByName(this.context, ...args));
+ }
+ get opener() {
+ return getWebFrame(binding._getOpener(this.context));
+ }
+ get parent() {
+ return getWebFrame(binding._getParent(this.context));
+ }
+ get top() {
+ return getWebFrame(binding._getTop(this.context));
+ }
+ get firstChild() {
+ return getWebFrame(binding._getFirstChild(this.context));
+ }
+ get nextSibling() {
+ return getWebFrame(binding._getNextSibling(this.context));
+ }
+ get routingId() {
+ return binding._getRoutingId(this.context);
+ }
+ // Deprecations
+ // TODO(nitsakh): Remove in 6.0
+ setIsolatedWorldSecurityOrigin(worldId, securityOrigin) {
+ electron_1.deprecate.warn('webFrame.setIsolatedWorldSecurityOrigin', 'webFrame.setIsolatedWorldInfo');
+ binding.setIsolatedWorldInfo(this.context, worldId, { securityOrigin });
+ }
+ setIsolatedWorldContentSecurityPolicy(worldId, csp) {
+ electron_1.deprecate.warn('webFrame.setIsolatedWorldContentSecurityPolicy', 'webFrame.setIsolatedWorldInfo');
+ binding.setIsolatedWorldInfo(this.context, worldId, {
+ 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.
+for (const name in binding) {
+ if (!name.startsWith('_')) { // some methods are manually populated above
+ // TODO(felixrieseberg): Once we can type web_frame natives, we could
+ // use a neat `keyof` here
+ WebFrame.prototype[name] = function (...args) {
+ return binding[name](this.context, ...args);
+ };
+ }
+}
+// Helper to return WebFrame or null depending on context.
+// TODO(zcbenz): Consider returning same WebFrame for the same frame.
+function getWebFrame(context) {
+ return context ? new WebFrame(context) : null;
+}
+const promisifiedMethods = new Set([
+ 'executeJavaScript',
+ '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
\ No newline at end of file
diff --git a/electronasar/development/renderer/callbacks-registry.js b/electronasar/development/renderer/callbacks-registry.js
new file mode 100644
index 0000000..20060c1
--- /dev/null
+++ b/electronasar/development/renderer/callbacks-registry.js
@@ -0,0 +1,52 @@
+'use strict';
+const v8Util = process.electronBinding('v8_util');
+class CallbacksRegistry {
+ constructor() {
+ this.nextId = 0;
+ 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('()'))
+ continue;
+ if (location.includes('electron.asar'))
+ 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
\ No newline at end of file
diff --git a/electronasar/development/renderer/chrome-api.js b/electronasar/development/renderer/chrome-api.js
new file mode 100644
index 0000000..c33d707
--- /dev/null
+++ b/electronasar/development/renderer/chrome-api.js
@@ -0,0 +1,174 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-renderer-internal");
+const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils");
+const url = require("url");
+// Todo: Import once extensions have been turned into TypeScript
+const Event = require('@electron/internal/renderer/extensions/event');
+class Tab {
+ constructor(tabId) {
+ this.id = tabId;
+ }
+}
+class MessageSender {
+ constructor(tabId, extensionId) {
+ this.tab = tabId ? new Tab(tabId) : null;
+ this.id = extensionId;
+ this.url = `chrome-extension://${extensionId}`;
+ }
+}
+class Port {
+ constructor(tabId, portId, extensionId, name) {
+ this.tabId = tabId;
+ this.portId = portId;
+ this.name = name;
+ this.disconnected = false;
+ this.onDisconnect = new Event();
+ this.onMessage = new Event();
+ this.onDisconnect = new Event();
+ this.onMessage = new Event();
+ this.sender = new MessageSender(tabId, extensionId);
+ ipc_renderer_internal_1.ipcRendererInternal.once(`CHROME_PORT_DISCONNECT_${portId}`, () => {
+ this._onDisconnect();
+ });
+ ipc_renderer_internal_1.ipcRendererInternal.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (_event, message) => {
+ const sendResponse = function () { console.error('sendResponse is not implemented'); };
+ this.onMessage.emit(JSON.parse(message), this.sender, sendResponse);
+ });
+ }
+ disconnect() {
+ if (this.disconnected)
+ return;
+ ipc_renderer_internal_1.ipcRendererInternal.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}`, JSON.stringify(message));
+ }
+ _onDisconnect() {
+ this.disconnected = true;
+ ipc_renderer_internal_1.ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`);
+ this.onDisconnect.emit();
+ }
+}
+// Inject chrome API to the |context|
+function injectTo(extensionId, context) {
+ const chrome = context.chrome = context.chrome || {};
+ ipc_renderer_internal_1.ipcRendererInternal.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (_event, tabId, portId, connectInfo) => {
+ chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name));
+ });
+ ipcRendererUtils.handle(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (_event, tabId, message) => {
+ return new Promise(resolve => {
+ chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve);
+ });
+ });
+ ipc_renderer_internal_1.ipcRendererInternal.on('CHROME_TABS_ONCREATED', (_event, tabId) => {
+ chrome.tabs.onCreated.emit(new Tab(tabId));
+ });
+ ipc_renderer_internal_1.ipcRendererInternal.on('CHROME_TABS_ONREMOVED', (_event, tabId) => {
+ chrome.tabs.onRemoved.emit(tabId);
+ });
+ chrome.runtime = {
+ id: extensionId,
+ // https://developer.chrome.com/extensions/runtime#method-getURL
+ getURL: function (path) {
+ return url.format({
+ protocol: 'chrome-extension',
+ slashes: true,
+ hostname: extensionId,
+ pathname: path
+ });
+ },
+ // https://developer.chrome.com/extensions/runtime#method-getManifest
+ getManifest: function () {
+ const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId);
+ return manifest;
+ },
+ // https://developer.chrome.com/extensions/runtime#method-connect
+ connect(...args) {
+ // Parse the optional args.
+ let targetExtensionId = extensionId;
+ let connectInfo = { name: '' };
+ if (args.length === 1) {
+ connectInfo = args[0];
+ }
+ else if (args.length === 2) {
+ [targetExtensionId, connectInfo] = args;
+ }
+ const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo);
+ return new Port(tabId, portId, extensionId, connectInfo.name);
+ },
+ // https://developer.chrome.com/extensions/runtime#method-sendMessage
+ sendMessage(...args) {
+ // Parse the optional args.
+ const targetExtensionId = extensionId;
+ let message;
+ let options;
+ let responseCallback = () => { };
+ if (typeof args[args.length - 1] === 'function') {
+ responseCallback = args.pop();
+ }
+ if (args.length === 1) {
+ [message] = args;
+ }
+ else if (args.length === 2) {
+ if (typeof args[0] === 'string') {
+ [extensionId, message] = args;
+ }
+ else {
+ [message, options] = args;
+ }
+ }
+ else {
+ [extensionId, message, options] = args;
+ }
+ if (options) {
+ console.error('options are not supported');
+ }
+ ipcRendererUtils.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback);
+ },
+ onConnect: new Event(),
+ onMessage: new Event(),
+ onInstalled: new Event()
+ };
+ chrome.tabs = {
+ // https://developer.chrome.com/extensions/tabs#method-executeScript
+ executeScript(tabId, details, resultCallback = () => { }) {
+ ipcRendererUtils.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details)
+ .then((result) => resultCallback([result]));
+ },
+ // https://developer.chrome.com/extensions/tabs#method-sendMessage
+ sendMessage(tabId, message, _options, responseCallback = () => { }) {
+ ipcRendererUtils.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback);
+ },
+ onUpdated: new Event(),
+ onCreated: new Event(),
+ onRemoved: new Event()
+ };
+ chrome.extension = {
+ getURL: chrome.runtime.getURL,
+ connect: chrome.runtime.connect,
+ onConnect: chrome.runtime.onConnect,
+ sendMessage: chrome.runtime.sendMessage,
+ onMessage: chrome.runtime.onMessage
+ };
+ 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();
+ // Electron has no concept of a browserAction but we should stub these APIs for compatibility
+ chrome.browserAction = {
+ setIcon() { },
+ setPopup() { }
+ };
+}
+exports.injectTo = injectTo;
+//# sourceMappingURL=chrome-api.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/content-scripts-injector.js b/electronasar/development/renderer/content-scripts-injector.js
new file mode 100644
index 0000000..a1c5b1e
--- /dev/null
+++ b/electronasar/development/renderer/content-scripts-injector.js
@@ -0,0 +1,109 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const electron_1 = require("electron");
+const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils");
+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.
+// https://developer.chrome.com/extensions/match_patterns
+const matchesPattern = function (pattern) {
+ if (pattern === '')
+ return true;
+ const regexp = new RegExp(`^${pattern.split('*').map(escapePattern).join('.*')}$`);
+ const url = `${location.protocol}//${location.host}${location.pathname}`;
+ return url.match(regexp);
+};
+// Run the code with chrome API integrated.
+const runContentScript = function (extensionId, url, code) {
+ // Assign unique world ID to each extension
+ const worldId = extensionWorldId[extensionId] ||
+ (extensionWorldId[extensionId] = getIsolatedWorldIdForInstance());
+ // store extension ID for content script to read in isolated world
+ v8Util.setHiddenValue(global, `extension-${worldId}`, extensionId);
+ electron_1.webFrame.setIsolatedWorldInfo(worldId, {
+ name: `${extensionId} [${worldId}]`
+ // TODO(samuelmaddock): read `content_security_policy` from extension manifest
+ // csp: manifest.content_security_policy,
+ });
+ const sources = [{ code, url }];
+ return electron_1.webFrame.executeJavaScriptInIsolatedWorld(worldId, sources);
+};
+const runAllContentScript = function (scripts, extensionId) {
+ for (const { url, code } of scripts) {
+ runContentScript.call(window, extensionId, url, code);
+ }
+};
+const runStylesheet = function (url, code) {
+ electron_1.webFrame.insertCSS(code);
+};
+const runAllStylesheet = function (css) {
+ for (const { url, code } of css) {
+ runStylesheet.call(window, url, code);
+ }
+};
+// Run injected scripts.
+// https://developer.chrome.com/extensions/content_scripts
+const injectContentScript = function (extensionId, script) {
+ if (!process.isMainFrame && !script.allFrames)
+ return;
+ if (!script.matches.some(matchesPattern))
+ return;
+ if (script.js) {
+ const fire = runAllContentScript.bind(window, script.js, extensionId);
+ if (script.runAt === 'document_start') {
+ process.once('document-start', fire);
+ }
+ else if (script.runAt === 'document_end') {
+ 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') {
+ process.once('document-start', fire);
+ }
+ else if (script.runAt === 'document_end') {
+ process.once('document-end', fire);
+ }
+ else {
+ document.addEventListener('DOMContentLoaded', fire);
+ }
+ }
+};
+// Handle the request of chrome.tabs.executeJavaScript.
+ipcRendererUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', function (event, extensionId, url, code) {
+ return runContentScript.call(window, extensionId, url, code);
+});
+module.exports = (getRenderProcessPreferences) => {
+ // Read the renderer process preferences.
+ const preferences = getRenderProcessPreferences();
+ if (preferences) {
+ for (const pref of preferences) {
+ if (pref.contentScripts) {
+ for (const script of pref.contentScripts) {
+ injectContentScript(pref.extensionId, script);
+ }
+ }
+ }
+ }
+};
+//# sourceMappingURL=content-scripts-injector.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/extensions/event.js b/electronasar/development/renderer/extensions/event.js
new file mode 100644
index 0000000..9d6c1cd
--- /dev/null
+++ b/electronasar/development/renderer/extensions/event.js
@@ -0,0 +1,22 @@
+'use strict';
+class Event {
+ constructor() {
+ this.listeners = [];
+ }
+ addListener(callback) {
+ this.listeners.push(callback);
+ }
+ removeListener(callback) {
+ const index = this.listeners.indexOf(callback);
+ 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
\ No newline at end of file
diff --git a/electronasar/development/renderer/extensions/i18n.js b/electronasar/development/renderer/extensions/i18n.js
new file mode 100644
index 0000000..83c4101
--- /dev/null
+++ b/electronasar/development/renderer/extensions/i18n.js
@@ -0,0 +1,53 @@
+'use strict';
+// Implementation of chrome.i18n.getMessage
+// https://developer.chrome.com/extensions/i18n#method-getMessage
+//
+// Does not implement predefined messages:
+// https://developer.chrome.com/extensions/i18n#overview-predefined
+const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
+const getMessages = (extensionId) => {
+ try {
+ const data = ipcRendererUtils.invokeSync('CHROME_GET_MESSAGES', extensionId);
+ return JSON.parse(data) || {};
+ }
+ catch (_a) {
+ return {};
+ }
+};
+const replaceNumberedSubstitutions = (message, substitutions) => {
+ return message.replace(/\$(\d+)/, (_, number) => {
+ const index = parseInt(number, 10) - 1;
+ return substitutions[index] || '';
+ });
+};
+const replacePlaceholders = (message, placeholders, substitutions) => {
+ if (typeof substitutions === 'string') {
+ substitutions = [substitutions];
+ }
+ if (!Array.isArray(substitutions)) {
+ substitutions = [];
+ }
+ if (placeholders) {
+ Object.keys(placeholders).forEach((name) => {
+ let { content } = placeholders[name];
+ content = replaceNumberedSubstitutions(content, substitutions);
+ message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content);
+ });
+ }
+ return replaceNumberedSubstitutions(message, substitutions);
+};
+const getMessage = (extensionId, messageName, substitutions) => {
+ const messages = getMessages(extensionId);
+ if (messages.hasOwnProperty(messageName)) {
+ const { message, placeholders } = messages[messageName];
+ return replacePlaceholders(message, placeholders, substitutions);
+ }
+};
+exports.setup = (extensionId) => {
+ return {
+ getMessage(messageName, substitutions) {
+ return getMessage(extensionId, messageName, substitutions);
+ }
+ };
+};
+//# sourceMappingURL=i18n.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/extensions/storage.js b/electronasar/development/renderer/extensions/storage.js
new file mode 100644
index 0000000..f649571
--- /dev/null
+++ b/electronasar/development/renderer/extensions/storage.js
@@ -0,0 +1,88 @@
+'use strict';
+const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
+const getStorage = (storageType, extensionId, callback) => {
+ if (typeof callback !== 'function')
+ throw new TypeError('No callback provided');
+ ipcRendererUtils.invoke('CHROME_STORAGE_READ', storageType, extensionId)
+ .then(data => {
+ if (data !== null) {
+ callback(JSON.parse(data));
+ }
+ else {
+ // Disabled due to false positive in StandardJS
+ // eslint-disable-next-line standard/no-callback-literal
+ callback({});
+ }
+ });
+};
+const setStorage = (storageType, extensionId, storage, callback) => {
+ const json = JSON.stringify(storage);
+ ipcRendererUtils.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json)
+ .then(() => {
+ if (callback)
+ callback();
+ });
+};
+const getStorageManager = (storageType, extensionId) => {
+ return {
+ get(keys, callback) {
+ getStorage(storageType, extensionId, storage => {
+ if (keys == null)
+ return callback(storage);
+ let defaults = {};
+ switch (typeof keys) {
+ case 'string':
+ keys = [keys];
+ break;
+ case 'object':
+ if (!Array.isArray(keys)) {
+ defaults = keys;
+ keys = Object.keys(keys);
+ }
+ 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);
+ }
+ };
+};
+module.exports = {
+ setup: extensionId => ({
+ sync: getStorageManager('sync', extensionId),
+ local: getStorageManager('local', extensionId)
+ })
+};
+//# sourceMappingURL=storage.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/extensions/web-navigation.js b/electronasar/development/renderer/extensions/web-navigation.js
new file mode 100644
index 0000000..87cae98
--- /dev/null
+++ b/electronasar/development/renderer/extensions/web-navigation.js
@@ -0,0 +1,19 @@
+'use strict';
+const Event = require('@electron/internal/renderer/extensions/event');
+const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
+class WebNavigation {
+ constructor() {
+ this.onBeforeNavigate = new Event();
+ this.onCompleted = new Event();
+ ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event, details) => {
+ this.onBeforeNavigate.emit(details);
+ });
+ ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event, details) => {
+ this.onCompleted.emit(details);
+ });
+ }
+}
+exports.setup = () => {
+ return new WebNavigation();
+};
+//# sourceMappingURL=web-navigation.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/init.js b/electronasar/development/renderer/init.js
new file mode 100644
index 0000000..0403cd1
--- /dev/null
+++ b/electronasar/development/renderer/init.js
@@ -0,0 +1,178 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const events_1 = require("events");
+const path = require("path");
+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
+// init.js, we need to restore it here.
+process.argv.splice(1, 1);
+// Clear search paths.
+require('../common/reset-search-paths');
+// Import common settings.
+require('@electron/internal/common/init');
+const globalPaths = Module.globalPaths;
+// Expose public APIs.
+globalPaths.push(path.join(__dirname, 'api', 'exports'));
+// The global variable will be used by ipc for event dispatching
+const v8Util = process.electronBinding('v8_util');
+const ipcEmitter = new events_1.EventEmitter();
+const ipcInternalEmitter = new events_1.EventEmitter();
+v8Util.setHiddenValue(global, 'ipc', ipcEmitter);
+v8Util.setHiddenValue(global, 'ipc-internal', ipcInternalEmitter);
+const savedProcess = process;
+v8Util.setHiddenValue(global, 'ipcNative', {
+ onMessage(internal, channel, args, senderId) {
+ const sender = internal ? ipcInternalEmitter : ipcEmitter;
+ sender.emit(channel, { sender, senderId }, ...args);
+ savedProcess.activateUvLoop();
+ }
+});
+// Use electron module after everything is ready.
+const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
+const { webFrameInit } = require('@electron/internal/renderer/web-frame-init');
+webFrameInit();
+// Process command line arguments.
+const { hasSwitch, getSwitchValue } = process.electronBinding('command_line');
+const parseOption = function (name, defaultValue, converter) {
+ return hasSwitch(name)
+ ? (converter
+ ? converter(getSwitchValue(name))
+ : getSwitchValue(name))
+ : defaultValue;
+};
+const contextIsolation = hasSwitch('context-isolation');
+const nodeIntegration = hasSwitch('node-integration');
+const webviewTag = hasSwitch('webview-tag');
+const isHiddenPage = hasSwitch('hidden-page');
+const usesNativeWindowOpen = hasSwitch('native-window-open');
+const preloadScript = parseOption('preload', null);
+const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter));
+const appPath = parseOption('app-path', null);
+const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value));
+const openerId = parseOption('opener-id', null, value => parseInt(value));
+// The arguments to be passed to isolated world.
+const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen };
+// The webContents preload script is loaded after the session preload scripts.
+if (preloadScript) {
+ preloadScripts.push(preloadScript);
+}
+switch (window.location.protocol) {
+ case 'devtools:': {
+ // Override some inspector APIs.
+ require('@electron/internal/renderer/inspector');
+ break;
+ }
+ case 'chrome-extension:': {
+ // Inject the chrome.* APIs that chrome extensions require
+ require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window);
+ break;
+ }
+ case 'chrome:':
+ break;
+ default: {
+ // Override default web functions.
+ const { windowSetup } = require('@electron/internal/renderer/window-setup');
+ windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen);
+ // Inject content scripts.
+ require('@electron/internal/renderer/content-scripts-injector')(process.getRenderProcessPreferences);
+ }
+}
+// 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) {
+ // Export node bindings to global.
+ global.require = require;
+ global.module = module;
+ // Set the __filename to the path of html file if it is file: protocol.
+ if (window.location.protocol === 'file:') {
+ const location = window.location;
+ let pathname = location.pathname;
+ if (process.platform === 'win32') {
+ if (pathname[0] === '/')
+ pathname = pathname.substr(1);
+ 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.__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) {
+ // We do not want to add `uncaughtException` to our definitions
+ // because we don't want anyone else (anywhere) to throw that kind
+ // of error.
+ global.process.emit('uncaughtException', error);
+ return true;
+ }
+ 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;
+ });
+}
+const errorUtils = require('@electron/internal/common/error-utils');
+// Load the preload scripts.
+for (const preloadScript of preloadScripts) {
+ try {
+ require(preloadScript);
+ }
+ catch (error) {
+ console.error(`Unable to load preload script: ${preloadScript}`);
+ console.error(`${error}`);
+ ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, errorUtils.serialize(error));
+ }
+}
+// Warn about security issues
+if (process.isMainFrame) {
+ const { securityWarnings } = require('@electron/internal/renderer/security-warnings');
+ securityWarnings(nodeIntegration);
+}
+//# sourceMappingURL=init.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/inspector.js b/electronasar/development/renderer/inspector.js
new file mode 100644
index 0000000..3c1f84e
--- /dev/null
+++ b/electronasar/development/renderer/inspector.js
@@ -0,0 +1,53 @@
+"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 () {
+ // Use menu API to show context menu.
+ window.InspectorFrontendHost.showContextMenuAtPoint = createMenu;
+ // correct for Chromium returning undefined for filesystem
+ window.Persistence.FileSystemWorkspaceBinding.completeURL = completeURL;
+ // Use dialog API to override file chooser dialog.
+ window.UI.createFileSelectorElement = createFileSelectorElement;
+};
+// Extra / is needed as a result of MacOS requiring absolute paths
+function completeURL(project, path) {
+ project = 'file:///';
+ return `${project}${path}`;
+}
+// The DOM implementation expects (message?: string) => boolean
+window.confirm = function (message, title) {
+ return ipc_renderer_internal_utils_1.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title);
+};
+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 createMenu = function (x, y, items) {
+ const isEditMenu = useEditMenuItems(x, y, items);
+ ipc_renderer_internal_utils_1.invoke('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => {
+ if (typeof id === 'number') {
+ window.DevToolsAPI.contextMenuItemSelected(id);
+ }
+ window.DevToolsAPI.contextMenuCleared();
+ });
+};
+const showFileChooserDialog = function (callback) {
+ ipc_renderer_internal_utils_1.invoke('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => {
+ if (path && data) {
+ callback(dataToHtml5FileObject(path, data));
+ }
+ });
+};
+const dataToHtml5FileObject = function (path, data) {
+ return new File([data], path);
+};
+const createFileSelectorElement = function (callback) {
+ const fileSelectorElement = document.createElement('span');
+ fileSelectorElement.style.display = 'none';
+ fileSelectorElement.click = showFileChooserDialog.bind(this, callback);
+ return fileSelectorElement;
+};
+//# sourceMappingURL=inspector.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/ipc-renderer-internal-utils.js b/electronasar/development/renderer/ipc-renderer-internal-utils.js
new file mode 100644
index 0000000..2dc6e8d
--- /dev/null
+++ b/electronasar/development/renderer/ipc-renderer-internal-utils.js
@@ -0,0 +1,42 @@
+"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
\ No newline at end of file
diff --git a/electronasar/development/renderer/ipc-renderer-internal.js b/electronasar/development/renderer/ipc-renderer-internal.js
new file mode 100644
index 0000000..8e1ca4f
--- /dev/null
+++ b/electronasar/development/renderer/ipc-renderer-internal.js
@@ -0,0 +1,20 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const binding = process.electronBinding('ipc');
+const v8Util = process.electronBinding('v8_util');
+// Created by init.js.
+exports.ipcRendererInternal = v8Util.getHiddenValue(global, 'ipc-internal');
+const internal = true;
+exports.ipcRendererInternal.send = function (channel, ...args) {
+ return binding.ipc.send(internal, channel, args);
+};
+exports.ipcRendererInternal.sendSync = function (channel, ...args) {
+ return binding.ipc.sendSync(internal, channel, args)[0];
+};
+exports.ipcRendererInternal.sendTo = function (webContentsId, channel, ...args) {
+ return binding.ipc.sendTo(internal, false, webContentsId, channel, args);
+};
+exports.ipcRendererInternal.sendToAll = function (webContentsId, channel, ...args) {
+ return binding.ipc.sendTo(internal, true, webContentsId, channel, args);
+};
+//# sourceMappingURL=ipc-renderer-internal.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/security-warnings.js b/electronasar/development/renderer/security-warnings.js
new file mode 100644
index 0000000..5dfc97e
--- /dev/null
+++ b/electronasar/development/renderer/security-warnings.js
@@ -0,0 +1,239 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const electron_1 = require("electron");
+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.
+ * It does so by determining whether we're running as Electron,
+ * which indicates that a developer is currently looking at the
+ * app.
+ *
+ * @returns {boolean} - Should we log?
+ */
+const shouldLogSecurityWarnings = function () {
+ if (shouldLog !== null) {
+ return shouldLog;
+ }
+ const { platform, execPath, env } = process;
+ switch (platform) {
+ case 'darwin':
+ shouldLog = execPath.endsWith('MacOS/Electron') ||
+ execPath.includes('Electron.app/Contents/Frameworks/');
+ break;
+ case 'freebsd':
+ case 'linux':
+ shouldLog = execPath.endsWith('/electron');
+ break;
+ case 'win32':
+ shouldLog = execPath.endsWith('\\electron.exe');
+ break;
+ default:
+ shouldLog = false;
+ }
+ if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) ||
+ (window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) {
+ shouldLog = false;
+ }
+ if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) ||
+ (window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) {
+ shouldLog = true;
+ }
+ return shouldLog;
+};
+/**
+ * Checks if the current window is remote.
+ *
+ * @returns {boolean} - Is this a remote protocol?
+ */
+const getIsRemoteProtocol = function () {
+ if (window && window.location && window.location.protocol) {
+ return /^(http|ftp)s?/gi.test(window.location.protocol);
+ }
+};
+/**
+ * Tries to determine whether a CSP without `unsafe-eval` is set.
+ *
+ * @returns {boolean} Is a CSP with `unsafe-eval` set?
+ */
+const isUnsafeEvalEnabled = function () {
+ return electron_1.webFrame.executeJavaScript(`(${(() => {
+ try {
+ new Function(''); // eslint-disable-line no-new,no-new-func
+ }
+ catch (_a) {
+ return false;
+ }
+ return true;
+ }).toString()})()`, false);
+};
+const moreInformation = `\nFor more information and help, consult
+https://electronjs.org/docs/tutorial/security.\n This warning will not show up
+once the app is packaged.`;
+/**
+ * #1 Only load secure content
+ *
+ * Checks the loaded resources on the current page and logs a
+ * message about all resources loaded over HTTP or FTP.
+ */
+const warnAboutInsecureResources = function () {
+ if (!window || !window.performance || !window.performance.getEntriesByType) {
+ return;
+ }
+ const resources = window.performance
+ .getEntriesByType('resource')
+ .filter(({ name }) => /^(http|ftp):/gi.test(name || ''))
+ .map(({ name }) => `- ${name}`)
+ .join('\n');
+ if (!resources || resources.length === 0) {
+ return;
+ }
+ const warning = `This renderer process loads resources using insecure
+ protocols.This exposes users of this app to unnecessary security risks.
+ Consider loading the following resources over HTTPS or FTPS. \n ${resources}
+ \n ${moreInformation}`;
+ console.warn('%cElectron Security Warning (Insecure Resources)', 'font-weight: bold;', warning);
+};
+/**
+ * #2 on the checklist: Disable the Node.js integration in all renderers that
+ * display remote content
+ *
+ * Logs a warning message about Node integration.
+ */
+const warnAboutNodeWithRemoteContent = function (nodeIntegration) {
+ if (!nodeIntegration)
+ return;
+ if (getIsRemoteProtocol()) {
+ const warning = `This renderer process has Node.js integration enabled
+ and attempted to load remote content from '${window.location}'. This
+ 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);
+ }
+};
+// Currently missing since it has ramifications and is still experimental:
+// #3 Enable context isolation in all renderers that display remote content
+//
+// Currently missing since we can't easily programmatically check for those cases:
+// #4 Use ses.setPermissionRequestHandler() in all sessions that load remote content
+/**
+ * #5 on the checklist: Do not disable websecurity
+ *
+ * Logs a warning message about disabled webSecurity.
+ */
+const warnAboutDisabledWebSecurity = function (webPreferences) {
+ if (!webPreferences || webPreferences.webSecurity !== false)
+ return;
+ const warning = `This renderer process has "webSecurity" disabled. This
+ exposes users of this app to severe security risks.\n ${moreInformation}`;
+ console.warn('%cElectron Security Warning (Disabled webSecurity)', 'font-weight: bold;', warning);
+};
+/**
+ * #6 on the checklist: Define a Content-Security-Policy and use restrictive
+ * rules (i.e. script-src 'self')
+ *
+ * #7 on the checklist: Disable eval
+ *
+ * Logs a warning message about unset or insecure CSP
+ */
+const warnAboutInsecureCSP = function () {
+ isUnsafeEvalEnabled().then((enabled) => {
+ if (!enabled)
+ return;
+ const warning = `This renderer process has either no Content Security
+ Policy set or a policy with "unsafe-eval" enabled. This exposes users of
+ this app to unnecessary security risks.\n ${moreInformation}`;
+ console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)', 'font-weight: bold;', warning);
+ });
+};
+/**
+ * #8 on the checklist: Do not set allowRunningInsecureContent to true
+ *
+ * Logs a warning message about disabled webSecurity.
+ */
+const warnAboutInsecureContentAllowed = function (webPreferences) {
+ if (!webPreferences || !webPreferences.allowRunningInsecureContent)
+ return;
+ const warning = `This renderer process has "allowRunningInsecureContent"
+ enabled. This exposes users of this app to severe security risks.\n
+ ${moreInformation}`;
+ console.warn('%cElectron Security Warning (allowRunningInsecureContent)', 'font-weight: bold;', warning);
+};
+/**
+ * #9 on the checklist: Do not enable experimental features
+ *
+ * Logs a warning message about experimental features.
+ */
+const warnAboutExperimentalFeatures = function (webPreferences) {
+ if (!webPreferences || (!webPreferences.experimentalFeatures)) {
+ return;
+ }
+ const warning = `This renderer process has "experimentalFeatures" enabled.
+ This exposes users of this app to some security risk. If you do not need
+ this feature, you should disable it.\n ${moreInformation}`;
+ console.warn('%cElectron Security Warning (experimentalFeatures)', 'font-weight: bold;', warning);
+};
+/**
+ * #10 on the checklist: Do not use enableBlinkFeatures
+ *
+ * Logs a warning message about enableBlinkFeatures
+ */
+const warnAboutEnableBlinkFeatures = function (webPreferences) {
+ if (!webPreferences ||
+ !webPreferences.hasOwnProperty('enableBlinkFeatures') ||
+ (webPreferences.enableBlinkFeatures && webPreferences.enableBlinkFeatures.length === 0)) {
+ return;
+ }
+ const warning = `This renderer process has additional "enableBlinkFeatures"
+ enabled. This exposes users of this app to some security risk. If you do not
+ need this feature, you should disable it.\n ${moreInformation}`;
+ console.warn('%cElectron Security Warning (enableBlinkFeatures)', 'font-weight: bold;', warning);
+};
+/**
+ * #11 on the checklist: Do Not Use allowpopups
+ *
+ * Logs a warning message about allowed popups
+ */
+const warnAboutAllowedPopups = function () {
+ if (document && document.querySelectorAll) {
+ const domElements = document.querySelectorAll('[allowpopups]');
+ if (!domElements || domElements.length === 0) {
+ return;
+ }
+ const warning = `A has "allowpopups" set to true. This exposes
+ 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
+ ${moreInformation}`;
+ console.warn('%cElectron Security Warning (allowpopups)', 'font-weight: bold;', warning);
+ }
+};
+// Currently missing since we can't easily programmatically check for it:
+// #12WebViews: Verify the options and params of all `` tags
+const logSecurityWarnings = function (webPreferences, nodeIntegration) {
+ warnAboutNodeWithRemoteContent(nodeIntegration);
+ warnAboutDisabledWebSecurity(webPreferences);
+ warnAboutInsecureResources();
+ warnAboutInsecureContentAllowed(webPreferences);
+ warnAboutExperimentalFeatures(webPreferences);
+ warnAboutEnableBlinkFeatures(webPreferences);
+ warnAboutInsecureCSP();
+ warnAboutAllowedPopups();
+};
+const getWebPreferences = function () {
+ try {
+ return ipc_renderer_internal_utils_1.invokeSync('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES');
+ }
+ catch (error) {
+ console.warn(`getLastWebPreferences() failed: ${error}`);
+ }
+};
+function securityWarnings(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
\ No newline at end of file
diff --git a/electronasar/development/renderer/web-frame-init.js b/electronasar/development/renderer/web-frame-init.js
new file mode 100644
index 0000000..29b03f1
--- /dev/null
+++ b/electronasar/development/renderer/web-frame-init.js
@@ -0,0 +1,14 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const electron_1 = require("electron");
+const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils");
+exports.webFrameInit = () => {
+ // Call webFrame method
+ ipcRendererUtils.handle('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, ...args) => {
+ // The TypeScript compiler cannot handle the sheer number of
+ // call signatures here and simply gives up. Incorrect invocations
+ // will be caught by "keyof WebFrameMethod" though.
+ return electron_1.webFrame[method](...args);
+ });
+};
+//# sourceMappingURL=web-frame-init.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/web-view/guest-view-internal.js b/electronasar/development/renderer/web-view/guest-view-internal.js
new file mode 100644
index 0000000..c16720c
--- /dev/null
+++ b/electronasar/development/renderer/web-view/guest-view-internal.js
@@ -0,0 +1,111 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const electron_1 = require("electron");
+const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-renderer-internal");
+const ipc_renderer_internal_utils_1 = require("@electron/internal/renderer/ipc-renderer-internal-utils");
+const WEB_VIEW_EVENTS = {
+ 'load-commit': ['url', 'isMainFrame'],
+ 'did-attach': [],
+ 'did-finish-load': [],
+ 'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
+ 'did-frame-finish-load': ['isMainFrame', 'frameProcessId', 'frameRoutingId'],
+ 'did-start-loading': [],
+ 'did-stop-loading': [],
+ 'dom-ready': [],
+ 'console-message': ['level', 'message', 'line', 'sourceId'],
+ 'context-menu': ['params'],
+ 'devtools-opened': [],
+ 'devtools-closed': [],
+ 'devtools-focused': [],
+ 'new-window': ['url', 'frameName', 'disposition', 'options'],
+ 'will-navigate': ['url'],
+ 'did-start-navigation': ['url', 'isInPlace', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
+ 'did-navigate': ['url', 'httpResponseCode', 'httpStatusText'],
+ 'did-frame-navigate': ['url', 'httpResponseCode', 'httpStatusText', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
+ 'did-navigate-in-page': ['url', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
+ 'focus-change': ['focus', 'guestInstanceId'],
+ 'close': [],
+ 'crashed': [],
+ 'plugin-crashed': ['name', 'version'],
+ 'destroyed': [],
+ 'page-title-updated': ['title', 'explicitSet'],
+ 'page-favicon-updated': ['favicons'],
+ 'enter-html-full-screen': [],
+ 'leave-html-full-screen': [],
+ 'media-started-playing': [],
+ 'media-paused': [],
+ 'found-in-page': ['result'],
+ 'did-change-theme-color': ['themeColor'],
+ 'update-target-url': ['url']
+};
+const DEPRECATED_EVENTS = {
+ 'page-title-updated': 'page-title-set'
+};
+const dispatchEvent = function (webView, eventName, eventKey, ...args) {
+ if (DEPRECATED_EVENTS[eventName] != null) {
+ dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args);
+ }
+ const domEvent = new Event(eventName);
+ WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => {
+ domEvent[prop] = args[index];
+ });
+ webView.dispatchEvent(domEvent);
+ if (eventName === 'load-commit') {
+ webView.onLoadCommit(domEvent);
+ }
+ else if (eventName === 'focus-change') {
+ 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) {
+ ipc_renderer_internal_1.ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`);
+ ipc_renderer_internal_1.ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`);
+ ipc_renderer_internal_1.ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`);
+}
+exports.deregisterEvents = deregisterEvents;
+function createGuest(params) {
+ return ipc_renderer_internal_utils_1.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params);
+}
+exports.createGuest = createGuest;
+function createGuestSync(params) {
+ return ipc_renderer_internal_utils_1.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params);
+}
+exports.createGuestSync = createGuestSync;
+function destroyGuest(guestInstanceId) {
+ ipc_renderer_internal_utils_1.invoke('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId);
+}
+exports.destroyGuest = destroyGuest;
+function attachGuest(elementInstanceId, guestInstanceId, params, contentWindow) {
+ const embedderFrameId = electron_1.webFrame.getWebFrameId(contentWindow);
+ if (embedderFrameId < 0) { // this error should not happen.
+ throw new Error('Invalid embedder frame');
+ }
+ ipc_renderer_internal_utils_1.invoke('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
\ No newline at end of file
diff --git a/electronasar/development/renderer/web-view/web-view-attributes.js b/electronasar/development/renderer/web-view/web-view-attributes.js
new file mode 100644
index 0000000..c3a139f
--- /dev/null
+++ b/electronasar/development/renderer/web-view/web-view-attributes.js
@@ -0,0 +1,251 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const ipcRendererUtils = require("@electron/internal/renderer/ipc-renderer-internal-utils");
+const web_view_impl_1 = require("@electron/internal/renderer/web-view/web-view-impl");
+// Helper function to resolve url set in attribute.
+const a = document.createElement('a');
+const resolveURL = function (url) {
+ if (!url)
+ return '';
+ a.href = url;
+ return a.href;
+};
+// Attribute objects.
+// Default implementation of a WebView attribute.
+class WebViewAttribute {
+ constructor(name, webViewImpl) {
+ this.name = name;
+ this.webViewImpl = webViewImpl;
+ this.ignoreMutation = false;
+ // Called when the attribute's value changes.
+ this.handleMutation = () => undefined;
+ this.name = name;
+ this.value = webViewImpl.webviewNode[name] || '';
+ this.webViewImpl = webViewImpl;
+ this.defineProperty();
+ }
+ // Retrieves and returns the attribute's value.
+ getValue() {
+ return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value;
+ }
+ // Sets the attribute's value.
+ setValue(value) {
+ this.webViewImpl.webviewNode.setAttribute(this.name, value || '');
+ }
+ // Changes the attribute's value without triggering its mutation handler.
+ setValueIgnoreMutation(value) {
+ this.ignoreMutation = true;
+ this.setValue(value);
+ this.ignoreMutation = false;
+ }
+ // Defines this attribute as a property on the webview node.
+ defineProperty() {
+ return Object.defineProperty(this.webViewImpl.webviewNode, this.name, {
+ get: () => {
+ return this.getValue();
+ },
+ set: (value) => {
+ return this.setValue(value);
+ },
+ enumerable: true
+ });
+ }
+}
+// An attribute that is treated as a Boolean.
+class BooleanAttribute extends WebViewAttribute {
+ getValue() {
+ return this.webViewImpl.webviewNode.hasAttribute(this.name);
+ }
+ setValue(value) {
+ if (value) {
+ this.webViewImpl.webviewNode.setAttribute(this.name, '');
+ }
+ else {
+ this.webViewImpl.webviewNode.removeAttribute(this.name);
+ }
+ }
+}
+// Attribute representing the state of the storage partition.
+class PartitionAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("partition" /* ATTRIBUTE_PARTITION */, webViewImpl);
+ this.webViewImpl = webViewImpl;
+ this.validPartitionId = true;
+ this.handleMutation = (oldValue, newValue) => {
+ newValue = newValue || '';
+ // The partition cannot change if the webview has already navigated.
+ if (!this.webViewImpl.beforeFirstNavigation) {
+ console.error("The object has already navigated, so its partition cannot be changed." /* ERROR_MSG_ALREADY_NAVIGATED */);
+ this.setValueIgnoreMutation(oldValue);
+ return;
+ }
+ if (newValue === 'persist:') {
+ this.validPartitionId = false;
+ console.error("Invalid partition attribute." /* ERROR_MSG_INVALID_PARTITION_ATTRIBUTE */);
+ }
+ };
+ }
+}
+// Attribute that handles the location and navigation of the webview.
+class SrcAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("src" /* ATTRIBUTE_SRC */, webViewImpl);
+ this.webViewImpl = webViewImpl;
+ this.handleMutation = (oldValue, newValue) => {
+ // Once we have navigated, we don't allow clearing the src attribute.
+ // Once 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;
+ }
+ this.parse();
+ };
+ this.setupMutationObserver();
+ }
+ getValue() {
+ if (this.webViewImpl.webviewNode.hasAttribute(this.name)) {
+ return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name));
+ }
+ else {
+ return this.value;
+ }
+ }
+ setValueIgnoreMutation(value) {
+ 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
+ // mutation observer |observer|, and then get handled even though we do not
+ // want to handle this mutation.
+ this.observer.takeRecords();
+ }
+ // The purpose of this mutation observer is to catch assignment to the src
+ // 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.
+ 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]
+ };
+ this.observer.observe(this.webViewImpl.webviewNode, params);
+ }
+ parse() {
+ if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes["partition" /* ATTRIBUTE_PARTITION */].validPartitionId || !this.getValue()) {
+ return;
+ }
+ if (this.webViewImpl.guestInstanceId == null) {
+ if (this.webViewImpl.beforeFirstNavigation) {
+ 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.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args);
+ }
+}
+// Attribute specifies HTTP referrer.
+class HttpReferrerAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("httpreferrer" /* ATTRIBUTE_HTTPREFERRER */, webViewImpl);
+ }
+}
+// Attribute specifies user agent
+class UserAgentAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("useragent" /* ATTRIBUTE_USERAGENT */, webViewImpl);
+ }
+}
+// Attribute that set preload script.
+class PreloadAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("preload" /* ATTRIBUTE_PRELOAD */, webViewImpl);
+ }
+ getValue() {
+ if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) {
+ return this.value;
+ }
+ let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name));
+ 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;
+ }
+}
+// Attribute that specifies the blink features to be enabled.
+class BlinkFeaturesAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("blinkfeatures" /* ATTRIBUTE_BLINKFEATURES */, webViewImpl);
+ }
+}
+// Attribute that specifies the blink features to be disabled.
+class DisableBlinkFeaturesAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("disableblinkfeatures" /* ATTRIBUTE_DISABLEBLINKFEATURES */, webViewImpl);
+ }
+}
+// Attribute that specifies the web preferences to be enabled.
+class WebPreferencesAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("webpreferences" /* ATTRIBUTE_WEBPREFERENCES */, webViewImpl);
+ }
+}
+class EnableRemoteModuleAttribute extends WebViewAttribute {
+ constructor(webViewImpl) {
+ super("enableremotemodule" /* ATTRIBUTE_ENABLEREMOTEMODULE */, webViewImpl);
+ }
+ getValue() {
+ return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false';
+ }
+ setValue(value) {
+ this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false');
+ }
+}
+// Sets up all of the webview attributes.
+web_view_impl_1.WebViewImpl.prototype.setupWebViewAttributes = function () {
+ this.attributes = {};
+ this.attributes["partition" /* ATTRIBUTE_PARTITION */] = new PartitionAttribute(this);
+ this.attributes["src" /* ATTRIBUTE_SRC */] = new SrcAttribute(this);
+ this.attributes["httpreferrer" /* ATTRIBUTE_HTTPREFERRER */] = new HttpReferrerAttribute(this);
+ this.attributes["useragent" /* ATTRIBUTE_USERAGENT */] = new UserAgentAttribute(this);
+ this.attributes["nodeintegration" /* ATTRIBUTE_NODEINTEGRATION */] = new BooleanAttribute("nodeintegration" /* ATTRIBUTE_NODEINTEGRATION */, this);
+ this.attributes["nodeintegrationinsubframes" /* ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES */] = new BooleanAttribute("nodeintegrationinsubframes" /* ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES */, this);
+ this.attributes["plugins" /* ATTRIBUTE_PLUGINS */] = new BooleanAttribute("plugins" /* ATTRIBUTE_PLUGINS */, this);
+ this.attributes["disablewebsecurity" /* ATTRIBUTE_DISABLEWEBSECURITY */] = new BooleanAttribute("disablewebsecurity" /* ATTRIBUTE_DISABLEWEBSECURITY */, this);
+ this.attributes["allowpopups" /* ATTRIBUTE_ALLOWPOPUPS */] = new BooleanAttribute("allowpopups" /* ATTRIBUTE_ALLOWPOPUPS */, this);
+ this.attributes["enableremotemodule" /* ATTRIBUTE_ENABLEREMOTEMODULE */] = new EnableRemoteModuleAttribute(this);
+ this.attributes["preload" /* ATTRIBUTE_PRELOAD */] = new PreloadAttribute(this);
+ this.attributes["blinkfeatures" /* ATTRIBUTE_BLINKFEATURES */] = new BlinkFeaturesAttribute(this);
+ this.attributes["disableblinkfeatures" /* ATTRIBUTE_DISABLEBLINKFEATURES */] = new DisableBlinkFeaturesAttribute(this);
+ this.attributes["webpreferences" /* ATTRIBUTE_WEBPREFERENCES */] = new WebPreferencesAttribute(this);
+};
+//# sourceMappingURL=web-view-attributes.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/web-view/web-view-constants.js b/electronasar/development/renderer/web-view/web-view-constants.js
new file mode 100644
index 0000000..97cfb5f
--- /dev/null
+++ b/electronasar/development/renderer/web-view/web-view-constants.js
@@ -0,0 +1,3 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+//# sourceMappingURL=web-view-constants.js.map
\ No newline at end of file
diff --git a/electronasar/development/renderer/web-view/web-view-element.js b/electronasar/development/renderer/web-view/web-view-element.js
new file mode 100644
index 0000000..6c7092e
--- /dev/null
+++ b/electronasar/development/renderer/web-view/web-view-element.js
@@ -0,0 +1,102 @@
+"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 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 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
\ No newline at end of file
diff --git a/electronasar/development/renderer/web-view/web-view-impl.js b/electronasar/development/renderer/web-view/web-view-impl.js
new file mode 100644
index 0000000..3bb03e5
--- /dev/null
+++ b/electronasar/development/renderer/web-view/web-view-impl.js
@@ -0,0 +1,233 @@
+"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 = '';
+ 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 element to the DOM.
+ reset() {
+ // If guestInstanceId is defined then the 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 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' 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
\ No newline at end of file
diff --git a/electronasar/development/renderer/web-view/web-view-init.js b/electronasar/development/renderer/web-view/web-view-init.js
new file mode 100644
index 0000000..e1951a7
--- /dev/null
+++ b/electronasar/development/renderer/web-view/web-view-init.js
@@ -0,0 +1,33 @@
+"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 ``.
+ 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
\ No newline at end of file
diff --git a/electronasar/development/renderer/window-setup.js b/electronasar/development/renderer/window-setup.js
new file mode 100644
index 0000000..cbc2ebf
--- /dev/null
+++ b/electronasar/development/renderer/window-setup.js
@@ -0,0 +1,247 @@
+"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;
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+ 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;
+ 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:
+// - window.history.back()
+// - window.history.forward()
+// - window.history.go()
+// - window.history.length
+// - window.open()
+// - window.opener.blur()
+// - window.opener.close()
+// - window.opener.eval()
+// - window.opener.focus()
+// - window.opener.location
+// - window.opener.print()
+// - window.opener.postMessage()
+// - window.prompt()
+// - document.hidden
+// - document.visibilityState
+const { defineProperty } = Object;
+// Helper function to resolve relative url.
+const a = window.document.createElement('a');
+const resolveURL = function (url) {
+ a.href = url;
+ return a.href;
+};
+// Use this method to ensure values expected as strings in the main process
+// are convertible to strings in the renderer process. This ensures exceptions
+// converting values to strings are thrown in this process.
+const toString = (value) => {
+ return value != null ? `${value}` : value;
+};
+const windowProxies = {};
+const getOrCreateProxy = (guestId) => {
+ let proxy = windowProxies[guestId];
+ if (proxy == null) {
+ proxy = new BrowserWindowProxy(guestId);
+ windowProxies[guestId] = proxy;
+ }
+ return proxy;
+};
+const removeProxy = (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 ipc_renderer_internal_1.ipcRendererInternal.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
+], LocationProxy.prototype, "hash", void 0);
+__decorate([
+ LocationProxy.ProxyProperty
+], LocationProxy.prototype, "href", void 0);
+__decorate([
+ LocationProxy.ProxyProperty
+], LocationProxy.prototype, "host", void 0);
+__decorate([
+ LocationProxy.ProxyProperty
+], 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);
+ }
+}
+exports.windowSetup = (guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen) => {
+ if (guestInstanceId == null) {
+ // Override default window.close.
+ window.close = function () {
+ ipc_renderer_internal_1.ipcRendererInternal.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 (url != null && url !== '') {
+ url = resolveURL(url);
+ }
+ const guestId = ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features));
+ if (guestId != null) {
+ return getOrCreateProxy(guestId);
+ }
+ else {
+ return null;
+ }
+ };
+ if (openerId != null) {
+ window.opener = getOrCreateProxy(openerId);
+ }
+ }
+ // But we do not support prompt().
+ window.prompt = function () {
+ throw new Error('prompt() is and will not be supported.');
+ };
+ 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 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
\ No newline at end of file
diff --git a/electronasar/development/worker/init.js b/electronasar/development/worker/init.js
new file mode 100644
index 0000000..492e6a1
--- /dev/null
+++ b/electronasar/development/worker/init.js
@@ -0,0 +1,30 @@
+'use strict';
+const path = require('path');
+const Module = require('module');
+// We modified the original process.argv to let node.js load the
+// init.js, we need to restore it here.
+process.argv.splice(1, 1);
+// Clear search paths.
+require('../common/reset-search-paths');
+// Import common settings.
+require('@electron/internal/common/init');
+// Expose public APIs.
+Module.globalPaths.push(path.join(__dirname, 'api', 'exports'));
+// Export node bindings to global.
+global.require = require;
+global.module = module;
+// Set the __filename to the path of html file if it is file: protocol.
+if (self.location.protocol === 'file:') {
+ const pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.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.__dirname = __dirname;
+}
+//# sourceMappingURL=init.js.map
\ No newline at end of file