\ X S {"files":{"browser":{"files":{"api":{"files":{"lib":{"files":{"app.js":{"size":3536,"offset":"0"},"auto-updater":{"files":{"auto-updater-native.js":{"size":208,"offset":"3841"},"auto-updater-win.js":{"size":1837,"offset":"4049"},"squirrel-update-win.js":{"size":2880,"offset":"5886"}}},"auto-updater.js":{"size":305,"offset":"3536"},"browser-window.js":{"size":7637,"offset":"8766"},"content-tracing.js":{"size":58,"offset":"16403"},"dialog.js":{"size":5557,"offset":"16461"},"exports":{"files":{"electron.js":{"size":2237,"offset":"22018"}}},"global-shortcut.js":{"size":132,"offset":"24255"},"ipc-main.js":{"size":92,"offset":"24387"},"ipc.js":{"size":255,"offset":"24479"},"menu-item.js":{"size":3616,"offset":"24734"},"menu.js":{"size":9962,"offset":"28350"},"navigation-controller.js":{"size":6129,"offset":"38312"},"power-monitor.js":{"size":213,"offset":"44441"},"power-save-blocker.js":{"size":143,"offset":"44654"},"protocol.js":{"size":1102,"offset":"44797"},"screen.js":{"size":182,"offset":"45899"},"session.js":{"size":926,"offset":"46081"},"tray.js":{"size":747,"offset":"47007"},"web-contents.js":{"size":7314,"offset":"47754"}}}}},"lib":{"files":{"chrome-extension.js":{"size":4616,"offset":"55068"},"desktop-capturer.js":{"size":2829,"offset":"59684"},"guest-view-manager.js":{"size":7753,"offset":"62513"},"guest-window-manager.js":{"size":4894,"offset":"70266"},"init.js":{"size":4519,"offset":"75160"},"objects-registry.js":{"size":2721,"offset":"79679"},"rpc-server.js":{"size":11348,"offset":"82400"}}}}},"common":{"files":{"api":{"files":{"lib":{"files":{"callbacks-registry.js":{"size":1819,"offset":"93748"},"clipboard.js":{"size":259,"offset":"95567"},"crash-reporter.js":{"size":3057,"offset":"95826"},"deprecate.js":{"size":2762,"offset":"98883"},"exports":{"files":{"electron.js":{"size":1482,"offset":"101645"}}},"native-image.js":{"size":233,"offset":"103127"},"shell.js":{"size":48,"offset":"103360"}}}}},"lib":{"files":{"init.js":{"size":1729,"offset":"103408"},"reset-search-paths.js":{"size":1100,"offset":"105137"}}}}},"renderer":{"files":{"api":{"files":{"lib":{"files":{"desktop-capturer.js":{"size":1503,"offset":"106237"},"exports":{"files":{"electron.js":{"size":815,"offset":"107740"}}},"ipc-renderer.js":{"size":798,"offset":"108555"},"ipc.js":{"size":922,"offset":"109353"},"remote.js":{"size":10131,"offset":"110275"},"screen.js":{"size":53,"offset":"120406"},"web-frame.js":{"size":684,"offset":"120459"}}}}},"lib":{"files":{"chrome-api.js":{"size":293,"offset":"121143"},"init.js":{"size":4573,"offset":"121436"},"inspector.js":{"size":2347,"offset":"126009"},"override.js":{"size":6854,"offset":"128356"},"web-view":{"files":{"guest-view-internal.js":{"size":4776,"offset":"135210"},"web-view-attributes.js":{"size":11386,"offset":"139986"},"web-view-constants.js":{"size":1206,"offset":"151372"},"web-view.js":{"size":16193,"offset":"152578"}}}}}}}}}const deprecate = require('electron').deprecate; const session = require('electron').session; const Menu = require('electron').Menu; const EventEmitter = require('events').EventEmitter; const bindings = process.atomBinding('app'); const downloadItemBindings = process.atomBinding('download_item'); const app = bindings.app; var slice = [].slice; app.__proto__ = EventEmitter.prototype; app.setApplicationMenu = function(menu) { return Menu.setApplicationMenu(menu); }; app.getApplicationMenu = function() { return Menu.getApplicationMenu(); }; app.commandLine = { appendSwitch: bindings.appendSwitch, appendArgument: bindings.appendArgument }; if (process.platform === 'darwin') { app.dock = { bounce: function(type) { if (type == null) { type = 'informational'; } return bindings.dockBounce(type); }, cancelBounce: bindings.dockCancelBounce, setBadge: bindings.dockSetBadgeText, getBadge: bindings.dockGetBadgeText, hide: bindings.dockHide, show: bindings.dockShow, setMenu: bindings.dockSetMenu, setIcon: bindings.dockSetIcon }; } var appPath = null; app.setAppPath = function(path) { return appPath = path; }; app.getAppPath = function() { return appPath; }; // Routes the events to webContents. var ref1 = ['login', 'certificate-error', 'select-client-certificate']; var fn = function(name) { return app.on(name, function() { var args, event, webContents; event = arguments[0], webContents = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : []; return webContents.emit.apply(webContents, [name, event].concat(slice.call(args))); }); }; var i, len; for (i = 0, len = ref1.length; i < len; i++) { fn(ref1[i]); } // Deprecated. app.getHomeDir = deprecate('app.getHomeDir', 'app.getPath', function() { return this.getPath('home'); }); app.getDataPath = deprecate('app.getDataPath', 'app.getPath', function() { return this.getPath('userData'); }); app.setDataPath = deprecate('app.setDataPath', 'app.setPath', function(path) { return this.setPath('userData', path); }); app.resolveProxy = deprecate('app.resolveProxy', 'session.defaultSession.resolveProxy', function(url, callback) { return session.defaultSession.resolveProxy(url, callback); }); deprecate.rename(app, 'terminate', 'quit'); deprecate.event(app, 'finish-launching', 'ready', function() { // give default app a chance to setup default menu. return setImmediate((function(_this) { return function() { return _this.emit('finish-launching'); }; })(this)); }); deprecate.event(app, 'activate-with-no-open-windows', 'activate', function(event, hasVisibleWindows) { if (!hasVisibleWindows) { return this.emit('activate-with-no-open-windows', event); } }); deprecate.event(app, 'select-certificate', 'select-client-certificate'); // Wrappers for native classes. var wrapDownloadItem = function(downloadItem) { // downloadItem is an EventEmitter. downloadItem.__proto__ = EventEmitter.prototype; // Deprecated. deprecate.property(downloadItem, 'url', 'getURL'); deprecate.property(downloadItem, 'filename', 'getFilename'); deprecate.property(downloadItem, 'mimeType', 'getMimeType'); return deprecate.rename(downloadItem, 'getUrl', 'getURL'); }; downloadItemBindings._setWrapDownloadItem(wrapDownloadItem); // Only one App object pemitted. module.exports = app; const deprecate = require('electron').deprecate; const autoUpdater = process.platform === 'win32' ? require('./auto-updater/auto-updater-win') : require('./auto-updater/auto-updater-native'); // Deprecated. deprecate.rename(autoUpdater, 'setFeedUrl', 'setFeedURL'); module.exports = autoUpdater; const EventEmitter = require('events').EventEmitter; const autoUpdater = process.atomBinding('auto_updater').autoUpdater; autoUpdater.__proto__ = EventEmitter.prototype; module.exports = autoUpdater; 'use strict'; const app = require('electron').app; const EventEmitter = require('events').EventEmitter; const squirrelUpdate = require('./squirrel-update-win'); class AutoUpdater extends EventEmitter { constructor() { super(); } quitAndInstall() { squirrelUpdate.processStart(); return app.quit(); } setFeedURL(updateURL) { return 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'); return squirrelUpdate.download(this.updateURL, (function(_this) { return function(error, update) { if (error != null) { return _this.emitError(error); } if (update == null) { return _this.emit('update-not-available'); } _this.emit('update-available'); return squirrelUpdate.update(_this.updateURL, function(error) { var date, releaseNotes, version; if (error != null) { return _this.emitError(error); } releaseNotes = update.releaseNotes, version = update.version; // Following information is not available on Windows, so fake them. date = new Date; return _this.emit('update-downloaded', {}, releaseNotes, version, date, _this.updateURL, function() { return _this.quitAndInstall(); }); }); }; })(this)); } // Private: Emit both error object and message, this is to keep compatibility // with Old APIs. emitError(message) { return this.emit('error', new Error(message), message); } } module.exports = new AutoUpdater; 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); // Spawn a command and invoke the callback when it completes with an error // and the output from standard out. var spawnUpdate = function(args, detached, callback) { var error, errorEmitted, spawnedProcess, stderr, stdout; try { spawnedProcess = spawn(updateExe, args, { detached: detached }); } catch (error1) { error = error1; // Shouldn't happen, but still guard it. process.nextTick(function() { return callback(error); }); return; } stdout = ''; stderr = ''; spawnedProcess.stdout.on('data', function(data) { return stdout += data; }); spawnedProcess.stderr.on('data', function(data) { return stderr += data; }); errorEmitted = false; spawnedProcess.on('error', function(error) { errorEmitted = true; return callback(error); }); return spawnedProcess.on('exit', function(code, signal) { // We may have already emitted an error. if (errorEmitted) { return; } // Process terminated with error. if (code !== 0) { return callback("Command failed: " + (signal != null ? signal : code) + "\n" + stderr); } // Success. return callback(null, stdout); }); }; // Start an instance of the installed app. exports.processStart = function() { return spawnUpdate(['--processStart', exeName], true, function() {}); }; // Download the releases specified by the URL and write new results to stdout. exports.download = function(updateURL, callback) { return spawnUpdate(['--download', updateURL], false, function(error, stdout) { var json, ref, ref1, update; if (error != null) { return callback(error); } try { // Last line of output is the JSON details about the releases 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 (jsonError) { 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 (error) { return false; } }; const ipcMain = require('electron').ipcMain; const deprecate = require('electron').deprecate; const EventEmitter = require('events').EventEmitter; const BrowserWindow = process.atomBinding('window').BrowserWindow; BrowserWindow.prototype.__proto__ = EventEmitter.prototype; BrowserWindow.prototype._init = function() { // avoid recursive require. var app, menu; app = require('electron').app; // Simulate the application menu on platforms other than OS X. if (process.platform !== 'darwin') { menu = app.getApplicationMenu(); if (menu != null) { this.setMenu(menu); } } // Make new windows requested by links behave like "window.open" this.webContents.on('-new-window', function(event, url, frameName) { var options; options = { show: true, width: 800, height: 600 }; return ipcMain.emit('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options); }); // window.resizeTo(...) // window.moveTo(...) this.webContents.on('move', (function(_this) { return function(event, size) { return _this.setBounds(size); }; })(this)); // Hide the auto-hide menu when webContents is focused. this.webContents.on('activate', (function(_this) { return function() { if (process.platform !== 'darwin' && _this.isMenuBarAutoHide() && _this.isMenuBarVisible()) { return _this.setMenuBarVisibility(false); } }; })(this)); // Forward the crashed event. this.webContents.on('crashed', (function(_this) { return function() { return _this.emit('crashed'); }; })(this)); // Change window title to page title. this.webContents.on('page-title-updated', (event, title) => { // The page-title-updated event is not emitted immediately (see #3645), so // when the callback is called the BrowserWindow might have been closed. if (this.isDestroyed()) return; // Route the event to BrowserWindow. this.emit('page-title-updated', event, title); if (!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 OS X 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() { return this.focus(); }); // Redirect focus/blur event to app instance too. this.on('blur', (function(_this) { return function(event) { return app.emit('browser-window-blur', event, _this); }; })(this)); this.on('focus', (function(_this) { return function(event) { return app.emit('browser-window-focus', event, _this); }; })(this)); // Notify the creation of the window. app.emit('browser-window-created', {}, this); // Be compatible with old APIs. this.webContents.on('devtools-focused', (function(_this) { return function() { return _this.emit('devtools-focused'); }; })(this)); this.webContents.on('devtools-opened', (function(_this) { return function() { return _this.emit('devtools-opened'); }; })(this)); this.webContents.on('devtools-closed', (function(_this) { return function() { return _this.emit('devtools-closed'); }; })(this)); return Object.defineProperty(this, 'devToolsWebContents', { enumerable: true, configurable: false, get: function() { return this.webContents.devToolsWebContents; } }); }; BrowserWindow.getFocusedWindow = function() { var i, len, window, windows; windows = BrowserWindow.getAllWindows(); for (i = 0, len = windows.length; i < len; i++) { window = windows[i]; if (window.isFocused()) { return window; } } return null; }; BrowserWindow.fromWebContents = function(webContents) { var i, len, ref1, window, windows; windows = BrowserWindow.getAllWindows(); for (i = 0, len = windows.length; i < len; i++) { window = windows[i]; if ((ref1 = window.webContents) != null ? ref1.equal(webContents) : void 0) { return window; } } }; BrowserWindow.fromDevToolsWebContents = function(webContents) { var i, len, ref1, window, windows; windows = BrowserWindow.getAllWindows(); for (i = 0, len = windows.length; i < len; i++) { window = windows[i]; if ((ref1 = window.devToolsWebContents) != null ? ref1.equal(webContents) : void 0) { return window; } } }; // Helpers. BrowserWindow.prototype.loadURL = function() { return this.webContents.loadURL.apply(this.webContents, arguments); }; BrowserWindow.prototype.getURL = function() { return this.webContents.getURL(); }; BrowserWindow.prototype.reload = function() { return this.webContents.reload.apply(this.webContents, arguments); }; BrowserWindow.prototype.send = function() { return this.webContents.send.apply(this.webContents, arguments); }; BrowserWindow.prototype.openDevTools = function() { return this.webContents.openDevTools.apply(this.webContents, arguments); }; BrowserWindow.prototype.closeDevTools = function() { return this.webContents.closeDevTools(); }; BrowserWindow.prototype.isDevToolsOpened = function() { return this.webContents.isDevToolsOpened(); }; BrowserWindow.prototype.isDevToolsFocused = function() { return this.webContents.isDevToolsFocused(); }; BrowserWindow.prototype.toggleDevTools = function() { return this.webContents.toggleDevTools(); }; BrowserWindow.prototype.inspectElement = function() { return this.webContents.inspectElement.apply(this.webContents, arguments); }; BrowserWindow.prototype.inspectServiceWorker = function() { return this.webContents.inspectServiceWorker(); }; // Deprecated. deprecate.member(BrowserWindow, 'undo', 'webContents'); deprecate.member(BrowserWindow, 'redo', 'webContents'); deprecate.member(BrowserWindow, 'cut', 'webContents'); deprecate.member(BrowserWindow, 'copy', 'webContents'); deprecate.member(BrowserWindow, 'paste', 'webContents'); deprecate.member(BrowserWindow, 'selectAll', 'webContents'); deprecate.member(BrowserWindow, 'reloadIgnoringCache', 'webContents'); deprecate.member(BrowserWindow, 'isLoading', 'webContents'); deprecate.member(BrowserWindow, 'isWaitingForResponse', 'webContents'); deprecate.member(BrowserWindow, 'stop', 'webContents'); deprecate.member(BrowserWindow, 'isCrashed', 'webContents'); deprecate.member(BrowserWindow, 'print', 'webContents'); deprecate.member(BrowserWindow, 'printToPDF', 'webContents'); deprecate.rename(BrowserWindow, 'restart', 'reload'); deprecate.rename(BrowserWindow, 'loadUrl', 'loadURL'); deprecate.rename(BrowserWindow, 'getUrl', 'getURL'); BrowserWindow.prototype.executeJavaScriptInDevTools = deprecate('executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', function(code) { var ref1; return (ref1 = this.devToolsWebContents) != null ? ref1.executeJavaScript(code) : void 0; }); BrowserWindow.prototype.getPageTitle = deprecate('getPageTitle', 'webContents.getTitle', function() { var ref1; return (ref1 = this.webContents) != null ? ref1.getTitle() : void 0; }); module.exports = BrowserWindow; module.exports = process.atomBinding('content_tracing'); const app = require('electron').app; const BrowserWindow = require('electron').BrowserWindow; const binding = process.atomBinding('dialog'); const v8Util = process.atomBinding('v8_util'); var slice = [].slice; var includes = [].includes; var fileDialogProperties = { openFile: 1 << 0, openDirectory: 1 << 1, multiSelections: 1 << 2, createDirectory: 1 << 3 }; var messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']; var messageBoxOptions = { noLink: 1 << 0 }; var parseArgs = function(window, options, callback) { if (!(window === null || (window != null ? window.constructor : void 0) === BrowserWindow)) { // Shift. callback = options; options = window; window = null; } if ((callback == null) && typeof options === 'function') { // Shift. callback = options; options = null; } return [window, options, callback]; }; var checkAppInitialized = function() { if (!app.isReady()) { throw new Error('dialog module can only be used after app is ready'); } }; module.exports = { showOpenDialog: function() { var args, callback, options, prop, properties, ref1, value, window, wrappedCallback; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; checkAppInitialized(); ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2]; if (options == null) { options = { title: 'Open', properties: ['openFile'] }; } if (options.properties == null) { options.properties = ['openFile']; } if (!Array.isArray(options.properties)) { throw new TypeError('Properties need to be array'); } properties = 0; for (prop in fileDialogProperties) { value = fileDialogProperties[prop]; if (includes.call(options.properties, prop)) { properties |= value; } } if (options.title == null) { options.title = ''; } if (options.defaultPath == null) { options.defaultPath = ''; } if (options.filters == null) { options.filters = []; } wrappedCallback = typeof callback === 'function' ? function(success, result) { return callback(success ? result : void 0); } : null; return binding.showOpenDialog(String(options.title), String(options.defaultPath), options.filters, properties, window, wrappedCallback); }, showSaveDialog: function() { var args, callback, options, ref1, window, wrappedCallback; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; checkAppInitialized(); ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2]; if (options == null) { options = { title: 'Save' }; } if (options.title == null) { options.title = ''; } if (options.defaultPath == null) { options.defaultPath = ''; } if (options.filters == null) { options.filters = []; } wrappedCallback = typeof callback === 'function' ? function(success, result) { return callback(success ? result : void 0); } : null; return binding.showSaveDialog(String(options.title), String(options.defaultPath), options.filters, window, wrappedCallback); }, showMessageBox: function() { var args, callback, flags, i, j, len, messageBoxType, options, ref1, ref2, ref3, text, window; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; checkAppInitialized(); ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2]; if (options == null) { options = { type: 'none' }; } if (options.type == null) { options.type = 'none'; } messageBoxType = messageBoxTypes.indexOf(options.type); if (!(messageBoxType > -1)) { throw new TypeError('Invalid message box type'); } if (!Array.isArray(options.buttons)) { throw new TypeError('Buttons need to be array'); } if (options.title == null) { options.title = ''; } if (options.message == null) { options.message = ''; } if (options.detail == null) { options.detail = ''; } if (options.icon == null) { options.icon = null; } if (options.defaultId == null) { options.defaultId = -1; } // Choose a default button to get selected when dialog is cancelled. if (options.cancelId == null) { options.cancelId = 0; ref2 = options.buttons; for (i = j = 0, len = ref2.length; j < len; i = ++j) { text = ref2[i]; if ((ref3 = text.toLowerCase()) === 'cancel' || ref3 === 'no') { options.cancelId = i; break; } } } flags = options.noLink ? messageBoxOptions.noLink : 0; return binding.showMessageBox(messageBoxType, options.buttons, options.defaultId, options.cancelId, flags, options.title, options.message, options.detail, options.icon, window, callback); }, showErrorBox: function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return binding.showErrorBox.apply(binding, args); } }; // Mark standard asynchronous functions. var ref1 = ['showMessageBox', 'showOpenDialog', 'showSaveDialog']; var j, len, api; for (j = 0, len = ref1.length; j < len; j++) { api = ref1[j]; v8Util.setHiddenValue(module.exports[api], 'asynchronous', true); } const common = require('../../../../common/api/lib/exports/electron'); // Import common modules. common.defineProperties(exports); Object.defineProperties(exports, { // Browser side modules, please sort with alphabet order. app: { enumerable: true, get: function() { return require('../app'); } }, autoUpdater: { enumerable: true, get: function() { return require('../auto-updater'); } }, BrowserWindow: { enumerable: true, get: function() { return require('../browser-window'); } }, contentTracing: { enumerable: true, get: function() { return require('../content-tracing'); } }, dialog: { enumerable: true, get: function() { return require('../dialog'); } }, ipcMain: { enumerable: true, get: function() { return require('../ipc-main'); } }, globalShortcut: { enumerable: true, get: function() { return require('../global-shortcut'); } }, Menu: { enumerable: true, get: function() { return require('../menu'); } }, MenuItem: { enumerable: true, get: function() { return require('../menu-item'); } }, powerMonitor: { enumerable: true, get: function() { return require('../power-monitor'); } }, powerSaveBlocker: { enumerable: true, get: function() { return require('../power-save-blocker'); } }, protocol: { enumerable: true, get: function() { return require('../protocol'); } }, screen: { enumerable: true, get: function() { return require('../screen'); } }, session: { enumerable: true, get: function() { return require('../session'); } }, Tray: { enumerable: true, get: function() { return require('../tray'); } }, // The internal modules, invisible unless you know their names. NavigationController: { get: function() { return require('../navigation-controller'); } }, webContents: { get: function() { return require('../web-contents'); } } }); var globalShortcut; globalShortcut = process.atomBinding('global_shortcut').globalShortcut; module.exports = globalShortcut; const EventEmitter = require('events').EventEmitter; module.exports = new EventEmitter; const deprecate = require('electron').deprecate; const ipcMain = require('electron').ipcMain; // This module is deprecated, we mirror everything from ipcMain. deprecate.warn('ipc module', 'require("electron").ipcMain'); module.exports = ipcMain; var MenuItem, methodInBrowserWindow, nextCommandId, rolesMap; nextCommandId = 0; // Maps role to methods of webContents rolesMap = { undo: 'undo', redo: 'redo', cut: 'cut', copy: 'copy', paste: 'paste', selectall: 'selectAll', minimize: 'minimize', close: 'close' }; // Maps methods that should be called directly on the BrowserWindow instance methodInBrowserWindow = { minimize: true, close: true }; MenuItem = (function() { MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']; function MenuItem(options) { var click, ref; const Menu = require('electron').Menu; click = options.click, this.selector = options.selector, this.type = options.type, this.role = options.role, this.label = options.label, this.sublabel = options.sublabel, this.accelerator = options.accelerator, this.icon = options.icon, this.enabled = options.enabled, this.visible = options.visible, this.checked = options.checked, this.submenu = options.submenu; 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' && ((ref = this.submenu) != null ? ref.constructor : void 0) !== 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', ''); this.overrideProperty('sublabel', ''); this.overrideProperty('enabled', true); this.overrideProperty('visible', true); this.overrideProperty('checked', false); if (MenuItem.types.indexOf(this.type) === -1) { throw new Error("Unknown menu type " + this.type); } this.commandId = ++nextCommandId; this.click = (function(_this) { return function(focusedWindow) { // Manually flip the checked flags when clicked. var methodName, ref1, ref2; if ((ref1 = _this.type) === 'checkbox' || ref1 === 'radio') { _this.checked = !_this.checked; } if (_this.role && rolesMap[_this.role] && process.platform !== 'darwin' && (focusedWindow != null)) { methodName = rolesMap[_this.role]; if (methodInBrowserWindow[methodName]) { return focusedWindow[methodName](); } else { return (ref2 = focusedWindow.webContents) != null ? ref2[methodName]() : void 0; } } else if (typeof click === 'function') { return click(_this, focusedWindow); } else if (typeof _this.selector === 'string') { return Menu.sendActionToFirstResponder(_this.selector); } }; })(this); } MenuItem.prototype.overrideProperty = function(name, defaultValue) { if (defaultValue == null) { defaultValue = null; } return this[name] != null ? this[name] : this[name] = defaultValue; }; MenuItem.prototype.overrideReadOnlyProperty = function(name, defaultValue) { if (defaultValue == null) { defaultValue = null; } if (this[name] == null) { this[name] = defaultValue; } return Object.defineProperty(this, name, { enumerable: true, writable: false, value: this[name] }); }; return MenuItem; })(); module.exports = MenuItem; const BrowserWindow = require('electron').BrowserWindow; const MenuItem = require('electron').MenuItem; const EventEmitter = require('events').EventEmitter; const v8Util = process.atomBinding('v8_util'); const bindings = process.atomBinding('menu'); // Automatically generated radio menu item's group id. var nextGroupId = 0; // Search between seperators to find a radio menu item and return its group id, // otherwise generate a group id. var generateGroupId = function(items, pos) { var i, item, j, k, ref1, ref2, ref3; if (pos > 0) { for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) { item = items[i]; if (item.type === 'radio') { return item.groupId; } if (item.type === 'separator') { break; } } } else if (pos < items.length) { for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) { item = items[i]; if (item.type === 'radio') { return item.groupId; } if (item.type === 'separator') { break; } } } return ++nextGroupId; }; // Returns the index of item according to |id|. var indexOfItemById = function(items, id) { var i, item, j, len; for (i = j = 0, len = items.length; j < len; i = ++j) { item = items[i]; if (item.id === id) { return i; } } return -1; }; // Returns the index of where to insert the item according to |position|. var indexToInsertByPosition = function(items, position) { var id, insertIndex, query, ref1; if (!position) { return items.length; } ref1 = position.split('='), query = ref1[0], id = ref1[1]; insertIndex = indexOfItemById(items, id); if (insertIndex === -1 && query !== 'endof') { console.warn("Item with id '" + id + "' is not found"); return items.length; } switch (query) { case 'after': insertIndex++; break; case 'endof': // If the |id| doesn't exist, then create a new group with the |id|. if (insertIndex === -1) { items.push({ id: id, type: 'separator' }); insertIndex = items.length - 1; } // Find the end of the group. insertIndex++; while (insertIndex < items.length && items[insertIndex].type !== 'separator') { insertIndex++; } } return insertIndex; }; const Menu = bindings.Menu; Menu.prototype.__proto__ = EventEmitter.prototype; Menu.prototype._init = function() { this.commandsMap = {}; this.groupsMap = {}; this.items = []; return this.delegate = { isCommandIdChecked: (function(_this) { return function(commandId) { var ref1; return (ref1 = _this.commandsMap[commandId]) != null ? ref1.checked : void 0; }; })(this), isCommandIdEnabled: (function(_this) { return function(commandId) { var ref1; return (ref1 = _this.commandsMap[commandId]) != null ? ref1.enabled : void 0; }; })(this), isCommandIdVisible: (function(_this) { return function(commandId) { var ref1; return (ref1 = _this.commandsMap[commandId]) != null ? ref1.visible : void 0; }; })(this), getAcceleratorForCommandId: (function(_this) { return function(commandId) { var ref1; return (ref1 = _this.commandsMap[commandId]) != null ? ref1.accelerator : void 0; }; })(this), getIconForCommandId: (function(_this) { return function(commandId) { var ref1; return (ref1 = _this.commandsMap[commandId]) != null ? ref1.icon : void 0; }; })(this), executeCommand: (function(_this) { return function(commandId) { var ref1; return (ref1 = _this.commandsMap[commandId]) != null ? ref1.click(BrowserWindow.getFocusedWindow()) : void 0; }; })(this), menuWillShow: (function(_this) { return function() { // Make sure radio groups have at least one menu item seleted. var checked, group, id, j, len, radioItem, ref1, results; ref1 = _this.groupsMap; results = []; for (id in ref1) { group = ref1[id]; checked = false; for (j = 0, len = group.length; j < len; j++) { radioItem = group[j]; if (!radioItem.checked) { continue; } checked = true; break; } if (!checked) { results.push(v8Util.setHiddenValue(group[0], 'checked', true)); } else { results.push(void 0); } } return results; }; })(this) }; }; Menu.prototype.popup = function(window, x, y, positioningItem) { if (typeof window != 'object' || window.constructor !== BrowserWindow) { // Shift. positioningItem = y; y = x; x = window; window = BrowserWindow.getFocusedWindow(); } // Default parameters. if (typeof x !== 'number') x = -1; if (typeof y !== 'number') y = -1; if (typeof positioningItem !== 'number') positioningItem = 0; this.popupAt(window, x, y, positioningItem); }; Menu.prototype.append = function(item) { return this.insert(this.getItemCount(), item); }; Menu.prototype.insert = function(pos, item) { var base, name; if ((item != null ? item.constructor : void 0) !== MenuItem) { throw new TypeError('Invalid item'); } switch (item.type) { case 'normal': this.insertItem(pos, item.commandId, item.label); break; case 'checkbox': this.insertCheckItem(pos, item.commandId, item.label); break; case 'separator': this.insertSeparator(pos); break; case 'submenu': this.insertSubMenu(pos, item.commandId, item.label, item.submenu); break; case 'radio': // Grouping radio menu items. item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos)); if ((base = this.groupsMap)[name = item.groupId] == null) { base[name] = []; } 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: function() { return v8Util.getHiddenValue(item, 'checked'); }, set: (function(_this) { return function() { var j, len, otherItem, ref1; ref1 = _this.groupsMap[item.groupId]; for (j = 0, len = ref1.length; j < len; j++) { otherItem = ref1[j]; if (otherItem !== item) { v8Util.setHiddenValue(otherItem, 'checked', false); } } return v8Util.setHiddenValue(item, 'checked', true); }; })(this) }); this.insertRadioItem(pos, item.commandId, item.label, item.groupId); } if (item.sublabel != null) { this.setSublabel(pos, item.sublabel); } if (item.icon != null) { this.setIcon(pos, item.icon); } if (item.role != null) { this.setRole(pos, item.role); } // Make menu accessable to items. item.overrideReadOnlyProperty('menu', this); // Remember the items. this.items.splice(pos, 0, item); return this.commandsMap[item.commandId] = item; }; // Force menuWillShow to be called Menu.prototype._callMenuWillShow = function() { var item, j, len, ref1, ref2, results; if ((ref1 = this.delegate) != null) { ref1.menuWillShow(); } ref2 = this.items; results = []; for (j = 0, len = ref2.length; j < len; j++) { item = ref2[j]; if (item.submenu != null) { results.push(item.submenu._callMenuWillShow()); } } return results; }; var applicationMenu = null; Menu.setApplicationMenu = function(menu) { var j, len, results, w, windows; if (!(menu === null || menu.constructor === Menu)) { throw new TypeError('Invalid menu'); } // Keep a reference. applicationMenu = menu; if (process.platform === 'darwin') { if (menu === null) { return; } menu._callMenuWillShow(); return bindings.setApplicationMenu(menu); } else { windows = BrowserWindow.getAllWindows(); results = []; for (j = 0, len = windows.length; j < len; j++) { w = windows[j]; results.push(w.setMenu(menu)); } return results; } }; Menu.getApplicationMenu = function() { return applicationMenu; }; Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder; Menu.buildFromTemplate = function(template) { var insertIndex, item, j, k, key, len, len1, menu, menuItem, positionedTemplate, value; if (!Array.isArray(template)) { throw new TypeError('Invalid template for Menu'); } positionedTemplate = []; insertIndex = 0; for (j = 0, len = template.length; j < len; j++) { item = template[j]; if (item.position) { insertIndex = indexToInsertByPosition(positionedTemplate, item.position); } else { // If no |position| is specified, insert after last item. insertIndex++; } positionedTemplate.splice(insertIndex, 0, item); } menu = new Menu; for (k = 0, len1 = positionedTemplate.length; k < len1; k++) { item = positionedTemplate[k]; if (typeof item !== 'object') { throw new TypeError('Invalid template for MenuItem'); } menuItem = new MenuItem(item); for (key in item) { value = item[key]; if (menuItem[key] == null) { menuItem[key] = value; } } menu.append(menuItem); } return menu; }; module.exports = Menu; const ipcMain = require('electron').ipcMain; var slice = [].slice; // The history operation in renderer is redirected to browser. ipcMain.on('ATOM_SHELL_NAVIGATION_CONTROLLER', function() { var args, event, method, ref; event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : []; return (ref = event.sender)[method].apply(ref, args); }); ipcMain.on('ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', function() { var args, event, method, ref; event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : []; return event.returnValue = (ref = event.sender)[method].apply(ref, args); }); // 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. var 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', (function(_this) { return function(event, url, inPage, replaceEntry) { var currentEntry; if (_this.inPageIndex > -1 && !inPage) { // Navigated to a new page, clear in-page mark. _this.inPageIndex = -1; } else if (_this.inPageIndex === -1 && inPage) { // Started in-page navigations. _this.inPageIndex = _this.currentIndex; } if (_this.pendingIndex >= 0) { // Go to index. _this.currentIndex = _this.pendingIndex; _this.pendingIndex = -1; return _this.history[_this.currentIndex] = url; } else if (replaceEntry) { // Non-user initialized navigation. return _this.history[_this.currentIndex] = url; } else { // Normal navigation. Clear history. _this.history = _this.history.slice(0, _this.currentIndex + 1); currentEntry = _this.history[_this.currentIndex]; if ((currentEntry != null ? currentEntry.url : void 0) !== url) { _this.currentIndex++; return _this.history.push(url); } } }; })(this)); } NavigationController.prototype.loadURL = function(url, options) { if (options == null) { options = {}; } this.pendingIndex = -1; this.webContents._loadURL(url, options); return this.webContents.emit('load-url', url, options); }; NavigationController.prototype.getURL = function() { if (this.currentIndex === -1) { return ''; } else { return this.history[this.currentIndex]; } }; NavigationController.prototype.stop = function() { this.pendingIndex = -1; return this.webContents._stop(); }; NavigationController.prototype.reload = function() { this.pendingIndex = this.currentIndex; return this.webContents._loadURL(this.getURL(), {}); }; NavigationController.prototype.reloadIgnoringCache = function() { this.pendingIndex = this.currentIndex; return this.webContents._loadURL(this.getURL(), { extraHeaders: "pragma: no-cache\n" }); }; NavigationController.prototype.canGoBack = function() { return this.getActiveIndex() > 0; }; NavigationController.prototype.canGoForward = function() { return this.getActiveIndex() < this.history.length - 1; }; NavigationController.prototype.canGoToIndex = function(index) { return index >= 0 && index < this.history.length; }; NavigationController.prototype.canGoToOffset = function(offset) { return this.canGoToIndex(this.currentIndex + offset); }; NavigationController.prototype.clearHistory = function() { this.history = []; this.currentIndex = -1; this.pendingIndex = -1; return 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) { var pendingIndex; if (!this.canGoToOffset(offset)) { return; } 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; const EventEmitter = require('events').EventEmitter; const powerMonitor = process.atomBinding('power_monitor').powerMonitor; powerMonitor.__proto__ = EventEmitter.prototype; module.exports = powerMonitor; var powerSaveBlocker; powerSaveBlocker = process.atomBinding('power_save_blocker').powerSaveBlocker; module.exports = powerSaveBlocker; const app = require('electron').app; if (!app.isReady()) { throw new Error('Can not initialize protocol module before app is ready'); } const protocol = process.atomBinding('protocol').protocol; // Warn about removed APIs. var logAndThrow = function(callback, message) { console.error(message); if (callback) { return callback(new Error(message)); } else { throw new Error(message); } }; protocol.registerProtocol = function(scheme, handler, callback) { return logAndThrow(callback, 'registerProtocol API has been replaced by the register[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.'); }; protocol.isHandledProtocol = function(scheme, callback) { return logAndThrow(callback, 'isHandledProtocol API has been replaced by isProtocolHandled.'); }; protocol.interceptProtocol = function(scheme, handler, callback) { return logAndThrow(callback, 'interceptProtocol API has been replaced by the intercept[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.'); }; module.exports = protocol; const EventEmitter = require('events').EventEmitter; const screen = process.atomBinding('screen').screen; screen.__proto__ = EventEmitter.prototype; module.exports = screen; const EventEmitter = require('events').EventEmitter; const bindings = process.atomBinding('session'); const PERSIST_PERFIX = 'persist:'; // Returns the Session from |partition| string. exports.fromPartition = function(partition) { if (partition == null) { partition = ''; } if (partition === '') { return exports.defaultSession; } if (partition.startsWith(PERSIST_PERFIX)) { return bindings.fromPartition(partition.substr(PERSIST_PERFIX.length), false); } else { return bindings.fromPartition(partition, true); } }; // Returns the default session. Object.defineProperty(exports, 'defaultSession', { enumerable: true, get: function() { return bindings.fromPartition('', false); } }); var wrapSession = function(session) { // session is an EventEmitter. return session.__proto__ = EventEmitter.prototype; }; bindings._setWrapSession(wrapSession); const deprecate = require('electron').deprecate; const EventEmitter = require('events').EventEmitter; const Tray = process.atomBinding('tray').Tray; Tray.prototype.__proto__ = EventEmitter.prototype; Tray.prototype._init = function() { // Deprecated. deprecate.rename(this, 'popContextMenu', 'popUpContextMenu'); deprecate.event(this, 'clicked', 'click'); deprecate.event(this, 'double-clicked', 'double-click'); deprecate.event(this, 'right-clicked', 'right-click'); return deprecate.event(this, 'balloon-clicked', 'balloon-click'); }; Tray.prototype.setContextMenu = function(menu) { this._setContextMenu(menu); // Keep a strong reference of menu. return this.menu = menu; }; module.exports = Tray; 'use strict'; const EventEmitter = require('events').EventEmitter; const deprecate = require('electron').deprecate; const ipcMain = require('electron').ipcMain; const NavigationController = require('electron').NavigationController; const Menu = require('electron').Menu; const binding = process.atomBinding('web_contents'); const debuggerBinding = process.atomBinding('debugger'); let slice = [].slice; let nextId = 0; let getNextId = function() { return ++nextId; }; let PDFPageSize = { 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" } }; // Following methods are mapped to webFrame. const webFrameMethods = [ 'executeJavaScript', 'insertText', 'setZoomFactor', 'setZoomLevel', 'setZoomLevelLimits' ]; let wrapWebContents = function(webContents) { // webContents is an EventEmitter. var controller, method, name, ref1; webContents.__proto__ = EventEmitter.prototype; // Every remote callback from renderer process would add a listenter to the // render-view-deleted event, so ignore the listenters warning. webContents.setMaxListeners(0); // WebContents::send(channel, args..) webContents.send = function() { var args, channel; channel = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; return this._send(channel, slice.call(args)); }; // The navigation controller. controller = new NavigationController(webContents); ref1 = NavigationController.prototype; for (name in ref1) { method = ref1[name]; if (method instanceof Function) { (function(name, method) { return webContents[name] = function() { return method.apply(controller, arguments); }; })(name, method); } } // Mapping webFrame methods. for (let method of webFrameMethods) { webContents[method] = function() { let args = Array.prototype.slice.call(arguments); this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args); }; } // Make sure webContents.executeJavaScript would run the code only when the // webContents has been loaded. const executeJavaScript = webContents.executeJavaScript; webContents.executeJavaScript = function(code, hasUserGesture) { if (this.getURL() && !this.isLoading()) return executeJavaScript.call(this, code, hasUserGesture); else return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture)); }; // Dispatch IPC messages to the ipc module. webContents.on('ipc-message', function(event, packed) { var args, channel; channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : []; return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args))); }); webContents.on('ipc-message-sync', function(event, packed) { var args, channel; channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : []; Object.defineProperty(event, 'returnValue', { set: function(value) { return event.sendReply(JSON.stringify(value)); } }); return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args))); }); // Handle context menu action request from pepper plugin. webContents.on('pepper-context-menu', function(event, params) { var menu; menu = Menu.buildFromTemplate(params.menu); return menu.popup(params.x, params.y); }); // This error occurs when host could not be found. webContents.on('did-fail-provisional-load', function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; // Calling loadURL during this event might cause crash, so delay the event // until next tick. return setImmediate((function(_this) { return function() { return _this.emit.apply(_this, ['did-fail-load'].concat(slice.call(args))); }; })(this)); }); // Delays the page-title-updated event to next tick. webContents.on('-page-title-updated', function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return setImmediate((function(_this) { return function() { return _this.emit.apply(_this, ['page-title-updated'].concat(slice.call(args))); }; })(this)); }); // Deprecated. deprecate.rename(webContents, 'loadUrl', 'loadURL'); deprecate.rename(webContents, 'getUrl', 'getURL'); deprecate.event(webContents, 'page-title-set', 'page-title-updated', function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return this.emit.apply(this, ['page-title-set'].concat(slice.call(args))); }); return webContents.printToPDF = function(options, callback) { var printingSetting; printingSetting = { pageRage: [], mediaSize: {}, landscape: false, color: 2, headerFooterEnabled: false, marginsType: 0, isFirstRequest: false, requestID: getNextId(), previewModifiable: true, printToPDF: true, printWithCloudPrint: false, printWithPrivet: false, printWithExtension: false, deviceName: "Save as PDF", generateDraftData: true, fitToPageEnabled: false, duplex: 0, copies: 1, collate: true, shouldPrintBackgrounds: false, shouldPrintSelectionOnly: false }; 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 && PDFPageSize[options.pageSize]) { printingSetting.mediaSize = PDFPageSize[options.pageSize]; } else { printingSetting.mediaSize = PDFPageSize['A4']; } return this._printToPDF(printingSetting, callback); }; }; // Wrapper for native class. let wrapDebugger = function(webContentsDebugger) { // debugger is an EventEmitter. webContentsDebugger.__proto__ = EventEmitter.prototype; }; binding._setWrapWebContents(wrapWebContents); debuggerBinding._setWrapDebugger(wrapDebugger); module.exports.create = function(options) { if (options == null) { options = {}; } return binding.create(options); }; const electron = require('electron'); const app = electron.app; const fs = require('fs'); const path = require('path'); const url = require('url'); // Mapping between hostname and file path. var hostPathMap = {}; var hostPathMapNextKey = 0; var getHostForPath = function(path) { var key; key = "extension-" + (++hostPathMapNextKey); hostPathMap[key] = path; return key; }; var getPathForHost = function(host) { return hostPathMap[host]; }; // Cache extensionInfo. var extensionInfoMap = {}; var getExtensionInfoFromPath = function(srcDirectory) { var manifest, page; manifest = JSON.parse(fs.readFileSync(path.join(srcDirectory, 'manifest.json'))); if (extensionInfoMap[manifest.name] == null) { // We can not use 'file://' directly because all resources in the extension // will be treated as relative to the root in Chrome. page = url.format({ protocol: 'chrome-extension', slashes: true, hostname: getHostForPath(srcDirectory), pathname: manifest.devtools_page }); extensionInfoMap[manifest.name] = { startPage: page, name: manifest.name, srcDirectory: srcDirectory, exposeExperimentalAPIs: true }; return extensionInfoMap[manifest.name]; } }; // The loaded extensions cache and its persistent path. var loadedExtensions = null; var loadedExtensionsPath = null; app.on('will-quit', function() { try { loadedExtensions = Object.keys(extensionInfoMap).map(function(key) { return extensionInfoMap[key].srcDirectory; }); try { fs.mkdirSync(path.dirname(loadedExtensionsPath)); } catch (error) { // Ignore error } return fs.writeFileSync(loadedExtensionsPath, JSON.stringify(loadedExtensions)); } catch (error) { // Ignore error } }); // We can not use protocol or BrowserWindow until app is ready. app.once('ready', function() { var BrowserWindow, chromeExtensionHandler, i, init, len, protocol, srcDirectory; protocol = electron.protocol, BrowserWindow = electron.BrowserWindow; // Load persistented extensions. loadedExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions'); try { loadedExtensions = JSON.parse(fs.readFileSync(loadedExtensionsPath)); if (!Array.isArray(loadedExtensions)) { loadedExtensions = []; } // Preheat the extensionInfo cache. for (i = 0, len = loadedExtensions.length; i < len; i++) { srcDirectory = loadedExtensions[i]; getExtensionInfoFromPath(srcDirectory); } } catch (error) { // Ignore error } // The chrome-extension: can map a extension URL request to real file path. chromeExtensionHandler = function(request, callback) { var directory, parsed; parsed = url.parse(request.url); if (!(parsed.hostname && (parsed.path != null))) { return callback(); } if (!/extension-\d+/.test(parsed.hostname)) { return callback(); } directory = getPathForHost(parsed.hostname); if (directory == null) { return callback(); } return callback(path.join(directory, parsed.path)); }; protocol.registerFileProtocol('chrome-extension', chromeExtensionHandler, function(error) { if (error) { return console.error('Unable to register chrome-extension protocol'); } }); BrowserWindow.prototype._loadDevToolsExtensions = function(extensionInfoArray) { var ref; return (ref = this.devToolsWebContents) != null ? ref.executeJavaScript("DevToolsAPI.addExtensions(" + (JSON.stringify(extensionInfoArray)) + ");") : void 0; }; BrowserWindow.addDevToolsExtension = function(srcDirectory) { var extensionInfo, j, len1, ref, window; extensionInfo = getExtensionInfoFromPath(srcDirectory); if (extensionInfo) { ref = BrowserWindow.getAllWindows(); for (j = 0, len1 = ref.length; j < len1; j++) { window = ref[j]; window._loadDevToolsExtensions([extensionInfo]); } return extensionInfo.name; } }; BrowserWindow.removeDevToolsExtension = function(name) { return delete extensionInfoMap[name]; }; // Load persistented extensions when devtools is opened. init = BrowserWindow.prototype._init; return BrowserWindow.prototype._init = function() { init.call(this); return this.on('devtools-opened', function() { return this._loadDevToolsExtensions(Object.keys(extensionInfoMap).map(function(key) { return extensionInfoMap[key]; })); }); }; }); const ipcMain = require('electron').ipcMain; const desktopCapturer = process.atomBinding('desktop_capturer').desktopCapturer; var deepEqual = function(opt1, opt2) { return JSON.stringify(opt1) === JSON.stringify(opt2); }; // A queue for holding all requests from renderer process. var requestsQueue = []; ipcMain.on('ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function(event, captureWindow, captureScreen, thumbnailSize, id) { var request; request = { id: id, options: { captureWindow: captureWindow, captureScreen: captureScreen, thumbnailSize: thumbnailSize }, webContents: event.sender }; requestsQueue.push(request); if (requestsQueue.length === 1) { desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize); } // If the WebContents is destroyed before receiving result, just remove the // reference from requestsQueue to make the module not send the result to it. return event.sender.once('destroyed', function() { return request.webContents = null; }); }); desktopCapturer.emit = function(event, name, sources) { // Receiving sources result from main process, now send them back to renderer. var captureScreen, captureWindow, handledRequest, i, len, ref, ref1, ref2, request, result, source, thumbnailSize, unhandledRequestsQueue; handledRequest = requestsQueue.shift(0); result = (function() { var i, len, results; results = []; for (i = 0, len = sources.length; i < len; i++) { source = sources[i]; results.push({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataUrl() }); } return results; })(); if ((ref = handledRequest.webContents) != null) { ref.send("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + handledRequest.id, result); } // Check the queue to see whether there is other same request. If has, handle // it for reducing redunplicated `desktopCaptuer.startHandling` calls. unhandledRequestsQueue = []; for (i = 0, len = requestsQueue.length; i < len; i++) { request = requestsQueue[i]; if (deepEqual(handledRequest.options, request.options)) { if ((ref1 = request.webContents) != null) { ref1.send("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + request.id, result); } } else { unhandledRequestsQueue.push(request); } } requestsQueue = unhandledRequestsQueue; // If the requestsQueue is not empty, start a new request handling. if (requestsQueue.length > 0) { ref2 = requestsQueue[0].options, captureWindow = ref2.captureWindow, captureScreen = ref2.captureScreen, thumbnailSize = ref2.thumbnailSize; return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize); } }; const ipcMain = require('electron').ipcMain; const webContents = require('electron').webContents; var slice = [].slice; // Doesn't exist in early initialization. var webViewManager = null; var supportedWebViewEvents = ['load-commit', 'did-finish-load', 'did-fail-load', 'did-frame-finish-load', 'did-start-loading', 'did-stop-loading', 'did-get-response-details', 'did-get-redirect-request', 'dom-ready', 'console-message', 'devtools-opened', 'devtools-closed', 'devtools-focused', 'new-window', 'will-navigate', 'did-navigate', 'did-navigate-in-page', 'close', 'crashed', 'gpu-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']; var nextInstanceId = 0; var guestInstances = {}; var embedderElementsMap = {}; var reverseEmbedderElementsMap = {}; // Moves the last element of array to the first one. var moveLastToFirst = function(list) { return list.unshift(list.pop()); }; // Generate guestInstanceId. var getNextInstanceId = function() { return ++nextInstanceId; }; // Create a new guest instance. var createGuest = function(embedder, params) { var destroy, destroyEvents, event, fn, guest, i, id, j, len, len1, listeners; if (webViewManager == null) { webViewManager = process.atomBinding('web_view_manager'); } id = getNextInstanceId(embedder); guest = webContents.create({ isGuest: true, partition: params.partition, embedder: embedder }); guestInstances[id] = { guest: guest, embedder: embedder }; // Destroy guest when the embedder is gone or navigated. destroyEvents = ['will-destroy', 'crashed', 'did-navigate']; destroy = function() { if (guestInstances[id] != null) { return destroyGuest(embedder, id); } }; for (i = 0, len = destroyEvents.length; i < len; i++) { event = destroyEvents[i]; embedder.once(event, destroy); // Users might also listen to the crashed event, so We must ensure the guest // is destroyed before users' listener gets called. It is done by moving our // listener to the first one in queue. listeners = embedder._events[event]; if (Array.isArray(listeners)) { moveLastToFirst(listeners); } } guest.once('destroyed', function() { var j, len1, results; results = []; for (j = 0, len1 = destroyEvents.length; j < len1; j++) { event = destroyEvents[j]; results.push(embedder.removeListener(event, destroy)); } return results; }); // Init guest web view after attached. guest.once('did-attach', function() { var opts; params = this.attachParams; delete this.attachParams; this.viewInstanceId = params.instanceId; this.setSize({ normal: { width: params.elementWidth, height: params.elementHeight }, enableAutoSize: params.autosize, min: { width: params.minwidth, height: params.minheight }, max: { width: params.maxwidth, height: params.maxheight } }); if (params.src) { opts = {}; if (params.httpreferrer) { opts.httpReferrer = params.httpreferrer; } if (params.useragent) { opts.userAgent = params.useragent; } this.loadURL(params.src, opts); } if (params.allowtransparency != null) { this.setAllowTransparency(params.allowtransparency); } return guest.allowPopups = params.allowpopups; }); // Dispatch events to embedder. fn = function(event) { return guest.on(event, function() { var args = 2 <= arguments.length ? slice.call(arguments, 1) : []; return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + guest.viewInstanceId, event].concat(slice.call(args))); }); }; for (j = 0, len1 = supportedWebViewEvents.length; j < len1; j++) { event = supportedWebViewEvents[j]; fn(event); } // Dispatch guest's IPC messages to embedder. guest.on('ipc-message-host', function(_, packed) { var args, channel; channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : []; return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + guest.viewInstanceId, channel].concat(slice.call(args))); }); // Autosize. guest.on('size-changed', function() { var args = 2 <= arguments.length ? slice.call(arguments, 1) : []; return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + guest.viewInstanceId].concat(slice.call(args))); }); return id; }; // Attach the guest to an element of embedder. var attachGuest = function(embedder, elementInstanceId, guestInstanceId, params) { var guest, key, oldGuestInstanceId, ref1, webPreferences; guest = guestInstances[guestInstanceId].guest; // Destroy the old guest when attaching. key = (embedder.getId()) + "-" + elementInstanceId; oldGuestInstanceId = embedderElementsMap[key]; if (oldGuestInstanceId != null) { // Reattachment to the same guest is not currently supported. if (oldGuestInstanceId === guestInstanceId) { return; } if (guestInstances[oldGuestInstanceId] == null) { return; } destroyGuest(embedder, oldGuestInstanceId); } webPreferences = { guestInstanceId: guestInstanceId, nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false, plugins: params.plugins, webSecurity: !params.disablewebsecurity, blinkFeatures: params.blinkfeatures }; if (params.preload) { webPreferences.preloadURL = params.preload; } webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences); guest.attachParams = params; embedderElementsMap[key] = guestInstanceId; return reverseEmbedderElementsMap[guestInstanceId] = key; }; // Destroy an existing guest instance. var destroyGuest = function(embedder, id) { var key; webViewManager.removeGuest(embedder, id); guestInstances[id].guest.destroy(); delete guestInstances[id]; key = reverseEmbedderElementsMap[id]; if (key != null) { delete reverseEmbedderElementsMap[id]; return delete embedderElementsMap[key]; } }; ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', function(event, params, requestId) { return event.sender.send("ATOM_SHELL_RESPONSE_" + requestId, createGuest(event.sender, params)); }); ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', function(event, elementInstanceId, guestInstanceId, params) { return attachGuest(event.sender, elementInstanceId, guestInstanceId, params); }); ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', function(event, id) { return destroyGuest(event.sender, id); }); ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', function(event, id, params) { var ref1; return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0; }); ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', function(event, id, allowtransparency) { var ref1; return (ref1 = guestInstances[id]) != null ? ref1.guest.setAllowTransparency(allowtransparency) : void 0; }); // Returns WebContents from its guest id. exports.getGuest = function(id) { var ref1; return (ref1 = guestInstances[id]) != null ? ref1.guest : void 0; }; // Returns the embedder of the guest. exports.getEmbedder = function(id) { var ref1; return (ref1 = guestInstances[id]) != null ? ref1.embedder : void 0; }; const ipcMain = require('electron').ipcMain; const BrowserWindow = require('electron').BrowserWindow; var hasProp = {}.hasOwnProperty; var slice = [].slice; var frameToGuest = {}; // Copy attribute of |parent| to |child| if it is not defined in |child|. var mergeOptions = function(child, parent) { var key, value; for (key in parent) { if (!hasProp.call(parent, key)) continue; value = parent[key]; if (!(key in child)) { if (typeof value === 'object') { child[key] = mergeOptions({}, value); } else { child[key] = value; } } } return child; }; // Merge |options| with the |embedder|'s window's options. var mergeBrowserWindowOptions = function(embedder, options) { if (embedder.browserWindowOptions != null) { // Inherit the original options if it is a BrowserWindow. mergeOptions(options, embedder.browserWindowOptions); } else { // Or only inherit web-preferences if it is a webview. if (options.webPreferences == null) { options.webPreferences = {}; } mergeOptions(options.webPreferences, embedder.getWebPreferences()); } return options; }; // Create a new guest created by |embedder| with |options|. var createGuest = function(embedder, url, frameName, options) { var closedByEmbedder, closedByUser, guest, guestId, ref1; guest = frameToGuest[frameName]; if (frameName && (guest != null)) { guest.loadURL(url); return guest.id; } // Remember the embedder window's id. if (options.webPreferences == null) { options.webPreferences = {}; } options.webPreferences.openerId = (ref1 = BrowserWindow.fromWebContents(embedder)) != null ? ref1.id : void 0; guest = new BrowserWindow(options); guest.loadURL(url); // 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. guestId = guest.id; closedByEmbedder = function() { guest.removeListener('closed', closedByUser); return guest.destroy(); }; closedByUser = function() { embedder.send("ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_" + guestId); return embedder.removeListener('render-view-deleted', closedByEmbedder); }; embedder.once('render-view-deleted', closedByEmbedder); guest.once('closed', closedByUser); if (frameName) { frameToGuest[frameName] = guest; guest.frameName = frameName; guest.once('closed', function() { return delete frameToGuest[frameName]; }); } return guest.id; }; // Routed window.open messages. ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function() { var args, event, frameName, options, url; event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; url = args[0], frameName = args[1], options = args[2]; options = mergeBrowserWindowOptions(event.sender, options); event.sender.emit('new-window', event, url, frameName, 'new-window', options); if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) { return event.returnValue = null; } else { return event.returnValue = createGuest(event.sender, url, frameName, options); } }); ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function(event, guestId) { var ref1; return (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1.destroy() : void 0; }); ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function() { var args, guestId, method, ref1; guestId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : []; return (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1[method].apply(ref1, args) : void 0; }); ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function(event, guestId, message, targetOrigin, sourceOrigin) { var guestContents, ref1, ref2, sourceId; sourceId = (ref1 = BrowserWindow.fromWebContents(event.sender)) != null ? ref1.id : void 0; if (sourceId == null) { return; } guestContents = (ref2 = BrowserWindow.fromId(guestId)) != null ? ref2.webContents : void 0; if ((guestContents != null ? guestContents.getURL().indexOf(targetOrigin) : void 0) === 0 || targetOrigin === '*') { return guestContents != null ? guestContents.send('ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) : void 0; } }); ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function() { var args, guestId, method, ref1, ref2; guestId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : []; return (ref1 = BrowserWindow.fromId(guestId)) != null ? (ref2 = ref1.webContents) != null ? ref2[method].apply(ref2, args) : void 0 : void 0; }); const fs = require('fs'); const path = require('path'); const util = require('util'); const Module = require('module'); var slice = [].slice; // We modified the original process.argv to let node.js load the atom.js, // we need to restore it here. process.argv.splice(1, 1); // Clear search paths. require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths')); // Import common settings. require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')); var globalPaths = Module.globalPaths; if (!process.env.ELECTRON_HIDE_INTERNAL_MODULES) { globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib')); } // Expose public APIs. globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib', '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. var consoleLog = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return process.log(util.format.apply(util, args) + "\n"); }; var streamWrite = function(chunk, encoding, callback) { if (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; // Always returns EOF for stdin stream. var Readable = require('stream').Readable; var stdin = new Readable; stdin.push(null); process.__defineGetter__('stdin', function() { return stdin; }); } // Don't quit on fatal error. process.on('uncaughtException', function(error) { // Do nothing if the user has a custom uncaught exception handler. var dialog, message, ref, stack; if (process.listeners('uncaughtException').length > 1) { return; } // Show error in GUI. dialog = require('electron').dialog; stack = (ref = error.stack) != null ? ref : error.name + ": " + error.message; message = "Uncaught Exception:\n" + stack; return dialog.showErrorBox('A JavaScript error occurred in the main process', message); }); // Emit 'exit' event on quit. var app = require('electron').app; app.on('quit', function(event, exitCode) { return process.emit('exit', exitCode); }); // Map process.exit to app.exit, which quits gracefully. process.exit = app.exit; // Load the RPC server. require('./rpc-server'); // Load the guest view manager. require('./guest-view-manager'); require('./guest-window-manager'); // Now we try to load app's package.json. var packageJson = null; var searchPaths = ['app', 'app.asar', 'default_app']; var i, len, packagePath; for (i = 0, len = searchPaths.length; i < len; i++) { packagePath = searchPaths[i]; try { packagePath = path.join(process.resourcesPath, packagePath); packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json'))); break; } catch (error) { 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); } else if (packageJson.name != null) { app.setName(packageJson.name); } // Set application's desktop name. if (packageJson.desktopName != null) { app.setDesktopName(packageJson.desktopName); } else { app.setDesktopName((app.getName()) + ".desktop"); } // Chrome 42 disables NPAPI plugins by default, reenable them here app.commandLine.appendSwitch('enable-npapi'); // 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); // Load the chrome extension support. require('./chrome-extension'); // Load internal desktop-capturer module. require('./desktop-capturer'); // Set main startup script of the app. var mainStartupScript = packageJson.main || 'index.js'; // Finally load app's main.js and transfer control to C++. Module._load(path.join(packagePath, mainStartupScript), Module, true); 'use strict'; const EventEmitter = require('events').EventEmitter; const v8Util = process.atomBinding('v8_util'); class ObjectsRegistry extends EventEmitter { constructor() { super(); this.setMaxListeners(Number.MAX_VALUE); this.nextId = 0; // Stores all objects by ref-counting. // (id) => {object, count} this.storage = {}; // Stores the IDs of objects referenced by WebContents. // (webContentsId) => {(id) => (count)} this.owners = {}; } // Register a new object, the object would be kept referenced until you release // it explicitly. add(webContentsId, obj) { var base, base1, id; id = this.saveToStorage(obj); // Remember the owner. if ((base = this.owners)[webContentsId] == null) { base[webContentsId] = {}; } if ((base1 = this.owners[webContentsId])[id] == null) { base1[id] = 0; } this.owners[webContentsId][id]++; // Returns object's id return id; } // Get an object according to its ID. get(id) { var ref; return (ref = this.storage[id]) != null ? ref.object : void 0; } // Dereference an object according to its ID. remove(webContentsId, id) { var pointer; this.dereference(id, 1); // Also reduce the count in owner. pointer = this.owners[webContentsId]; if (pointer == null) { return; } --pointer[id]; if (pointer[id] === 0) { return delete pointer[id]; } } // Clear all references to objects refrenced by the WebContents. clear(webContentsId) { var count, id, ref; this.emit("clear-" + webContentsId); if (this.owners[webContentsId] == null) { return; } ref = this.owners[webContentsId]; for (id in ref) { count = ref[id]; this.dereference(id, count); } return delete this.owners[webContentsId]; } // Private: Saves the object into storage and assigns an ID for it. saveToStorage(object) { var id; id = v8Util.getHiddenValue(object, 'atomId'); if (!id) { id = ++this.nextId; this.storage[id] = { count: 0, object: object }; v8Util.setHiddenValue(object, 'atomId', id); } ++this.storage[id].count; return id; } // Private: Dereference the object from store. dereference(id, count) { var pointer; pointer = this.storage[id]; if (pointer == null) { return; } pointer.count -= count; if (pointer.count === 0) { v8Util.deleteHiddenValue(pointer.object, 'atomId'); return delete this.storage[id]; } } } module.exports = new ObjectsRegistry; 'use strict'; const electron = require('electron'); const ipcMain = electron.ipcMain; const objectsRegistry = require('./objects-registry'); const v8Util = process.atomBinding('v8_util'); const IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap; var slice = [].slice; // Convert a real value into meta data. var valueToMeta = function(sender, value, optimizeSimpleObject) { var el, field, i, len, meta, name; if (optimizeSimpleObject == null) { optimizeSimpleObject = false; } meta = { type: typeof value }; if (Buffer.isBuffer(value)) { meta.type = 'buffer'; } if (value === null) { meta.type = 'value'; } if (Array.isArray(value)) { meta.type = 'array'; } if (value instanceof Error) { meta.type = 'error'; } if (value instanceof Date) { meta.type = 'date'; } if ((value != null ? value.constructor.name : void 0) === 'Promise') { meta.type = 'promise'; } // Treat simple objects as value. if (optimizeSimpleObject && meta.type === 'object' && v8Util.getHiddenValue(value, 'simple')) { meta.type = 'value'; } // Treat the arguments object as array. if (meta.type === 'object' && (value.hasOwnProperty('callee')) && (value.length != null)) { meta.type = 'array'; } if (meta.type === 'array') { meta.members = []; for (i = 0, len = value.length; i < len; i++) { el = value[i]; meta.members.push(valueToMeta(sender, el)); } } else if (meta.type === 'object' || meta.type === 'function') { meta.name = 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.getId(), value); meta.members = (function() { var results; results = []; for (name in value) { field = value[name]; results.push({ name: name, type: typeof field }); } return results; })(); } else if (meta.type === 'buffer') { meta.value = Array.prototype.slice.call(value, 0); } else if (meta.type === 'promise') { meta.then = valueToMeta(sender, value.then.bind(value)); } 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. var plainObjectToMeta = function(obj) { return Object.getOwnPropertyNames(obj).map(function(name) { return { name: name, value: obj[name] }; }); }; // Convert Error into meta data. var exceptionToMeta = function(error) { return { type: 'exception', message: error.message, stack: error.stack || error }; }; // Convert array of meta data from renderer into array of real values. var unwrapArgs = function(sender, args) { var metaToValue; metaToValue = function(meta) { var i, len, member, ref, rendererReleased, returnValue; switch (meta.type) { case 'value': return meta.value; case 'remote-object': return objectsRegistry.get(meta.id); case 'array': return unwrapArgs(sender, meta.value); case 'buffer': return new Buffer(meta.value); case 'date': return new Date(meta.value); case 'promise': return Promise.resolve({ then: metaToValue(meta.then) }); case 'object': let ret = v8Util.createObjectWithName(meta.name); ref = meta.members; for (i = 0, len = ref.length; i < len; i++) { member = ref[i]; ret[member.name] = metaToValue(member.value); } return ret; case 'function-with-return-value': returnValue = metaToValue(meta.value); return function() { return returnValue; }; case 'function': // Cache the callbacks in renderer. if (!sender.callbacks) { sender.callbacks = new IDWeakMap; sender.on('render-view-deleted', function() { return this.callbacks.clear(); }); } if (sender.callbacks.has(meta.id)) return sender.callbacks.get(meta.id); // Prevent the callback from being called when its page is gone. rendererReleased = false; sender.once('render-view-deleted', function() { rendererReleased = true; }); let callIntoRenderer = function(...args) { if (rendererReleased || sender.isDestroyed()) throw new Error(`Attempting to call a function in a renderer window that has been closed or released. Function provided here: ${meta.location}.`); sender.send('ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args)); }; v8Util.setDestructor(callIntoRenderer, function() { if (!rendererReleased && !sender.isDestroyed()) sender.send('ATOM_RENDERER_RELEASE_CALLBACK', meta.id); }); sender.callbacks.set(meta.id, callIntoRenderer); return callIntoRenderer; default: throw new TypeError("Unknown type: " + meta.type); } }; return args.map(metaToValue); }; // Call a function and send reply asynchronously if it's a an asynchronous // style function and the caller didn't pass a callback. var callFunction = function(event, func, caller, args) { var funcMarkedAsync, funcName, funcPassedCallback, ref, ret; funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous'); funcPassedCallback = typeof args[args.length - 1] === 'function'; try { if (funcMarkedAsync && !funcPassedCallback) { args.push(function(ret) { return event.returnValue = valueToMeta(event.sender, ret, true); }); return func.apply(caller, args); } else { ret = func.apply(caller, args); return event.returnValue = valueToMeta(event.sender, ret, true); } } catch (error) { // Catch functions thrown further down in function invocation and wrap // them with the function name so it's easier to trace things like // `Error processing argument -1.` funcName = (ref = func.name) != null ? ref : "anonymous"; throw new Error("Could not call remote function `" + funcName + "`. Check that the function signature is correct. Underlying error: " + error.message); } }; // Send by BrowserWindow when its render view is deleted. process.on('ATOM_BROWSER_RELEASE_RENDER_VIEW', function(id) { return objectsRegistry.clear(id); }); ipcMain.on('ATOM_BROWSER_REQUIRE', function(event, module) { try { return event.returnValue = valueToMeta(event.sender, process.mainModule.require(module)); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_GET_BUILTIN', function(event, module) { try { return event.returnValue = valueToMeta(event.sender, electron[module]); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_GLOBAL', function(event, name) { try { return event.returnValue = valueToMeta(event.sender, global[name]); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_CURRENT_WINDOW', function(event) { try { return event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow()); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_CURRENT_WEB_CONTENTS', function(event) { return event.returnValue = valueToMeta(event.sender, event.sender); }); ipcMain.on('ATOM_BROWSER_CONSTRUCTOR', function(event, id, args) { var constructor, obj; try { args = unwrapArgs(event.sender, args); constructor = objectsRegistry.get(id); // Call new with array of arguments. // http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible obj = new (Function.prototype.bind.apply(constructor, [null].concat(args))); return event.returnValue = valueToMeta(event.sender, obj); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_FUNCTION_CALL', function(event, id, args) { var func; try { args = unwrapArgs(event.sender, args); func = objectsRegistry.get(id); return callFunction(event, func, global, args); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_MEMBER_CONSTRUCTOR', function(event, id, method, args) { var constructor, obj; try { args = unwrapArgs(event.sender, args); constructor = objectsRegistry.get(id)[method]; // Call new with array of arguments. obj = new (Function.prototype.bind.apply(constructor, [null].concat(args))); return event.returnValue = valueToMeta(event.sender, obj); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_MEMBER_CALL', function(event, id, method, args) { var obj; try { args = unwrapArgs(event.sender, args); obj = objectsRegistry.get(id); return callFunction(event, obj[method], obj, args); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_MEMBER_SET', function(event, id, name, value) { var obj; try { obj = objectsRegistry.get(id); obj[name] = value; return event.returnValue = null; } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_MEMBER_GET', function(event, id, name) { var obj; try { obj = objectsRegistry.get(id); return event.returnValue = valueToMeta(event.sender, obj[name]); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_DEREFERENCE', function(event, id) { return objectsRegistry.remove(event.sender.getId(), id); }); ipcMain.on('ATOM_BROWSER_GUEST_WEB_CONTENTS', function(event, guestInstanceId) { var guestViewManager; try { guestViewManager = require('./guest-view-manager'); return event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId)); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); ipcMain.on('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function() { var args, event, guest, guestInstanceId, guestViewManager, method; event = arguments[0], guestInstanceId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : []; try { guestViewManager = require('./guest-view-manager'); guest = guestViewManager.getGuest(guestInstanceId); return guest[method].apply(guest, args); } catch (error) { return event.returnValue = exceptionToMeta(error); } }); 'use strict'; var slice = [].slice; const v8Util = process.atomBinding('v8_util'); class CallbacksRegistry { constructor() { this.nextId = 0; this.callbacks = {}; } add(callback) { // The callback is already added. var filenameAndLine, id, location, match, ref, regexp, stackString; id = v8Util.getHiddenValue(callback, 'callbackId'); if (id != null) { return id; } id = ++this.nextId; // Capture the location of the function and put it in the ID string, // so that release errors can be tracked down easily. regexp = /at (.*)/gi; stackString = (new Error).stack; while ((match = regexp.exec(stackString)) !== null) { location = match[1]; if (location.indexOf('(native)') !== -1) { continue; } if (location.indexOf('atom.asar') !== -1) { continue; } 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) { var ref; return (ref = this.callbacks[id]) != null ? ref : function() {}; } call() { var args, id, ref; id = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; return (ref = this.get(id)).call.apply(ref, [global].concat(slice.call(args))); } apply() { var args, id, ref; id = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; return (ref = this.get(id)).apply.apply(ref, [global].concat(slice.call(args))); } remove(id) { return delete this.callbacks[id]; } } module.exports = CallbacksRegistry; if (process.platform === 'linux' && process.type === 'renderer') { // On Linux we could not access clipboard in renderer process. module.exports = require('electron').remote.clipboard; } else { module.exports = process.atomBinding('clipboard'); } const os = require('os'); const path = require('path'); const spawn = require('child_process').spawn; const electron = require('electron'); const binding = process.atomBinding('crash_reporter'); var CrashReporter = (function() { function CrashReporter() {} CrashReporter.prototype.start = function(options) { var app, args, autoSubmit, companyName, deprecate, env, extra, ignoreSystemCrashHandler, start, submitURL; if (options == null) { options = {}; } this.productName = options.productName, companyName = options.companyName, submitURL = options.submitURL, autoSubmit = options.autoSubmit, ignoreSystemCrashHandler = options.ignoreSystemCrashHandler, extra = options.extra; // Deprecated. deprecate = electron.deprecate; if (options.submitUrl) { if (submitURL == null) { submitURL = options.submitUrl; } deprecate.warn('submitUrl', 'submitURL'); } app = (process.type === 'browser' ? electron : electron.remote).app; if (this.productName == null) { this.productName = app.getName(); } if (autoSubmit == null) { autoSubmit = true; } if (ignoreSystemCrashHandler == null) { ignoreSystemCrashHandler = false; } if (extra == null) { extra = {}; } if (extra._productName == null) { extra._productName = this.productName; } if (extra._companyName == null) { extra._companyName = companyName; } if (extra._version == null) { extra._version = app.getVersion(); } if (companyName == null) { deprecate.log('companyName is now a required option to crashReporter.start'); return; } if (submitURL == null) { deprecate.log('submitURL is now a required option to crashReporter.start'); return; } start = (function(_this) { return function() { return binding.start(_this.productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra); }; })(this); if (process.platform === 'win32') { args = ["--reporter-url=" + submitURL, "--application-name=" + this.productName, "--v=1"]; env = { ATOM_SHELL_INTERNAL_CRASH_SERVICE: 1 }; spawn(process.execPath, args, { env: env, detached: true }); } return start(); }; CrashReporter.prototype.getLastCrashReport = function() { var reports; reports = this.getUploadedReports(); if (reports.length > 0) { return reports[0]; } else { return null; } }; CrashReporter.prototype.getUploadedReports = function() { var log, tmpdir; tmpdir = process.platform === 'win32' ? os.tmpdir() : '/tmp'; log = process.platform === 'darwin' ? path.join(tmpdir, this.productName + " Crashes") : path.join(tmpdir, this.productName + " Crashes", 'uploads.log'); return binding._getUploadedReports(log); }; return CrashReporter; })(); module.exports = new CrashReporter; // Deprecate a method. var deprecate, slice = [].slice; deprecate = function(oldName, newName, fn) { var warned; warned = false; return function() { if (!(warned || process.noDeprecation)) { warned = true; deprecate.warn(oldName, newName); } return fn.apply(this, arguments); }; }; // The method is renamed. deprecate.rename = function(object, oldName, newName) { var newMethod, warned; warned = false; newMethod = function() { if (!(warned || process.noDeprecation)) { warned = true; deprecate.warn(oldName, newName); } return this[newName].apply(this, arguments); }; if (typeof object === 'function') { return object.prototype[oldName] = newMethod; } else { return object[oldName] = newMethod; } }; // Forward the method to member. deprecate.member = function(object, method, member) { var warned; warned = false; return object.prototype[method] = function() { if (!(warned || process.noDeprecation)) { warned = true; deprecate.warn(method, member + "." + method); } return this[member][method].apply(this[member], arguments); }; }; // Deprecate a property. deprecate.property = function(object, property, method) { return Object.defineProperty(object, property, { get: function() { var warned; warned = false; if (!(warned || process.noDeprecation)) { warned = true; deprecate.warn(property + " property", method + " method"); } return this[method](); } }); }; // Deprecate an event. deprecate.event = function(emitter, oldName, newName, fn) { var warned; warned = false; return emitter.on(newName, function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; // there is listeners for old API. if (this.listenerCount(oldName) > 0) { if (!(warned || process.noDeprecation)) { warned = true; deprecate.warn("'" + oldName + "' event", "'" + newName + "' event"); } if (fn != null) { return fn.apply(this, arguments); } else { return this.emit.apply(this, [oldName].concat(slice.call(args))); } } }); }; // Print deprecation warning. deprecate.warn = function(oldName, newName) { return deprecate.log(oldName + " is deprecated. Use " + newName + " instead."); }; // Print deprecation message. deprecate.log = function(message) { if (process.throwDeprecation) { throw new Error(message); } else if (process.traceDeprecation) { return console.trace(message); } else { return console.warn("(electron) " + message); } }; module.exports = deprecate; // Do not expose the internal modules to `require`. const hideInternalModules = function() { var globalPaths = require('module').globalPaths; if (globalPaths.length === 3) { // Remove the "common/api/lib" and "browser-or-renderer/api/lib". return globalPaths.splice(0, 2); } }; // Attaches properties to |exports|. exports.defineProperties = function(exports) { return Object.defineProperties(exports, { hideInternalModules: { enumerable: true, value: hideInternalModules }, // Common modules, please sort with alphabet order. clipboard: { // Must be enumerable, otherwise it woulde be invisible to remote module. enumerable: true, get: function() { return require('../clipboard'); } }, crashReporter: { enumerable: true, get: function() { return require('../crash-reporter'); } }, nativeImage: { enumerable: true, get: function() { return require('../native-image'); } }, shell: { enumerable: true, get: function() { return require('../shell'); } }, // The internal modules, invisible unless you know their names. CallbacksRegistry: { get: function() { return require('../callbacks-registry'); } }, deprecate: { get: function() { return require('../deprecate'); } } }); }; const deprecate = require('electron').deprecate; const nativeImage = process.atomBinding('native_image'); // Deprecated. deprecate.rename(nativeImage, 'createFromDataUrl', 'createFromDataURL'); module.exports = nativeImage; module.exports = process.atomBinding('shell'); const path = require('path'); const timers = require('timers'); const Module = require('module'); process.atomBinding = function(name) { try { return process.binding("atom_" + process.type + "_" + name); } catch (error) { if (/No such module/.test(error.message)) { return process.binding("atom_common_" + name); } } }; if (!process.env.ELECTRON_HIDE_INTERNAL_MODULES) { // Add common/api/lib to module search paths. Module.globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib')); } // 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. var wrapWithActivateUvLoop = function(func) { return function() { process.activateUvLoop(); return func.apply(this, arguments); }; }; process.nextTick = wrapWithActivateUvLoop(process.nextTick); global.setImmediate = wrapWithActivateUvLoop(timers.setImmediate); global.clearImmediate = timers.clearImmediate; if (process.type === 'browser') { // 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. global.setTimeout = wrapWithActivateUvLoop(timers.setTimeout); global.setInterval = wrapWithActivateUvLoop(timers.setInterval); } const path = require('path'); const Module = require('module'); // Clear Node's global search paths. Module.globalPaths.length = 0; // Clear current and parent(init.coffee)'s search paths. module.paths = []; module.parent.paths = []; // Prevent Node from adding paths outside this app to search paths. Module._nodeModulePaths = function(from) { var dir, i, part, parts, paths, skipOutsidePaths, splitRe, tip; from = path.resolve(from); // If "from" is outside the app then we do nothing. skipOutsidePaths = from.startsWith(process.resourcesPath); // Following logoic is copied from module.js. splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//; paths = []; parts = from.split(splitRe); for (tip = i = parts.length - 1; i >= 0; tip = i += -1) { part = parts[tip]; if (part === 'node_modules') { continue; } dir = parts.slice(0, tip + 1).join(path.sep); if (skipOutsidePaths && !dir.startsWith(process.resourcesPath)) { break; } paths.push(path.join(dir, 'node_modules')); } return paths; }; const ipcRenderer = require('electron').ipcRenderer; const nativeImage = require('electron').nativeImage; var nextId = 0; var includes = [].includes; var getNextId = function() { return ++nextId; }; // |options.type| can not be empty and has to include 'window' or 'screen'. var isValid = function(options) { return ((options != null ? options.types : void 0) != null) && Array.isArray(options.types); }; exports.getSources = function(options, callback) { var captureScreen, captureWindow, id; if (!isValid(options)) { return callback(new Error('Invalid options')); } captureWindow = includes.call(options.types, 'window'); captureScreen = includes.call(options.types, 'screen'); if (options.thumbnailSize == null) { options.thumbnailSize = { width: 150, height: 150 }; } id = getNextId(); ipcRenderer.send('ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id); return ipcRenderer.once("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + id, function(event, sources) { var source; return callback(null, (function() { var i, len, results; results = []; for (i = 0, len = sources.length; i < len; i++) { source = sources[i]; results.push({ id: source.id, name: source.name, thumbnail: nativeImage.createFromDataURL(source.thumbnail) }); } return results; })()); }); }; const common = require('../../../../common/api/lib/exports/electron'); // Import common modules. common.defineProperties(exports); Object.defineProperties(exports, { // Renderer side modules, please sort with alphabet order. desktopCapturer: { enumerable: true, get: function() { return require('../desktop-capturer'); } }, ipcRenderer: { enumerable: true, get: function() { return require('../ipc-renderer'); } }, remote: { enumerable: true, get: function() { return require('../remote'); } }, screen: { enumerable: true, get: function() { return require('../screen'); } }, webFrame: { enumerable: true, get: function() { return require('../web-frame'); } } }); const binding = process.atomBinding('ipc'); const v8Util = process.atomBinding('v8_util'); var slice = [].slice; // Created by init.coffee. const ipcRenderer = v8Util.getHiddenValue(global, 'ipc'); ipcRenderer.send = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return binding.send('ipc-message', slice.call(args)); }; ipcRenderer.sendSync = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return JSON.parse(binding.sendSync('ipc-message-sync', slice.call(args))); }; ipcRenderer.sendToHost = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return binding.send('ipc-message-host', slice.call(args)); }; module.exports = ipcRenderer; const ipcRenderer = require('electron').ipcRenderer; const deprecate = require('electron').deprecate; const EventEmitter = require('events').EventEmitter; var slice = [].slice; // This module is deprecated, we mirror everything from ipcRenderer. deprecate.warn('ipc module', 'require("electron").ipcRenderer'); // Routes events of ipcRenderer. var ipc = new EventEmitter; ipcRenderer.emit = function() { var channel = arguments[0]; var args = 3 <= arguments.length ? slice.call(arguments, 2) : []; ipc.emit.apply(ipc, [channel].concat(slice.call(args))); return EventEmitter.prototype.emit.apply(ipcRenderer, arguments); }; // Deprecated. for (var method in ipcRenderer) { if (method.startsWith('send')) { ipc[method] = ipcRenderer[method]; } } deprecate.rename(ipc, 'sendChannel', 'send'); deprecate.rename(ipc, 'sendChannelSync', 'sendSync'); module.exports = ipc; const ipcRenderer = require('electron').ipcRenderer; const CallbacksRegistry = require('electron').CallbacksRegistry; const v8Util = process.atomBinding('v8_util'); const callbacksRegistry = new CallbacksRegistry; var includes = [].includes; // Check for circular reference. var isCircular = function(field, visited) { if (typeof field === 'object') { if (includes.call(visited, field)) { return true; } visited.push(field); } return false; }; // Convert the arguments object into an array of meta data. var wrapArgs = function(args, visited) { var valueToMeta; if (visited == null) { visited = []; } valueToMeta = function(value) { var field, prop, ret; if (Array.isArray(value)) { return { type: 'array', value: wrapArgs(value, visited) }; } else if (Buffer.isBuffer(value)) { return { type: 'buffer', value: Array.prototype.slice.call(value, 0) }; } else if (value instanceof Date) { return { type: 'date', value: value.getTime() }; } else if ((value != null ? value.constructor.name : void 0) === 'Promise') { return { type: 'promise', then: valueToMeta(value.then.bind(value)) }; } else if ((value != null) && typeof value === 'object' && v8Util.getHiddenValue(value, 'atomId')) { return { type: 'remote-object', id: v8Util.getHiddenValue(value, 'atomId') }; } else if ((value != null) && typeof value === 'object') { ret = { type: 'object', name: value.constructor.name, members: [] }; for (prop in value) { field = value[prop]; ret.members.push({ name: prop, value: valueToMeta(isCircular(field, visited) ? null : field) }); } return ret; } 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') }; } else { return { type: 'value', value: value }; } }; return Array.prototype.slice.call(args).map(valueToMeta); }; // Convert meta data from browser into real value. var metaToValue = function(meta) { var el, i, j, len, len1, member, ref1, ref2, results, ret; switch (meta.type) { case 'value': return meta.value; case 'array': ref1 = meta.members; results = []; for (i = 0, len = ref1.length; i < len; i++) { el = ref1[i]; results.push(metaToValue(el)); } return results; case 'buffer': return new Buffer(meta.value); case 'promise': return Promise.resolve({ then: metaToValue(meta.then) }); case 'error': return metaToPlainObject(meta); case 'date': return new Date(meta.value); case 'exception': throw new Error(meta.message + "\n" + meta.stack); default: if (meta.type === 'function') { // A shadow class to represent the remote function object. ret = (function() { function RemoteFunction() { var obj; if (this.constructor === RemoteFunction) { // Constructor call. obj = ipcRenderer.sendSync('ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments)); /* Returning object in constructor will replace constructed object with the returned object. http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this */ return metaToValue(obj); } else { // Function call. obj = ipcRenderer.sendSync('ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments)); return metaToValue(obj); } } return RemoteFunction; })(); } else { ret = v8Util.createObjectWithName(meta.name); } // Polulate delegate members. ref2 = meta.members; for (j = 0, len1 = ref2.length; j < len1; j++) { member = ref2[j]; if (member.type === 'function') { ret[member.name] = createRemoteMemberFunction(meta.id, member.name); } else { Object.defineProperty(ret, member.name, createRemoteMemberProperty(meta.id, member.name)); } } // Track delegate object's life time, and tell the browser to clean up // when the object is GCed. v8Util.setDestructor(ret, function() { return ipcRenderer.send('ATOM_BROWSER_DEREFERENCE', meta.id); }); // Remember object's id. v8Util.setHiddenValue(ret, 'atomId', meta.id); return ret; } }; // Construct a plain object from the meta. var metaToPlainObject = function(meta) { var i, len, name, obj, ref1, ref2, value; obj = (function() { switch (meta.type) { case 'error': return new Error; default: return {}; } })(); ref1 = meta.members; for (i = 0, len = ref1.length; i < len; i++) { ref2 = ref1[i], name = ref2.name, value = ref2.value; obj[name] = value; } return obj; }; // Create a RemoteMemberFunction instance. // This function's content should not be inlined into metaToValue, otherwise V8 // may consider it circular reference. var createRemoteMemberFunction = function(metaId, name) { return (function() { function RemoteMemberFunction() { var ret; if (this.constructor === RemoteMemberFunction) { // Constructor call. ret = ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_CONSTRUCTOR', metaId, name, wrapArgs(arguments)); return metaToValue(ret); } else { // Call member function. ret = ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_CALL', metaId, name, wrapArgs(arguments)); return metaToValue(ret); } } return RemoteMemberFunction; })(); }; // Create configuration for defineProperty. // This function's content should not be inlined into metaToValue, otherwise V8 // may consider it circular reference. var createRemoteMemberProperty = function(metaId, name) { return { enumerable: true, configurable: false, set: function(value) { // Set member data. ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_SET', metaId, name, value); return value; }, get: function() { // Get member data. return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_GET', metaId, name)); } }; }; // Browser calls a callback in renderer. ipcRenderer.on('ATOM_RENDERER_CALLBACK', function(event, id, args) { return callbacksRegistry.apply(id, metaToValue(args)); }); // A callback in browser is released. ipcRenderer.on('ATOM_RENDERER_RELEASE_CALLBACK', function(event, id) { return callbacksRegistry.remove(id); }); // List all built-in modules in browser process. const browserModules = require('../../../browser/api/lib/exports/electron'); // And add a helper receiver for each one. var fn = function(name) { return Object.defineProperty(exports, name, { get: function() { return exports.getBuiltin(name); } }); }; for (var name in browserModules) { fn(name); } // Get remote module. // (Just like node's require, the modules are cached permanently, note that this // is safe leak since the object is not expected to get freed in browser) var moduleCache = {}; exports.require = function(module) { var meta; if (moduleCache[module] != null) { return moduleCache[module]; } meta = ipcRenderer.sendSync('ATOM_BROWSER_REQUIRE', module); return moduleCache[module] = metaToValue(meta); }; // Optimize require('electron'). moduleCache.electron = exports; // Alias to remote.require('electron').xxx. var builtinCache = {}; exports.getBuiltin = function(module) { var meta; if (builtinCache[module] != null) { return builtinCache[module]; } meta = ipcRenderer.sendSync('ATOM_BROWSER_GET_BUILTIN', module); return builtinCache[module] = metaToValue(meta); }; // Get current BrowserWindow object. var windowCache = null; exports.getCurrentWindow = function() { var meta; if (windowCache != null) { return windowCache; } meta = ipcRenderer.sendSync('ATOM_BROWSER_CURRENT_WINDOW'); return windowCache = metaToValue(meta); }; // Get current WebContents object. var webContentsCache = null; exports.getCurrentWebContents = function() { var meta; if (webContentsCache != null) { return webContentsCache; } meta = ipcRenderer.sendSync('ATOM_BROWSER_CURRENT_WEB_CONTENTS'); return webContentsCache = metaToValue(meta); }; // Get a global object in browser. exports.getGlobal = function(name) { var meta; meta = ipcRenderer.sendSync('ATOM_BROWSER_GLOBAL', name); return metaToValue(meta); }; // Get the process object in browser. var processCache = null; exports.__defineGetter__('process', function() { if (processCache == null) { processCache = exports.getGlobal('process'); } return processCache; }); // Create a funtion that will return the specifed value when called in browser. exports.createFunctionWithReturnValue = function(returnValue) { var func; func = function() { return returnValue; }; v8Util.setHiddenValue(func, 'returnValue', true); return func; }; // Get the guest WebContents from guestInstanceId. exports.getGuestWebContents = function(guestInstanceId) { var meta; meta = ipcRenderer.sendSync('ATOM_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId); return metaToValue(meta); }; module.exports = require('electron').remote.screen; 'use strict'; const deprecate = require('electron').deprecate; const EventEmitter = require('events').EventEmitter; const webFrame = process.atomBinding('web_frame').webFrame; // webFrame is an EventEmitter. webFrame.__proto__ = EventEmitter.prototype; // Lots of webview would subscribe to webFrame's events. webFrame.setMaxListeners(0); // Deprecated. deprecate.rename(webFrame, 'registerUrlSchemeAsSecure', 'registerURLSchemeAsSecure'); deprecate.rename(webFrame, 'registerUrlSchemeAsBypassingCSP', 'registerURLSchemeAsBypassingCSP'); deprecate.rename(webFrame, 'registerUrlSchemeAsPrivileged', 'registerURLSchemeAsPrivileged'); module.exports = webFrame; const url = require('url'); const chrome = window.chrome = window.chrome || {}; chrome.extension = { getURL: function(path) { return url.format({ protocol: location.protocol, slashes: true, hostname: location.hostname, pathname: path }); } }; 'user strict'; const events = require('events'); const path = require('path'); const Module = require('module'); // We modified the original process.argv to let node.js load the // atom-renderer.js, we need to restore it here. process.argv.splice(1, 1); // Clear search paths. require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths')); // Import common settings. require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')); var globalPaths = Module.globalPaths; if (!process.env.ELECTRON_HIDE_INTERNAL_MODULES) { globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib')); } // Expose public APIs. globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib', 'exports')); // The global variable will be used by ipc for event dispatching var v8Util = process.atomBinding('v8_util'); v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter); // Use electron module after everything is ready. const electron = require('electron'); // Call webFrame method. electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => { electron.webFrame[method].apply(electron.webFrame, args); }); // Process command line arguments. var nodeIntegration = 'false'; var preloadScript = null; var ref = process.argv; var i, len, arg; for (i = 0, len = ref.length; i < len; i++) { arg = ref[i]; if (arg.indexOf('--guest-instance-id=') === 0) { // This is a guest web view. process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1)); } else if (arg.indexOf('--opener-id=') === 0) { // This is a guest BrowserWindow. process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1)); } else if (arg.indexOf('--node-integration=') === 0) { nodeIntegration = arg.substr(arg.indexOf('=') + 1); } else if (arg.indexOf('--preload=') === 0) { preloadScript = arg.substr(arg.indexOf('=') + 1); } } if (location.protocol === 'chrome-devtools:') { // Override some inspector APIs. require('./inspector'); nodeIntegration = 'true'; } else if (location.protocol === 'chrome-extension:') { // Add implementations of chrome API. require('./chrome-api'); nodeIntegration = 'true'; } else { // Override default web functions. require('./override'); // Load webview tag implementation. if (process.guestInstanceId == null) { require('./web-view/web-view'); require('./web-view/web-view-attributes'); } } if (nodeIntegration === 'true' || nodeIntegration === 'all' || nodeIntegration === 'except-iframe' || nodeIntegration === 'manual-enable-iframe') { // 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:') { var pathname = process.platform === 'win32' && window.location.pathname[0] === '/' ? window.location.pathname.substr(1) : window.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; } // Redirect window.onerror to uncaughtException. window.onerror = function(message, filename, lineno, colno, error) { if (global.process.listeners('uncaughtException').length > 0) { global.process.emit('uncaughtException', error); return true; } else { return false; } }; // Emit the 'exit' event when page is unloading. window.addEventListener('unload', function() { return process.emit('exit'); }); } else { // Delete Node's symbols after the Environment has been loaded. process.once('loaded', function() { delete global.process; delete global.setImmediate; delete global.clearImmediate; return delete global.global; }); } // Load the script specfied by the "preload" attribute. if (preloadScript) { try { require(preloadScript); } catch (error) { if (error.code === 'MODULE_NOT_FOUND') { console.error("Unable to load preload script " + preloadScript); } else { console.error(error); console.error(error.stack); } } } window.onload = function() { // Use menu API to show context menu. InspectorFrontendHost.showContextMenuAtPoint = createMenu; // Use dialog API to override file chooser dialog. return WebInspector.createFileSelectorElement = createFileSelectorElement; }; var convertToMenuTemplate = function(items) { var fn, i, item, len, template; template = []; fn = function(item) { var transformed; transformed = item.type === 'subMenu' ? { type: 'submenu', label: item.label, enabled: item.enabled, submenu: convertToMenuTemplate(item.subItems) } : item.type === 'separator' ? { type: 'separator' } : item.type === 'checkbox' ? { type: 'checkbox', label: item.label, enabled: item.enabled, checked: item.checked } : { type: 'normal', label: item.label, enabled: item.enabled }; if (item.id != null) { transformed.click = function() { DevToolsAPI.contextMenuItemSelected(item.id); return DevToolsAPI.contextMenuCleared(); }; } return template.push(transformed); }; for (i = 0, len = items.length; i < len; i++) { item = items[i]; fn(item); } return template; }; var createMenu = function(x, y, items) { const remote = require('electron').remote; const Menu = remote.Menu; const menu = Menu.buildFromTemplate(convertToMenuTemplate(items)); // The menu is expected to show asynchronously. return setTimeout(function() { return menu.popup(remote.getCurrentWindow()); }); }; var showFileChooserDialog = function(callback) { var dialog, files, remote; remote = require('electron').remote; dialog = remote.dialog; files = dialog.showOpenDialog({}); if (files != null) { return callback(pathToHtml5FileObject(files[0])); } }; var pathToHtml5FileObject = function(path) { var blob, fs; fs = require('fs'); blob = new Blob([fs.readFileSync(path)]); blob.name = path; return blob; }; var createFileSelectorElement = function(callback) { var fileSelectorElement; fileSelectorElement = document.createElement('span'); fileSelectorElement.style.display = 'none'; fileSelectorElement.click = showFileChooserDialog.bind(this, callback); return fileSelectorElement; }; const ipcRenderer = require('electron').ipcRenderer; const remote = require('electron').remote; var slice = [].slice; // Helper function to resolve relative url. var a = window.top.document.createElement('a'); var resolveURL = function(url) { a.href = url; return a.href; }; // Window object returned by "window.open". var BrowserWindowProxy = (function() { BrowserWindowProxy.proxies = {}; BrowserWindowProxy.getOrCreate = function(guestId) { var base; return (base = this.proxies)[guestId] != null ? base[guestId] : base[guestId] = new BrowserWindowProxy(guestId); }; BrowserWindowProxy.remove = function(guestId) { return delete this.proxies[guestId]; }; function BrowserWindowProxy(guestId1) { this.guestId = guestId1; this.closed = false; ipcRenderer.once("ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_" + this.guestId, (function(_this) { return function() { BrowserWindowProxy.remove(_this.guestId); return _this.closed = true; }; })(this)); } BrowserWindowProxy.prototype.close = function() { return ipcRenderer.send('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', this.guestId); }; BrowserWindowProxy.prototype.focus = function() { return ipcRenderer.send('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'focus'); }; BrowserWindowProxy.prototype.blur = function() { return ipcRenderer.send('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'blur'); }; BrowserWindowProxy.prototype.postMessage = function(message, targetOrigin) { if (targetOrigin == null) { targetOrigin = '*'; } return ipcRenderer.send('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, targetOrigin, location.origin); }; BrowserWindowProxy.prototype["eval"] = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return ipcRenderer.send.apply(ipcRenderer, ['ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'executeJavaScript'].concat(slice.call(args))); }; return BrowserWindowProxy; })(); if (process.guestInstanceId == null) { // Override default window.close. window.close = function() { return remote.getCurrentWindow().close(); }; } // Make the browser window or guest view emit "new-window" event. window.open = function(url, frameName, features) { var feature, guestId, i, ints, j, len, len1, name, options, ref1, ref2, value; if (frameName == null) { frameName = ''; } if (features == null) { features = ''; } options = {}; ints = ['x', 'y', 'width', 'height', 'min-width', 'max-width', 'min-height', 'max-height', 'zoom-factor']; // Make sure to get rid of excessive whitespace in the property name ref1 = features.split(/,\s*/); for (i = 0, len = ref1.length; i < len; i++) { feature = ref1[i]; ref2 = feature.split(/\s*=/), name = ref2[0], value = ref2[1]; options[name] = value === 'yes' || value === '1' ? true : value === 'no' || value === '0' ? false : 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; } // Resolve relative urls. url = resolveURL(url); for (j = 0, len1 = ints.length; j < len1; j++) { name = ints[j]; if (options[name] != null) { options[name] = parseInt(options[name], 10); } } guestId = ipcRenderer.sendSync('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, frameName, options); if (guestId) { return BrowserWindowProxy.getOrCreate(guestId); } else { return null; } }; // Use the dialog API to implement alert(). window.alert = function(message, title) { var buttons; if (title == null) { title = ''; } buttons = ['OK']; message = message.toString(); remote.dialog.showMessageBox(remote.getCurrentWindow(), { message: message, title: title, buttons: buttons }); // Alert should always return undefined. }; // And the confirm(). window.confirm = function(message, title) { var buttons, cancelId; if (title == null) { title = ''; } buttons = ['OK', 'Cancel']; cancelId = 1; return !remote.dialog.showMessageBox(remote.getCurrentWindow(), { message: message, title: title, buttons: buttons, cancelId: cancelId }); }; // But we do not support prompt(). window.prompt = function() { throw new Error('prompt() is and will not be supported.'); }; if (process.openerId != null) { window.opener = BrowserWindowProxy.getOrCreate(process.openerId); } ipcRenderer.on('ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', function(event, sourceId, message, sourceOrigin) { // Manually dispatch event instead of using postMessage because we also need to // set event.source. event = document.createEvent('Event'); event.initEvent('message', false, false); event.data = message; event.origin = sourceOrigin; event.source = BrowserWindowProxy.getOrCreate(sourceId); return window.dispatchEvent(event); }); // Forward history operations to browser. var sendHistoryOperation = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return ipcRenderer.send.apply(ipcRenderer, ['ATOM_SHELL_NAVIGATION_CONTROLLER'].concat(slice.call(args))); }; var getHistoryOperation = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return ipcRenderer.sendSync.apply(ipcRenderer, ['ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER'].concat(slice.call(args))); }; window.history.back = function() { return sendHistoryOperation('goBack'); }; window.history.forward = function() { return sendHistoryOperation('goForward'); }; window.history.go = function(offset) { return sendHistoryOperation('goToOffset', offset); }; Object.defineProperty(window.history, 'length', { get: function() { return getHistoryOperation('length'); } }); // Make document.hidden and document.visibilityState return the correct value. Object.defineProperty(document, 'hidden', { get: function() { var currentWindow; currentWindow = remote.getCurrentWindow(); return currentWindow.isMinimized() || !currentWindow.isVisible(); } }); Object.defineProperty(document, 'visibilityState', { get: function() { if (document.hidden) { return "hidden"; } else { return "visible"; } } }); const ipcRenderer = require('electron').ipcRenderer; const webFrame = require('electron').webFrame; var slice = [].slice; var requestId = 0; var WEB_VIEW_EVENTS = { 'load-commit': ['url', 'isMainFrame'], 'did-finish-load': [], 'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL'], 'did-frame-finish-load': ['isMainFrame'], 'did-start-loading': [], 'did-stop-loading': [], 'did-get-response-details': ['status', 'newURL', 'originalURL', 'httpResponseCode', 'requestMethod', 'referrer', 'headers'], 'did-get-redirect-request': ['oldURL', 'newURL', 'isMainFrame'], 'dom-ready': [], 'console-message': ['level', 'message', 'line', 'sourceId'], 'devtools-opened': [], 'devtools-closed': [], 'devtools-focused': [], 'new-window': ['url', 'frameName', 'disposition', 'options'], 'will-navigate': ['url'], 'did-navigate': ['url'], 'did-navigate-in-page': ['url'], 'close': [], 'crashed': [], 'gpu-crashed': [], 'plugin-crashed': ['name', 'version'], 'media-started-playing': [], 'media-paused': [], 'did-change-theme-color': ['themeColor'], 'destroyed': [], 'page-title-updated': ['title', 'explicitSet'], 'page-favicon-updated': ['favicons'], 'enter-html-full-screen': [], 'leave-html-full-screen': [], 'found-in-page': ['result'] }; var DEPRECATED_EVENTS = { 'page-title-updated': 'page-title-set' }; var dispatchEvent = function() { var args, domEvent, eventKey, eventName, f, i, j, len, ref1, webView; webView = arguments[0], eventName = arguments[1], eventKey = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : []; if (DEPRECATED_EVENTS[eventName] != null) { dispatchEvent.apply(null, [webView, DEPRECATED_EVENTS[eventName], eventKey].concat(slice.call(args))); } domEvent = new Event(eventName); ref1 = WEB_VIEW_EVENTS[eventKey]; for (i = j = 0, len = ref1.length; j < len; i = ++j) { f = ref1[i]; domEvent[f] = args[i]; } webView.dispatchEvent(domEvent); if (eventName === 'load-commit') { return webView.onLoadCommit(domEvent); } }; module.exports = { registerEvents: function(webView, viewInstanceId) { ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + viewInstanceId, function() { var eventName = arguments[1]; var args = 3 <= arguments.length ? slice.call(arguments, 2) : []; return dispatchEvent.apply(null, [webView, eventName, eventName].concat(slice.call(args))); }); ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + viewInstanceId, function() { var channel = arguments[1]; var args = 3 <= arguments.length ? slice.call(arguments, 2) : []; var domEvent = new Event('ipc-message'); domEvent.channel = channel; domEvent.args = slice.call(args); return webView.dispatchEvent(domEvent); }); return ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + viewInstanceId, function() { var args, domEvent, f, i, j, len, ref1; args = 2 <= arguments.length ? slice.call(arguments, 1) : []; domEvent = new Event('size-changed'); ref1 = ['oldWidth', 'oldHeight', 'newWidth', 'newHeight']; for (i = j = 0, len = ref1.length; j < len; i = ++j) { f = ref1[i]; domEvent[f] = args[i]; } return webView.onSizeChanged(domEvent); }); }, deregisterEvents: function(viewInstanceId) { ipcRenderer.removeAllListeners("ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + viewInstanceId); ipcRenderer.removeAllListeners("ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + viewInstanceId); return ipcRenderer.removeAllListeners("ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + viewInstanceId); }, createGuest: function(params, callback) { requestId++; ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId); return ipcRenderer.once("ATOM_SHELL_RESPONSE_" + requestId, callback); }, attachGuest: function(elementInstanceId, guestInstanceId, params) { ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', elementInstanceId, guestInstanceId, params); return webFrame.attachGuest(elementInstanceId); }, destroyGuest: function(guestInstanceId) { return ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId); }, setSize: function(guestInstanceId, params) { return ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', guestInstanceId, params); }, setAllowTransparency: function(guestInstanceId, allowtransparency) { return ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', guestInstanceId, allowtransparency); } }; 'use strict'; const WebViewImpl = require('./web-view'); const guestViewInternal = require('./guest-view-internal'); const webViewConstants = require('./web-view-constants'); const remote = require('electron').remote; // Helper function to resolve url set in attribute. var a = document.createElement('a'); var resolveURL = function(url) { a.href = url; return a.href; }; // Attribute objects. // Default implementation of a WebView attribute. class WebViewAttribute { constructor(name, webViewImpl) { this.name = name; this.value = webViewImpl.webviewNode[name] || ''; this.webViewImpl = webViewImpl; this.ignoreMutation = false; 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) { return 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); return this.ignoreMutation = false; } // Defines this attribute as a property on the webview node. defineProperty() { return Object.defineProperty(this.webViewImpl.webviewNode, this.name, { get: (function(_this) { return function() { return _this.getValue(); }; })(this), set: (function(_this) { return function(value) { return _this.setValue(value); }; })(this), enumerable: true }); } // Called when the attribute's value changes. handleMutation() {} } // An attribute that is treated as a Boolean. class BooleanAttribute extends WebViewAttribute { constructor(name, webViewImpl) { super(name, webViewImpl); } getValue() { return this.webViewImpl.webviewNode.hasAttribute(this.name); } setValue(value) { if (!value) { return this.webViewImpl.webviewNode.removeAttribute(this.name); } else { return this.webViewImpl.webviewNode.setAttribute(this.name, ''); } } } // Attribute that specifies whether transparency is allowed in the webview. class AllowTransparencyAttribute extends BooleanAttribute { constructor(webViewImpl) { super(webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, webViewImpl); } handleMutation() { if (!this.webViewImpl.guestInstanceId) { return; } return guestViewInternal.setAllowTransparency(this.webViewImpl.guestInstanceId, this.getValue()); } } // Attribute used to define the demension limits of autosizing. class AutosizeDimensionAttribute extends WebViewAttribute { constructor(name, webViewImpl) { super(name, webViewImpl); } getValue() { return parseInt(this.webViewImpl.webviewNode.getAttribute(this.name)) || 0; } handleMutation() { if (!this.webViewImpl.guestInstanceId) { return; } return guestViewInternal.setSize(this.webViewImpl.guestInstanceId, { enableAutoSize: this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue(), min: { width: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0), height: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0) }, max: { width: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0), height: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0) } }); } } // Attribute that specifies whether the webview should be autosized. class AutosizeAttribute extends BooleanAttribute { constructor(webViewImpl) { super(webViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl); } } AutosizeAttribute.prototype.handleMutation = AutosizeDimensionAttribute.prototype.handleMutation; // Attribute representing the state of the storage partition. class PartitionAttribute extends WebViewAttribute { constructor(webViewImpl) { super(webViewConstants.ATTRIBUTE_PARTITION, webViewImpl); this.validPartitionId = true; } handleMutation(oldValue, newValue) { newValue = newValue || ''; // The partition cannot change if the webview has already navigated. if (!this.webViewImpl.beforeFirstNavigation) { window.console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED); this.setValueIgnoreMutation(oldValue); return; } if (newValue === 'persist:') { this.validPartitionId = false; return window.console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE); } } } // Attribute that handles the location and navigation of the webview. class SrcAttribute extends WebViewAttribute { constructor(webViewImpl) { super(webViewConstants.ATTRIBUTE_SRC, webViewImpl); 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. return this.observer.takeRecords(); } 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; } return this.parse(); } // 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() { var params; this.observer = new MutationObserver((function(_this) { return function(mutations) { var i, len, mutation, newValue, oldValue; for (i = 0, len = mutations.length; i < len; i++) { mutation = mutations[i]; oldValue = mutation.oldValue; newValue = _this.getValue(); if (oldValue !== newValue) { return; } _this.handleMutation(oldValue, newValue); } }; })(this)); params = { attributes: true, attributeOldValue: true, attributeFilter: [this.name] }; return this.observer.observe(this.webViewImpl.webviewNode, params); } parse() { var guestContents, httpreferrer, opts, useragent; if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.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|. opts = {}; httpreferrer = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue(); if (httpreferrer) { opts.httpReferrer = httpreferrer; } useragent = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_USERAGENT].getValue(); if (useragent) { opts.userAgent = useragent; } guestContents = remote.getGuestWebContents(this.webViewImpl.guestInstanceId); return guestContents.loadURL(this.getValue(), opts); } } // Attribute specifies HTTP referrer. class HttpReferrerAttribute extends WebViewAttribute { constructor(webViewImpl) { super(webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl); } } // Attribute specifies user agent class UserAgentAttribute extends WebViewAttribute { constructor(webViewImpl) { super(webViewConstants.ATTRIBUTE_USERAGENT, webViewImpl); } } // Attribute that set preload script. class PreloadAttribute extends WebViewAttribute { constructor(webViewImpl) { super(webViewConstants.ATTRIBUTE_PRELOAD, webViewImpl); } getValue() { var preload, protocol; if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) { return this.value; } preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)); protocol = preload.substr(0, 5); if (protocol !== 'file:') { console.error(webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE); preload = ''; } return preload; } } // Attribute that specifies the blink features to be enabled. class BlinkFeaturesAttribute extends WebViewAttribute { constructor(webViewImpl) { super(webViewConstants.ATTRIBUTE_BLINKFEATURES, webViewImpl); } } // Sets up all of the webview attributes. WebViewImpl.prototype.setupWebViewAttributes = function() { var attribute, autosizeAttributes, i, len, results; this.attributes = {}; this.attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] = new AllowTransparencyAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE] = new AutosizeAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this); this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this); this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this); this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this); this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this); autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH]; results = []; for (i = 0, len = autosizeAttributes.length; i < len; i++) { attribute = autosizeAttributes[i]; results.push(this.attributes[attribute] = new AutosizeDimensionAttribute(attribute, this)); } return results; }; module.exports = { // Attributes. ATTRIBUTE_ALLOWTRANSPARENCY: 'allowtransparency', ATTRIBUTE_AUTOSIZE: 'autosize', ATTRIBUTE_MAXHEIGHT: 'maxheight', ATTRIBUTE_MAXWIDTH: 'maxwidth', ATTRIBUTE_MINHEIGHT: 'minheight', ATTRIBUTE_MINWIDTH: 'minwidth', ATTRIBUTE_NAME: 'name', ATTRIBUTE_PARTITION: 'partition', ATTRIBUTE_SRC: 'src', ATTRIBUTE_HTTPREFERRER: 'httpreferrer', ATTRIBUTE_NODEINTEGRATION: 'nodeintegration', ATTRIBUTE_PLUGINS: 'plugins', ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity', ATTRIBUTE_ALLOWPOPUPS: 'allowpopups', ATTRIBUTE_PRELOAD: 'preload', ATTRIBUTE_USERAGENT: 'useragent', ATTRIBUTE_BLINKFEATURES: 'blinkfeatures', // Internal attribute. ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid', // Error messages. ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.', ERROR_MSG_CANNOT_INJECT_SCRIPT: ': ' + 'Script cannot be injected into content until the page has loaded.', ERROR_MSG_INVALID_PARTITION_ATTRIBUTE: 'Invalid partition attribute.', ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE: 'Only "file:" protocol is supported in "preload" attribute.' }; 'user strict'; const deprecate = require('electron').deprecate; const webFrame = require('electron').webFrame; const remote = require('electron').remote; const ipcRenderer = require('electron').ipcRenderer; const v8Util = process.atomBinding('v8_util'); const guestViewInternal = require('./guest-view-internal'); const webViewConstants = require('./web-view-constants'); var hasProp = {}.hasOwnProperty; var slice = [].slice; // ID generator. var nextId = 0; var getNextId = function() { return ++nextId; }; // Represents the internal state of the WebView node. var WebViewImpl = (function() { function WebViewImpl(webviewNode) { var shadowRoot; this.webviewNode = webviewNode; v8Util.setHiddenValue(this.webviewNode, 'internal', this); this.attached = false; this.elementAttached = false; this.beforeFirstNavigation = true; // on* Event handlers. this.on = {}; this.browserPluginNode = this.createBrowserPluginNode(); shadowRoot = this.webviewNode.createShadowRoot(); this.setupWebViewAttributes(); this.setupFocusPropagation(); this.viewInstanceId = getNextId(); shadowRoot.appendChild(this.browserPluginNode); // Subscribe to host's zoom level changes. this.onZoomLevelChanged = (zoomLevel) => { this.webviewNode.setZoomLevel(zoomLevel); }; webFrame.on('zoom-level-changed', this.onZoomLevelChanged); } WebViewImpl.prototype.createBrowserPluginNode = function() { // We create BrowserPlugin as a custom element in order to observe changes // to attributes synchronously. var browserPluginNode; browserPluginNode = new WebViewImpl.BrowserPlugin(); v8Util.setHiddenValue(browserPluginNode, 'internal', this); return browserPluginNode; }; // Resets some state upon reattaching element to the DOM. WebViewImpl.prototype.reset = function() { // Unlisten the zoom-level-changed event. webFrame.removeListener('zoom-level-changed', this.onZoomLevelChanged); // 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.webContents = null; this.guestInstanceId = void 0; this.beforeFirstNavigation = true; this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true; } return this.internalInstanceId = 0; }; // Sets the .request property. WebViewImpl.prototype.setRequestPropertyOnWebViewNode = function(request) { return Object.defineProperty(this.webviewNode, 'request', { value: request, enumerable: true }); }; WebViewImpl.prototype.setupFocusPropagation = function() { if (!this.webviewNode.hasAttribute('tabIndex')) { // needs a tabIndex in order to be focusable. // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute // to allow to be focusable. // See http://crbug.com/231664. this.webviewNode.setAttribute('tabIndex', -1); } this.webviewNode.addEventListener('focus', (function(_this) { return function() { // Focus the BrowserPlugin when the takes focus. return _this.browserPluginNode.focus(); }; })(this)); return this.webviewNode.addEventListener('blur', (function(_this) { return function() { // Blur the BrowserPlugin when the loses focus. return _this.browserPluginNode.blur(); }; })(this)); }; // 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. WebViewImpl.prototype.handleWebviewAttributeMutation = function(attributeName, oldValue, newValue) { if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) { return; } // Let the changed attribute handle its own mutation; return this.attributes[attributeName].handleMutation(oldValue, newValue); }; WebViewImpl.prototype.handleBrowserPluginAttributeMutation = function(attributeName, oldValue, newValue) { if (attributeName === webViewConstants.ATTRIBUTE_INTERNALINSTANCEID && !oldValue && !!newValue) { this.browserPluginNode.removeAttribute(webViewConstants.ATTRIBUTE_INTERNALINSTANCEID); this.internalInstanceId = parseInt(newValue); // Track when the element resizes using the element resize callback. webFrame.registerElementResizeCallback(this.internalInstanceId, this.onElementResize.bind(this)); if (!this.guestInstanceId) { return; } return guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams()); } }; WebViewImpl.prototype.onSizeChanged = function(webViewEvent) { var maxHeight, maxWidth, minHeight, minWidth, newHeight, newWidth, node, width; newWidth = webViewEvent.newWidth; newHeight = webViewEvent.newHeight; node = this.webviewNode; width = node.offsetWidth; // Check the current bounds to make sure we do not resize // outside of current constraints. maxWidth = this.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() | width; maxHeight = this.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() | width; minWidth = this.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() | width; minHeight = this.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() | width; minWidth = Math.min(minWidth, maxWidth); minHeight = Math.min(minHeight, maxHeight); if (!this.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue() || (newWidth >= minWidth && newWidth <= maxWidth && newHeight >= minHeight && newHeight <= maxHeight)) { node.style.width = newWidth + 'px'; node.style.height = newHeight + 'px'; // Only fire the DOM event if the size of the has actually // changed. return this.dispatchEvent(webViewEvent); } }; WebViewImpl.prototype.onElementResize = function(newSize) { // Dispatch the 'resize' event. var resizeEvent; resizeEvent = new Event('resize', { bubbles: true }); resizeEvent.newWidth = newSize.width; resizeEvent.newHeight = newSize.height; this.dispatchEvent(resizeEvent); if (this.guestInstanceId) { return guestViewInternal.setSize(this.guestInstanceId, { normal: newSize }); } }; WebViewImpl.prototype.createGuest = function() { return guestViewInternal.createGuest(this.buildParams(), (function(_this) { return function(event, guestInstanceId) { return _this.attachWindow(guestInstanceId); }; })(this)); }; WebViewImpl.prototype.dispatchEvent = function(webViewEvent) { return this.webviewNode.dispatchEvent(webViewEvent); }; // Adds an 'on' property on the webview, which can be used to set/unset // an event handler. WebViewImpl.prototype.setupEventProperty = function(eventName) { var propertyName; propertyName = 'on' + eventName.toLowerCase(); return Object.defineProperty(this.webviewNode, propertyName, { get: (function(_this) { return function() { return _this.on[propertyName]; }; })(this), set: (function(_this) { return function(value) { if (_this.on[propertyName]) { _this.webviewNode.removeEventListener(eventName, _this.on[propertyName]); } _this.on[propertyName] = value; if (value) { return _this.webviewNode.addEventListener(eventName, value); } }; })(this), enumerable: true }); }; // Updates state upon loadcommit. WebViewImpl.prototype.onLoadCommit = function(webViewEvent) { var newValue, oldValue; oldValue = this.webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC); 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. return this.attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue); } }; WebViewImpl.prototype.onAttach = function(storagePartitionId) { return this.attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue(storagePartitionId); }; WebViewImpl.prototype.buildParams = function() { var attribute, attributeName, css, elementRect, params, ref1; params = { instanceId: this.viewInstanceId, userAgentOverride: this.userAgentOverride }; ref1 = this.attributes; for (attributeName in ref1) { if (!hasProp.call(ref1, attributeName)) continue; attribute = ref1[attributeName]; params[attributeName] = attribute.getValue(); } // When the WebView is not participating in layout (display:none) // then getBoundingClientRect() would report a width and height of 0. // However, in the case where the WebView has a fixed size we can // use that value to initially size the guest so as to avoid a relayout of // the on display:block. css = window.getComputedStyle(this.webviewNode, null); elementRect = this.webviewNode.getBoundingClientRect(); params.elementWidth = parseInt(elementRect.width) || parseInt(css.getPropertyValue('width')); params.elementHeight = parseInt(elementRect.height) || parseInt(css.getPropertyValue('height')); return params; }; WebViewImpl.prototype.attachWindow = function(guestInstanceId) { this.guestInstanceId = guestInstanceId; this.webContents = remote.getGuestWebContents(this.guestInstanceId); if (!this.internalInstanceId) { return true; } return guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams()); }; return WebViewImpl; })(); // Registers browser plugin custom element. var registerBrowserPluginElement = function() { var proto; proto = Object.create(HTMLObjectElement.prototype); proto.createdCallback = function() { this.setAttribute('type', 'application/browser-plugin'); this.setAttribute('id', 'browser-plugin-' + getNextId()); // The node fills in the container. this.style.display = 'block'; this.style.width = '100%'; return this.style.height = '100%'; }; proto.attributeChangedCallback = function(name, oldValue, newValue) { var internal; internal = v8Util.getHiddenValue(this, 'internal'); if (!internal) { return; } return internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue); }; proto.attachedCallback = function() { // Load the plugin immediately. return this.nonExistentAttribute; }; WebViewImpl.BrowserPlugin = webFrame.registerEmbedderCustomElement('browserplugin', { "extends": 'object', prototype: proto }); delete proto.createdCallback; delete proto.attachedCallback; delete proto.detachedCallback; return delete proto.attributeChangedCallback; }; // Registers custom element. var registerWebViewElement = function() { var createBlockHandler, createNonBlockHandler, i, j, len, len1, m, methods, nonblockMethods, proto; proto = Object.create(HTMLObjectElement.prototype); proto.createdCallback = function() { return new WebViewImpl(this); }; proto.attributeChangedCallback = function(name, oldValue, newValue) { var internal; internal = v8Util.getHiddenValue(this, 'internal'); if (!internal) { return; } return internal.handleWebviewAttributeMutation(name, oldValue, newValue); }; proto.detachedCallback = function() { var internal; internal = v8Util.getHiddenValue(this, 'internal'); if (!internal) { return; } guestViewInternal.deregisterEvents(internal.viewInstanceId); internal.elementAttached = false; return internal.reset(); }; proto.attachedCallback = function() { var internal; internal = v8Util.getHiddenValue(this, 'internal'); if (!internal) { return; } if (!internal.elementAttached) { guestViewInternal.registerEvents(internal, internal.viewInstanceId); internal.elementAttached = true; return internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse(); } }; // Public-facing API methods. methods = [ 'getURL', 'loadURL', 'getTitle', 'isLoading', 'isWaitingForResponse', 'stop', 'reload', 'reloadIgnoringCache', 'canGoBack', 'canGoForward', 'canGoToOffset', 'clearHistory', 'goBack', 'goForward', 'goToIndex', 'goToOffset', 'isCrashed', 'setUserAgent', 'getUserAgent', 'openDevTools', 'closeDevTools', 'isDevToolsOpened', 'isDevToolsFocused', 'inspectElement', 'setAudioMuted', 'isAudioMuted', 'undo', 'redo', 'cut', 'copy', 'paste', 'pasteAndMatchStyle', 'delete', 'selectAll', 'unselect', 'replace', 'replaceMisspelling', 'findInPage', 'stopFindInPage', 'getId', 'downloadURL', 'inspectServiceWorker', 'print', 'printToPDF' ]; nonblockMethods = [ 'executeJavaScript', 'insertCSS', 'insertText', 'send', 'sendInputEvent', 'setZoomFactor', 'setZoomLevel', 'setZoomLevelLimits', ]; // Forward proto.foo* method calls to WebViewImpl.foo*. createBlockHandler = function(m) { return function() { var args, internal, ref1; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; internal = v8Util.getHiddenValue(this, 'internal'); return (ref1 = internal.webContents)[m].apply(ref1, args); }; }; for (i = 0, len = methods.length; i < len; i++) { m = methods[i]; proto[m] = createBlockHandler(m); } createNonBlockHandler = function(m) { return function() { var args, internal; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; internal = v8Util.getHiddenValue(this, 'internal'); return ipcRenderer.send.apply(ipcRenderer, ['ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', internal.guestInstanceId, m].concat(slice.call(args))); }; }; for (j = 0, len1 = nonblockMethods.length; j < len1; j++) { m = nonblockMethods[j]; proto[m] = createNonBlockHandler(m); } // Deprecated. deprecate.rename(proto, 'getUrl', 'getURL'); window.WebView = webFrame.registerEmbedderCustomElement('webview', { prototype: proto }); // Delete the callbacks so developers cannot call them and produce unexpected // behavior. delete proto.createdCallback; delete proto.attachedCallback; delete proto.detachedCallback; return delete proto.attributeChangedCallback; }; var useCapture = true; var listener = function(event) { if (document.readyState === 'loading') { return; } registerBrowserPluginElement(); registerWebViewElement(); return window.removeEventListener(event.type, listener, useCapture); }; window.addEventListener('readystatechange', listener, true); module.exports = WebViewImpl;