diff --git a/.eslintrc.json b/.eslintrc.json index 83c0df2021fc3..c0b9e022c3a4b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,6 +6,7 @@ "browser": true }, "rules": { + "semi": ["error", "always"], "no-var": "error", "no-unused-vars": 0, "no-global-assign": 0, diff --git a/default_app/default_app.ts b/default_app/default_app.ts index 830c33e9f007f..c147b8c53907d 100644 --- a/default_app/default_app.ts +++ b/default_app/default_app.ts @@ -1,48 +1,48 @@ -import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron' -import * as path from 'path' +import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron'; +import * as path from 'path'; -let mainWindow: BrowserWindow | null = null +let mainWindow: BrowserWindow | null = null; // Quit when all windows are closed. app.on('window-all-closed', () => { - app.quit() -}) + app.quit(); +}); function decorateURL (url: string) { // safely add `?utm_source=default_app - const parsedUrl = new URL(url) - parsedUrl.searchParams.append('utm_source', 'default_app') - return parsedUrl.toString() + const parsedUrl = new URL(url); + parsedUrl.searchParams.append('utm_source', 'default_app'); + return parsedUrl.toString(); } // Find the shortest path to the electron binary -const absoluteElectronPath = process.execPath -const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath) +const absoluteElectronPath = process.execPath; +const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath); const electronPath = absoluteElectronPath.length < relativeElectronPath.length ? absoluteElectronPath - : relativeElectronPath + : relativeElectronPath; -const indexPath = path.resolve(app.getAppPath(), 'index.html') +const indexPath = path.resolve(app.getAppPath(), 'index.html'); function isTrustedSender (webContents: Electron.WebContents) { if (webContents !== (mainWindow && mainWindow.webContents)) { - return false + return false; } - const parsedUrl = new URL(webContents.getURL()) + const parsedUrl = new URL(webContents.getURL()); const urlPath = process.platform === 'win32' // Strip the prefixed "/" that occurs on windows ? path.resolve(parsedUrl.pathname.substr(1)) - : parsedUrl.pathname - return parsedUrl.protocol === 'file:' && urlPath === indexPath + : parsedUrl.pathname; + return parsedUrl.protocol === 'file:' && urlPath === indexPath; } ipcMain.handle('bootstrap', (event) => { - return isTrustedSender(event.sender) ? electronPath : null -}) + return isTrustedSender(event.sender) ? electronPath : null; +}); async function createWindow () { - await app.whenReady() + await app.whenReady(); const options: Electron.BrowserWindowConstructorOptions = { width: 960, @@ -57,46 +57,46 @@ async function createWindow () { }, useContentSize: true, show: false - } + }; if (process.platform === 'linux') { - options.icon = path.join(__dirname, 'icon.png') + options.icon = path.join(__dirname, 'icon.png'); } - mainWindow = new BrowserWindow(options) - mainWindow.on('ready-to-show', () => mainWindow!.show()) + mainWindow = new BrowserWindow(options); + mainWindow.on('ready-to-show', () => mainWindow!.show()); mainWindow.webContents.on('new-window', (event, url) => { - event.preventDefault() - shell.openExternal(decorateURL(url)) - }) + event.preventDefault(); + shell.openExternal(decorateURL(url)); + }); mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, done) => { - const parsedUrl = new URL(webContents.getURL()) + const parsedUrl = new URL(webContents.getURL()); const options: Electron.MessageBoxOptions = { title: 'Permission Request', message: `Allow '${parsedUrl.origin}' to access '${permission}'?`, buttons: ['OK', 'Cancel'], cancelId: 1 - } + }; dialog.showMessageBox(mainWindow!, options).then(({ response }) => { - done(response === 0) - }) - }) + done(response === 0); + }); + }); - return mainWindow + return mainWindow; } export const loadURL = async (appUrl: string) => { - mainWindow = await createWindow() - mainWindow.loadURL(appUrl) - mainWindow.focus() -} + mainWindow = await createWindow(); + mainWindow.loadURL(appUrl); + mainWindow.focus(); +}; export const loadFile = async (appPath: string) => { - mainWindow = await createWindow() - mainWindow.loadFile(appPath) - mainWindow.focus() -} + mainWindow = await createWindow(); + mainWindow.loadFile(appPath); + mainWindow.focus(); +}; diff --git a/default_app/main.ts b/default_app/main.ts index fba9562165fe9..aa652595d33a2 100644 --- a/default_app/main.ts +++ b/default_app/main.ts @@ -1,8 +1,8 @@ -import { app, dialog } from 'electron' +import { app, dialog } from 'electron'; -import * as fs from 'fs' -import * as path from 'path' -import * as url from 'url' +import * as fs from 'fs'; +import * as path from 'path'; +import * as url from 'url'; type DefaultAppOptions = { file: null | string; @@ -14,10 +14,10 @@ type DefaultAppOptions = { modules: string[]; } -const Module = require('module') +const Module = require('module'); // Parse command line options. -const argv = process.argv.slice(1) +const argv = process.argv.slice(1); const option: DefaultAppOptions = { file: null, @@ -27,50 +27,50 @@ const option: DefaultAppOptions = { interactive: false, abi: false, modules: [] -} +}; -let nextArgIsRequire = false +let nextArgIsRequire = false; for (const arg of argv) { if (nextArgIsRequire) { - option.modules.push(arg) - nextArgIsRequire = false - continue + option.modules.push(arg); + nextArgIsRequire = false; + continue; } else if (arg === '--version' || arg === '-v') { - option.version = true - break + option.version = true; + break; } else if (arg.match(/^--app=/)) { - option.file = arg.split('=')[1] - break + option.file = arg.split('=')[1]; + break; } else if (arg === '--interactive' || arg === '-i' || arg === '-repl') { - option.interactive = true + option.interactive = true; } else if (arg === '--test-type=webdriver') { - option.webdriver = true + option.webdriver = true; } else if (arg === '--require' || arg === '-r') { - nextArgIsRequire = true - continue + nextArgIsRequire = true; + continue; } else if (arg === '--abi' || arg === '-a') { - option.abi = true - continue + option.abi = true; + continue; } else if (arg === '--no-help') { - option.noHelp = true - continue + option.noHelp = true; + continue; } else if (arg[0] === '-') { - continue + continue; } else { - option.file = arg - break + option.file = arg; + break; } } if (nextArgIsRequire) { - console.error('Invalid Usage: --require [file]\n\n"file" is required') - process.exit(1) + console.error('Invalid Usage: --require [file]\n\n"file" is required'); + process.exit(1); } // Set up preload modules if (option.modules.length > 0) { - Module._preloadModules(option.modules) + Module._preloadModules(option.modules); } function loadApplicationPackage (packagePath: string) { @@ -79,102 +79,102 @@ function loadApplicationPackage (packagePath: string) { configurable: false, enumerable: true, value: true - }) + }); try { // Override app name and version. - packagePath = path.resolve(packagePath) - const packageJsonPath = path.join(packagePath, 'package.json') - let appPath + packagePath = path.resolve(packagePath); + const packageJsonPath = path.join(packagePath, 'package.json'); + let appPath; if (fs.existsSync(packageJsonPath)) { - let packageJson + let packageJson; try { - packageJson = require(packageJsonPath) + packageJson = require(packageJsonPath); } catch (e) { - showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`) - return + showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`); + return; } if (packageJson.version) { - app.setVersion(packageJson.version) + app.setVersion(packageJson.version); } if (packageJson.productName) { - app.name = packageJson.productName + app.name = packageJson.productName; } else if (packageJson.name) { - app.name = packageJson.name + app.name = packageJson.name; } - appPath = packagePath + appPath = packagePath; } try { - const filePath = Module._resolveFilename(packagePath, module, true) - app._setDefaultAppPaths(appPath || path.dirname(filePath)) + const filePath = Module._resolveFilename(packagePath, module, true); + app._setDefaultAppPaths(appPath || path.dirname(filePath)); } catch (e) { - showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`) - return + showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`); + return; } // Run the app. - Module._load(packagePath, module, true) + Module._load(packagePath, module, true); } catch (e) { - console.error('App threw an error during load') - console.error(e.stack || e) - throw e + console.error('App threw an error during load'); + console.error(e.stack || e); + throw e; } } function showErrorMessage (message: string) { - app.focus() - dialog.showErrorBox('Error launching app', message) - process.exit(1) + app.focus(); + dialog.showErrorBox('Error launching app', message); + process.exit(1); } async function loadApplicationByURL (appUrl: string) { - const { loadURL } = await import('./default_app') - loadURL(appUrl) + const { loadURL } = await import('./default_app'); + loadURL(appUrl); } async function loadApplicationByFile (appPath: string) { - const { loadFile } = await import('./default_app') - loadFile(appPath) + const { loadFile } = await import('./default_app'); + loadFile(appPath); } function startRepl () { if (process.platform === 'win32') { - console.error('Electron REPL not currently supported on Windows') - process.exit(1) + console.error('Electron REPL not currently supported on Windows'); + process.exit(1); } // prevent quitting - app.on('window-all-closed', () => {}) + app.on('window-all-closed', () => {}); - const repl = require('repl') + const repl = require('repl'); repl.start('> ').on('exit', () => { - process.exit(0) - }) + process.exit(0); + }); } // Start the specified app if there is one specified in command line, otherwise // start the default app. if (option.file && !option.webdriver) { - const file = option.file - const protocol = url.parse(file).protocol - const extension = path.extname(file) + const file = option.file; + const protocol = url.parse(file).protocol; + const extension = path.extname(file); if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') { - loadApplicationByURL(file) + loadApplicationByURL(file); } else if (extension === '.html' || extension === '.htm') { - loadApplicationByFile(path.resolve(file)) + loadApplicationByFile(path.resolve(file)); } else { - loadApplicationPackage(file) + loadApplicationPackage(file); } } else if (option.version) { - console.log('v' + process.versions.electron) - process.exit(0) + console.log('v' + process.versions.electron); + process.exit(0); } else if (option.abi) { - console.log(process.versions.modules) - process.exit(0) + console.log(process.versions.modules); + process.exit(0); } else if (option.interactive) { - startRepl() + startRepl(); } else { if (!option.noHelp) { const welcomeMessage = ` @@ -192,10 +192,10 @@ Options: -i, --interactive Open a REPL to the main process. -r, --require Module to preload (option can be repeated). -v, --version Print the version. - -a, --abi Print the Node ABI version.` + -a, --abi Print the Node ABI version.`; - console.log(welcomeMessage) + console.log(welcomeMessage); } - loadApplicationByFile('index.html') + loadApplicationByFile('index.html'); } diff --git a/default_app/preload.ts b/default_app/preload.ts index 855aa26ca3ba9..b18130a1d0f73 100644 --- a/default_app/preload.ts +++ b/default_app/preload.ts @@ -1,53 +1,53 @@ -import { ipcRenderer, contextBridge } from 'electron' +import { ipcRenderer, contextBridge } from 'electron'; async function getOcticonSvg (name: string) { try { - const response = await fetch(`octicon/${name}.svg`) - const div = document.createElement('div') - div.innerHTML = await response.text() - return div + const response = await fetch(`octicon/${name}.svg`); + const div = document.createElement('div'); + div.innerHTML = await response.text(); + return div; } catch { - return null + return null; } } async function loadSVG (element: HTMLSpanElement) { for (const cssClass of element.classList) { if (cssClass.startsWith('octicon-')) { - const icon = await getOcticonSvg(cssClass.substr(8)) + const icon = await getOcticonSvg(cssClass.substr(8)); if (icon) { for (const elemClass of element.classList) { - icon.classList.add(elemClass) + icon.classList.add(elemClass); } - element.before(icon) - element.remove() - break + element.before(icon); + element.remove(); + break; } } } } async function initialize () { - const electronPath = await ipcRenderer.invoke('bootstrap') + const electronPath = await ipcRenderer.invoke('bootstrap'); function replaceText (selector: string, text: string) { - const element = document.querySelector(selector) + const element = document.querySelector(selector); if (element) { - element.innerText = text + element.innerText = text; } } - replaceText('.electron-version', `Electron v${process.versions.electron}`) - replaceText('.chrome-version', `Chromium v${process.versions.chrome}`) - replaceText('.node-version', `Node v${process.versions.node}`) - replaceText('.v8-version', `v8 v${process.versions.v8}`) - replaceText('.command-example', `${electronPath} path-to-app`) + replaceText('.electron-version', `Electron v${process.versions.electron}`); + replaceText('.chrome-version', `Chromium v${process.versions.chrome}`); + replaceText('.node-version', `Node v${process.versions.node}`); + replaceText('.v8-version', `v8 v${process.versions.v8}`); + replaceText('.command-example', `${electronPath} path-to-app`); for (const element of document.querySelectorAll('.octicon')) { - loadSVG(element) + loadSVG(element); } } contextBridge.exposeInMainWorld('electronDefaultApp', { initialize -}) +}); diff --git a/lib/browser/api/app.ts b/lib/browser/api/app.ts index 0c155333226b1..e4b6a94937783 100644 --- a/lib/browser/api/app.ts +++ b/lib/browser/api/app.ts @@ -1,44 +1,44 @@ -import * as fs from 'fs' -import * as path from 'path' +import * as fs from 'fs'; +import * as path from 'path'; -import { deprecate, Menu } from 'electron' -import { EventEmitter } from 'events' +import { deprecate, Menu } from 'electron'; +import { EventEmitter } from 'events'; -const bindings = process.electronBinding('app') -const commandLine = process.electronBinding('command_line') -const { app, App } = bindings +const bindings = process.electronBinding('app'); +const commandLine = process.electronBinding('command_line'); +const { app, App } = bindings; // Only one app object permitted. -export default app +export default app; -let dockMenu: Electron.Menu | null = null +let dockMenu: Electron.Menu | null = null; // App is an EventEmitter. -Object.setPrototypeOf(App.prototype, EventEmitter.prototype) -EventEmitter.call(app as any) +Object.setPrototypeOf(App.prototype, EventEmitter.prototype); +EventEmitter.call(app as any); // Properties. -const nativeASGetter = app.isAccessibilitySupportEnabled -const nativeASSetter = app.setAccessibilitySupportEnabled +const nativeASGetter = app.isAccessibilitySupportEnabled; +const nativeASSetter = app.setAccessibilitySupportEnabled; Object.defineProperty(App.prototype, 'accessibilitySupportEnabled', { get: () => nativeASGetter.call(app), set: (enabled) => nativeASSetter.call(app, enabled) -}) +}); -const nativeBCGetter = app.getBadgeCount -const nativeBCSetter = app.setBadgeCount +const nativeBCGetter = app.getBadgeCount; +const nativeBCSetter = app.setBadgeCount; Object.defineProperty(App.prototype, 'badgeCount', { get: () => nativeBCGetter.call(app), set: (count) => nativeBCSetter.call(app, count) -}) +}); -const nativeNGetter = app.getName -const nativeNSetter = app.setName +const nativeNGetter = app.getName; +const nativeNSetter = app.setName; Object.defineProperty(App.prototype, 'name', { get: () => nativeNGetter.call(app), set: (name) => nativeNSetter.call(app, name) -}) +}); Object.assign(app, { commandLine: { @@ -47,98 +47,98 @@ Object.assign(app, { appendSwitch: (theSwitch: string, value?: string) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)), appendArgument: (arg: string) => commandLine.appendArgument(String(arg)) } as Electron.CommandLine -}) +}); // we define this here because it'd be overly complicated to // do in native land Object.defineProperty(app, 'applicationMenu', { get () { - return Menu.getApplicationMenu() + return Menu.getApplicationMenu(); }, set (menu: Electron.Menu | null) { - return Menu.setApplicationMenu(menu) + return Menu.setApplicationMenu(menu); } -}) +}); App.prototype.isPackaged = (() => { - const execFile = path.basename(process.execPath).toLowerCase() + const execFile = path.basename(process.execPath).toLowerCase(); if (process.platform === 'win32') { - return execFile !== 'electron.exe' + return execFile !== 'electron.exe'; } - return execFile !== 'electron' -})() + return execFile !== 'electron'; +})(); app._setDefaultAppPaths = (packagePath) => { // Set the user path according to application's name. - app.setPath('userData', path.join(app.getPath('appData'), app.name!)) - app.setPath('userCache', path.join(app.getPath('cache'), app.name!)) - app.setAppPath(packagePath) + app.setPath('userData', path.join(app.getPath('appData'), app.name!)); + app.setPath('userCache', path.join(app.getPath('cache'), app.name!)); + app.setAppPath(packagePath); // Add support for --user-data-dir= if (app.commandLine.hasSwitch('user-data-dir')) { - const userDataDir = app.commandLine.getSwitchValue('user-data-dir') - if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir) + const userDataDir = app.commandLine.getSwitchValue('user-data-dir'); + if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir); } -} +}; if (process.platform === 'darwin') { - const setDockMenu = app.dock!.setMenu + const setDockMenu = app.dock!.setMenu; app.dock!.setMenu = (menu) => { - dockMenu = menu - setDockMenu(menu) - } - app.dock!.getMenu = () => dockMenu + dockMenu = menu; + setDockMenu(menu); + }; + app.dock!.getMenu = () => dockMenu; } if (process.platform === 'linux') { - const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m - const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m + const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m; + const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m; const getStatus = (pid: number) => { try { - return fs.readFileSync(`/proc/${pid}/status`, 'utf8') + return fs.readFileSync(`/proc/${pid}/status`, 'utf8'); } catch { - return '' + return ''; } - } + }; const getEntry = (file: string, pattern: RegExp) => { - const match = file.match(pattern) - return match ? parseInt(match[1], 10) : 0 - } + const match = file.match(pattern); + return match ? parseInt(match[1], 10) : 0; + }; const getProcessMemoryInfo = (pid: number) => { - const file = getStatus(pid) + const file = getStatus(pid); return { workingSetSize: getEntry(file, patternVmRSS), peakWorkingSetSize: getEntry(file, patternVmHWM) - } - } + }; + }; - const nativeFn = app.getAppMetrics + const nativeFn = app.getAppMetrics; app.getAppMetrics = () => { - const metrics = nativeFn.call(app) + const metrics = nativeFn.call(app); for (const metric of metrics) { - metric.memory = getProcessMemoryInfo(metric.pid) + metric.memory = getProcessMemoryInfo(metric.pid); } - return metrics - } + return metrics; + }; } // Routes the events to webContents. -const events = ['certificate-error', 'select-client-certificate'] +const events = ['certificate-error', 'select-client-certificate']; for (const name of events) { app.on(name as 'certificate-error', (event, webContents, ...args: any[]) => { - webContents.emit(name, event, ...args) - }) + webContents.emit(name, event, ...args); + }); } // Deprecate allowRendererProcessReuse but only if they set it to false, no need to log if // they are setting it to true -deprecate.removeProperty(app, 'allowRendererProcessReuse', [false]) +deprecate.removeProperty(app, 'allowRendererProcessReuse', [false]); // Wrappers for native classes. -const { DownloadItem } = process.electronBinding('download_item') -Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype) +const { DownloadItem } = process.electronBinding('download_item'); +Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype); diff --git a/lib/browser/api/auto-updater.js b/lib/browser/api/auto-updater.js index db3e8da158c3d..3435247a68ccc 100644 --- a/lib/browser/api/auto-updater.js +++ b/lib/browser/api/auto-updater.js @@ -1,7 +1,7 @@ -'use strict' +'use strict'; if (process.platform === 'win32') { - module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win') + module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win'); } else { - module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native') + module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native'); } diff --git a/lib/browser/api/auto-updater/auto-updater-native.js b/lib/browser/api/auto-updater/auto-updater-native.js index 666cd758c8871..ded4a36681c3f 100644 --- a/lib/browser/api/auto-updater/auto-updater-native.js +++ b/lib/browser/api/auto-updater/auto-updater-native.js @@ -1,10 +1,10 @@ -'use strict' +'use strict'; -const EventEmitter = require('events').EventEmitter -const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater') +const EventEmitter = require('events').EventEmitter; +const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater'); // AutoUpdater is an EventEmitter. -Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype) -EventEmitter.call(autoUpdater) +Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype); +EventEmitter.call(autoUpdater); -module.exports = autoUpdater +module.exports = autoUpdater; diff --git a/lib/browser/api/auto-updater/auto-updater-win.js b/lib/browser/api/auto-updater/auto-updater-win.js index cb8e6a0db51c8..93e7a94ac58e7 100644 --- a/lib/browser/api/auto-updater/auto-updater-win.js +++ b/lib/browser/api/auto-updater/auto-updater-win.js @@ -1,74 +1,74 @@ -'use strict' +'use strict'; -const { app } = require('electron') -const { EventEmitter } = require('events') -const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win') +const { app } = require('electron'); +const { EventEmitter } = require('events'); +const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win'); class AutoUpdater extends EventEmitter { quitAndInstall () { if (!this.updateAvailable) { - return this.emitError('No update available, can\'t quit and install') + return this.emitError('No update available, can\'t quit and install'); } - squirrelUpdate.processStart() - app.quit() + squirrelUpdate.processStart(); + app.quit(); } getFeedURL () { - return this.updateURL + return this.updateURL; } setFeedURL (options) { - let updateURL + let updateURL; if (typeof options === 'object') { if (typeof options.url === 'string') { - updateURL = options.url + updateURL = options.url; } else { - throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call') + throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call'); } } else if (typeof options === 'string') { - updateURL = options + updateURL = options; } else { - throw new Error('Expected an options object with a \'url\' property to be provided') + throw new Error('Expected an options object with a \'url\' property to be provided'); } - this.updateURL = updateURL + this.updateURL = updateURL; } checkForUpdates () { if (!this.updateURL) { - return this.emitError('Update URL is not set') + return this.emitError('Update URL is not set'); } if (!squirrelUpdate.supported()) { - return this.emitError('Can not find Squirrel') + return this.emitError('Can not find Squirrel'); } - this.emit('checking-for-update') + this.emit('checking-for-update'); squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => { if (error != null) { - return this.emitError(error) + return this.emitError(error); } if (update == null) { - return this.emit('update-not-available') + return this.emit('update-not-available'); } - this.updateAvailable = true - this.emit('update-available') + this.updateAvailable = true; + this.emit('update-available'); squirrelUpdate.update(this.updateURL, (error) => { if (error != null) { - return this.emitError(error) + return this.emitError(error); } - const { releaseNotes, version } = update + const { releaseNotes, version } = update; // Date is not available on Windows, so fake it. - const date = new Date() + const date = new Date(); this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => { - this.quitAndInstall() - }) - }) - }) + this.quitAndInstall(); + }); + }); + }); } // Private: Emit both error object and message, this is to keep compatibility // with Old APIs. emitError (message) { - this.emit('error', new Error(message), message) + this.emit('error', new Error(message), message); } } -module.exports = new AutoUpdater() +module.exports = new AutoUpdater(); diff --git a/lib/browser/api/auto-updater/squirrel-update-win.js b/lib/browser/api/auto-updater/squirrel-update-win.js index b25f8eb64debf..fd237620617df 100644 --- a/lib/browser/api/auto-updater/squirrel-update-win.js +++ b/lib/browser/api/auto-updater/squirrel-update-win.js @@ -1,24 +1,24 @@ -'use strict' +'use strict'; -const fs = require('fs') -const path = require('path') -const spawn = require('child_process').spawn +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) +const appFolder = path.dirname(process.execPath); // i.e. my-app/Update.exe -const updateExe = path.resolve(appFolder, '..', 'Update.exe') -const exeName = path.basename(process.execPath) -let spawnedArgs = [] -let spawnedProcess +const updateExe = path.resolve(appFolder, '..', 'Update.exe'); +const exeName = path.basename(process.execPath); +let spawnedArgs = []; +let spawnedProcess; -const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]) +const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]); // Spawn a command and invoke the callback when it completes with an error // and the output from standard out. const spawnUpdate = function (args, detached, callback) { - let error, errorEmitted, stderr, stdout + let error, errorEmitted, stderr, stdout; try { // Ensure we don't spawn multiple squirrel processes @@ -28,92 +28,92 @@ const spawnUpdate = function (args, detached, callback) { if (spawnedProcess && !isSameArgs(args)) { // Disabled for backwards compatibility: // eslint-disable-next-line standard/no-callback-literal - return callback(`AutoUpdater process with arguments ${args} is already running`) + return callback(`AutoUpdater process with arguments ${args} is already running`); } else if (!spawnedProcess) { spawnedProcess = spawn(updateExe, args, { detached: detached, windowsHide: true - }) - spawnedArgs = args || [] + }); + spawnedArgs = args || []; } } catch (error1) { - error = error1 + error = error1; // Shouldn't happen, but still guard it. process.nextTick(function () { - return callback(error) - }) - return + return callback(error); + }); + return; } - stdout = '' - stderr = '' + stdout = ''; + stderr = ''; - spawnedProcess.stdout.on('data', (data) => { stdout += data }) - spawnedProcess.stderr.on('data', (data) => { stderr += data }) + spawnedProcess.stdout.on('data', (data) => { stdout += data; }); + spawnedProcess.stderr.on('data', (data) => { stderr += data; }); - errorEmitted = false + errorEmitted = false; spawnedProcess.on('error', (error) => { - errorEmitted = true - callback(error) - }) + errorEmitted = true; + callback(error); + }); return spawnedProcess.on('exit', function (code, signal) { - spawnedProcess = undefined - spawnedArgs = [] + spawnedProcess = undefined; + spawnedArgs = []; // We may have already emitted an error. if (errorEmitted) { - return + return; } // Process terminated with error. if (code !== 0) { // Disabled for backwards compatibility: // eslint-disable-next-line standard/no-callback-literal - return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`) + return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`); } // Success. - callback(null, stdout) - }) -} + callback(null, stdout); + }); +}; // Start an instance of the installed app. exports.processStart = function () { - return spawnUpdate(['--processStartAndWait', exeName], true, function () {}) -} + return spawnUpdate(['--processStartAndWait', exeName], true, function () {}); +}; // Download the releases specified by the URL and write new results to stdout. exports.checkForUpdate = function (updateURL, callback) { return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) { - let ref, ref1, update + let ref, ref1, update; if (error != null) { - return callback(error) + return callback(error); } try { // Last line of output is the JSON details about the releases - const json = stdout.trim().split('\n').pop() - update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : undefined : undefined : undefined + const json = stdout.trim().split('\n').pop(); + update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : undefined : undefined : undefined; } catch { // Disabled for backwards compatibility: // eslint-disable-next-line standard/no-callback-literal - return callback(`Invalid result:\n${stdout}`) + return callback(`Invalid result:\n${stdout}`); } - return callback(null, update) - }) -} + return callback(null, update); + }); +}; // Update the application to the latest remote version specified by URL. exports.update = function (updateURL, callback) { - return spawnUpdate(['--update', updateURL], false, callback) -} + return spawnUpdate(['--update', updateURL], false, callback); +}; // Is the Update.exe installed with the current application? exports.supported = function () { try { - fs.accessSync(updateExe, fs.R_OK) - return true + fs.accessSync(updateExe, fs.R_OK); + return true; } catch { - return false + return false; } -} +}; diff --git a/lib/browser/api/browser-view.js b/lib/browser/api/browser-view.js index 5471b6359313e..2a57f5709d902 100644 --- a/lib/browser/api/browser-view.js +++ b/lib/browser/api/browser-view.js @@ -1,16 +1,16 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { BrowserView } = process.electronBinding('browser_view') +const { EventEmitter } = require('events'); +const { BrowserView } = process.electronBinding('browser_view'); -Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype) +Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype); BrowserView.fromWebContents = (webContents) => { for (const view of BrowserView.getAllViews()) { - if (view.webContents.equal(webContents)) return view + if (view.webContents.equal(webContents)) return view; } - return null -} + return null; +}; -module.exports = BrowserView +module.exports = BrowserView; diff --git a/lib/browser/api/browser-window.js b/lib/browser/api/browser-window.js index 957f65ed9aa95..403f5e7bfd5b9 100644 --- a/lib/browser/api/browser-window.js +++ b/lib/browser/api/browser-window.js @@ -1,29 +1,29 @@ -'use strict' +'use strict'; -const electron = require('electron') -const { WebContentsView, TopLevelWindow, deprecate } = electron -const { BrowserWindow } = process.electronBinding('window') +const electron = require('electron'); +const { WebContentsView, TopLevelWindow, deprecate } = electron; +const { BrowserWindow } = process.electronBinding('window'); -Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype) +Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype); BrowserWindow.prototype._init = function () { // Call parent class's _init. - TopLevelWindow.prototype._init.call(this) + TopLevelWindow.prototype._init.call(this); // Avoid recursive require. - const { app } = electron + const { app } = electron; // Create WebContentsView. - this.setContentView(new WebContentsView(this.webContents)) + this.setContentView(new WebContentsView(this.webContents)); - const nativeSetBounds = this.setBounds + const nativeSetBounds = this.setBounds; this.setBounds = (bounds, ...opts) => { bounds = { ...this.getBounds(), ...bounds - } - nativeSetBounds.call(this, bounds, ...opts) - } + }; + nativeSetBounds.call(this, bounds, ...opts); + }; // 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 @@ -34,172 +34,172 @@ BrowserWindow.prototype._init = function () { // Finder, we still do it on all platforms in case of other bugs we don't // know. this.webContents.once('load-url', function () { - this.focus() - }) + this.focus(); + }); // Redirect focus/blur event to app instance too. this.on('blur', (event) => { - app.emit('browser-window-blur', event, this) - }) + app.emit('browser-window-blur', event, this); + }); this.on('focus', (event) => { - app.emit('browser-window-focus', event, this) - }) + app.emit('browser-window-focus', event, this); + }); // Subscribe to visibilityState changes and pass to renderer process. - let isVisible = this.isVisible() && !this.isMinimized() + let isVisible = this.isVisible() && !this.isMinimized(); const visibilityChanged = () => { - const newState = this.isVisible() && !this.isMinimized() + const newState = this.isVisible() && !this.isMinimized(); if (isVisible !== newState) { - isVisible = newState - const visibilityState = isVisible ? 'visible' : 'hidden' - this.webContents.emit('-window-visibility-change', visibilityState) + isVisible = newState; + const visibilityState = isVisible ? 'visible' : 'hidden'; + this.webContents.emit('-window-visibility-change', visibilityState); } - } + }; - const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'] + const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']; for (const event of visibilityEvents) { - this.on(event, visibilityChanged) + this.on(event, visibilityChanged); } // Notify the creation of the window. - const event = process.electronBinding('event').createEmpty() - app.emit('browser-window-created', event, this) + const event = process.electronBinding('event').createEmpty(); + app.emit('browser-window-created', event, this); Object.defineProperty(this, 'devToolsWebContents', { enumerable: true, configurable: false, get () { - return this.webContents.devToolsWebContents + return this.webContents.devToolsWebContents; } - }) + }); // Properties Object.defineProperty(this, 'autoHideMenuBar', { get: () => this.isMenuBarAutoHide(), set: (autoHide) => this.setAutoHideMenuBar(autoHide) - }) + }); Object.defineProperty(this, 'minimizable', { get: () => this.isMinimizable(), set: (min) => this.setMinimizable(min) - }) + }); Object.defineProperty(this, 'maximizable', { get: () => this.isMaximizable(), set: (max) => this.setMaximizable(max) - }) + }); Object.defineProperty(this, 'resizable', { get: () => this.isResizable(), set: (res) => this.setResizable(res) - }) + }); Object.defineProperty(this, 'fullScreenable', { get: () => this.isFullScreenable(), set: (full) => this.setFullScreenable(full) - }) + }); Object.defineProperty(this, 'closable', { get: () => this.isClosable(), set: (close) => this.setClosable(close) - }) + }); Object.defineProperty(this, 'movable', { get: () => this.isMovable(), set: (move) => this.setMovable(move) - }) -} + }); +}; const isBrowserWindow = (win) => { - return win && win.constructor.name === 'BrowserWindow' -} + return win && win.constructor.name === 'BrowserWindow'; +}; BrowserWindow.fromId = (id) => { - const win = TopLevelWindow.fromId(id) - return isBrowserWindow(win) ? win : null -} + const win = TopLevelWindow.fromId(id); + return isBrowserWindow(win) ? win : null; +}; BrowserWindow.getAllWindows = () => { - return TopLevelWindow.getAllWindows().filter(isBrowserWindow) -} + return TopLevelWindow.getAllWindows().filter(isBrowserWindow); +}; BrowserWindow.getFocusedWindow = () => { for (const window of BrowserWindow.getAllWindows()) { - if (window.isFocused() || window.isDevToolsFocused()) return window + if (window.isFocused() || window.isDevToolsFocused()) return window; } - return null -} + return null; +}; BrowserWindow.fromWebContents = (webContents) => { for (const window of BrowserWindow.getAllWindows()) { - if (window.webContents && window.webContents.equal(webContents)) return window + if (window.webContents && window.webContents.equal(webContents)) return window; } - return null -} + return null; +}; BrowserWindow.fromBrowserView = (browserView) => { for (const window of BrowserWindow.getAllWindows()) { - if (window.getBrowserView() === browserView) return window + if (window.getBrowserView() === browserView) return window; } - return null -} + return null; +}; // Helpers. Object.assign(BrowserWindow.prototype, { loadURL (...args) { - return this.webContents.loadURL(...args) + return this.webContents.loadURL(...args); }, getURL (...args) { - return this.webContents.getURL() + return this.webContents.getURL(); }, loadFile (...args) { - return this.webContents.loadFile(...args) + return this.webContents.loadFile(...args); }, reload (...args) { - return this.webContents.reload(...args) + return this.webContents.reload(...args); }, send (...args) { - return this.webContents.send(...args) + return this.webContents.send(...args); }, openDevTools (...args) { - return this.webContents.openDevTools(...args) + return this.webContents.openDevTools(...args); }, closeDevTools () { - return this.webContents.closeDevTools() + return this.webContents.closeDevTools(); }, isDevToolsOpened () { - return this.webContents.isDevToolsOpened() + return this.webContents.isDevToolsOpened(); }, isDevToolsFocused () { - return this.webContents.isDevToolsFocused() + return this.webContents.isDevToolsFocused(); }, toggleDevTools () { - return this.webContents.toggleDevTools() + return this.webContents.toggleDevTools(); }, inspectElement (...args) { - return this.webContents.inspectElement(...args) + return this.webContents.inspectElement(...args); }, inspectSharedWorker () { - return this.webContents.inspectSharedWorker() + return this.webContents.inspectSharedWorker(); }, inspectServiceWorker () { - return this.webContents.inspectServiceWorker() + return this.webContents.inspectServiceWorker(); }, showDefinitionForSelection () { - return this.webContents.showDefinitionForSelection() + return this.webContents.showDefinitionForSelection(); }, capturePage (...args) { - return this.webContents.capturePage(...args) + return this.webContents.capturePage(...args); }, setTouchBar (touchBar) { - electron.TouchBar._setOnWindow(touchBar, this) + electron.TouchBar._setOnWindow(touchBar, this); }, setBackgroundThrottling (allowed) { - this.webContents.setBackgroundThrottling(allowed) + this.webContents.setBackgroundThrottling(allowed); } -}) +}); -module.exports = BrowserWindow +module.exports = BrowserWindow; diff --git a/lib/browser/api/content-tracing.js b/lib/browser/api/content-tracing.js index 64565f72db3ce..9aa9792954baf 100644 --- a/lib/browser/api/content-tracing.js +++ b/lib/browser/api/content-tracing.js @@ -1,2 +1,2 @@ -'use strict' -module.exports = process.electronBinding('content_tracing') +'use strict'; +module.exports = process.electronBinding('content_tracing'); diff --git a/lib/browser/api/crash-reporter.js b/lib/browser/api/crash-reporter.js index d5f8f8480dd81..cb27917661631 100644 --- a/lib/browser/api/crash-reporter.js +++ b/lib/browser/api/crash-reporter.js @@ -1,12 +1,12 @@ -'use strict' +'use strict'; -const CrashReporter = require('@electron/internal/common/crash-reporter') -const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init') +const CrashReporter = require('@electron/internal/common/crash-reporter'); +const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init'); class CrashReporterMain extends CrashReporter { init (options) { - return crashReporterInit(options) + return crashReporterInit(options); } } -module.exports = new CrashReporterMain() +module.exports = new CrashReporterMain(); diff --git a/lib/browser/api/dialog.js b/lib/browser/api/dialog.js index 2bdbd36bcdab9..24285704843af 100644 --- a/lib/browser/api/dialog.js +++ b/lib/browser/api/dialog.js @@ -1,13 +1,13 @@ -'use strict' +'use strict'; -const { app, BrowserWindow, deprecate } = require('electron') -const binding = process.electronBinding('dialog') -const v8Util = process.electronBinding('v8_util') +const { app, BrowserWindow, deprecate } = require('electron'); +const binding = process.electronBinding('dialog'); +const v8Util = process.electronBinding('v8_util'); const DialogType = { OPEN: 'OPEN', SAVE: 'SAVE' -} +}; const saveFileDialogProperties = { createDirectory: 1 << 0, @@ -15,7 +15,7 @@ const saveFileDialogProperties = { treatPackageAsDirectory: 1 << 2, showOverwriteConfirmation: 1 << 3, dontAddToRecent: 1 << 4 -} +}; const openFileDialogProperties = { openFile: 1 << 0, @@ -27,15 +27,15 @@ const openFileDialogProperties = { noResolveAliases: 1 << 6, // macOS treatPackageAsDirectory: 1 << 7, // macOS dontAddToRecent: 1 << 8 // Windows -} +}; const normalizeAccessKey = (text) => { - if (typeof text !== 'string') return text + if (typeof text !== 'string') return text; // macOS does not have access keys so remove single ampersands // and replace double ampersands with a single ampersand if (process.platform === 'darwin') { - return text.replace(/&(&?)/g, '$1') + return text.replace(/&(&?)/g, '$1'); } // Linux uses a single underscore as an access key prefix so escape @@ -44,41 +44,41 @@ const normalizeAccessKey = (text) => { // a single underscore if (process.platform === 'linux') { return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => { - if (after === '&') return after - return `_${after}` - }) + if (after === '&') return after; + return `_${after}`; + }); } - return text -} + return text; +}; const checkAppInitialized = function () { if (!app.isReady()) { - throw new Error('dialog module can only be used after app is ready') + throw new Error('dialog module can only be used after app is ready'); } -} +}; const setupDialogProperties = (type, properties) => { - const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties - let dialogProperties = 0 + const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties; + let dialogProperties = 0; for (const prop in dialogPropertiesTypes) { if (properties.includes(prop)) { - dialogProperties |= dialogPropertiesTypes[prop] + dialogProperties |= dialogPropertiesTypes[prop]; } } - return dialogProperties -} + return dialogProperties; +}; const saveDialog = (sync, window, options) => { - checkAppInitialized() + checkAppInitialized(); if (window && window.constructor !== BrowserWindow) { - options = window - window = null + options = window; + window = null; } - if (options == null) options = { title: 'Save' } + if (options == null) options = { title: 'Save' }; const { buttonLabel = '', @@ -90,33 +90,33 @@ const saveDialog = (sync, window, options) => { securityScopedBookmarks = false, nameFieldLabel = '', showsTagField = true - } = options + } = options; - if (typeof title !== 'string') throw new TypeError('Title must be a string') - if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string') - if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string') - if (typeof message !== 'string') throw new TypeError('Message must be a string') - if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string') + if (typeof title !== 'string') throw new TypeError('Title must be a string'); + if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string'); + if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string'); + if (typeof message !== 'string') throw new TypeError('Message must be a string'); + if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string'); - const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window } - settings.properties = setupDialogProperties(DialogType.SAVE, properties) + const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window }; + settings.properties = setupDialogProperties(DialogType.SAVE, properties); - return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings) -} + return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings); +}; const openDialog = (sync, window, options) => { - checkAppInitialized() + checkAppInitialized(); if (window && window.constructor !== BrowserWindow) { - options = window - window = null + options = window; + window = null; } if (options == null) { options = { title: 'Open', properties: ['openFile'] - } + }; } const { @@ -127,33 +127,33 @@ const openDialog = (sync, window, options) => { title = '', message = '', securityScopedBookmarks = false - } = options + } = options; - if (!Array.isArray(properties)) throw new TypeError('Properties must be an array') + if (!Array.isArray(properties)) throw new TypeError('Properties must be an array'); - if (typeof title !== 'string') throw new TypeError('Title must be a string') - if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string') - if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string') - if (typeof message !== 'string') throw new TypeError('Message must be a string') + if (typeof title !== 'string') throw new TypeError('Title must be a string'); + if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string'); + if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string'); + if (typeof message !== 'string') throw new TypeError('Message must be a string'); - const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window } - settings.properties = setupDialogProperties(DialogType.OPEN, properties) + const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }; + settings.properties = setupDialogProperties(DialogType.OPEN, properties); - return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings) -} + return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings); +}; const messageBox = (sync, window, options) => { - checkAppInitialized() + checkAppInitialized(); if (window && window.constructor !== BrowserWindow) { - options = window - window = null + options = window; + window = null; } - if (options == null) options = { type: 'none' } + if (options == null) options = { type: 'none' }; - const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'] - const messageBoxOptions = { noLink: 1 << 0 } + const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']; + const messageBoxOptions = { noLink: 1 << 0 }; let { buttons = [], @@ -167,32 +167,32 @@ const messageBox = (sync, window, options) => { message = '', title = '', type = 'none' - } = options - - const messageBoxType = messageBoxTypes.indexOf(type) - if (messageBoxType === -1) throw new TypeError('Invalid message box type') - if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array') - if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey) - if (typeof title !== 'string') throw new TypeError('Title must be a string') - if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean') - if (typeof message !== 'string') throw new TypeError('Message must be a string') - if (typeof detail !== 'string') throw new TypeError('Detail must be a string') - if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string') - - checkboxChecked = !!checkboxChecked + } = options; + + const messageBoxType = messageBoxTypes.indexOf(type); + if (messageBoxType === -1) throw new TypeError('Invalid message box type'); + if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array'); + if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey); + if (typeof title !== 'string') throw new TypeError('Title must be a string'); + if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean'); + if (typeof message !== 'string') throw new TypeError('Message must be a string'); + if (typeof detail !== 'string') throw new TypeError('Detail must be a string'); + if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string'); + + checkboxChecked = !!checkboxChecked; if (checkboxChecked && !checkboxLabel) { - throw new Error('checkboxChecked requires that checkboxLabel also be passed') + throw new Error('checkboxChecked requires that checkboxLabel also be passed'); } // Choose a default button to get selected when dialog is cancelled. if (cancelId == null) { // If the defaultId is set to 0, ensure the cancel button is a different index (1) - cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0 + cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0; for (let i = 0; i < buttons.length; i++) { - const text = buttons[i].toLowerCase() + const text = buttons[i].toLowerCase(); if (text === 'cancel' || text === 'no') { - cancelId = i - break + cancelId = i; + break; } } } @@ -210,57 +210,57 @@ const messageBox = (sync, window, options) => { checkboxLabel, checkboxChecked, icon - } + }; if (sync) { - return binding.showMessageBoxSync(settings) + return binding.showMessageBoxSync(settings); } else { - return binding.showMessageBox(settings) + return binding.showMessageBox(settings); } -} +}; module.exports = { showOpenDialog: function (window, options) { - return openDialog(false, window, options) + return openDialog(false, window, options); }, showOpenDialogSync: function (window, options) { - return openDialog(true, window, options) + return openDialog(true, window, options); }, showSaveDialog: function (window, options) { - return saveDialog(false, window, options) + return saveDialog(false, window, options); }, showSaveDialogSync: function (window, options) { - return saveDialog(true, window, options) + return saveDialog(true, window, options); }, showMessageBox: function (window, options) { - return messageBox(false, window, options) + return messageBox(false, window, options); }, showMessageBoxSync: function (window, options) { - return messageBox(true, window, options) + return messageBox(true, window, options); }, showErrorBox: function (...args) { - return binding.showErrorBox(...args) + return binding.showErrorBox(...args); }, showCertificateTrustDialog: function (window, options) { - if (window && window.constructor !== BrowserWindow) options = window + if (window && window.constructor !== BrowserWindow) options = window; if (options == null || typeof options !== 'object') { - throw new TypeError('options must be an object') + throw new TypeError('options must be an object'); } - const { certificate, message = '' } = options + const { certificate, message = '' } = options; if (certificate == null || typeof certificate !== 'object') { - throw new TypeError('certificate must be an object') + throw new TypeError('certificate must be an object'); } - if (typeof message !== 'string') throw new TypeError('message must be a string') + if (typeof message !== 'string') throw new TypeError('message must be a string'); - return binding.showCertificateTrustDialog(window, certificate, message) + return binding.showCertificateTrustDialog(window, certificate, message); } -} +}; diff --git a/lib/browser/api/exports/electron.ts b/lib/browser/api/exports/electron.ts index e88e54c4ea28b..904de54f69836 100644 --- a/lib/browser/api/exports/electron.ts +++ b/lib/browser/api/exports/electron.ts @@ -1,8 +1,8 @@ -import { defineProperties } from '@electron/internal/common/define-properties' -import { commonModuleList } from '@electron/internal/common/api/module-list' -import { browserModuleList } from '@electron/internal/browser/api/module-list' +import { defineProperties } from '@electron/internal/common/define-properties'; +import { commonModuleList } from '@electron/internal/common/api/module-list'; +import { browserModuleList } from '@electron/internal/browser/api/module-list'; -module.exports = {} +module.exports = {}; -defineProperties(module.exports, commonModuleList) -defineProperties(module.exports, browserModuleList) +defineProperties(module.exports, commonModuleList); +defineProperties(module.exports, browserModuleList); diff --git a/lib/browser/api/global-shortcut.js b/lib/browser/api/global-shortcut.js index f1c6e21a34023..b43431a0775fc 100644 --- a/lib/browser/api/global-shortcut.js +++ b/lib/browser/api/global-shortcut.js @@ -1,3 +1,3 @@ -'use strict' +'use strict'; -module.exports = process.electronBinding('global_shortcut').globalShortcut +module.exports = process.electronBinding('global_shortcut').globalShortcut; diff --git a/lib/browser/api/in-app-purchase.js b/lib/browser/api/in-app-purchase.js index 3efde208738dc..77243f1a7f2b6 100644 --- a/lib/browser/api/in-app-purchase.js +++ b/lib/browser/api/in-app-purchase.js @@ -1,22 +1,22 @@ -'use strict' +'use strict'; -const { deprecate } = require('electron') +const { deprecate } = require('electron'); if (process.platform === 'darwin') { - const { EventEmitter } = require('events') - const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase') + const { EventEmitter } = require('events'); + const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase'); // inAppPurchase is an EventEmitter. - Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype) - EventEmitter.call(inAppPurchase) + Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype); + EventEmitter.call(inAppPurchase); - module.exports = inAppPurchase + module.exports = inAppPurchase; } else { module.exports = { purchaseProduct: (productID, quantity, callback) => { - throw new Error('The inAppPurchase module can only be used on macOS') + throw new Error('The inAppPurchase module can only be used on macOS'); }, canMakePayments: () => false, getReceiptURL: () => '' - } + }; } diff --git a/lib/browser/api/ipc-main.ts b/lib/browser/api/ipc-main.ts index c71660d45388f..20c7fb9dc5cf6 100644 --- a/lib/browser/api/ipc-main.ts +++ b/lib/browser/api/ipc-main.ts @@ -1,8 +1,8 @@ -import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl' +import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'; -const ipcMain = new IpcMainImpl() +const ipcMain = new IpcMainImpl(); // Do not throw exception when channel name is "error". -ipcMain.on('error', () => {}) +ipcMain.on('error', () => {}); -export default ipcMain +export default ipcMain; diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index 07b0e9d06eb12..ddaf6315e7db6 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const { app } = require('electron') +const { app } = require('electron'); -const isMac = process.platform === 'darwin' -const isWindows = process.platform === 'win32' -const isLinux = process.platform === 'linux' +const isMac = process.platform === 'darwin'; +const isWindows = process.platform === 'win32'; +const isLinux = process.platform === 'linux'; const roles = { about: { get label () { - return isLinux ? 'About' : `About ${app.name}` + return isLinux ? 'About' : `About ${app.name}`; } }, close: { @@ -38,7 +38,7 @@ const roles = { accelerator: 'Shift+CmdOrCtrl+R', nonNativeMacOSRole: true, windowMethod: (window) => { - window.webContents.reloadIgnoringCache() + window.webContents.reloadIgnoringCache(); } }, front: { @@ -49,7 +49,7 @@ const roles = { }, hide: { get label () { - return `Hide ${app.name}` + return `Hide ${app.name}`; }, accelerator: 'Command+H' }, @@ -77,9 +77,9 @@ const roles = { quit: { get label () { switch (process.platform) { - case 'darwin': return `Quit ${app.name}` - case 'win32': return 'Exit' - default: return 'Quit' + case 'darwin': return `Quit ${app.name}`; + case 'win32': return 'Exit'; + default: return 'Quit'; } }, accelerator: isWindows ? undefined : 'CommandOrControl+Q', @@ -101,7 +101,7 @@ const roles = { accelerator: 'CommandOrControl+0', nonNativeMacOSRole: true, webContentsMethod: (webContents) => { - webContents.zoomLevel = 0 + webContents.zoomLevel = 0; } }, selectall: { @@ -134,7 +134,7 @@ const roles = { label: 'Toggle Full Screen', accelerator: isMac ? 'Control+Command+F' : 'F11', windowMethod: (window) => { - window.setFullScreen(!window.isFullScreen()) + window.setFullScreen(!window.isFullScreen()); } }, undo: { @@ -156,7 +156,7 @@ const roles = { accelerator: 'CommandOrControl+Plus', nonNativeMacOSRole: true, webContentsMethod: (webContents) => { - webContents.zoomLevel += 0.5 + webContents.zoomLevel += 0.5; } }, zoomout: { @@ -164,13 +164,13 @@ const roles = { accelerator: 'CommandOrControl+-', nonNativeMacOSRole: true, webContentsMethod: (webContents) => { - webContents.zoomLevel -= 0.5 + webContents.zoomLevel -= 0.5; } }, // App submenu should be used for Mac only appmenu: { get label () { - return app.name + return app.name; }, submenu: [ { role: 'about' }, @@ -249,71 +249,71 @@ const roles = { ]) ] } -} +}; -exports.roleList = roles +exports.roleList = roles; const canExecuteRole = (role) => { - if (!Object.prototype.hasOwnProperty.call(roles, role)) return false - if (!isMac) return true + if (!Object.prototype.hasOwnProperty.call(roles, role)) return false; + if (!isMac) return true; // macOS handles all roles natively except for a few - return roles[role].nonNativeMacOSRole -} + return roles[role].nonNativeMacOSRole; +}; exports.getDefaultLabel = (role) => { - return Object.prototype.hasOwnProperty.call(roles, role) ? roles[role].label : '' -} + return Object.prototype.hasOwnProperty.call(roles, role) ? roles[role].label : ''; +}; exports.getDefaultAccelerator = (role) => { - if (Object.prototype.hasOwnProperty.call(roles, role)) return roles[role].accelerator -} + if (Object.prototype.hasOwnProperty.call(roles, role)) return roles[role].accelerator; +}; exports.shouldRegisterAccelerator = (role) => { - const hasRoleRegister = Object.prototype.hasOwnProperty.call(roles, role) && roles[role].registerAccelerator !== undefined - return hasRoleRegister ? roles[role].registerAccelerator : true -} + const hasRoleRegister = Object.prototype.hasOwnProperty.call(roles, role) && roles[role].registerAccelerator !== undefined; + return hasRoleRegister ? roles[role].registerAccelerator : true; +}; exports.getDefaultSubmenu = (role) => { - if (!Object.prototype.hasOwnProperty.call(roles, role)) return + if (!Object.prototype.hasOwnProperty.call(roles, role)) return; - let { submenu } = roles[role] + let { submenu } = roles[role]; // remove null items from within the submenu if (Array.isArray(submenu)) { - submenu = submenu.filter((item) => item != null) + submenu = submenu.filter((item) => item != null); } - return submenu -} + return submenu; +}; exports.execute = (role, focusedWindow, focusedWebContents) => { - if (!canExecuteRole(role)) return false + if (!canExecuteRole(role)) return false; - const { appMethod, webContentsMethod, windowMethod } = roles[role] + const { appMethod, webContentsMethod, windowMethod } = roles[role]; if (appMethod) { - app[appMethod]() - return true + app[appMethod](); + return true; } if (windowMethod && focusedWindow != null) { if (typeof windowMethod === 'function') { - windowMethod(focusedWindow) + windowMethod(focusedWindow); } else { - focusedWindow[windowMethod]() + focusedWindow[windowMethod](); } - return true + return true; } if (webContentsMethod && focusedWebContents != null) { if (typeof webContentsMethod === 'function') { - webContentsMethod(focusedWebContents) + webContentsMethod(focusedWebContents); } else { - focusedWebContents[webContentsMethod]() + focusedWebContents[webContentsMethod](); } - return true + return true; } - return false -} + return false; +}; diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 211879ec673f4..171f2ead068e8 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -1,87 +1,87 @@ -'use strict' +'use strict'; -const roles = require('@electron/internal/browser/api/menu-item-roles') +const roles = require('@electron/internal/browser/api/menu-item-roles'); -let nextCommandId = 0 +let nextCommandId = 0; const MenuItem = function (options) { - const { Menu } = require('electron') + const { Menu } = require('electron'); // Preserve extra fields specified by user for (const key in options) { - if (!(key in this)) this[key] = options[key] + if (!(key in this)) this[key] = options[key]; } if (typeof this.role === 'string' || this.role instanceof String) { - this.role = this.role.toLowerCase() + this.role = this.role.toLowerCase(); } - this.submenu = this.submenu || roles.getDefaultSubmenu(this.role) + this.submenu = this.submenu || roles.getDefaultSubmenu(this.role); if (this.submenu != null && this.submenu.constructor !== Menu) { - this.submenu = Menu.buildFromTemplate(this.submenu) + this.submenu = Menu.buildFromTemplate(this.submenu); } if (this.type == null && this.submenu != null) { - this.type = 'submenu' + this.type = 'submenu'; } if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) { - throw new Error('Invalid submenu') + throw new Error('Invalid submenu'); } - this.overrideReadOnlyProperty('type', 'normal') - this.overrideReadOnlyProperty('role') - this.overrideReadOnlyProperty('accelerator') - this.overrideReadOnlyProperty('icon') - this.overrideReadOnlyProperty('submenu') + this.overrideReadOnlyProperty('type', 'normal'); + this.overrideReadOnlyProperty('role'); + this.overrideReadOnlyProperty('accelerator'); + this.overrideReadOnlyProperty('icon'); + this.overrideReadOnlyProperty('submenu'); - this.overrideProperty('label', roles.getDefaultLabel(this.role)) - this.overrideProperty('sublabel', '') - this.overrideProperty('toolTip', '') - this.overrideProperty('enabled', true) - this.overrideProperty('visible', true) - this.overrideProperty('checked', false) - this.overrideProperty('acceleratorWorksWhenHidden', true) - this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role)) + this.overrideProperty('label', roles.getDefaultLabel(this.role)); + this.overrideProperty('sublabel', ''); + this.overrideProperty('toolTip', ''); + this.overrideProperty('enabled', true); + this.overrideProperty('visible', true); + this.overrideProperty('checked', false); + this.overrideProperty('acceleratorWorksWhenHidden', true); + this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role)); if (!MenuItem.types.includes(this.type)) { - throw new Error(`Unknown menu item type: ${this.type}`) + throw new Error(`Unknown menu item type: ${this.type}`); } - this.overrideReadOnlyProperty('commandId', ++nextCommandId) + this.overrideReadOnlyProperty('commandId', ++nextCommandId); - const click = options.click + const click = options.click; this.click = (event, focusedWindow, focusedWebContents) => { // Manually flip the checked flags when clicked. if (this.type === 'checkbox' || this.type === 'radio') { - this.checked = !this.checked + this.checked = !this.checked; } if (!roles.execute(this.role, focusedWindow, focusedWebContents)) { if (typeof click === 'function') { - click(this, focusedWindow, event) + click(this, focusedWindow, event); } else if (typeof this.selector === 'string' && process.platform === 'darwin') { - Menu.sendActionToFirstResponder(this.selector) + Menu.sendActionToFirstResponder(this.selector); } } - } -} + }; +}; -MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] +MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']; MenuItem.prototype.getDefaultRoleAccelerator = function () { - return roles.getDefaultAccelerator(this.role) -} + return roles.getDefaultAccelerator(this.role); +}; MenuItem.prototype.overrideProperty = function (name, defaultValue = null) { if (this[name] == null) { - this[name] = defaultValue + this[name] = defaultValue; } -} +}; MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) { - this.overrideProperty(name, defaultValue) + this.overrideProperty(name, defaultValue); Object.defineProperty(this, name, { enumerable: true, writable: false, value: this[name] - }) -} + }); +}; -module.exports = MenuItem +module.exports = MenuItem; diff --git a/lib/browser/api/menu-utils.js b/lib/browser/api/menu-utils.js index 42636bf7da504..493f04c19c7b0 100644 --- a/lib/browser/api/menu-utils.js +++ b/lib/browser/api/menu-utils.js @@ -1,41 +1,41 @@ -'use strict' +'use strict'; function splitArray (arr, predicate) { const result = arr.reduce((multi, item) => { - const current = multi[multi.length - 1] + const current = multi[multi.length - 1]; if (predicate(item)) { - if (current.length > 0) multi.push([]) + if (current.length > 0) multi.push([]); } else { - current.push(item) + current.push(item); } - return multi - }, [[]]) + return multi; + }, [[]]); if (result[result.length - 1].length === 0) { - return result.slice(0, result.length - 1) + return result.slice(0, result.length - 1); } - return result + return result; } function joinArrays (arrays, joinIDs) { return arrays.reduce((joined, arr, i) => { if (i > 0 && arr.length) { if (joinIDs.length > 0) { - joined.push(joinIDs[0]) - joinIDs.splice(0, 1) + joined.push(joinIDs[0]); + joinIDs.splice(0, 1); } else { - joined.push({ type: 'separator' }) + joined.push({ type: 'separator' }); } } - return joined.concat(arr) - }, []) + return joined.concat(arr); + }, []); } function pushOntoMultiMap (map, key, value) { if (!map.has(key)) { - map.set(key, []) + map.set(key, []); } - map.get(key).push(value) + map.get(key).push(value); } function indexOfGroupContainingID (groups, id, ignoreGroup) { @@ -45,102 +45,102 @@ function indexOfGroupContainingID (groups, id, ignoreGroup) { candidateGroup.some( candidateItem => candidateItem.id === id ) - ) + ); } // Sort nodes topologically using a depth-first approach. Encountered cycles // are broken. function sortTopologically (originalOrder, edgesById) { - const sorted = [] - const marked = new Set() + const sorted = []; + const marked = new Set(); const visit = (mark) => { - if (marked.has(mark)) return - marked.add(mark) - const edges = edgesById.get(mark) + if (marked.has(mark)) return; + marked.add(mark); + const edges = edgesById.get(mark); if (edges != null) { - edges.forEach(visit) + edges.forEach(visit); } - sorted.push(mark) - } + sorted.push(mark); + }; - originalOrder.forEach(visit) - return sorted + originalOrder.forEach(visit); + return sorted; } function attemptToMergeAGroup (groups) { for (let i = 0; i < groups.length; i++) { - const group = groups[i] + const group = groups[i]; for (const item of group) { - const toIDs = [...(item.before || []), ...(item.after || [])] + const toIDs = [...(item.before || []), ...(item.after || [])]; for (const id of toIDs) { - const index = indexOfGroupContainingID(groups, id, group) - if (index === -1) continue - const mergeTarget = groups[index] + const index = indexOfGroupContainingID(groups, id, group); + if (index === -1) continue; + const mergeTarget = groups[index]; - mergeTarget.push(...group) - groups.splice(i, 1) - return true + mergeTarget.push(...group); + groups.splice(i, 1); + return true; } } } - return false + return false; } function mergeGroups (groups) { - let merged = true + let merged = true; while (merged) { - merged = attemptToMergeAGroup(groups) + merged = attemptToMergeAGroup(groups); } - return groups + return groups; } function sortItemsInGroup (group) { - const originalOrder = group.map((node, i) => i) - const edges = new Map() - const idToIndex = new Map(group.map((item, i) => [item.id, i])) + const originalOrder = group.map((node, i) => i); + const edges = new Map(); + const idToIndex = new Map(group.map((item, i) => [item.id, i])); group.forEach((item, i) => { if (item.before) { item.before.forEach(toID => { - const to = idToIndex.get(toID) + const to = idToIndex.get(toID); if (to != null) { - pushOntoMultiMap(edges, to, i) + pushOntoMultiMap(edges, to, i); } - }) + }); } if (item.after) { item.after.forEach(toID => { - const to = idToIndex.get(toID) + const to = idToIndex.get(toID); if (to != null) { - pushOntoMultiMap(edges, i, to) + pushOntoMultiMap(edges, i, to); } - }) + }); } - }) + }); - const sortedNodes = sortTopologically(originalOrder, edges) - return sortedNodes.map(i => group[i]) + const sortedNodes = sortTopologically(originalOrder, edges); + return sortedNodes.map(i => group[i]); } function findEdgesInGroup (groups, i, edges) { - const group = groups[i] + const group = groups[i]; for (const item of group) { if (item.beforeGroupContaining) { for (const id of item.beforeGroupContaining) { - const to = indexOfGroupContainingID(groups, id, group) + const to = indexOfGroupContainingID(groups, id, group); if (to !== -1) { - pushOntoMultiMap(edges, to, i) - return + pushOntoMultiMap(edges, to, i); + return; } } } if (item.afterGroupContaining) { for (const id of item.afterGroupContaining) { - const to = indexOfGroupContainingID(groups, id, group) + const to = indexOfGroupContainingID(groups, id, group); if (to !== -1) { - pushOntoMultiMap(edges, i, to) - return + pushOntoMultiMap(edges, i, to); + return; } } } @@ -148,29 +148,29 @@ function findEdgesInGroup (groups, i, edges) { } function sortGroups (groups) { - const originalOrder = groups.map((item, i) => i) - const edges = new Map() + const originalOrder = groups.map((item, i) => i); + const edges = new Map(); for (let i = 0; i < groups.length; i++) { - findEdgesInGroup(groups, i, edges) + findEdgesInGroup(groups, i, edges); } - const sortedGroupIndexes = sortTopologically(originalOrder, edges) - return sortedGroupIndexes.map(i => groups[i]) + const sortedGroupIndexes = sortTopologically(originalOrder, edges); + return sortedGroupIndexes.map(i => groups[i]); } function sortMenuItems (menuItems) { - const isSeparator = (item) => item.type === 'separator' - const separators = menuItems.filter(i => i.type === 'separator') + const isSeparator = (item) => item.type === 'separator'; + const separators = menuItems.filter(i => i.type === 'separator'); // Split the items into their implicit groups based upon separators. - const groups = splitArray(menuItems, isSeparator) - const mergedGroups = mergeGroups(groups) - const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup) - const sortedGroups = sortGroups(mergedGroupsWithSortedItems) + const groups = splitArray(menuItems, isSeparator); + const mergedGroups = mergeGroups(groups); + const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup); + const sortedGroups = sortGroups(mergedGroupsWithSortedItems); - const joined = joinArrays(sortedGroups, separators) - return joined + const joined = joinArrays(sortedGroups, separators); + return joined; } -module.exports = { sortMenuItems } +module.exports = { sortMenuItems }; diff --git a/lib/browser/api/menu.js b/lib/browser/api/menu.js index c36b76799b575..2a1102e9a8a1f 100644 --- a/lib/browser/api/menu.js +++ b/lib/browser/api/menu.js @@ -1,16 +1,16 @@ -'use strict' +'use strict'; -const { TopLevelWindow, MenuItem, webContents } = require('electron') -const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils') -const EventEmitter = require('events').EventEmitter -const v8Util = process.electronBinding('v8_util') -const bindings = process.electronBinding('menu') +const { TopLevelWindow, MenuItem, webContents } = require('electron'); +const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils'); +const EventEmitter = require('events').EventEmitter; +const v8Util = process.electronBinding('v8_util'); +const bindings = process.electronBinding('menu'); -const { Menu } = bindings -let applicationMenu = null -let groupIdIndex = 0 +const { Menu } = bindings; +let applicationMenu = null; +let groupIdIndex = 0; -Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype); // Menu Delegate. // This object should hold no reference to |Menu| to avoid cyclic reference. @@ -20,172 +20,172 @@ const delegate = { shouldCommandIdWorkWhenHidden: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].acceleratorWorksWhenHidden : undefined, isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined, getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => { - const command = menu.commandsMap[id] - if (!command) return - if (command.accelerator != null) return command.accelerator - if (useDefaultAccelerator) return command.getDefaultRoleAccelerator() + const command = menu.commandsMap[id]; + if (!command) return; + if (command.accelerator != null) return command.accelerator; + if (useDefaultAccelerator) return command.getDefaultRoleAccelerator(); }, shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined, executeCommand: (menu, event, id) => { - const command = menu.commandsMap[id] - if (!command) return - command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents()) + const command = menu.commandsMap[id]; + if (!command) return; + command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents()); }, menuWillShow: (menu) => { // Ensure radio groups have at least one menu item selected for (const id of Object.keys(menu.groupsMap)) { - const found = menu.groupsMap[id].find(item => item.checked) || null - if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true) + const found = menu.groupsMap[id].find(item => item.checked) || null; + if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true); } } -} +}; /* Instance Methods */ Menu.prototype._init = function () { - this.commandsMap = {} - this.groupsMap = {} - this.items = [] - this.delegate = delegate -} + this.commandsMap = {}; + this.groupsMap = {}; + this.items = []; + this.delegate = delegate; +}; Menu.prototype.popup = function (options = {}) { if (options == null || typeof options !== 'object') { - throw new TypeError('Options must be an object') + throw new TypeError('Options must be an object'); } - let { window, x, y, positioningItem, callback } = options + let { window, x, y, positioningItem, callback } = options; // no callback passed - if (!callback || typeof callback !== 'function') callback = () => {} + if (!callback || typeof callback !== 'function') callback = () => {}; // set defaults - if (typeof x !== 'number') x = -1 - if (typeof y !== 'number') y = -1 - if (typeof positioningItem !== 'number') positioningItem = -1 + if (typeof x !== 'number') x = -1; + if (typeof y !== 'number') y = -1; + if (typeof positioningItem !== 'number') positioningItem = -1; // find which window to use - const wins = TopLevelWindow.getAllWindows() + const wins = TopLevelWindow.getAllWindows(); if (!wins || wins.indexOf(window) === -1) { - window = TopLevelWindow.getFocusedWindow() + window = TopLevelWindow.getFocusedWindow(); if (!window && wins && wins.length > 0) { - window = wins[0] + window = wins[0]; } if (!window) { - throw new Error('Cannot open Menu without a TopLevelWindow present') + throw new Error('Cannot open Menu without a TopLevelWindow present'); } } - this.popupAt(window, x, y, positioningItem, callback) - return { browserWindow: window, x, y, position: positioningItem } -} + this.popupAt(window, x, y, positioningItem, callback); + return { browserWindow: window, x, y, position: positioningItem }; +}; Menu.prototype.closePopup = function (window) { if (window instanceof TopLevelWindow) { - this.closePopupAt(window.id) + this.closePopupAt(window.id); } else { // Passing -1 (invalid) would make closePopupAt close the all menu runners // belong to this menu. - this.closePopupAt(-1) + this.closePopupAt(-1); } -} +}; Menu.prototype.getMenuItemById = function (id) { - const items = this.items + const items = this.items; - let found = items.find(item => item.id === id) || null + let found = items.find(item => item.id === id) || null; for (let i = 0; !found && i < items.length; i++) { if (items[i].submenu) { - found = items[i].submenu.getMenuItemById(id) + found = items[i].submenu.getMenuItemById(id); } } - return found -} + return found; +}; Menu.prototype.append = function (item) { - return this.insert(this.getItemCount(), item) -} + return this.insert(this.getItemCount(), item); +}; Menu.prototype.insert = function (pos, item) { if ((item ? item.constructor : undefined) !== MenuItem) { - throw new TypeError('Invalid item') + throw new TypeError('Invalid item'); } if (pos < 0) { - throw new RangeError(`Position ${pos} cannot be less than 0`) + throw new RangeError(`Position ${pos} cannot be less than 0`); } else if (pos > this.getItemCount()) { - throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`) + throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`); } // insert item depending on its type - insertItemByType.call(this, item, pos) + insertItemByType.call(this, item, pos); // set item properties - if (item.sublabel) this.setSublabel(pos, item.sublabel) - if (item.toolTip) this.setToolTip(pos, item.toolTip) - if (item.icon) this.setIcon(pos, item.icon) - if (item.role) this.setRole(pos, item.role) + if (item.sublabel) this.setSublabel(pos, item.sublabel); + if (item.toolTip) this.setToolTip(pos, item.toolTip); + if (item.icon) this.setIcon(pos, item.icon); + if (item.role) this.setRole(pos, item.role); // Make menu accessable to items. - item.overrideReadOnlyProperty('menu', this) + item.overrideReadOnlyProperty('menu', this); // Remember the items. - this.items.splice(pos, 0, item) - this.commandsMap[item.commandId] = item -} + this.items.splice(pos, 0, item); + this.commandsMap[item.commandId] = item; +}; Menu.prototype._callMenuWillShow = function () { - if (this.delegate) this.delegate.menuWillShow(this) + if (this.delegate) this.delegate.menuWillShow(this); this.items.forEach(item => { - if (item.submenu) item.submenu._callMenuWillShow() - }) -} + if (item.submenu) item.submenu._callMenuWillShow(); + }); +}; /* Static Methods */ -Menu.getApplicationMenu = () => applicationMenu +Menu.getApplicationMenu = () => applicationMenu; -Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder +Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder; // set application menu with a preexisting menu Menu.setApplicationMenu = function (menu) { if (menu && menu.constructor !== Menu) { - throw new TypeError('Invalid menu') + throw new TypeError('Invalid menu'); } - applicationMenu = menu - v8Util.setHiddenValue(global, 'applicationMenuSet', true) + applicationMenu = menu; + v8Util.setHiddenValue(global, 'applicationMenuSet', true); if (process.platform === 'darwin') { - if (!menu) return - menu._callMenuWillShow() - bindings.setApplicationMenu(menu) + if (!menu) return; + menu._callMenuWillShow(); + bindings.setApplicationMenu(menu); } else { - const windows = TopLevelWindow.getAllWindows() - return windows.map(w => w.setMenu(menu)) + const windows = TopLevelWindow.getAllWindows(); + return windows.map(w => w.setMenu(menu)); } -} +}; Menu.buildFromTemplate = function (template) { if (!Array.isArray(template)) { - throw new TypeError('Invalid template for Menu: Menu template must be an array') + throw new TypeError('Invalid template for Menu: Menu template must be an array'); } if (!areValidTemplateItems(template)) { - throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type') + throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type'); } - const filtered = removeExtraSeparators(template) - const sorted = sortTemplate(filtered) + const filtered = removeExtraSeparators(template); + const sorted = sortTemplate(filtered); - const menu = new Menu() + const menu = new Menu(); sorted.forEach(item => { if (item instanceof MenuItem) { - menu.append(item) + menu.append(item); } else { - menu.append(new MenuItem(item)) + menu.append(new MenuItem(item)); } - }) + }); - return menu -} + return menu; +}; /* Helper Functions */ @@ -196,50 +196,50 @@ function areValidTemplateItems (template) { typeof item === 'object' && (Object.prototype.hasOwnProperty.call(item, 'label') || Object.prototype.hasOwnProperty.call(item, 'role') || - item.type === 'separator')) + item.type === 'separator')); } function sortTemplate (template) { - const sorted = sortMenuItems(template) + const sorted = sortMenuItems(template); for (const item of sorted) { if (Array.isArray(item.submenu)) { - item.submenu = sortTemplate(item.submenu) + item.submenu = sortTemplate(item.submenu); } } - return sorted + return sorted; } // Search between separators to find a radio menu item and return its group id function generateGroupId (items, pos) { if (pos > 0) { for (let idx = pos - 1; idx >= 0; idx--) { - if (items[idx].type === 'radio') return items[idx].groupId - if (items[idx].type === 'separator') break + if (items[idx].type === 'radio') return items[idx].groupId; + if (items[idx].type === 'separator') break; } } else if (pos < items.length) { for (let idx = pos; idx <= items.length - 1; idx++) { - if (items[idx].type === 'radio') return items[idx].groupId - if (items[idx].type === 'separator') break + if (items[idx].type === 'radio') return items[idx].groupId; + if (items[idx].type === 'separator') break; } } - groupIdIndex += 1 - return groupIdIndex + groupIdIndex += 1; + return groupIdIndex; } function removeExtraSeparators (items) { // fold adjacent separators together let ret = items.filter((e, idx, arr) => { - if (e.visible === false) return true - return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator' - }) + if (e.visible === false) return true; + return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator'; + }); // remove edge separators ret = ret.filter((e, idx, arr) => { - if (e.visible === false) return true - return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1) - }) + if (e.visible === false) return true; + return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1); + }); - return ret + return ret; } function insertItemByType (item, pos) { @@ -250,28 +250,28 @@ function insertItemByType (item, pos) { submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu), radio: () => { // Grouping radio menu items - item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos)) + item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos)); if (this.groupsMap[item.groupId] == null) { - this.groupsMap[item.groupId] = [] + this.groupsMap[item.groupId] = []; } - this.groupsMap[item.groupId].push(item) + this.groupsMap[item.groupId].push(item); // Setting a radio menu item should flip other items in the group. - v8Util.setHiddenValue(item, 'checked', item.checked) + v8Util.setHiddenValue(item, 'checked', item.checked); Object.defineProperty(item, 'checked', { enumerable: true, get: () => v8Util.getHiddenValue(item, 'checked'), set: () => { this.groupsMap[item.groupId].forEach(other => { - if (other !== item) v8Util.setHiddenValue(other, 'checked', false) - }) - v8Util.setHiddenValue(item, 'checked', true) + if (other !== item) v8Util.setHiddenValue(other, 'checked', false); + }); + v8Util.setHiddenValue(item, 'checked', true); } - }) - this.insertRadioItem(pos, item.commandId, item.label, item.groupId) + }); + this.insertRadioItem(pos, item.commandId, item.label, item.groupId); } - } - types[item.type]() + }; + types[item.type](); } -module.exports = Menu +module.exports = Menu; diff --git a/lib/browser/api/message-channel.ts b/lib/browser/api/message-channel.ts index 8b6bedc6b484b..bcaf7cbc4e6c6 100644 --- a/lib/browser/api/message-channel.ts +++ b/lib/browser/api/message-channel.ts @@ -1,12 +1,12 @@ -import { MessagePortMain } from '@electron/internal/browser/message-port-main' -const { createPair } = process.electronBinding('message_port') +import { MessagePortMain } from '@electron/internal/browser/message-port-main'; +const { createPair } = process.electronBinding('message_port'); export default class MessageChannelMain { port1: MessagePortMain; port2: MessagePortMain; constructor () { - const { port1, port2 } = createPair() - this.port1 = new MessagePortMain(port1) - this.port2 = new MessagePortMain(port2) + const { port1, port2 } = createPair(); + this.port1 = new MessagePortMain(port1); + this.port2 = new MessagePortMain(port2); } } diff --git a/lib/browser/api/module-keys.js b/lib/browser/api/module-keys.js index 40cbbbe49b529..172e9a1220ad4 100644 --- a/lib/browser/api/module-keys.js +++ b/lib/browser/api/module-keys.js @@ -1,11 +1,11 @@ -'use strict' +'use strict'; // TODO: Figure out a way to not duplicate this information between here and module-list // It is currently duplicated as module-list "require"s all the browser API file and the // remote module in the renderer process depends on that file. As a result webpack // includes all the browser API files in the renderer process as well and we want to avoid that -const features = process.electronBinding('features') +const features = process.electronBinding('features'); // Browser side modules, please sort alphabetically. module.exports = [ @@ -38,7 +38,7 @@ module.exports = [ { name: 'View' }, { name: 'webContents' }, { name: 'WebContentsView' } -] +]; if (features.isViewApiEnabled()) { module.exports.push( @@ -49,5 +49,5 @@ if (features.isViewApiEnabled()) { { name: 'MdTextButton' }, { name: 'ResizeArea' }, { name: 'TextField' } - ) + ); } diff --git a/lib/browser/api/module-list.ts b/lib/browser/api/module-list.ts index 1784439ba36c4..41e559a5b48d5 100644 --- a/lib/browser/api/module-list.ts +++ b/lib/browser/api/module-list.ts @@ -1,6 +1,6 @@ // TODO: Updating this file also required updating the module-keys file -const features = process.electronBinding('features') +const features = process.electronBinding('features'); // Browser side modules, please sort alphabetically. export const browserModuleList: ElectronInternal.ModuleEntry[] = [ @@ -33,7 +33,7 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [ { name: 'View', loader: () => require('./view') }, { name: 'webContents', loader: () => require('./web-contents') }, { name: 'WebContentsView', loader: () => require('./web-contents-view') } -] +]; if (features.isViewApiEnabled()) { browserModuleList.push( @@ -44,5 +44,5 @@ if (features.isViewApiEnabled()) { { name: 'MdTextButton', loader: () => require('./views/md-text-button') }, { name: 'ResizeArea', loader: () => require('./views/resize-area') }, { name: 'TextField', loader: () => require('./views/text-field') } - ) + ); } diff --git a/lib/browser/api/native-theme.ts b/lib/browser/api/native-theme.ts index 78d7e9049bf01..f39a0dd8ee1e7 100644 --- a/lib/browser/api/native-theme.ts +++ b/lib/browser/api/native-theme.ts @@ -1,8 +1,8 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'events'; -const { NativeTheme, nativeTheme } = process.electronBinding('native_theme') +const { NativeTheme, nativeTheme } = process.electronBinding('native_theme'); -Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype) -EventEmitter.call(nativeTheme as any) +Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype); +EventEmitter.call(nativeTheme as any); -module.exports = nativeTheme +module.exports = nativeTheme; diff --git a/lib/browser/api/net-log.js b/lib/browser/api/net-log.js index b333223f8c85f..11b8071880ab7 100644 --- a/lib/browser/api/net-log.js +++ b/lib/browser/api/net-log.js @@ -1,32 +1,32 @@ -'use strict' +'use strict'; // TODO(deepak1556): Deprecate and remove standalone netLog module, // it is now a property of session module. -const { app, session } = require('electron') +const { app, session } = require('electron'); // Fallback to default session. Object.setPrototypeOf(module.exports, new Proxy({}, { get (target, property) { - if (!app.isReady()) return + if (!app.isReady()) return; - const netLog = session.defaultSession.netLog + const netLog = session.defaultSession.netLog; - if (!Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(netLog), property)) return + if (!Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(netLog), property)) return; // check for properties on the prototype chain that aren't functions - if (typeof netLog[property] !== 'function') return netLog[property] + if (typeof netLog[property] !== 'function') return netLog[property]; // Returning a native function directly would throw error. - return (...args) => netLog[property](...args) + return (...args) => netLog[property](...args); }, ownKeys () { - if (!app.isReady()) return [] + if (!app.isReady()) return []; - return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog)) + return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog)); }, getOwnPropertyDescriptor (target) { - return { configurable: true, enumerable: true } + return { configurable: true, enumerable: true }; } -})) +})); diff --git a/lib/browser/api/net.js b/lib/browser/api/net.js index 314b1610eaaee..ba43b001dc402 100644 --- a/lib/browser/api/net.js +++ b/lib/browser/api/net.js @@ -1,16 +1,16 @@ -'use strict' +'use strict'; -const url = require('url') -const { EventEmitter } = require('events') -const { Readable, Writable } = require('stream') -const { app } = require('electron') -const { Session } = process.electronBinding('session') -const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net') -const { URLLoader } = net +const url = require('url'); +const { EventEmitter } = require('events'); +const { Readable, Writable } = require('stream'); +const { app } = require('electron'); +const { Session } = process.electronBinding('session'); +const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net'); +const { URLLoader } = net; -Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype) +Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype); -const kSupportedProtocols = new Set(['http:', 'https:']) +const kSupportedProtocols = new Set(['http:', 'https:']); // set of headers that Node.js discards duplicates for // see https://nodejs.org/api/http.html#http_message_headers @@ -33,27 +33,27 @@ const discardableDuplicateHeaders = new Set([ 'server', 'age', 'expires' -]) +]); class IncomingMessage extends Readable { constructor (responseHead) { - super() - this._shouldPush = false - this._data = [] - this._responseHead = responseHead + super(); + this._shouldPush = false; + this._data = []; + this._responseHead = responseHead; } get statusCode () { - return this._responseHead.statusCode + return this._responseHead.statusCode; } get statusMessage () { - return this._responseHead.statusMessage + return this._responseHead.statusMessage; } get headers () { - const filteredHeaders = {} - const { rawHeaders } = this._responseHead + const filteredHeaders = {}; + const { rawHeaders } = this._responseHead; rawHeaders.forEach(header => { if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) && discardableDuplicateHeaders.has(header.key)) { @@ -63,147 +63,147 @@ class IncomingMessage extends Readable { // keep set-cookie as an array per Node.js rules // see https://nodejs.org/api/http.html#http_message_headers if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) { - filteredHeaders[header.key].push(header.value) + filteredHeaders[header.key].push(header.value); } else { - filteredHeaders[header.key] = [header.value] + filteredHeaders[header.key] = [header.value]; } } else { // for non-cookie headers, the values are joined together with ', ' if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) { - filteredHeaders[header.key] += `, ${header.value}` + filteredHeaders[header.key] += `, ${header.value}`; } else { - filteredHeaders[header.key] = header.value + filteredHeaders[header.key] = header.value; } } } - }) - return filteredHeaders + }); + return filteredHeaders; } get httpVersion () { - return `${this.httpVersionMajor}.${this.httpVersionMinor}` + return `${this.httpVersionMajor}.${this.httpVersionMinor}`; } get httpVersionMajor () { - return this._responseHead.httpVersion.major + return this._responseHead.httpVersion.major; } get httpVersionMinor () { - return this._responseHead.httpVersion.minor + return this._responseHead.httpVersion.minor; } get rawTrailers () { - throw new Error('HTTP trailers are not supported') + throw new Error('HTTP trailers are not supported'); } get trailers () { - throw new Error('HTTP trailers are not supported') + throw new Error('HTTP trailers are not supported'); } _storeInternalData (chunk) { - this._data.push(chunk) - this._pushInternalData() + this._data.push(chunk); + this._pushInternalData(); } _pushInternalData () { while (this._shouldPush && this._data.length > 0) { - const chunk = this._data.shift() - this._shouldPush = this.push(chunk) + const chunk = this._data.shift(); + this._shouldPush = this.push(chunk); } } _read () { - this._shouldPush = true - this._pushInternalData() + this._shouldPush = true; + this._pushInternalData(); } } /** Writable stream that buffers up everything written to it. */ class SlurpStream extends Writable { constructor () { - super() - this._data = Buffer.alloc(0) + super(); + this._data = Buffer.alloc(0); } _write (chunk, encoding, callback) { - this._data = Buffer.concat([this._data, chunk]) - callback() + this._data = Buffer.concat([this._data, chunk]); + callback(); } - data () { return this._data } + data () { return this._data; } } class ChunkedBodyStream extends Writable { constructor (clientRequest) { - super() - this._clientRequest = clientRequest + super(); + this._clientRequest = clientRequest; } _write (chunk, encoding, callback) { if (this._downstream) { - this._downstream.write(chunk).then(callback, callback) + this._downstream.write(chunk).then(callback, callback); } else { // the contract of _write is that we won't be called again until we call // the callback, so we're good to just save a single chunk. - this._pendingChunk = chunk - this._pendingCallback = callback + this._pendingChunk = chunk; + this._pendingCallback = callback; // The first write to a chunked body stream begins the request. - this._clientRequest._startRequest() + this._clientRequest._startRequest(); } } _final (callback) { - this._downstream.done() - callback() + this._downstream.done(); + callback(); } startReading (pipe) { if (this._downstream) { - throw new Error('two startReading calls???') + throw new Error('two startReading calls???'); } - this._downstream = pipe + this._downstream = pipe; if (this._pendingChunk) { const doneWriting = (maybeError) => { - const cb = this._pendingCallback - delete this._pendingCallback - delete this._pendingChunk - cb(maybeError) - } - this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting) + const cb = this._pendingCallback; + delete this._pendingCallback; + delete this._pendingChunk; + cb(maybeError); + }; + this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting); } } } function parseOptions (options) { if (typeof options === 'string') { - options = url.parse(options) + options = url.parse(options); } else { - options = { ...options } + options = { ...options }; } - const method = (options.method || 'GET').toUpperCase() - let urlStr = options.url + const method = (options.method || 'GET').toUpperCase(); + let urlStr = options.url; if (!urlStr) { - const urlObj = {} - const protocol = options.protocol || 'http:' + const urlObj = {}; + const protocol = options.protocol || 'http:'; if (!kSupportedProtocols.has(protocol)) { - throw new Error('Protocol "' + protocol + '" not supported') + throw new Error('Protocol "' + protocol + '" not supported'); } - urlObj.protocol = protocol + urlObj.protocol = protocol; if (options.host) { - urlObj.host = options.host + urlObj.host = options.host; } else { if (options.hostname) { - urlObj.hostname = options.hostname + urlObj.hostname = options.hostname; } else { - urlObj.hostname = 'localhost' + urlObj.hostname = 'localhost'; } if (options.port) { - urlObj.port = options.port + urlObj.port = options.port; } } @@ -214,22 +214,22 @@ function parseOptions (options) { // well, and b) possibly too restrictive for real-world usage. That's // why it only scans for spaces because those are guaranteed to create // an invalid request. - throw new TypeError('Request path contains unescaped characters') + throw new TypeError('Request path contains unescaped characters'); } - const pathObj = url.parse(options.path || '/') - urlObj.pathname = pathObj.pathname - urlObj.search = pathObj.search - urlObj.hash = pathObj.hash - urlStr = url.format(urlObj) + const pathObj = url.parse(options.path || '/'); + urlObj.pathname = pathObj.pathname; + urlObj.search = pathObj.search; + urlObj.hash = pathObj.hash; + urlStr = url.format(urlObj); } - const redirectPolicy = options.redirect || 'follow' + const redirectPolicy = options.redirect || 'follow'; if (!['follow', 'error', 'manual'].includes(redirectPolicy)) { - throw new Error('redirect mode should be one of follow, error or manual') + throw new Error('redirect mode should be one of follow, error or manual'); } if (options.headers != null && typeof options.headers !== 'object') { - throw new TypeError('headers must be an object') + throw new TypeError('headers must be an object'); } const urlLoaderOptions = { @@ -237,180 +237,180 @@ function parseOptions (options) { url: urlStr, redirectPolicy, extraHeaders: options.headers || {} - } + }; for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders)) { if (!_isValidHeaderName(name)) { - throw new Error(`Invalid header name: '${name}'`) + throw new Error(`Invalid header name: '${name}'`); } if (!_isValidHeaderValue(value.toString())) { - throw new Error(`Invalid value for header '${name}': '${value}'`) + throw new Error(`Invalid value for header '${name}': '${value}'`); } } if (options.session) { if (options.session instanceof Session) { - urlLoaderOptions.session = options.session + urlLoaderOptions.session = options.session; } else { - throw new TypeError('`session` should be an instance of the Session class') + throw new TypeError('`session` should be an instance of the Session class'); } } else if (options.partition) { if (typeof options.partition === 'string') { - urlLoaderOptions.partition = options.partition + urlLoaderOptions.partition = options.partition; } else { - throw new TypeError('`partition` should be a string') + throw new TypeError('`partition` should be a string'); } } - return urlLoaderOptions + return urlLoaderOptions; } class ClientRequest extends Writable { constructor (options, callback) { - super({ autoDestroy: true }) + super({ autoDestroy: true }); if (!app.isReady()) { - throw new Error('net module can only be used after app is ready') + throw new Error('net module can only be used after app is ready'); } if (callback) { - this.once('response', callback) + this.once('response', callback); } - const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options) - this._urlLoaderOptions = urlLoaderOptions - this._redirectPolicy = redirectPolicy - this._started = false + const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options); + this._urlLoaderOptions = urlLoaderOptions; + this._redirectPolicy = redirectPolicy; + this._started = false; } set chunkedEncoding (value) { if (this._started) { - throw new Error('chunkedEncoding can only be set before the request is started') + throw new Error('chunkedEncoding can only be set before the request is started'); } if (typeof this._chunkedEncoding !== 'undefined') { - throw new Error('chunkedEncoding can only be set once') + throw new Error('chunkedEncoding can only be set once'); } - this._chunkedEncoding = !!value + this._chunkedEncoding = !!value; if (this._chunkedEncoding) { - this._body = new ChunkedBodyStream(this) + this._body = new ChunkedBodyStream(this); this._urlLoaderOptions.body = (pipe) => { - this._body.startReading(pipe) - } + this._body.startReading(pipe); + }; } } setHeader (name, value) { if (typeof name !== 'string') { - throw new TypeError('`name` should be a string in setHeader(name, value)') + throw new TypeError('`name` should be a string in setHeader(name, value)'); } if (value == null) { - throw new Error('`value` required in setHeader("' + name + '", value)') + throw new Error('`value` required in setHeader("' + name + '", value)'); } if (this._started || this._firstWrite) { - throw new Error('Can\'t set headers after they are sent') + throw new Error('Can\'t set headers after they are sent'); } if (!_isValidHeaderName(name)) { - throw new Error(`Invalid header name: '${name}'`) + throw new Error(`Invalid header name: '${name}'`); } if (!_isValidHeaderValue(value.toString())) { - throw new Error(`Invalid value for header '${name}': '${value}'`) + throw new Error(`Invalid value for header '${name}': '${value}'`); } - const key = name.toLowerCase() - this._urlLoaderOptions.extraHeaders[key] = value + const key = name.toLowerCase(); + this._urlLoaderOptions.extraHeaders[key] = value; } getHeader (name) { if (name == null) { - throw new Error('`name` is required for getHeader(name)') + throw new Error('`name` is required for getHeader(name)'); } - const key = name.toLowerCase() - return this._urlLoaderOptions.extraHeaders[key] + const key = name.toLowerCase(); + return this._urlLoaderOptions.extraHeaders[key]; } removeHeader (name) { if (name == null) { - throw new Error('`name` is required for removeHeader(name)') + throw new Error('`name` is required for removeHeader(name)'); } if (this._started || this._firstWrite) { - throw new Error('Can\'t remove headers after they are sent') + throw new Error('Can\'t remove headers after they are sent'); } - const key = name.toLowerCase() - delete this._urlLoaderOptions.extraHeaders[key] + const key = name.toLowerCase(); + delete this._urlLoaderOptions.extraHeaders[key]; } _write (chunk, encoding, callback) { - this._firstWrite = true + this._firstWrite = true; if (!this._body) { - this._body = new SlurpStream() + this._body = new SlurpStream(); this._body.on('finish', () => { - this._urlLoaderOptions.body = this._body.data() - this._startRequest() - }) + this._urlLoaderOptions.body = this._body.data(); + this._startRequest(); + }); } // TODO: is this the right way to forward to another stream? - this._body.write(chunk, encoding, callback) + this._body.write(chunk, encoding, callback); } _final (callback) { if (this._body) { // TODO: is this the right way to forward to another stream? - this._body.end(callback) + this._body.end(callback); } else { // end() called without a body, go ahead and start the request - this._startRequest() - callback() + this._startRequest(); + callback(); } } _startRequest () { - this._started = true + this._started = true; const stringifyValues = (obj) => { - const ret = {} + const ret = {}; for (const k of Object.keys(obj)) { - ret[k] = obj[k].toString() + ret[k] = obj[k].toString(); } - return ret - } - const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) } - this._urlLoader = new URLLoader(opts) + return ret; + }; + const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) }; + this._urlLoader = new URLLoader(opts); this._urlLoader.on('response-started', (event, finalUrl, responseHead) => { - const response = this._response = new IncomingMessage(responseHead) - this.emit('response', response) - }) + const response = this._response = new IncomingMessage(responseHead); + this.emit('response', response); + }); this._urlLoader.on('data', (event, data) => { - this._response._storeInternalData(Buffer.from(data)) - }) + this._response._storeInternalData(Buffer.from(data)); + }); this._urlLoader.on('complete', () => { - if (this._response) { this._response._storeInternalData(null) } - }) + if (this._response) { this._response._storeInternalData(null); } + }); this._urlLoader.on('error', (event, netErrorString) => { - const error = new Error(netErrorString) - if (this._response) this._response.destroy(error) - this._die(error) - }) + const error = new Error(netErrorString); + if (this._response) this._response.destroy(error); + this._die(error); + }); this._urlLoader.on('login', (event, authInfo, callback) => { - const handled = this.emit('login', authInfo, callback) + const handled = this.emit('login', authInfo, callback); if (!handled) { // If there were no listeners, cancel the authentication request. - callback() + callback(); } - }) + }); this._urlLoader.on('redirect', (event, redirectInfo, headers) => { - const { statusCode, newMethod, newUrl } = redirectInfo + const { statusCode, newMethod, newUrl } = redirectInfo; if (this._redirectPolicy === 'error') { - this._die(new Error('Attempted to redirect, but redirect policy was \'error\'')) + this._die(new Error('Attempted to redirect, but redirect policy was \'error\'')); } else if (this._redirectPolicy === 'manual') { - let _followRedirect = false - this._followRedirectCb = () => { _followRedirect = true } + let _followRedirect = false; + this._followRedirectCb = () => { _followRedirect = true; }; try { - this.emit('redirect', statusCode, newMethod, newUrl, headers) + this.emit('redirect', statusCode, newMethod, newUrl, headers); } finally { - this._followRedirectCb = null + this._followRedirectCb = null; if (!_followRedirect && !this._aborted) { - this._die(new Error('Redirect was cancelled')) + this._die(new Error('Redirect was cancelled')); } } } else if (this._redirectPolicy === 'follow') { @@ -418,61 +418,61 @@ class ClientRequest extends Writable { // allowed but does nothing. (Perhaps it should throw an error // though...? Since the redirect will happen regardless.) try { - this._followRedirectCb = () => {} - this.emit('redirect', statusCode, newMethod, newUrl, headers) + this._followRedirectCb = () => {}; + this.emit('redirect', statusCode, newMethod, newUrl, headers); } finally { - this._followRedirectCb = null + this._followRedirectCb = null; } } else { - this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`)) + this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`)); } - }) + }); this._urlLoader.on('upload-progress', (event, position, total) => { - this._uploadProgress = { active: true, started: true, current: position, total } - this.emit('upload-progress', position, total) // Undocumented, for now - }) + this._uploadProgress = { active: true, started: true, current: position, total }; + this.emit('upload-progress', position, total); // Undocumented, for now + }); this._urlLoader.on('download-progress', (event, current) => { if (this._response) { - this._response.emit('download-progress', current) // Undocumented, for now + this._response.emit('download-progress', current); // Undocumented, for now } - }) + }); } followRedirect () { if (this._followRedirectCb) { - this._followRedirectCb() + this._followRedirectCb(); } else { - throw new Error('followRedirect() called, but was not waiting for a redirect') + throw new Error('followRedirect() called, but was not waiting for a redirect'); } } abort () { if (!this._aborted) { - process.nextTick(() => { this.emit('abort') }) + process.nextTick(() => { this.emit('abort'); }); } - this._aborted = true - this._die() + this._aborted = true; + this._die(); } _die (err) { - this.destroy(err) + this.destroy(err); if (this._urlLoader) { - this._urlLoader.cancel() - if (this._response) this._response.destroy(err) + this._urlLoader.cancel(); + if (this._response) this._response.destroy(err); } } getUploadProgress () { - return this._uploadProgress ? { ...this._uploadProgress } : { active: false } + return this._uploadProgress ? { ...this._uploadProgress } : { active: false }; } } Net.prototype.request = function (options, callback) { - return new ClientRequest(options, callback) -} + return new ClientRequest(options, callback); +}; -net.ClientRequest = ClientRequest +net.ClientRequest = ClientRequest; -module.exports = net +module.exports = net; diff --git a/lib/browser/api/notification.js b/lib/browser/api/notification.js index b835278b0bfa7..862f2646471a3 100644 --- a/lib/browser/api/notification.js +++ b/lib/browser/api/notification.js @@ -1,10 +1,10 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { Notification, isSupported } = process.electronBinding('notification') +const { EventEmitter } = require('events'); +const { Notification, isSupported } = process.electronBinding('notification'); -Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype); -Notification.isSupported = isSupported +Notification.isSupported = isSupported; -module.exports = Notification +module.exports = Notification; diff --git a/lib/browser/api/power-monitor.ts b/lib/browser/api/power-monitor.ts index 793b3a093ccc6..89c958d404bdb 100644 --- a/lib/browser/api/power-monitor.ts +++ b/lib/browser/api/power-monitor.ts @@ -1,14 +1,14 @@ -'use strict' +'use strict'; -import { createLazyInstance } from '../utils' +import { createLazyInstance } from '../utils'; -const { EventEmitter } = require('events') -const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor') +const { EventEmitter } = require('events'); +const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor'); // PowerMonitor is an EventEmitter. -Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype) +Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype); -const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true) +const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true); if (process.platform === 'linux') { // In order to delay system shutdown when e.preventDefault() is invoked @@ -18,21 +18,21 @@ if (process.platform === 'linux') { // // So here we watch for 'shutdown' listeners to be added or removed and // set or unset our shutdown delay lock accordingly. - const { app } = require('electron') + const { app } = require('electron'); app.whenReady().then(() => { powerMonitor.on('newListener', (event: string) => { // whenever the listener count is incremented to one... if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { - powerMonitor.blockShutdown() + powerMonitor.blockShutdown(); } - }) + }); powerMonitor.on('removeListener', (event: string) => { // whenever the listener count is decremented to zero... if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { - powerMonitor.unblockShutdown() + powerMonitor.unblockShutdown(); } - }) - }) + }); + }); } -module.exports = powerMonitor +module.exports = powerMonitor; diff --git a/lib/browser/api/power-save-blocker.js b/lib/browser/api/power-save-blocker.js index 010b934c11010..928a8605cdcfe 100644 --- a/lib/browser/api/power-save-blocker.js +++ b/lib/browser/api/power-save-blocker.js @@ -1,3 +1,3 @@ -'use strict' +'use strict'; -module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker +module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker; diff --git a/lib/browser/api/protocol.ts b/lib/browser/api/protocol.ts index 91780c3c7340c..ca543fb53ce86 100644 --- a/lib/browser/api/protocol.ts +++ b/lib/browser/api/protocol.ts @@ -1,29 +1,29 @@ -import { app, session } from 'electron' +import { app, session } from 'electron'; // Global protocol APIs. -const protocol = process.electronBinding('protocol') +const protocol = process.electronBinding('protocol'); // Fallback protocol APIs of default session. Object.setPrototypeOf(protocol, new Proxy({}, { get (_target, property) { - if (!app.isReady()) return + if (!app.isReady()) return; - const protocol = session.defaultSession!.protocol - if (!Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(protocol), property)) return + const protocol = session.defaultSession!.protocol; + if (!Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(protocol), property)) return; // Returning a native function directly would throw error. - return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args) + return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args); }, ownKeys () { - if (!app.isReady()) return [] + if (!app.isReady()) return []; - return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol)) + return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol)); }, getOwnPropertyDescriptor () { - return { configurable: true, enumerable: true } + return { configurable: true, enumerable: true }; } -})) +})); -export default protocol +export default protocol; diff --git a/lib/browser/api/screen.ts b/lib/browser/api/screen.ts index c0c1ea9587fd7..a07e54aff9259 100644 --- a/lib/browser/api/screen.ts +++ b/lib/browser/api/screen.ts @@ -1,10 +1,10 @@ -'use strict' +'use strict'; -import { createLazyInstance } from '../utils' -const { EventEmitter } = require('events') -const { Screen, createScreen } = process.electronBinding('screen') +import { createLazyInstance } from '../utils'; +const { EventEmitter } = require('events'); +const { Screen, createScreen } = process.electronBinding('screen'); // Screen is an EventEmitter. -Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype); -module.exports = createLazyInstance(createScreen, Screen, true) +module.exports = createLazyInstance(createScreen, Screen, true); diff --git a/lib/browser/api/session.js b/lib/browser/api/session.js index 779b84ce537f5..3babd2a2cb0ec 100644 --- a/lib/browser/api/session.js +++ b/lib/browser/api/session.js @@ -1,25 +1,25 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { app, deprecate } = require('electron') -const { fromPartition, Session, Cookies, Protocol, ServiceWorkerContext } = process.electronBinding('session') +const { EventEmitter } = require('events'); +const { app, deprecate } = require('electron'); +const { fromPartition, Session, Cookies, Protocol, ServiceWorkerContext } = process.electronBinding('session'); // Public API. Object.defineProperties(exports, { defaultSession: { enumerable: true, - get () { return fromPartition('') } + get () { return fromPartition(''); } }, fromPartition: { enumerable: true, value: fromPartition } -}) +}); -Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype) -Object.setPrototypeOf(ServiceWorkerContext.prototype, EventEmitter.prototype) -Object.setPrototypeOf(Session.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype); +Object.setPrototypeOf(ServiceWorkerContext.prototype, EventEmitter.prototype); +Object.setPrototypeOf(Session.prototype, EventEmitter.prototype); Session.prototype._init = function () { - app.emit('session-created', this) -} + app.emit('session-created', this); +}; diff --git a/lib/browser/api/system-preferences.ts b/lib/browser/api/system-preferences.ts index e052a016bc935..9ad72dec0ea24 100644 --- a/lib/browser/api/system-preferences.ts +++ b/lib/browser/api/system-preferences.ts @@ -1,41 +1,41 @@ -import { EventEmitter } from 'events' -import { deprecate } from 'electron' -const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences') +import { EventEmitter } from 'events'; +import { deprecate } from 'electron'; +const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences'); // SystemPreferences is an EventEmitter. -Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype) -EventEmitter.call(systemPreferences) +Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype); +EventEmitter.call(systemPreferences); if ('getAppLevelAppearance' in systemPreferences) { - const nativeALAGetter = systemPreferences.getAppLevelAppearance - const nativeALASetter = systemPreferences.setAppLevelAppearance + const nativeALAGetter = systemPreferences.getAppLevelAppearance; + const nativeALASetter = systemPreferences.setAppLevelAppearance; Object.defineProperty(SystemPreferences.prototype, 'appLevelAppearance', { get: () => nativeALAGetter.call(systemPreferences), set: (appearance) => nativeALASetter.call(systemPreferences, appearance) - }) + }); } if ('getEffectiveAppearance' in systemPreferences) { - const nativeEAGetter = systemPreferences.getAppLevelAppearance + const nativeEAGetter = systemPreferences.getAppLevelAppearance; Object.defineProperty(SystemPreferences.prototype, 'effectiveAppearance', { get: () => nativeEAGetter.call(systemPreferences) - }) + }); } SystemPreferences.prototype.isDarkMode = deprecate.moveAPI( SystemPreferences.prototype.isDarkMode, 'systemPreferences.isDarkMode()', 'nativeTheme.shouldUseDarkColors' -) +); SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI( SystemPreferences.prototype.isInvertedColorScheme, 'systemPreferences.isInvertedColorScheme()', 'nativeTheme.shouldUseInvertedColorScheme' -) +); SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI( SystemPreferences.prototype.isHighContrastColorScheme, 'systemPreferences.isHighContrastColorScheme()', 'nativeTheme.shouldUseHighContrastColors' -) +); -module.exports = systemPreferences +module.exports = systemPreferences; diff --git a/lib/browser/api/top-level-window.js b/lib/browser/api/top-level-window.js index b74ebf37a6099..570a57350899f 100644 --- a/lib/browser/api/top-level-window.js +++ b/lib/browser/api/top-level-window.js @@ -1,24 +1,24 @@ -'use strict' +'use strict'; -const electron = require('electron') -const { EventEmitter } = require('events') -const { TopLevelWindow } = process.electronBinding('top_level_window') +const electron = require('electron'); +const { EventEmitter } = require('events'); +const { TopLevelWindow } = process.electronBinding('top_level_window'); -Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype) +Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype); TopLevelWindow.prototype._init = function () { // Avoid recursive require. - const { app } = electron + const { app } = electron; // Simulate the application menu on platforms other than macOS. if (process.platform !== 'darwin') { - const menu = app.applicationMenu - if (menu) this.setMenu(menu) + const menu = app.applicationMenu; + if (menu) this.setMenu(menu); } -} +}; TopLevelWindow.getFocusedWindow = () => { - return TopLevelWindow.getAllWindows().find((win) => win.isFocused()) -} + return TopLevelWindow.getAllWindows().find((win) => win.isFocused()); +}; -module.exports = TopLevelWindow +module.exports = TopLevelWindow; diff --git a/lib/browser/api/touch-bar.js b/lib/browser/api/touch-bar.js index c7200a6361686..d92a85782622e 100644 --- a/lib/browser/api/touch-bar.js +++ b/lib/browser/api/touch-bar.js @@ -1,365 +1,365 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') +const { EventEmitter } = require('events'); -let nextItemID = 1 +let nextItemID = 1; class TouchBar extends EventEmitter { // Bind a touch bar to a window static _setOnWindow (touchBar, window) { if (window._touchBar != null) { - window._touchBar._removeFromWindow(window) + window._touchBar._removeFromWindow(window); } if (touchBar == null) { - window._setTouchBarItems([]) - return + window._setTouchBarItems([]); + return; } if (Array.isArray(touchBar)) { - touchBar = new TouchBar(touchBar) + touchBar = new TouchBar(touchBar); } - touchBar._addToWindow(window) + touchBar._addToWindow(window); } constructor (options) { - super() + super(); if (options == null) { - throw new Error('Must specify options object as first argument') + throw new Error('Must specify options object as first argument'); } - let { items, escapeItem } = options + let { items, escapeItem } = options; if (!Array.isArray(items)) { - items = [] + items = []; } this.changeListener = (item) => { - this.emit('change', item.id, item.type) - } + this.emit('change', item.id, item.type); + }; - this.windowListeners = {} - this.items = {} - this.ordereredItems = [] - this.escapeItem = escapeItem + this.windowListeners = {}; + this.items = {}; + this.ordereredItems = []; + this.escapeItem = escapeItem; const registerItem = (item) => { - this.items[item.id] = item - item.on('change', this.changeListener) + this.items[item.id] = item; + item.on('change', this.changeListener); if (item.child instanceof TouchBar) { - item.child.ordereredItems.forEach(registerItem) + item.child.ordereredItems.forEach(registerItem); } - } + }; - let hasOtherItemsProxy = false - const idSet = new Set() + let hasOtherItemsProxy = false; + const idSet = new Set(); items.forEach((item) => { if (!(item instanceof TouchBarItem)) { - throw new Error('Each item must be an instance of TouchBarItem') + throw new Error('Each item must be an instance of TouchBarItem'); } if (item.type === 'other_items_proxy') { if (!hasOtherItemsProxy) { - hasOtherItemsProxy = true + hasOtherItemsProxy = true; } else { - throw new Error('Must only have one OtherItemsProxy per TouchBar') + throw new Error('Must only have one OtherItemsProxy per TouchBar'); } } if (!idSet.has(item.id)) { - idSet.add(item.id) + idSet.add(item.id); } else { - throw new Error('Cannot add a single instance of TouchBarItem multiple times in a TouchBar') + throw new Error('Cannot add a single instance of TouchBarItem multiple times in a TouchBar'); } - }) + }); // register in separate loop after all items are validated for (const item of items) { - this.ordereredItems.push(item) - registerItem(item) + this.ordereredItems.push(item); + registerItem(item); } } set escapeItem (item) { if (item != null && !(item instanceof TouchBarItem)) { - throw new Error('Escape item must be an instance of TouchBarItem') + throw new Error('Escape item must be an instance of TouchBarItem'); } if (this.escapeItem != null) { - this.escapeItem.removeListener('change', this.changeListener) + this.escapeItem.removeListener('change', this.changeListener); } - this._escapeItem = item + this._escapeItem = item; if (this.escapeItem != null) { - this.escapeItem.on('change', this.changeListener) + this.escapeItem.on('change', this.changeListener); } - this.emit('escape-item-change', item) + this.emit('escape-item-change', item); } get escapeItem () { - return this._escapeItem + return this._escapeItem; } _addToWindow (window) { - const { id } = window + const { id } = window; // Already added to window - if (Object.prototype.hasOwnProperty.call(this.windowListeners, id)) return + if (Object.prototype.hasOwnProperty.call(this.windowListeners, id)) return; - window._touchBar = this + window._touchBar = this; const changeListener = (itemID) => { - window._refreshTouchBarItem(itemID) - } - this.on('change', changeListener) + window._refreshTouchBarItem(itemID); + }; + this.on('change', changeListener); const escapeItemListener = (item) => { - window._setEscapeTouchBarItem(item != null ? item : {}) - } - this.on('escape-item-change', escapeItemListener) + window._setEscapeTouchBarItem(item != null ? item : {}); + }; + this.on('escape-item-change', escapeItemListener); const interactionListener = (event, itemID, details) => { - let item = this.items[itemID] + let item = this.items[itemID]; if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) { - item = this.escapeItem + item = this.escapeItem; } if (item != null && item.onInteraction != null) { - item.onInteraction(details) + item.onInteraction(details); } - } - window.on('-touch-bar-interaction', interactionListener) + }; + window.on('-touch-bar-interaction', interactionListener); const removeListeners = () => { - this.removeListener('change', changeListener) - this.removeListener('escape-item-change', escapeItemListener) - window.removeListener('-touch-bar-interaction', interactionListener) - window.removeListener('closed', removeListeners) - window._touchBar = null - delete this.windowListeners[id] + this.removeListener('change', changeListener); + this.removeListener('escape-item-change', escapeItemListener); + window.removeListener('-touch-bar-interaction', interactionListener); + window.removeListener('closed', removeListeners); + window._touchBar = null; + delete this.windowListeners[id]; const unregisterItems = (items) => { for (const item of items) { - item.removeListener('change', this.changeListener) + item.removeListener('change', this.changeListener); if (item.child instanceof TouchBar) { - unregisterItems(item.child.ordereredItems) + unregisterItems(item.child.ordereredItems); } } - } - unregisterItems(this.ordereredItems) + }; + unregisterItems(this.ordereredItems); if (this.escapeItem) { - this.escapeItem.removeListener('change', this.changeListener) + this.escapeItem.removeListener('change', this.changeListener); } - } - window.once('closed', removeListeners) - this.windowListeners[id] = removeListeners + }; + window.once('closed', removeListeners); + this.windowListeners[id] = removeListeners; - window._setTouchBarItems(this.ordereredItems) - escapeItemListener(this.escapeItem) + window._setTouchBarItems(this.ordereredItems); + escapeItemListener(this.escapeItem); } _removeFromWindow (window) { - const removeListeners = this.windowListeners[window.id] - if (removeListeners != null) removeListeners() + const removeListeners = this.windowListeners[window.id]; + if (removeListeners != null) removeListeners(); } } class TouchBarItem extends EventEmitter { constructor () { - super() - this._addImmutableProperty('id', `${nextItemID++}`) - this._parents = [] + super(); + this._addImmutableProperty('id', `${nextItemID++}`); + this._parents = []; } _addImmutableProperty (name, value) { Object.defineProperty(this, name, { get: function () { - return value + return value; }, set: function () { - throw new Error(`Cannot override property ${name}`) + throw new Error(`Cannot override property ${name}`); }, enumerable: true, configurable: false - }) + }); } _addLiveProperty (name, initialValue) { - const privateName = `_${name}` - this[privateName] = initialValue + const privateName = `_${name}`; + this[privateName] = initialValue; Object.defineProperty(this, name, { get: function () { - return this[privateName] + return this[privateName]; }, set: function (value) { - this[privateName] = value - this.emit('change', this) + this[privateName] = value; + this.emit('change', this); }, enumerable: true - }) + }); } _addParent (item) { - const existing = this._parents.some(test => test.id === item.id) + const existing = this._parents.some(test => test.id === item.id); if (!existing) { this._parents.push({ id: item.id, type: item.type - }) + }); } } } TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'button') - this._addLiveProperty('label', config.label) - this._addLiveProperty('accessibilityLabel', config.accessibilityLabel) - this._addLiveProperty('backgroundColor', config.backgroundColor) - this._addLiveProperty('icon', config.icon) - this._addLiveProperty('iconPosition', config.iconPosition) - this._addLiveProperty('enabled', typeof config.enabled !== 'boolean' ? true : config.enabled) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'button'); + this._addLiveProperty('label', config.label); + this._addLiveProperty('accessibilityLabel', config.accessibilityLabel); + this._addLiveProperty('backgroundColor', config.backgroundColor); + this._addLiveProperty('icon', config.icon); + this._addLiveProperty('iconPosition', config.iconPosition); + this._addLiveProperty('enabled', typeof config.enabled !== 'boolean' ? true : config.enabled); if (typeof config.click === 'function') { this._addImmutableProperty('onInteraction', () => { - config.click() - }) + config.click(); + }); } } -} +}; TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'colorpicker') - this._addLiveProperty('availableColors', config.availableColors) - this._addLiveProperty('selectedColor', config.selectedColor) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'colorpicker'); + this._addLiveProperty('availableColors', config.availableColors); + this._addLiveProperty('selectedColor', config.selectedColor); if (typeof config.change === 'function') { this._addImmutableProperty('onInteraction', (details) => { - this._selectedColor = details.color - config.change(details.color) - }) + this._selectedColor = details.color; + config.change(details.color); + }); } } -} +}; TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'group') - const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items) - this._addLiveProperty('child', defaultChild) - this.child.ordereredItems.forEach((item) => item._addParent(this)) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'group'); + const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items); + this._addLiveProperty('child', defaultChild); + this.child.ordereredItems.forEach((item) => item._addParent(this)); } -} +}; TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'label') - this._addLiveProperty('label', config.label) - this._addLiveProperty('accessibilityLabel', config.accessibilityLabel) - this._addLiveProperty('textColor', config.textColor) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'label'); + this._addLiveProperty('label', config.label); + this._addLiveProperty('accessibilityLabel', config.accessibilityLabel); + this._addLiveProperty('textColor', config.textColor); } -} +}; TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'popover') - this._addLiveProperty('label', config.label) - this._addLiveProperty('icon', config.icon) - this._addLiveProperty('showCloseButton', config.showCloseButton) - const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items) - this._addLiveProperty('child', defaultChild) - this.child.ordereredItems.forEach((item) => item._addParent(this)) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'popover'); + this._addLiveProperty('label', config.label); + this._addLiveProperty('icon', config.icon); + this._addLiveProperty('showCloseButton', config.showCloseButton); + const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items); + this._addLiveProperty('child', defaultChild); + this.child.ordereredItems.forEach((item) => item._addParent(this)); } -} +}; TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'slider') - this._addLiveProperty('label', config.label) - this._addLiveProperty('minValue', config.minValue) - this._addLiveProperty('maxValue', config.maxValue) - this._addLiveProperty('value', config.value) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'slider'); + this._addLiveProperty('label', config.label); + this._addLiveProperty('minValue', config.minValue); + this._addLiveProperty('maxValue', config.maxValue); + this._addLiveProperty('value', config.value); if (typeof config.change === 'function') { this._addImmutableProperty('onInteraction', (details) => { - this._value = details.value - config.change(details.value) - }) + this._value = details.value; + config.change(details.value); + }); } } -} +}; TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'spacer') - this._addImmutableProperty('size', config.size) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'spacer'); + this._addImmutableProperty('size', config.size); } -} +}; TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'segmented_control') - this._addLiveProperty('segmentStyle', config.segmentStyle) - this._addLiveProperty('segments', config.segments || []) - this._addLiveProperty('selectedIndex', config.selectedIndex) - this._addLiveProperty('mode', config.mode) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'segmented_control'); + this._addLiveProperty('segmentStyle', config.segmentStyle); + this._addLiveProperty('segments', config.segments || []); + this._addLiveProperty('selectedIndex', config.selectedIndex); + this._addLiveProperty('mode', config.mode); if (typeof config.change === 'function') { this._addImmutableProperty('onInteraction', (details) => { - this._selectedIndex = details.selectedIndex - config.change(details.selectedIndex, details.isSelected) - }) + this._selectedIndex = details.selectedIndex; + config.change(details.selectedIndex, details.isSelected); + }); } } -} +}; TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - let { select, highlight } = config - this._addImmutableProperty('type', 'scrubber') - this._addLiveProperty('items', config.items) - this._addLiveProperty('selectedStyle', config.selectedStyle || null) - this._addLiveProperty('overlayStyle', config.overlayStyle || null) - this._addLiveProperty('showArrowButtons', config.showArrowButtons || false) - this._addLiveProperty('mode', config.mode || 'free') - - const cont = typeof config.continuous === 'undefined' ? true : config.continuous - this._addLiveProperty('continuous', cont) + super(); + if (config == null) config = {}; + let { select, highlight } = config; + this._addImmutableProperty('type', 'scrubber'); + this._addLiveProperty('items', config.items); + this._addLiveProperty('selectedStyle', config.selectedStyle || null); + this._addLiveProperty('overlayStyle', config.overlayStyle || null); + this._addLiveProperty('showArrowButtons', config.showArrowButtons || false); + this._addLiveProperty('mode', config.mode || 'free'); + + const cont = typeof config.continuous === 'undefined' ? true : config.continuous; + this._addLiveProperty('continuous', cont); if (typeof select === 'function' || typeof highlight === 'function') { - if (select == null) select = () => {} - if (highlight == null) highlight = () => {} + if (select == null) select = () => {}; + if (highlight == null) highlight = () => {}; this._addImmutableProperty('onInteraction', (details) => { if (details.type === 'select' && typeof select === 'function') { - select(details.selectedIndex) + select(details.selectedIndex); } else if (details.type === 'highlight' && typeof highlight === 'function') { - highlight(details.highlightedIndex) + highlight(details.highlightedIndex); } - }) + }); } } -} +}; TouchBar.TouchBarOtherItemsProxy = class TouchBarOtherItemsProxy extends TouchBarItem { constructor (config) { - super() - this._addImmutableProperty('type', 'other_items_proxy') + super(); + this._addImmutableProperty('type', 'other_items_proxy'); } -} +}; -module.exports = TouchBar +module.exports = TouchBar; diff --git a/lib/browser/api/tray.js b/lib/browser/api/tray.js index b7402858bf181..ac8cc88724fcb 100644 --- a/lib/browser/api/tray.js +++ b/lib/browser/api/tray.js @@ -1,9 +1,9 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { deprecate } = require('electron') -const { Tray } = process.electronBinding('tray') +const { EventEmitter } = require('events'); +const { deprecate } = require('electron'); +const { Tray } = process.electronBinding('tray'); -Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype); -module.exports = Tray +module.exports = Tray; diff --git a/lib/browser/api/view.js b/lib/browser/api/view.js index 578a2306a1855..246e9a2b491c6 100644 --- a/lib/browser/api/view.js +++ b/lib/browser/api/view.js @@ -1,11 +1,11 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { View } = process.electronBinding('view') +const { EventEmitter } = require('events'); +const { View } = process.electronBinding('view'); -Object.setPrototypeOf(View.prototype, EventEmitter.prototype) +Object.setPrototypeOf(View.prototype, EventEmitter.prototype); View.prototype._init = function () { -} +}; -module.exports = View +module.exports = View; diff --git a/lib/browser/api/views/box-layout.js b/lib/browser/api/views/box-layout.js index 5c4d98fd5600b..f9c89f8861d47 100644 --- a/lib/browser/api/views/box-layout.js +++ b/lib/browser/api/views/box-layout.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { LayoutManager } = electron -const { BoxLayout } = process.electronBinding('box_layout') +const { LayoutManager } = electron; +const { BoxLayout } = process.electronBinding('box_layout'); -Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype) +Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype); BoxLayout.prototype._init = function () { // Call parent class's _init. - LayoutManager.prototype._init.call(this) -} + LayoutManager.prototype._init.call(this); +}; -module.exports = BoxLayout +module.exports = BoxLayout; diff --git a/lib/browser/api/views/button.js b/lib/browser/api/views/button.js index cb4cbc82962c7..d3b094eb0fb6b 100644 --- a/lib/browser/api/views/button.js +++ b/lib/browser/api/views/button.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { View } = electron -const { Button } = process.electronBinding('button') +const { View } = electron; +const { Button } = process.electronBinding('button'); -Object.setPrototypeOf(Button.prototype, View.prototype) +Object.setPrototypeOf(Button.prototype, View.prototype); Button.prototype._init = function () { // Call parent class's _init. - View.prototype._init.call(this) -} + View.prototype._init.call(this); +}; -module.exports = Button +module.exports = Button; diff --git a/lib/browser/api/views/label-button.js b/lib/browser/api/views/label-button.js index ff2f990e0d0e4..0a205a177dfe2 100644 --- a/lib/browser/api/views/label-button.js +++ b/lib/browser/api/views/label-button.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { Button } = electron -const { LabelButton } = process.electronBinding('label_button') +const { Button } = electron; +const { LabelButton } = process.electronBinding('label_button'); -Object.setPrototypeOf(LabelButton.prototype, Button.prototype) +Object.setPrototypeOf(LabelButton.prototype, Button.prototype); LabelButton.prototype._init = function () { // Call parent class's _init. - Button.prototype._init.call(this) -} + Button.prototype._init.call(this); +}; -module.exports = LabelButton +module.exports = LabelButton; diff --git a/lib/browser/api/views/layout-manager.js b/lib/browser/api/views/layout-manager.js index b9da420ad6986..e7b3b8c8f1360 100644 --- a/lib/browser/api/views/layout-manager.js +++ b/lib/browser/api/views/layout-manager.js @@ -1,8 +1,8 @@ -'use strict' +'use strict'; -const { LayoutManager } = process.electronBinding('layout_manager') +const { LayoutManager } = process.electronBinding('layout_manager'); LayoutManager.prototype._init = function () { -} +}; -module.exports = LayoutManager +module.exports = LayoutManager; diff --git a/lib/browser/api/views/md-text-button.js b/lib/browser/api/views/md-text-button.js index 8671a80dbc377..2528bf487e542 100644 --- a/lib/browser/api/views/md-text-button.js +++ b/lib/browser/api/views/md-text-button.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { LabelButton } = electron -const { MdTextButton } = process.electronBinding('md_text_button') +const { LabelButton } = electron; +const { MdTextButton } = process.electronBinding('md_text_button'); -Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype) +Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype); MdTextButton.prototype._init = function () { // Call parent class's _init. - LabelButton.prototype._init.call(this) -} + LabelButton.prototype._init.call(this); +}; -module.exports = MdTextButton +module.exports = MdTextButton; diff --git a/lib/browser/api/views/resize-area.js b/lib/browser/api/views/resize-area.js index e8067aa425469..d36c3805f54af 100644 --- a/lib/browser/api/views/resize-area.js +++ b/lib/browser/api/views/resize-area.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { View } = electron -const { ResizeArea } = process.electronBinding('resize_area') +const { View } = electron; +const { ResizeArea } = process.electronBinding('resize_area'); -Object.setPrototypeOf(ResizeArea.prototype, View.prototype) +Object.setPrototypeOf(ResizeArea.prototype, View.prototype); ResizeArea.prototype._init = function () { // Call parent class's _init. - View.prototype._init.call(this) -} + View.prototype._init.call(this); +}; -module.exports = ResizeArea +module.exports = ResizeArea; diff --git a/lib/browser/api/views/text-field.js b/lib/browser/api/views/text-field.js index fafd5bccd01db..eeb740019ada1 100644 --- a/lib/browser/api/views/text-field.js +++ b/lib/browser/api/views/text-field.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { View } = electron -const { TextField } = process.electronBinding('text_field') +const { View } = electron; +const { TextField } = process.electronBinding('text_field'); -Object.setPrototypeOf(TextField.prototype, View.prototype) +Object.setPrototypeOf(TextField.prototype, View.prototype); TextField.prototype._init = function () { // Call parent class's _init. - View.prototype._init.call(this) -} + View.prototype._init.call(this); +}; -module.exports = TextField +module.exports = TextField; diff --git a/lib/browser/api/web-contents-view.js b/lib/browser/api/web-contents-view.js index eb028bb0908fa..12e53faa1a304 100644 --- a/lib/browser/api/web-contents-view.js +++ b/lib/browser/api/web-contents-view.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { View } = electron -const { WebContentsView } = process.electronBinding('web_contents_view') +const { View } = electron; +const { WebContentsView } = process.electronBinding('web_contents_view'); -Object.setPrototypeOf(WebContentsView.prototype, View.prototype) +Object.setPrototypeOf(WebContentsView.prototype, View.prototype); WebContentsView.prototype._init = function () { // Call parent class's _init. - View.prototype._init.call(this) -} + View.prototype._init.call(this); +}; -module.exports = WebContentsView +module.exports = WebContentsView; diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index 3e9b352a0a6f9..bedc202c1ecaa 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -1,27 +1,27 @@ -'use strict' +'use strict'; -const features = process.electronBinding('features') -const { EventEmitter } = require('events') -const electron = require('electron') -const path = require('path') -const url = require('url') -const { app, ipcMain, session } = electron +const features = process.electronBinding('features'); +const { EventEmitter } = require('events'); +const electron = require('electron'); +const path = require('path'); +const url = require('url'); +const { app, ipcMain, session } = electron; -const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager') -const NavigationController = require('@electron/internal/browser/navigation-controller') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') -const { MessagePortMain } = require('@electron/internal/browser/message-port-main') +const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager'); +const NavigationController = require('@electron/internal/browser/navigation-controller'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); +const { MessagePortMain } = require('@electron/internal/browser/message-port-main'); // session is not used here, the purpose is to make sure session is initalized // before the webContents module. // eslint-disable-next-line session -let nextId = 0 +let nextId = 0; const getNextId = function () { - return ++nextId -} + return ++nextId; +}; // Stock page sizes const PDFPageSizes = { @@ -62,7 +62,7 @@ const PDFPageSizes = { width_microns: 279400, custom_display_name: 'Tabloid' } -} +}; // Default printing setting const defaultPrintingSetting = { @@ -94,90 +94,90 @@ const defaultPrintingSetting = { // 2 = color - see ColorModel in //printing/print_job_constants.h color: 2, collate: true -} +}; // JavaScript implementations of WebContents. -const binding = process.electronBinding('web_contents') -const { WebContents } = binding +const binding = process.electronBinding('web_contents'); +const { WebContents } = binding; -Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype) -Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype) +Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype); +Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype); // WebContents::send(channel, args..) // WebContents::sendToAll(channel, args..) WebContents.prototype.send = function (channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } - const internal = false - const sendToAll = false + const internal = false; + const sendToAll = false; - return this._send(internal, sendToAll, channel, args) -} + return this._send(internal, sendToAll, channel, args); +}; WebContents.prototype.postMessage = function (...args) { if (Array.isArray(args[2])) { - args[2] = args[2].map(o => o instanceof MessagePortMain ? o._internalPort : o) + args[2] = args[2].map(o => o instanceof MessagePortMain ? o._internalPort : o); } - this._postMessage(...args) -} + this._postMessage(...args); +}; WebContents.prototype.sendToAll = function (channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } - const internal = false - const sendToAll = true + const internal = false; + const sendToAll = true; - return this._send(internal, sendToAll, channel, args) -} + return this._send(internal, sendToAll, channel, args); +}; WebContents.prototype._sendInternal = function (channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } - const internal = true - const sendToAll = false + const internal = true; + const sendToAll = false; - return this._send(internal, sendToAll, channel, args) -} + return this._send(internal, sendToAll, channel, args); +}; WebContents.prototype._sendInternalToAll = function (channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } - const internal = true - const sendToAll = true + const internal = true; + const sendToAll = true; - return this._send(internal, sendToAll, channel, args) -} + return this._send(internal, sendToAll, channel, args); +}; WebContents.prototype.sendToFrame = function (frameId, channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } else if (typeof frameId !== 'number') { - throw new Error('Missing required frameId argument') + throw new Error('Missing required frameId argument'); } - const internal = false - const sendToAll = false + const internal = false; + const sendToAll = false; - return this._sendToFrame(internal, sendToAll, frameId, channel, args) -} + return this._sendToFrame(internal, sendToAll, frameId, channel, args); +}; WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } else if (typeof frameId !== 'number') { - throw new Error('Missing required frameId argument') + throw new Error('Missing required frameId argument'); } - const internal = true - const sendToAll = false + const internal = true; + const sendToAll = false; - return this._sendToFrame(internal, sendToAll, frameId, channel, args) -} + return this._sendToFrame(internal, sendToAll, frameId, channel, args); +}; // Following methods are mapped to webFrame. const webFrameMethods = [ @@ -185,138 +185,138 @@ const webFrameMethods = [ 'insertText', 'removeInsertedCSS', 'setVisualZoomLevelLimits' -] +]; for (const method of webFrameMethods) { WebContents.prototype[method] = function (...args) { - return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args) - } + return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args); + }; } const waitTillCanExecuteJavaScript = async (webContents) => { - if (webContents.getURL() && !webContents.isLoadingMainFrame()) return + if (webContents.getURL() && !webContents.isLoadingMainFrame()) return; return new Promise((resolve) => { webContents.once('did-stop-loading', () => { - resolve() - }) - }) -} + resolve(); + }); + }); +}; // Make sure WebContents::executeJavaScript would run the code only when the // WebContents has been loaded. WebContents.prototype.executeJavaScript = async function (code, hasUserGesture) { - await waitTillCanExecuteJavaScript(this) - return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture) -} + await waitTillCanExecuteJavaScript(this); + return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture); +}; WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (code, hasUserGesture) { - await waitTillCanExecuteJavaScript(this) - return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', code, hasUserGesture) -} + await waitTillCanExecuteJavaScript(this); + return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', code, hasUserGesture); +}; // Translate the options of printToPDF. WebContents.prototype.printToPDF = function (options) { const printSettings = { ...defaultPrintingSetting, requestID: getNextId() - } + }; if (options.landscape !== undefined) { if (typeof options.landscape !== 'boolean') { - const error = new Error('landscape must be a Boolean') - return Promise.reject(error) + const error = new Error('landscape must be a Boolean'); + return Promise.reject(error); } - printSettings.landscape = options.landscape + printSettings.landscape = options.landscape; } if (options.scaleFactor !== undefined) { if (typeof options.scaleFactor !== 'number') { - const error = new Error('scaleFactor must be a Number') - return Promise.reject(error) + const error = new Error('scaleFactor must be a Number'); + return Promise.reject(error); } - printSettings.scaleFactor = options.scaleFactor + printSettings.scaleFactor = options.scaleFactor; } if (options.marginsType !== undefined) { if (typeof options.marginsType !== 'number') { - const error = new Error('marginsType must be a Number') - return Promise.reject(error) + const error = new Error('marginsType must be a Number'); + return Promise.reject(error); } - printSettings.marginsType = options.marginsType + printSettings.marginsType = options.marginsType; } if (options.printSelectionOnly !== undefined) { if (typeof options.printSelectionOnly !== 'boolean') { - const error = new Error('printSelectionOnly must be a Boolean') - return Promise.reject(error) + const error = new Error('printSelectionOnly must be a Boolean'); + return Promise.reject(error); } - printSettings.shouldPrintSelectionOnly = options.printSelectionOnly + printSettings.shouldPrintSelectionOnly = options.printSelectionOnly; } if (options.printBackground !== undefined) { if (typeof options.printBackground !== 'boolean') { - const error = new Error('printBackground must be a Boolean') - return Promise.reject(error) + const error = new Error('printBackground must be a Boolean'); + return Promise.reject(error); } - printSettings.shouldPrintBackgrounds = options.printBackground + printSettings.shouldPrintBackgrounds = options.printBackground; } if (options.pageRanges !== undefined) { - const pageRanges = options.pageRanges + const pageRanges = options.pageRanges; if (!Object.prototype.hasOwnProperty.call(pageRanges, 'from') || !Object.prototype.hasOwnProperty.call(pageRanges, 'to')) { - const error = new Error('pageRanges must be an Object with \'from\' and \'to\' properties') - return Promise.reject(error) + const error = new Error('pageRanges must be an Object with \'from\' and \'to\' properties'); + return Promise.reject(error); } if (typeof pageRanges.from !== 'number') { - const error = new Error('pageRanges.from must be a Number') - return Promise.reject(error) + const error = new Error('pageRanges.from must be a Number'); + return Promise.reject(error); } if (typeof pageRanges.to !== 'number') { - const error = new Error('pageRanges.to must be a Number') - return Promise.reject(error) + const error = new Error('pageRanges.to must be a Number'); + return Promise.reject(error); } // Chromium uses 1-based page ranges, so increment each by 1. printSettings.pageRange = [{ from: pageRanges.from + 1, to: pageRanges.to + 1 - }] + }]; } if (options.headerFooter !== undefined) { - const headerFooter = options.headerFooter - printSettings.headerFooterEnabled = true + const headerFooter = options.headerFooter; + printSettings.headerFooterEnabled = true; if (typeof headerFooter === 'object') { if (!headerFooter.url || !headerFooter.title) { - const error = new Error('url and title properties are required for headerFooter') - return Promise.reject(error) + const error = new Error('url and title properties are required for headerFooter'); + return Promise.reject(error); } if (typeof headerFooter.title !== 'string') { - const error = new Error('headerFooter.title must be a String') - return Promise.reject(error) + const error = new Error('headerFooter.title must be a String'); + return Promise.reject(error); } - printSettings.title = headerFooter.title + printSettings.title = headerFooter.title; if (typeof headerFooter.url !== 'string') { - const error = new Error('headerFooter.url must be a String') - return Promise.reject(error) + const error = new Error('headerFooter.url must be a String'); + return Promise.reject(error); } - printSettings.url = headerFooter.url + printSettings.url = headerFooter.url; } else { - const error = new Error('headerFooter must be an Object') - return Promise.reject(error) + const error = new Error('headerFooter must be an Object'); + return Promise.reject(error); } } // Optionally set size for PDF. if (options.pageSize !== undefined) { - const pageSize = options.pageSize + const pageSize = options.pageSize; if (typeof pageSize === 'object') { if (!pageSize.height || !pageSize.width) { - const error = new Error('height and width properties are required for pageSize') - return Promise.reject(error) + const error = new Error('height and width properties are required for pageSize'); + return Promise.reject(error); } // Dimensions in Microns // 1 meter = 10^6 microns @@ -325,28 +325,28 @@ WebContents.prototype.printToPDF = function (options) { custom_display_name: 'Custom', height_microns: Math.ceil(pageSize.height), width_microns: Math.ceil(pageSize.width) - } + }; } else if (PDFPageSizes[pageSize]) { - printSettings.mediaSize = PDFPageSizes[pageSize] + printSettings.mediaSize = PDFPageSizes[pageSize]; } else { - const error = new Error(`Unsupported pageSize: ${pageSize}`) - return Promise.reject(error) + const error = new Error(`Unsupported pageSize: ${pageSize}`); + return Promise.reject(error); } } else { - printSettings.mediaSize = PDFPageSizes.A4 + printSettings.mediaSize = PDFPageSizes.A4; } // Chromium expects this in a 0-100 range number, not as float - printSettings.scaleFactor = Math.ceil(printSettings.scaleFactor) % 100 + printSettings.scaleFactor = Math.ceil(printSettings.scaleFactor) % 100; // PrinterType enum from //printing/print_job_constants.h - printSettings.printerType = 2 + printSettings.printerType = 2; if (features.isPrintingEnabled()) { - return this._printToPDF(printSettings) + return this._printToPDF(printSettings); } else { - const error = new Error('Printing feature is disabled') - return Promise.reject(error) + const error = new Error('Printing feature is disabled'); + return Promise.reject(error); } -} +}; WebContents.prototype.print = function (options = {}, callback) { // TODO(codebytere): deduplicate argument sanitization by moving rest of @@ -354,10 +354,10 @@ WebContents.prototype.print = function (options = {}, callback) { if (typeof options === 'object') { // Optionally set size for PDF. if (options.pageSize !== undefined) { - const pageSize = options.pageSize + const pageSize = options.pageSize; if (typeof pageSize === 'object') { if (!pageSize.height || !pageSize.width) { - throw new Error('height and width properties are required for pageSize') + throw new Error('height and width properties are required for pageSize'); } // Dimensions in Microns - 1 meter = 10^6 microns options.mediaSize = { @@ -365,40 +365,40 @@ WebContents.prototype.print = function (options = {}, callback) { custom_display_name: 'Custom', height_microns: Math.ceil(pageSize.height), width_microns: Math.ceil(pageSize.width) - } + }; } else if (PDFPageSizes[pageSize]) { - options.mediaSize = PDFPageSizes[pageSize] + options.mediaSize = PDFPageSizes[pageSize]; } else { - throw new Error(`Unsupported pageSize: ${pageSize}`) + throw new Error(`Unsupported pageSize: ${pageSize}`); } } } if (features.isPrintingEnabled()) { if (callback) { - this._print(options, callback) + this._print(options, callback); } else { - this._print(options) + this._print(options); } } else { - console.error('Error: Printing feature is disabled.') + console.error('Error: Printing feature is disabled.'); } -} +}; WebContents.prototype.getPrinters = function () { if (features.isPrintingEnabled()) { - return this._getPrinters() + return this._getPrinters(); } else { - console.error('Error: Printing feature is disabled.') - return [] + console.error('Error: Printing feature is disabled.'); + return []; } -} +}; WebContents.prototype.loadFile = function (filePath, options = {}) { if (typeof filePath !== 'string') { - throw new Error('Must pass filePath as a string') + throw new Error('Must pass filePath as a string'); } - const { query, search, hash } = options + const { query, search, hash } = options; return this.loadURL(url.format({ protocol: 'file', @@ -407,104 +407,104 @@ WebContents.prototype.loadFile = function (filePath, options = {}) { query, search, hash - })) -} + })); +}; const addReplyToEvent = (event) => { event.reply = (...args) => { - event.sender.sendToFrame(event.frameId, ...args) - } -} + event.sender.sendToFrame(event.frameId, ...args); + }; +}; const addReplyInternalToEvent = (event) => { Object.defineProperty(event, '_replyInternal', { configurable: false, enumerable: false, value: (...args) => { - event.sender._sendToFrameInternal(event.frameId, ...args) + event.sender._sendToFrameInternal(event.frameId, ...args); } - }) -} + }); +}; const addReturnValueToEvent = (event) => { Object.defineProperty(event, 'returnValue', { set: (value) => event.sendReply([value]), get: () => {} - }) -} + }); +}; // Add JavaScript wrappers for WebContents class. WebContents.prototype._init = function () { // The navigation controller. - NavigationController.call(this, this) + NavigationController.call(this, this); // Every remote callback from renderer process would add a listener to the // render-view-deleted event, so ignore the listeners warning. - this.setMaxListeners(0) + this.setMaxListeners(0); // Dispatch IPC messages to the ipc module. this.on('-ipc-message', function (event, internal, channel, args) { if (internal) { - addReplyInternalToEvent(event) - ipcMainInternal.emit(channel, event, ...args) + addReplyInternalToEvent(event); + ipcMainInternal.emit(channel, event, ...args); } else { - addReplyToEvent(event) - this.emit('ipc-message', event, channel, ...args) - ipcMain.emit(channel, event, ...args) + addReplyToEvent(event); + this.emit('ipc-message', event, channel, ...args); + ipcMain.emit(channel, event, ...args); } - }) + }); this.on('-ipc-invoke', function (event, internal, channel, args) { - event._reply = (result) => event.sendReply({ result }) + event._reply = (result) => event.sendReply({ result }); event._throw = (error) => { - console.error(`Error occurred in handler for '${channel}':`, error) - event.sendReply({ error: error.toString() }) - } - const target = internal ? ipcMainInternal : ipcMain + console.error(`Error occurred in handler for '${channel}':`, error); + event.sendReply({ error: error.toString() }); + }; + const target = internal ? ipcMainInternal : ipcMain; if (target._invokeHandlers.has(channel)) { - target._invokeHandlers.get(channel)(event, ...args) + target._invokeHandlers.get(channel)(event, ...args); } else { - event._throw(`No handler registered for '${channel}'`) + event._throw(`No handler registered for '${channel}'`); } - }) + }); this.on('-ipc-message-sync', function (event, internal, channel, args) { - addReturnValueToEvent(event) + addReturnValueToEvent(event); if (internal) { - addReplyInternalToEvent(event) - ipcMainInternal.emit(channel, event, ...args) + addReplyInternalToEvent(event); + ipcMainInternal.emit(channel, event, ...args); } else { - addReplyToEvent(event) - this.emit('ipc-message-sync', event, channel, ...args) - ipcMain.emit(channel, event, ...args) + addReplyToEvent(event); + this.emit('ipc-message-sync', event, channel, ...args); + ipcMain.emit(channel, event, ...args); } - }) + }); this.on('-ipc-ports', function (event, internal, channel, message, ports) { - event.ports = ports.map(p => new MessagePortMain(p)) - ipcMain.emit(channel, event, message) - }) + event.ports = ports.map(p => new MessagePortMain(p)); + ipcMain.emit(channel, event, message); + }); // Handle context menu action request from pepper plugin. this.on('pepper-context-menu', function (event, params, callback) { // Access Menu via electron.Menu to prevent circular require. - const menu = electron.Menu.buildFromTemplate(params.menu) + const menu = electron.Menu.buildFromTemplate(params.menu); menu.popup({ window: event.sender.getOwnerBrowserWindow(), x: params.x, y: params.y, callback - }) - }) + }); + }); this.on('crashed', (event, ...args) => { - app.emit('renderer-process-crashed', event, this, ...args) - }) + app.emit('renderer-process-crashed', event, this, ...args); + }); // The devtools requests the webContents to reload. this.on('devtools-reload-page', function () { - this.reload() - }) + this.reload(); + }); // Handle window.open for BrowserWindow and BrowserView. if (['browserView', 'window'].includes(this.getType())) { @@ -516,9 +516,9 @@ WebContents.prototype._init = function () { show: true, width: 800, height: 600 - } - internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures, postData) - }) + }; + internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures, postData); + }); // Create a new browser window for the native implementation of // "window.open", used in sandbox and nativeWindowOpen mode. @@ -526,8 +526,8 @@ WebContents.prototype._init = function () { userGesture, left, top, width, height, url, frameName) => { if ((disposition !== 'foreground-tab' && disposition !== 'new-window' && disposition !== 'background-tab')) { - event.preventDefault() - return + event.preventDefault(); + return; } const options = { @@ -537,70 +537,70 @@ WebContents.prototype._init = function () { width: width || 800, height: height || 600, webContents - } - const referrer = { url: '', policy: 'default' } - internalWindowOpen(event, url, referrer, frameName, disposition, options) - }) + }; + const referrer = { url: '', policy: 'default' }; + internalWindowOpen(event, url, referrer, frameName, disposition, options); + }); } this.on('login', (event, ...args) => { - app.emit('login', event, this, ...args) - }) + app.emit('login', event, this, ...args); + }); - const event = process.electronBinding('event').createEmpty() - app.emit('web-contents-created', event, this) + const event = process.electronBinding('event').createEmpty(); + app.emit('web-contents-created', event, this); // Properties Object.defineProperty(this, 'audioMuted', { get: () => this.isAudioMuted(), set: (muted) => this.setAudioMuted(muted) - }) + }); Object.defineProperty(this, 'userAgent', { get: () => this.getUserAgent(), set: (agent) => this.setUserAgent(agent) - }) + }); Object.defineProperty(this, 'zoomLevel', { get: () => this.getZoomLevel(), set: (level) => this.setZoomLevel(level) - }) + }); Object.defineProperty(this, 'zoomFactor', { get: () => this.getZoomFactor(), set: (factor) => this.setZoomFactor(factor) - }) + }); Object.defineProperty(this, 'frameRate', { get: () => this.getFrameRate(), set: (rate) => this.setFrameRate(rate) - }) -} + }); +}; // Public APIs. module.exports = { create (options = {}) { - return binding.create(options) + return binding.create(options); }, fromId (id) { - return binding.fromId(id) + return binding.fromId(id); }, getFocusedWebContents () { - let focused = null + let focused = null; for (const contents of binding.getAllWebContents()) { - if (!contents.isFocused()) continue - if (focused == null) focused = contents + if (!contents.isFocused()) continue; + if (focused == null) focused = contents; // Return webview web contents which may be embedded inside another // web contents that is also reporting as focused - if (contents.getType() === 'webview') return contents + if (contents.getType() === 'webview') return contents; } - return focused + return focused; }, getAllWebContents () { - return binding.getAllWebContents() + return binding.getAllWebContents(); } -} +}; diff --git a/lib/browser/chrome-extension-shim.js b/lib/browser/chrome-extension-shim.js index 8eecd67b46d51..4f4a651ea9ba8 100644 --- a/lib/browser/chrome-extension-shim.js +++ b/lib/browser/chrome-extension-shim.js @@ -1,37 +1,37 @@ -'use strict' +'use strict'; // This is a temporary shim to aid in transition from the old // BrowserWindow-based extensions stuff to the new native-backed extensions // API. if (!process.electronBinding('features').isExtensionsEnabled()) { - throw new Error('Attempted to load JS chrome-extension shim without //extensions support enabled') + throw new Error('Attempted to load JS chrome-extension shim without //extensions support enabled'); } -const { app, session, BrowserWindow, deprecate } = require('electron') +const { app, session, BrowserWindow, deprecate } = require('electron'); app.whenReady().then(function () { const addExtension = function (srcDirectory) { - return session.defaultSession.loadExtension(srcDirectory) - } + return session.defaultSession.loadExtension(srcDirectory); + }; const removeExtension = function (name) { - const extension = session.defaultSession.getAllExtensions().find(e => e.name === name) - if (extension) { session.defaultSession.removeExtension(extension.id) } - } + const extension = session.defaultSession.getAllExtensions().find(e => e.name === name); + if (extension) { session.defaultSession.removeExtension(extension.id); } + }; const getExtensions = function () { - const extensions = {} + const extensions = {}; session.defaultSession.getAllExtensions().forEach(e => { - extensions[e.name] = e - }) - return extensions - } + extensions[e.name] = e; + }); + return extensions; + }; - BrowserWindow.addExtension = deprecate.moveAPI(addExtension, 'BrowserWindow.addExtension', 'session.loadExtension') - BrowserWindow.removeExtension = deprecate.moveAPI(removeExtension, 'BrowserWindow.removeExtension', 'session.removeExtension') - BrowserWindow.getExtensions = deprecate.moveAPI(getExtensions, 'BrowserWindow.getExtensions', 'session.getAllExtensions') - BrowserWindow.addDevToolsExtension = deprecate.moveAPI(addExtension, 'BrowserWindow.addDevToolsExtension', 'session.loadExtension') - BrowserWindow.removeDevToolsExtension = deprecate.moveAPI(removeExtension, 'BrowserWindow.removeDevToolsExtension', 'session.removeExtension') - BrowserWindow.getDevToolsExtensions = deprecate.moveAPI(getExtensions, 'BrowserWindow.getDevToolsExtensions', 'session.getAllExtensions') -}) + BrowserWindow.addExtension = deprecate.moveAPI(addExtension, 'BrowserWindow.addExtension', 'session.loadExtension'); + BrowserWindow.removeExtension = deprecate.moveAPI(removeExtension, 'BrowserWindow.removeExtension', 'session.removeExtension'); + BrowserWindow.getExtensions = deprecate.moveAPI(getExtensions, 'BrowserWindow.getExtensions', 'session.getAllExtensions'); + BrowserWindow.addDevToolsExtension = deprecate.moveAPI(addExtension, 'BrowserWindow.addDevToolsExtension', 'session.loadExtension'); + BrowserWindow.removeDevToolsExtension = deprecate.moveAPI(removeExtension, 'BrowserWindow.removeDevToolsExtension', 'session.removeExtension'); + BrowserWindow.getDevToolsExtensions = deprecate.moveAPI(getExtensions, 'BrowserWindow.getDevToolsExtensions', 'session.getAllExtensions'); +}); diff --git a/lib/browser/chrome-extension.js b/lib/browser/chrome-extension.js index 2bb9c4e5f5ecd..a4232a6f9d9e7 100644 --- a/lib/browser/chrome-extension.js +++ b/lib/browser/chrome-extension.js @@ -1,105 +1,105 @@ -'use strict' +'use strict'; if (process.electronBinding('features').isExtensionsEnabled()) { - throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled') + throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled'); } -const { app, webContents, BrowserWindow } = require('electron') -const { getAllWebContents } = process.electronBinding('web_contents') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') +const { app, webContents, BrowserWindow } = require('electron'); +const { getAllWebContents } = process.electronBinding('web_contents'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); -const { Buffer } = require('buffer') -const fs = require('fs') -const path = require('path') -const url = require('url') -const util = require('util') +const { Buffer } = require('buffer'); +const fs = require('fs'); +const path = require('path'); +const url = require('url'); +const util = require('util'); // Mapping between extensionId(hostname) and manifest. -const manifestMap = {} // extensionId => manifest -const manifestNameMap = {} // name => manifest -const devToolsExtensionNames = new Set() +const manifestMap = {}; // extensionId => manifest +const manifestNameMap = {}; // name => manifest +const devToolsExtensionNames = new Set(); const generateExtensionIdFromName = function (name) { - return name.replace(/[\W_]+/g, '-').toLowerCase() -} + return name.replace(/[\W_]+/g, '-').toLowerCase(); +}; const isWindowOrWebView = function (webContents) { - const type = webContents.getType() - return type === 'window' || type === 'webview' -} + const type = webContents.getType(); + return type === 'window' || type === 'webview'; +}; const isBackgroundPage = function (webContents) { - return webContents.getType() === 'backgroundPage' -} + return webContents.getType() === 'backgroundPage'; +}; // Create or get manifest object from |srcDirectory|. const getManifestFromPath = function (srcDirectory) { - let manifest - let manifestContent + let manifest; + let manifestContent; try { - manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json')) + manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json')); } catch (readError) { - console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`) - console.warn(readError.stack || readError) - throw readError + console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`); + console.warn(readError.stack || readError); + throw readError; } try { - manifest = JSON.parse(manifestContent) + manifest = JSON.parse(manifestContent); } catch (parseError) { - console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`) - console.warn(parseError.stack || parseError) - throw parseError + console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`); + console.warn(parseError.stack || parseError); + throw parseError; } if (!manifestNameMap[manifest.name]) { - const extensionId = generateExtensionIdFromName(manifest.name) - manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest + const extensionId = generateExtensionIdFromName(manifest.name); + manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest; let extensionURL = url.format({ protocol: 'chrome-extension', slashes: true, hostname: extensionId, pathname: manifest.devtools_page - }) + }); // Chromium requires that startPage matches '([^:]+:\/\/[^/]*)\/' // We also can't use the file:// protocol here since that would make Chromium // treat all extension resources as being relative to root which we don't want. - if (!manifest.devtools_page) extensionURL += '/' + if (!manifest.devtools_page) extensionURL += '/'; Object.assign(manifest, { srcDirectory: srcDirectory, extensionId: extensionId, startPage: extensionURL - }) + }); - return manifest + return manifest; } else if (manifest && manifest.name) { - console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`) - return manifest + console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`); + return manifest; } -} +}; // Manage the background pages. -const backgroundPages = {} +const backgroundPages = {}; const startBackgroundPages = function (manifest) { - if (backgroundPages[manifest.extensionId] || !manifest.background) return + if (backgroundPages[manifest.extensionId] || !manifest.background) return; - let html - let name + let html; + let name; if (manifest.background.page) { - name = manifest.background.page - html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page)) + name = manifest.background.page; + html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page)); } else { - name = '_generated_background_page.html' + name = '_generated_background_page.html'; const scripts = manifest.background.scripts.map((name) => { - return `` - }).join('') - html = Buffer.from(`${scripts}`) + return ``; + }).join(''); + html = Buffer.from(`${scripts}`); } const contents = webContents.create({ @@ -107,36 +107,36 @@ const startBackgroundPages = function (manifest) { type: 'backgroundPage', sandbox: true, enableRemoteModule: false - }) - backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name } + }); + backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name }; contents.loadURL(url.format({ protocol: 'chrome-extension', slashes: true, hostname: manifest.extensionId, pathname: name - })) -} + })); +}; const removeBackgroundPages = function (manifest) { - if (!backgroundPages[manifest.extensionId]) return + if (!backgroundPages[manifest.extensionId]) return; - backgroundPages[manifest.extensionId].webContents.destroy() - delete backgroundPages[manifest.extensionId] -} + backgroundPages[manifest.extensionId].webContents.destroy(); + delete backgroundPages[manifest.extensionId]; +}; const sendToBackgroundPages = function (...args) { for (const page of Object.values(backgroundPages)) { if (!page.webContents.isDestroyed()) { - page.webContents._sendInternalToAll(...args) + page.webContents._sendInternalToAll(...args); } } -} +}; // Dispatch web contents events to Chrome APIs const hookWebContentsEvents = function (webContents) { - const tabId = webContents.id + const tabId = webContents.id; - sendToBackgroundPages('CHROME_TABS_ONCREATED') + sendToBackgroundPages('CHROME_TABS_ONCREATED'); webContents.on('will-navigate', (event, url) => { sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', { @@ -146,8 +146,8 @@ const hookWebContentsEvents = function (webContents) { tabId: tabId, timeStamp: Date.now(), url: url - }) - }) + }); + }); webContents.on('did-navigate', (event, url) => { sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', { @@ -157,189 +157,189 @@ const hookWebContentsEvents = function (webContents) { tabId: tabId, timeStamp: Date.now(), url: url - }) - }) + }); + }); webContents.once('destroyed', () => { - sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId) - }) -} + sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId); + }); +}; // Handle the chrome.* API messages. -let nextId = 0 +let nextId = 0; ipcMainUtils.handleSync('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) { if (isBackgroundPage(event.sender)) { - throw new Error('chrome.runtime.connect is not supported in background page') + throw new Error('chrome.runtime.connect is not supported in background page'); } - const page = backgroundPages[extensionId] + const page = backgroundPages[extensionId]; if (!page || page.webContents.isDestroyed()) { - throw new Error(`Connect to unknown extension ${extensionId}`) + throw new Error(`Connect to unknown extension ${extensionId}`); } - const tabId = page.webContents.id - const portId = ++nextId + const tabId = page.webContents.id; + const portId = ++nextId; event.sender.once('render-view-deleted', () => { - if (page.webContents.isDestroyed()) return - page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`) - }) - page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo) + if (page.webContents.isDestroyed()) return; + page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`); + }); + page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo); - return { tabId, portId } -}) + return { tabId, portId }; +}); ipcMainUtils.handleSync('CHROME_EXTENSION_MANIFEST', function (event, extensionId) { - const manifest = manifestMap[extensionId] + const manifest = manifestMap[extensionId]; if (!manifest) { - throw new Error(`Invalid extensionId: ${extensionId}`) + throw new Error(`Invalid extensionId: ${extensionId}`); } - return manifest -}) + return manifest; +}); ipcMainInternal.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) { if (isBackgroundPage(event.sender)) { - throw new Error('chrome.runtime.sendMessage is not supported in background page') + throw new Error('chrome.runtime.sendMessage is not supported in background page'); } - const page = backgroundPages[extensionId] + const page = backgroundPages[extensionId]; if (!page || page.webContents.isDestroyed()) { - throw new Error(`Connect to unknown extension ${extensionId}`) + throw new Error(`Connect to unknown extension ${extensionId}`); } - return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message) -}) + return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message); +}); ipcMainInternal.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) { - const contents = webContents.fromId(tabId) + const contents = webContents.fromId(tabId); if (!contents) { - throw new Error(`Sending message to unknown tab ${tabId}`) + throw new Error(`Sending message to unknown tab ${tabId}`); } - const senderTabId = isBackgroundPage(event.sender) ? null : event.sender.id + const senderTabId = isBackgroundPage(event.sender) ? null : event.sender.id; - return ipcMainUtils.invokeInWebContents(contents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message) -}) + return ipcMainUtils.invokeInWebContents(contents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message); +}); const getLanguage = () => { - return app.getLocale().replace(/-.*$/, '').toLowerCase() -} + return app.getLocale().replace(/-.*$/, '').toLowerCase(); +}; const getMessagesPath = (extensionId) => { - const metadata = manifestMap[extensionId] + const metadata = manifestMap[extensionId]; if (!metadata) { - throw new Error(`Invalid extensionId: ${extensionId}`) + throw new Error(`Invalid extensionId: ${extensionId}`); } - const localesDirectory = path.join(metadata.srcDirectory, '_locales') - const language = getLanguage() + const localesDirectory = path.join(metadata.srcDirectory, '_locales'); + const language = getLanguage(); try { - const filename = path.join(localesDirectory, language, 'messages.json') - fs.accessSync(filename, fs.constants.R_OK) - return filename + const filename = path.join(localesDirectory, language, 'messages.json'); + fs.accessSync(filename, fs.constants.R_OK); + return filename; } catch { - const defaultLocale = metadata.default_locale || 'en' - return path.join(localesDirectory, defaultLocale, 'messages.json') + const defaultLocale = metadata.default_locale || 'en'; + return path.join(localesDirectory, defaultLocale, 'messages.json'); } -} +}; ipcMainUtils.handleSync('CHROME_GET_MESSAGES', async function (event, extensionId) { - const messagesPath = getMessagesPath(extensionId) - return fs.promises.readFile(messagesPath, 'utf8') -}) + const messagesPath = getMessagesPath(extensionId); + return fs.promises.readFile(messagesPath, 'utf8'); +}); -const validStorageTypes = new Set(['sync', 'local']) +const validStorageTypes = new Set(['sync', 'local']); const getChromeStoragePath = (storageType, extensionId) => { if (!validStorageTypes.has(storageType)) { - throw new Error(`Invalid storageType: ${storageType}`) + throw new Error(`Invalid storageType: ${storageType}`); } if (!manifestMap[extensionId]) { - throw new Error(`Invalid extensionId: ${extensionId}`) + throw new Error(`Invalid extensionId: ${extensionId}`); } - return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`) -} + return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`); +}; ipcMainInternal.handle('CHROME_STORAGE_READ', async function (event, storageType, extensionId) { - const filePath = getChromeStoragePath(storageType, extensionId) + const filePath = getChromeStoragePath(storageType, extensionId); try { - return await fs.promises.readFile(filePath, 'utf8') + return await fs.promises.readFile(filePath, 'utf8'); } catch (error) { if (error.code === 'ENOENT') { - return null + return null; } else { - throw error + throw error; } } -}) +}); ipcMainInternal.handle('CHROME_STORAGE_WRITE', async function (event, storageType, extensionId, data) { - const filePath = getChromeStoragePath(storageType, extensionId) + const filePath = getChromeStoragePath(storageType, extensionId); try { - await fs.promises.mkdir(path.dirname(filePath), { recursive: true }) + await fs.promises.mkdir(path.dirname(filePath), { recursive: true }); } catch { // we just ignore the errors of mkdir } - return fs.promises.writeFile(filePath, data, 'utf8') -}) + return fs.promises.writeFile(filePath, data, 'utf8'); +}); const isChromeExtension = function (pageURL) { - const { protocol } = url.parse(pageURL) - return protocol === 'chrome-extension:' -} + const { protocol } = url.parse(pageURL); + return protocol === 'chrome-extension:'; +}; const assertChromeExtension = function (contents, api) { - const pageURL = contents._getURL() + const pageURL = contents._getURL(); if (!isChromeExtension(pageURL)) { - console.error(`Blocked ${pageURL} from calling ${api}`) - throw new Error(`Blocked ${api}`) + console.error(`Blocked ${pageURL} from calling ${api}`); + throw new Error(`Blocked ${api}`); } -} +}; ipcMainInternal.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) { - assertChromeExtension(event.sender, 'chrome.tabs.executeScript()') + assertChromeExtension(event.sender, 'chrome.tabs.executeScript()'); - const contents = webContents.fromId(tabId) + const contents = webContents.fromId(tabId); if (!contents) { - throw new Error(`Sending message to unknown tab ${tabId}`) + throw new Error(`Sending message to unknown tab ${tabId}`); } - let code, url + let code, url; if (details.file) { - const manifest = manifestMap[extensionId] - code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file))) - url = `chrome-extension://${extensionId}${details.file}` + const manifest = manifestMap[extensionId]; + code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file))); + url = `chrome-extension://${extensionId}${details.file}`; } else { - code = details.code - url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js` + code = details.code; + url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`; } - return ipcMainUtils.invokeInWebContents(contents, false, 'CHROME_TABS_EXECUTE_SCRIPT', extensionId, url, code) -}) + return ipcMainUtils.invokeInWebContents(contents, false, 'CHROME_TABS_EXECUTE_SCRIPT', extensionId, url, code); +}); exports.getContentScripts = () => { - return Object.values(contentScripts) -} + return Object.values(contentScripts); +}; // Transfer the content scripts to renderer. -const contentScripts = {} +const contentScripts = {}; const injectContentScripts = function (manifest) { - if (contentScripts[manifest.name] || !manifest.content_scripts) return + if (contentScripts[manifest.name] || !manifest.content_scripts) return; const readArrayOfFiles = function (relativePath) { return { url: `chrome-extension://${manifest.extensionId}/${relativePath}`, code: String(fs.readFileSync(path.join(manifest.srcDirectory, relativePath))) - } - } + }; + }; const contentScriptToEntry = function (script) { return { @@ -348,25 +348,25 @@ const injectContentScripts = function (manifest) { css: script.css ? script.css.map(readArrayOfFiles) : [], runAt: script.run_at || 'document_idle', allFrames: script.all_frames || false - } - } + }; + }; try { const entry = { extensionId: manifest.extensionId, contentScripts: manifest.content_scripts.map(contentScriptToEntry) - } - contentScripts[manifest.name] = entry + }; + contentScripts[manifest.name] = entry; } catch (e) { - console.error('Failed to read content scripts', e) + console.error('Failed to read content scripts', e); } -} +}; const removeContentScripts = function (manifest) { - if (!contentScripts[manifest.name]) return + if (!contentScripts[manifest.name]) return; - delete contentScripts[manifest.name] -} + delete contentScripts[manifest.name]; +}; // Transfer the |manifest| to a format that can be recognized by the // |DevToolsAPI.addExtensions|. @@ -376,167 +376,167 @@ const manifestToExtensionInfo = function (manifest) { srcDirectory: manifest.srcDirectory, name: manifest.name, exposeExperimentalAPIs: true - } -} + }; +}; // Load the extensions for the window. const loadExtension = function (manifest) { - startBackgroundPages(manifest) - injectContentScripts(manifest) -} + startBackgroundPages(manifest); + injectContentScripts(manifest); +}; const loadDevToolsExtensions = function (win, manifests) { - if (!win.devToolsWebContents) return + if (!win.devToolsWebContents) return; - manifests.forEach(loadExtension) + manifests.forEach(loadExtension); - const extensionInfoArray = manifests.map(manifestToExtensionInfo) + const extensionInfoArray = manifests.map(manifestToExtensionInfo); extensionInfoArray.forEach((extension) => { - win.devToolsWebContents._grantOriginAccess(extension.startPage) - }) + win.devToolsWebContents._grantOriginAccess(extension.startPage); + }); extensionInfoArray.forEach((extensionInfo) => { - const info = JSON.stringify(extensionInfo) - win.devToolsWebContents.executeJavaScript(`Extensions.extensionServer._addExtension(${info})`) - }) -} + const info = JSON.stringify(extensionInfo); + win.devToolsWebContents.executeJavaScript(`Extensions.extensionServer._addExtension(${info})`); + }); +}; app.on('web-contents-created', function (event, webContents) { - if (!isWindowOrWebView(webContents)) return + if (!isWindowOrWebView(webContents)) return; - hookWebContentsEvents(webContents) + hookWebContentsEvents(webContents); webContents.on('devtools-opened', function () { - loadDevToolsExtensions(webContents, Object.values(manifestMap)) - }) -}) + loadDevToolsExtensions(webContents, Object.values(manifestMap)); + }); +}); // The chrome-extension: can map a extension URL request to real file path. const chromeExtensionHandler = function (request, callback) { - const parsed = url.parse(request.url) - if (!parsed.hostname || !parsed.path) return callback() + const parsed = url.parse(request.url); + if (!parsed.hostname || !parsed.path) return callback(); - const manifest = manifestMap[parsed.hostname] - if (!manifest) return callback() + const manifest = manifestMap[parsed.hostname]; + if (!manifest) return callback(); - const page = backgroundPages[parsed.hostname] + const page = backgroundPages[parsed.hostname]; if (page && parsed.path === `/${page.name}`) { // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal return callback({ mimeType: 'text/html', data: page.html - }) + }); } fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) { if (err) { // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal - return callback(-6) // FILE_NOT_FOUND + return callback(-6); // FILE_NOT_FOUND } else { - return callback(content) + return callback(content); } - }) -} + }); +}; app.on('session-created', function (ses) { - ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler) -}) + ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler); +}); // The persistent path of "DevTools Extensions" preference file. -let loadedDevToolsExtensionsPath = null +let loadedDevToolsExtensionsPath = null; app.on('will-quit', function () { try { const loadedDevToolsExtensions = Array.from(devToolsExtensionNames) - .map(name => manifestNameMap[name].srcDirectory) + .map(name => manifestNameMap[name].srcDirectory); if (loadedDevToolsExtensions.length > 0) { try { - fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath)) + fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath)); } catch { // Ignore error } - fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions)) + fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions)); } else { - fs.unlinkSync(loadedDevToolsExtensionsPath) + fs.unlinkSync(loadedDevToolsExtensionsPath); } } catch { // Ignore error } -}) +}); // We can not use protocol or BrowserWindow until app is ready. app.whenReady().then(function () { // The public API to add/remove extensions. BrowserWindow.addExtension = function (srcDirectory) { - const manifest = getManifestFromPath(srcDirectory) + const manifest = getManifestFromPath(srcDirectory); if (manifest) { - loadExtension(manifest) + loadExtension(manifest); for (const webContents of getAllWebContents()) { if (isWindowOrWebView(webContents)) { - loadDevToolsExtensions(webContents, [manifest]) + loadDevToolsExtensions(webContents, [manifest]); } } - return manifest.name + return manifest.name; } - } + }; BrowserWindow.removeExtension = function (name) { - const manifest = manifestNameMap[name] - if (!manifest) return + const manifest = manifestNameMap[name]; + if (!manifest) return; - removeBackgroundPages(manifest) - removeContentScripts(manifest) - delete manifestMap[manifest.extensionId] - delete manifestNameMap[name] - } + removeBackgroundPages(manifest); + removeContentScripts(manifest); + delete manifestMap[manifest.extensionId]; + delete manifestNameMap[name]; + }; BrowserWindow.getExtensions = function () { - const extensions = {} + const extensions = {}; Object.keys(manifestNameMap).forEach(function (name) { - const manifest = manifestNameMap[name] - extensions[name] = { name: manifest.name, version: manifest.version } - }) - return extensions - } + const manifest = manifestNameMap[name]; + extensions[name] = { name: manifest.name, version: manifest.version }; + }); + return extensions; + }; BrowserWindow.addDevToolsExtension = function (srcDirectory) { - const manifestName = BrowserWindow.addExtension(srcDirectory) + const manifestName = BrowserWindow.addExtension(srcDirectory); if (manifestName) { - devToolsExtensionNames.add(manifestName) + devToolsExtensionNames.add(manifestName); } - return manifestName - } + return manifestName; + }; BrowserWindow.removeDevToolsExtension = function (name) { - BrowserWindow.removeExtension(name) - devToolsExtensionNames.delete(name) - } + BrowserWindow.removeExtension(name); + devToolsExtensionNames.delete(name); + }; BrowserWindow.getDevToolsExtensions = function () { - const extensions = BrowserWindow.getExtensions() - const devExtensions = {} + const extensions = BrowserWindow.getExtensions(); + const devExtensions = {}; Array.from(devToolsExtensionNames).forEach(function (name) { - if (!extensions[name]) return - devExtensions[name] = extensions[name] - }) - return devExtensions - } + if (!extensions[name]) return; + devExtensions[name] = extensions[name]; + }); + return devExtensions; + }; // Load persisted extensions. - loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions') + loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions'); try { - const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath)) + const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath)); if (Array.isArray(loadedDevToolsExtensions)) { for (const srcDirectory of loadedDevToolsExtensions) { // Start background pages and set content scripts. - BrowserWindow.addDevToolsExtension(srcDirectory) + BrowserWindow.addDevToolsExtension(srcDirectory); } } } catch (error) { if (process.env.ELECTRON_ENABLE_LOGGING && error.code !== 'ENOENT') { - console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath) - console.error(error) + console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath); + console.error(error); } } -}) +}); diff --git a/lib/browser/crash-reporter-init.js b/lib/browser/crash-reporter-init.js index 4df564e8b3a29..566e320cf59fc 100644 --- a/lib/browser/crash-reporter-init.js +++ b/lib/browser/crash-reporter-init.js @@ -1,25 +1,25 @@ -'use strict' +'use strict'; -const { app } = require('electron') -const path = require('path') +const { app } = require('electron'); +const path = require('path'); const getTempDirectory = function () { try { - return app.getPath('temp') + return app.getPath('temp'); } catch { // Delibrately laze-load the os module, this file is on the hot // path when booting Electron and os takes between 5 - 8ms to load and we do not need it yet - return require('os').tmpdir() + return require('os').tmpdir(); } -} +}; exports.crashReporterInit = function (options) { - const productName = options.productName || app.name - const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`) + const productName = options.productName || app.name; + const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`); return { productName, crashesDirectory, appVersion: app.getVersion() - } -} + }; +}; diff --git a/lib/browser/default-menu.ts b/lib/browser/default-menu.ts index 9f15002ff5ad6..b71c4f07d632c 100644 --- a/lib/browser/default-menu.ts +++ b/lib/browser/default-menu.ts @@ -1,11 +1,11 @@ -import { shell, Menu } from 'electron' +import { shell, Menu } from 'electron'; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); -const isMac = process.platform === 'darwin' +const isMac = process.platform === 'darwin'; export const setDefaultApplicationMenu = () => { - if (v8Util.getHiddenValue(global, 'applicationMenuSet')) return + if (v8Util.getHiddenValue(global, 'applicationMenuSet')) return; const helpMenu: Electron.MenuItemConstructorOptions = { role: 'help', @@ -13,32 +13,32 @@ export const setDefaultApplicationMenu = () => { { label: 'Learn More', click: async () => { - await shell.openExternal('https://electronjs.org') + await shell.openExternal('https://electronjs.org'); } }, { label: 'Documentation', click: async () => { - const version = process.versions.electron - await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`) + const version = process.versions.electron; + await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`); } }, { label: 'Community Discussions', click: async () => { - await shell.openExternal('https://discuss.atom.io/c/electron') + await shell.openExternal('https://discuss.atom.io/c/electron'); } }, { label: 'Search Issues', click: async () => { - await shell.openExternal('https://github.com/electron/electron/issues') + await shell.openExternal('https://github.com/electron/electron/issues'); } } ] - } + }; - const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' } + const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' }; const template: Electron.MenuItemConstructorOptions[] = [ ...(isMac ? [macAppMenu] : []), { role: 'fileMenu' }, @@ -46,8 +46,8 @@ export const setDefaultApplicationMenu = () => { { role: 'viewMenu' }, { role: 'windowMenu' }, helpMenu - ] + ]; - const menu = Menu.buildFromTemplate(template) - Menu.setApplicationMenu(menu) -} + const menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); +}; diff --git a/lib/browser/desktop-capturer.ts b/lib/browser/desktop-capturer.ts index 4badb96019886..d693bfbce4eb5 100644 --- a/lib/browser/desktop-capturer.ts +++ b/lib/browser/desktop-capturer.ts @@ -1,62 +1,62 @@ -const { createDesktopCapturer } = process.electronBinding('desktop_capturer') +const { createDesktopCapturer } = process.electronBinding('desktop_capturer'); -const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b) +const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b); let currentlyRunning: { options: ElectronInternal.GetSourcesOptions; getSources: Promise; -}[] = [] +}[] = []; export const getSources = (event: Electron.IpcMainEvent, options: ElectronInternal.GetSourcesOptions) => { for (const running of currentlyRunning) { if (deepEqual(running.options, options)) { // If a request is currently running for the same options // return that promise - return running.getSources + return running.getSources; } } const getSources = new Promise((resolve, reject) => { - let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer() + let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer(); const stopRunning = () => { if (capturer) { - delete capturer._onerror - delete capturer._onfinished - capturer = null + delete capturer._onerror; + delete capturer._onfinished; + capturer = null; } // Remove from currentlyRunning once we resolve or reject - currentlyRunning = currentlyRunning.filter(running => running.options !== options) - } + currentlyRunning = currentlyRunning.filter(running => running.options !== options); + }; capturer._onerror = (error: string) => { - stopRunning() - reject(error) - } + stopRunning(); + reject(error); + }; capturer._onfinished = (sources: Electron.DesktopCapturerSource[], fetchWindowIcons: boolean) => { - stopRunning() + stopRunning(); resolve(sources.map(source => ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataURL(), display_id: source.display_id, appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null - }))) - } + }))); + }; - capturer.startHandling(options.captureWindow, options.captureScreen, options.thumbnailSize, options.fetchWindowIcons) + capturer.startHandling(options.captureWindow, options.captureScreen, options.thumbnailSize, options.fetchWindowIcons); // If the WebContents is destroyed before receiving result, just remove the // reference to emit and the capturer itself so that it never dispatches // back to the renderer - event.sender.once('destroyed', () => stopRunning()) - }) + event.sender.once('destroyed', () => stopRunning()); + }); currentlyRunning.push({ options, getSources - }) + }); - return getSources -} + return getSources; +}; diff --git a/lib/browser/devtools.ts b/lib/browser/devtools.ts index 090ed6be82546..2e134979494fd 100644 --- a/lib/browser/devtools.ts +++ b/lib/browser/devtools.ts @@ -1,9 +1,9 @@ -import { dialog, Menu } from 'electron' -import * as fs from 'fs' -import * as url from 'url' +import { dialog, Menu } from 'electron'; +import * as fs from 'fs'; +import * as url from 'url'; -import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal' -import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils' +import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'; +import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils'; const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: number) => void) { return items.map(function (item) { @@ -23,15 +23,15 @@ const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: type: 'normal', label: item.label, enabled: item.enabled - } + }; if (item.id != null) { - transformed.click = () => handler(item.id) + transformed.click = () => handler(item.id); } - return transformed - }) -} + return transformed; + }); +}; const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] { return [ @@ -44,56 +44,56 @@ const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] { { role: 'pasteAndMatchStyle' }, { role: 'delete' }, { role: 'selectAll' } - ] -} + ]; +}; const isChromeDevTools = function (pageURL: string) { - const { protocol } = url.parse(pageURL) - return protocol === 'devtools:' -} + const { protocol } = url.parse(pageURL); + return protocol === 'devtools:'; +}; const assertChromeDevTools = function (contents: Electron.WebContents, api: string) { - const pageURL = contents._getURL() + const pageURL = contents._getURL(); if (!isChromeDevTools(pageURL)) { - console.error(`Blocked ${pageURL} from calling ${api}`) - throw new Error(`Blocked ${api}`) + console.error(`Blocked ${pageURL} from calling ${api}`); + throw new Error(`Blocked ${api}`); } -} +}; ipcMainInternal.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainInvokeEvent, items: ContextMenuItem[], isEditMenu: boolean) { return new Promise(resolve => { - assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()') + assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()'); - const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve) - const menu = Menu.buildFromTemplate(template) - const window = event.sender.getOwnerBrowserWindow() + const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve); + const menu = Menu.buildFromTemplate(template); + const window = event.sender.getOwnerBrowserWindow(); - menu.popup({ window, callback: () => resolve() }) - }) -}) + menu.popup({ window, callback: () => resolve() }); + }); +}); ipcMainInternal.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainInvokeEvent) { - assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()') + assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()'); - const result = await dialog.showOpenDialog({}) - if (result.canceled) return [] + const result = await dialog.showOpenDialog({}); + if (result.canceled) return []; - const path = result.filePaths[0] - const data = await fs.promises.readFile(path) + const path = result.filePaths[0]; + const data = await fs.promises.readFile(path); - return [path, data] -}) + return [path, data]; +}); ipcMainUtils.handleSync('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainInvokeEvent, message: string = '', title: string = '') { - assertChromeDevTools(event.sender, 'window.confirm()') + assertChromeDevTools(event.sender, 'window.confirm()'); const options = { message: String(message), title: String(title), buttons: ['OK', 'Cancel'], cancelId: 1 - } - const window = event.sender.getOwnerBrowserWindow() - const { response } = await dialog.showMessageBox(window, options) - return response === 0 -}) + }; + const window = event.sender.getOwnerBrowserWindow(); + const { response } = await dialog.showMessageBox(window, options); + return response === 0; +}); diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index 77842fd099511..b27a43bba71b6 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -1,14 +1,14 @@ -'use strict' +'use strict'; -const { webContents } = require('electron') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') -const parseFeaturesString = require('@electron/internal/common/parse-features-string') -const { syncMethods, asyncMethods, properties } = require('@electron/internal/common/web-view-methods') -const { serialize } = require('@electron/internal/common/type-utils') +const { webContents } = require('electron'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); +const parseFeaturesString = require('@electron/internal/common/parse-features-string'); +const { syncMethods, asyncMethods, properties } = require('@electron/internal/common/web-view-methods'); +const { serialize } = require('@electron/internal/common/type-utils'); // Doesn't exist in early initialization. -let webViewManager = null +let webViewManager = null; const supportedWebViewEvents = [ 'load-commit', @@ -43,155 +43,155 @@ const supportedWebViewEvents = [ 'found-in-page', 'did-change-theme-color', 'update-target-url' -] +]; -const guestInstances = {} -const embedderElementsMap = {} +const guestInstances = {}; +const embedderElementsMap = {}; function sanitizeOptionsForGuest (options) { - const ret = { ...options } + const ret = { ...options }; // WebContents values can't be sent over IPC. - delete ret.webContents - return ret + delete ret.webContents; + return ret; } // Create a new guest instance. const createGuest = function (embedder, params) { if (webViewManager == null) { - webViewManager = process.electronBinding('web_view_manager') + webViewManager = process.electronBinding('web_view_manager'); } const guest = webContents.create({ type: 'webview', partition: params.partition, embedder: embedder - }) - const guestInstanceId = guest.id + }); + const guestInstanceId = guest.id; guestInstances[guestInstanceId] = { guest: guest, embedder: embedder - } + }; // Clear the guest from map when it is destroyed. guest.once('destroyed', () => { if (Object.prototype.hasOwnProperty.call(guestInstances, guestInstanceId)) { - detachGuest(embedder, guestInstanceId) + detachGuest(embedder, guestInstanceId); } - }) + }); // Init guest web view after attached. guest.once('did-attach', function (event) { - params = this.attachParams - delete this.attachParams + params = this.attachParams; + delete this.attachParams; - const previouslyAttached = this.viewInstanceId != null - this.viewInstanceId = params.instanceId + const previouslyAttached = this.viewInstanceId != null; + this.viewInstanceId = params.instanceId; // Only load URL and set size on first attach if (previouslyAttached) { - return + return; } if (params.src) { - const opts = {} + const opts = {}; if (params.httpreferrer) { - opts.httpReferrer = params.httpreferrer + opts.httpReferrer = params.httpreferrer; } if (params.useragent) { - opts.userAgent = params.useragent + opts.userAgent = params.useragent; } - this.loadURL(params.src, opts) + this.loadURL(params.src, opts); } - embedder.emit('did-attach-webview', event, guest) - }) + embedder.emit('did-attach-webview', event, guest); + }); const sendToEmbedder = (channel, ...args) => { if (!embedder.isDestroyed()) { - embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args) + embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args); } - } + }; // Dispatch events to embedder. const fn = function (event) { guest.on(event, function (_, ...args) { - sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args) - }) - } + sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args); + }); + }; for (const event of supportedWebViewEvents) { - fn(event) + fn(event); } guest.on('new-window', function (event, url, frameName, disposition, options, additionalFeatures, referrer) { sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', 'new-window', url, frameName, disposition, sanitizeOptionsForGuest(options), - additionalFeatures, referrer) - }) + additionalFeatures, referrer); + }); // Dispatch guest's IPC messages to embedder. guest.on('ipc-message-host', function (_, channel, args) { - sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args) - }) + sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args); + }); // Notify guest of embedder window visibility when it is ready // FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed guest.on('dom-ready', function () { - const guestInstance = guestInstances[guestInstanceId] + const guestInstance = guestInstances[guestInstanceId]; if (guestInstance != null && guestInstance.visibilityState != null) { - guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState) + guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState); } - }) + }); // Forward internal web contents event to embedder to handle // native window.open setup guest.on('-add-new-contents', (...args) => { if (guest.getLastWebPreferences().nativeWindowOpen === true) { - const embedder = getEmbedder(guestInstanceId) + const embedder = getEmbedder(guestInstanceId); if (embedder != null) { - embedder.emit('-add-new-contents', ...args) + embedder.emit('-add-new-contents', ...args); } } - }) + }); - return guestInstanceId -} + return guestInstanceId; +}; // Attach the guest to an element of embedder. const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { - const embedder = event.sender + const embedder = event.sender; // Destroy the old guest when attaching. - const key = `${embedder.id}-${elementInstanceId}` - const oldGuestInstanceId = embedderElementsMap[key] + const key = `${embedder.id}-${elementInstanceId}`; + const oldGuestInstanceId = embedderElementsMap[key]; if (oldGuestInstanceId != null) { // Reattachment to the same guest is just a no-op. if (oldGuestInstanceId === guestInstanceId) { - return + return; } - const oldGuestInstance = guestInstances[oldGuestInstanceId] + const oldGuestInstance = guestInstances[oldGuestInstanceId]; if (oldGuestInstance) { - oldGuestInstance.guest.detachFromOuterFrame() + oldGuestInstance.guest.detachFromOuterFrame(); } } - const guestInstance = guestInstances[guestInstanceId] + const guestInstance = guestInstances[guestInstanceId]; // If this isn't a valid guest instance then do nothing. if (!guestInstance) { - throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`) + throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`); } - const { guest } = guestInstance + const { guest } = guestInstance; if (guest.hostWebContents !== event.sender) { - throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`) + throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`); } // If this guest is already attached to an element then remove it if (guestInstance.elementInstanceId) { - const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}` - delete embedderElementsMap[oldKey] + const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}`; + delete embedderElementsMap[oldKey]; // Remove guest from embedder if moving across web views if (guest.viewInstanceId !== params.instanceId) { - webViewManager.removeGuest(guestInstance.embedder, guestInstanceId) - guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`) + webViewManager.removeGuest(guestInstance.embedder, guestInstanceId); + guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`); } } @@ -206,7 +206,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn webSecurity: !params.disablewebsecurity, enableBlinkFeatures: params.blinkfeatures, disableBlinkFeatures: params.disableblinkfeatures - } + }; // parse the 'webpreferences' attribute string, if set // this uses the same parsing rules as window.open uses for its features @@ -214,14 +214,14 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn parseFeaturesString(params.webpreferences, function (key, value) { if (value === undefined) { // no value was specified, default it to true - value = true + value = true; } - webPreferences[key] = value - }) + webPreferences[key] = value; + }); } if (params.preload) { - webPreferences.preloadURL = params.preload + webPreferences.preloadURL = params.preload; } // Security options that guest will always inherit from embedder @@ -233,203 +233,203 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn ['enableRemoteModule', false], ['sandbox', true], ['nodeIntegrationInSubFrames', false] - ]) + ]); // Inherit certain option values from embedder - const lastWebPreferences = embedder.getLastWebPreferences() + const lastWebPreferences = embedder.getLastWebPreferences(); for (const [name, value] of inheritedWebPreferences) { if (lastWebPreferences[name] === value) { - webPreferences[name] = value + webPreferences[name] = value; } } - embedder.emit('will-attach-webview', event, webPreferences, params) + embedder.emit('will-attach-webview', event, webPreferences, params); if (event.defaultPrevented) { - if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId - guest.destroy() - return + if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId; + guest.destroy(); + return; } - guest.attachParams = params - embedderElementsMap[key] = guestInstanceId + guest.attachParams = params; + embedderElementsMap[key] = guestInstanceId; - guest.setEmbedder(embedder) - guestInstance.embedder = embedder - guestInstance.elementInstanceId = elementInstanceId + guest.setEmbedder(embedder); + guestInstance.embedder = embedder; + guestInstance.elementInstanceId = elementInstanceId; - watchEmbedder(embedder) + watchEmbedder(embedder); - webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences) - guest.attachToIframe(embedder, embedderFrameId) -} + webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences); + guest.attachToIframe(embedder, embedderFrameId); +}; // Remove an guest-embedder relationship. const detachGuest = function (embedder, guestInstanceId) { - const guestInstance = guestInstances[guestInstanceId] + const guestInstance = guestInstances[guestInstanceId]; if (embedder !== guestInstance.embedder) { - return + return; } - webViewManager.removeGuest(embedder, guestInstanceId) - delete guestInstances[guestInstanceId] + webViewManager.removeGuest(embedder, guestInstanceId); + delete guestInstances[guestInstanceId]; - const key = `${embedder.id}-${guestInstance.elementInstanceId}` - delete embedderElementsMap[key] -} + const key = `${embedder.id}-${guestInstance.elementInstanceId}`; + delete embedderElementsMap[key]; +}; // Once an embedder has had a guest attached we watch it for destruction to // destroy any remaining guests. -const watchedEmbedders = new Set() +const watchedEmbedders = new Set(); const watchEmbedder = function (embedder) { if (watchedEmbedders.has(embedder)) { - return + return; } - watchedEmbedders.add(embedder) + watchedEmbedders.add(embedder); // Forward embedder window visiblity change events to guest const onVisibilityChange = function (visibilityState) { for (const guestInstanceId of Object.keys(guestInstances)) { - const guestInstance = guestInstances[guestInstanceId] - guestInstance.visibilityState = visibilityState + const guestInstance = guestInstances[guestInstanceId]; + guestInstance.visibilityState = visibilityState; if (guestInstance.embedder === embedder) { - guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState) + guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState); } } - } - embedder.on('-window-visibility-change', onVisibilityChange) + }; + embedder.on('-window-visibility-change', onVisibilityChange); embedder.once('will-destroy', () => { // Usually the guestInstances is cleared when guest is destroyed, but it // may happen that the embedder gets manually destroyed earlier than guest, // and the embedder will be invalid in the usual code path. for (const guestInstanceId of Object.keys(guestInstances)) { - const guestInstance = guestInstances[guestInstanceId] + const guestInstance = guestInstances[guestInstanceId]; if (guestInstance.embedder === embedder) { - detachGuest(embedder, parseInt(guestInstanceId)) + detachGuest(embedder, parseInt(guestInstanceId)); } } // Clear the listeners. - embedder.removeListener('-window-visibility-change', onVisibilityChange) - watchedEmbedders.delete(embedder) - }) -} + embedder.removeListener('-window-visibility-change', onVisibilityChange); + watchedEmbedders.delete(embedder); + }); +}; -const isWebViewTagEnabledCache = new WeakMap() +const isWebViewTagEnabledCache = new WeakMap(); const isWebViewTagEnabled = function (contents) { if (!isWebViewTagEnabledCache.has(contents)) { - const webPreferences = contents.getLastWebPreferences() || {} - isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag) + const webPreferences = contents.getLastWebPreferences() || {}; + isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag); } - return isWebViewTagEnabledCache.get(contents) -} + return isWebViewTagEnabledCache.get(contents); +}; const makeSafeHandler = function (channel, handler) { return (event, ...args) => { if (isWebViewTagEnabled(event.sender)) { - return handler(event, ...args) + return handler(event, ...args); } else { - console.error(` IPC message ${channel} sent by WebContents with disabled (${event.sender.id})`) - throw new Error(' disabled') + console.error(` IPC message ${channel} sent by WebContents with disabled (${event.sender.id})`); + throw new Error(' disabled'); } - } -} + }; +}; const handleMessage = function (channel, handler) { - ipcMainInternal.handle(channel, makeSafeHandler(channel, handler)) -} + ipcMainInternal.handle(channel, makeSafeHandler(channel, handler)); +}; const handleMessageSync = function (channel, handler) { - ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler)) -} + ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler)); +}; handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) { - return createGuest(event.sender, params) -}) + return createGuest(event.sender, params); +}); handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { try { - attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params) + attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params); } catch (error) { - console.error(`Guest attach failed: ${error}`) + console.error(`Guest attach failed: ${error}`); } -}) +}); // this message is sent by the actual ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) { - const guest = getGuest(guestInstanceId) + const guest = getGuest(guestInstanceId); if (guest === event.sender) { - event.sender.emit('focus-change', {}, focus, guestInstanceId) + event.sender.emit('focus-change', {}, focus, guestInstanceId); } else { - console.error(`focus-change for guestInstanceId: ${guestInstanceId}`) + console.error(`focus-change for guestInstanceId: ${guestInstanceId}`); } -}) +}); handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) { - const guest = getGuestForWebContents(guestInstanceId, event.sender) + const guest = getGuestForWebContents(guestInstanceId, event.sender); if (!asyncMethods.has(method)) { - throw new Error(`Invalid method: ${method}`) + throw new Error(`Invalid method: ${method}`); } - return guest[method](...args) -}) + return guest[method](...args); +}); handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) { - const guest = getGuestForWebContents(guestInstanceId, event.sender) + const guest = getGuestForWebContents(guestInstanceId, event.sender); if (!syncMethods.has(method)) { - throw new Error(`Invalid method: ${method}`) + throw new Error(`Invalid method: ${method}`); } - return guest[method](...args) -}) + return guest[method](...args); +}); handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', function (event, guestInstanceId, property) { - const guest = getGuestForWebContents(guestInstanceId, event.sender) + const guest = getGuestForWebContents(guestInstanceId, event.sender); if (!properties.has(property)) { - throw new Error(`Invalid property: ${property}`) + throw new Error(`Invalid property: ${property}`); } - return guest[property] -}) + return guest[property]; +}); handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', function (event, guestInstanceId, property, val) { - const guest = getGuestForWebContents(guestInstanceId, event.sender) + const guest = getGuestForWebContents(guestInstanceId, event.sender); if (!properties.has(property)) { - throw new Error(`Invalid property: ${property}`) + throw new Error(`Invalid property: ${property}`); } - guest[property] = val -}) + guest[property] = val; +}); handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', async function (event, guestInstanceId, args) { - const guest = getGuestForWebContents(guestInstanceId, event.sender) + const guest = getGuestForWebContents(guestInstanceId, event.sender); - return serialize(await guest.capturePage(...args)) -}) + return serialize(await guest.capturePage(...args)); +}); // Returns WebContents from its guest id hosted in given webContents. const getGuestForWebContents = function (guestInstanceId, contents) { - const guest = getGuest(guestInstanceId) + const guest = getGuest(guestInstanceId); if (!guest) { - throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`) + throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`); } if (guest.hostWebContents !== contents) { - throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`) + throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`); } - return guest -} + return guest; +}; // Returns WebContents from its guest id. const getGuest = function (guestInstanceId) { - const guestInstance = guestInstances[guestInstanceId] - if (guestInstance != null) return guestInstance.guest -} + const guestInstance = guestInstances[guestInstanceId]; + if (guestInstance != null) return guestInstance.guest; +}; // Returns the embedder of the guest. const getEmbedder = function (guestInstanceId) { - const guestInstance = guestInstances[guestInstanceId] - if (guestInstance != null) return guestInstance.embedder -} + const guestInstance = guestInstances[guestInstanceId]; + if (guestInstance != null) return guestInstance.embedder; +}; -exports.isWebViewTagEnabled = isWebViewTagEnabled +exports.isWebViewTagEnabled = isWebViewTagEnabled; diff --git a/lib/browser/guest-window-manager.js b/lib/browser/guest-window-manager.js index b0523ecb0d2e4..b9aaaa6ce5bc2 100644 --- a/lib/browser/guest-window-manager.js +++ b/lib/browser/guest-window-manager.js @@ -1,14 +1,14 @@ -'use strict' +'use strict'; -const electron = require('electron') -const { BrowserWindow } = electron -const { isSameOrigin } = process.electronBinding('v8_util') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') -const parseFeaturesString = require('@electron/internal/common/parse-features-string') +const electron = require('electron'); +const { BrowserWindow } = electron; +const { isSameOrigin } = process.electronBinding('v8_util'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); +const parseFeaturesString = require('@electron/internal/common/parse-features-string'); -const hasProp = {}.hasOwnProperty -const frameToGuest = new Map() +const hasProp = {}.hasOwnProperty; +const frameToGuest = new Map(); // Security options that child windows will always inherit from parent windows const inheritedWebPreferences = new Map([ @@ -20,109 +20,109 @@ const inheritedWebPreferences = new Map([ ['sandbox', true], ['webviewTag', false], ['nodeIntegrationInSubFrames', false] -]) +]); // Copy attribute of |parent| to |child| if it is not defined in |child|. const mergeOptions = function (child, parent, visited) { // Check for circular reference. - if (visited == null) visited = new Set() - if (visited.has(parent)) return + if (visited == null) visited = new Set(); + if (visited.has(parent)) return; - visited.add(parent) + visited.add(parent); for (const key in parent) { - if (key === 'type') continue - if (!hasProp.call(parent, key)) continue - if (key in child && key !== 'webPreferences') continue + if (key === 'type') continue; + if (!hasProp.call(parent, key)) continue; + if (key in child && key !== 'webPreferences') continue; - const value = parent[key] + const value = parent[key]; if (typeof value === 'object' && !Array.isArray(value)) { - child[key] = mergeOptions(child[key] || {}, value, visited) + child[key] = mergeOptions(child[key] || {}, value, visited); } else { - child[key] = value + child[key] = value; } } - visited.delete(parent) + visited.delete(parent); - return child -} + return child; +}; // Merge |options| with the |embedder|'s window's options. const mergeBrowserWindowOptions = function (embedder, options) { if (options.webPreferences == null) { - options.webPreferences = {} + options.webPreferences = {}; } if (embedder.browserWindowOptions != null) { - let parentOptions = embedder.browserWindowOptions + let parentOptions = embedder.browserWindowOptions; // if parent's visibility is available, that overrides 'show' flag (#12125) - const win = BrowserWindow.fromWebContents(embedder.webContents) + const win = BrowserWindow.fromWebContents(embedder.webContents); if (win != null) { - parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() } + parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() }; } // Inherit the original options if it is a BrowserWindow. - mergeOptions(options, parentOptions) + mergeOptions(options, parentOptions); } else { // Or only inherit webPreferences if it is a webview. - mergeOptions(options.webPreferences, embedder.getLastWebPreferences()) + mergeOptions(options.webPreferences, embedder.getLastWebPreferences()); } // Inherit certain option values from parent window - const webPreferences = embedder.getLastWebPreferences() + const webPreferences = embedder.getLastWebPreferences(); for (const [name, value] of inheritedWebPreferences) { if (webPreferences[name] === value) { - options.webPreferences[name] = value + options.webPreferences[name] = value; } } if (!webPreferences.nativeWindowOpen) { // Sets correct openerId here to give correct options to 'new-window' event handler - options.webPreferences.openerId = embedder.id + options.webPreferences.openerId = embedder.id; } - return options -} + return options; +}; // Setup a new guest with |embedder| const setupGuest = function (embedder, frameName, guest, options) { // When |embedder| is destroyed we should also destroy attached guest, and if // guest is closed by user then we should prevent |embedder| from double // closing guest. - const guestId = guest.webContents.id + const guestId = guest.webContents.id; const closedByEmbedder = function () { - guest.removeListener('closed', closedByUser) - guest.destroy() - } + guest.removeListener('closed', closedByUser); + guest.destroy(); + }; const closedByUser = function () { - embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId) - embedder.removeListener('current-render-view-deleted', closedByEmbedder) - } - embedder.once('current-render-view-deleted', closedByEmbedder) - guest.once('closed', closedByUser) + embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId); + embedder.removeListener('current-render-view-deleted', closedByEmbedder); + }; + embedder.once('current-render-view-deleted', closedByEmbedder); + guest.once('closed', closedByUser); if (frameName) { - frameToGuest.set(frameName, guest) - guest.frameName = frameName + frameToGuest.set(frameName, guest); + guest.frameName = frameName; guest.once('closed', function () { - frameToGuest.delete(frameName) - }) + frameToGuest.delete(frameName); + }); } - return guestId -} + return guestId; +}; // Create a new guest created by |embedder| with |options|. const createGuest = function (embedder, url, referrer, frameName, options, postData) { - let guest = frameToGuest.get(frameName) + let guest = frameToGuest.get(frameName); if (frameName && (guest != null)) { - guest.loadURL(url) - return guest.webContents.id + guest.loadURL(url); + return guest.webContents.id; } // Remember the embedder window's id. if (options.webPreferences == null) { - options.webPreferences = {} + options.webPreferences = {}; } - guest = new BrowserWindow(options) + guest = new BrowserWindow(options); if (!options.webContents) { // We should not call `loadURL` if the window was constructed from an // existing webContents (window.open in a sandboxed renderer). @@ -131,236 +131,236 @@ const createGuest = function (embedder, url, referrer, frameName, options, postD // webContents is not necessary (it will navigate there anyway). const loadOptions = { httpReferrer: referrer - } + }; if (postData != null) { - loadOptions.postData = postData - loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded' + loadOptions.postData = postData; + loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'; if (postData.length > 0) { - const postDataFront = postData[0].bytes.toString() - const boundary = /^--.*[^-\r\n]/.exec(postDataFront) + const postDataFront = postData[0].bytes.toString(); + const boundary = /^--.*[^-\r\n]/.exec(postDataFront); if (boundary != null) { - loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}` + loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`; } } } - guest.loadURL(url, loadOptions) + guest.loadURL(url, loadOptions); } - return setupGuest(embedder, frameName, guest, options) -} + return setupGuest(embedder, frameName, guest, options); +}; const getGuestWindow = function (guestContents) { - let guestWindow = BrowserWindow.fromWebContents(guestContents) + let guestWindow = BrowserWindow.fromWebContents(guestContents); if (guestWindow == null) { - const hostContents = guestContents.hostWebContents + const hostContents = guestContents.hostWebContents; if (hostContents != null) { - guestWindow = BrowserWindow.fromWebContents(hostContents) + guestWindow = BrowserWindow.fromWebContents(hostContents); } } if (!guestWindow) { - throw new Error('getGuestWindow failed') + throw new Error('getGuestWindow failed'); } - return guestWindow -} + return guestWindow; +}; const isChildWindow = function (sender, target) { - return target.getLastWebPreferences().openerId === sender.id -} + return target.getLastWebPreferences().openerId === sender.id; +}; const isRelatedWindow = function (sender, target) { - return isChildWindow(sender, target) || isChildWindow(target, sender) -} + return isChildWindow(sender, target) || isChildWindow(target, sender); +}; const isScriptableWindow = function (sender, target) { - return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL()) -} + return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL()); +}; const isNodeIntegrationEnabled = function (sender) { - return sender.getLastWebPreferences().nodeIntegration === true -} + return sender.getLastWebPreferences().nodeIntegration === true; +}; // Checks whether |sender| can access the |target|: const canAccessWindow = function (sender, target) { return isChildWindow(sender, target) || isScriptableWindow(sender, target) || - isNodeIntegrationEnabled(sender) -} + isNodeIntegrationEnabled(sender); +}; // Routed window.open messages with raw options ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => { - if (url == null || url === '') url = 'about:blank' - if (frameName == null) frameName = '' - if (features == null) features = '' + if (url == null || url === '') url = 'about:blank'; + if (frameName == null) frameName = ''; + if (features == null) features = ''; - const options = {} + const options = {}; - const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor'] - const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag'] - const disposition = 'new-window' + const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']; + const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag']; + const disposition = 'new-window'; // Used to store additional features - const additionalFeatures = [] + const additionalFeatures = []; // Parse the features parseFeaturesString(features, function (key, value) { if (value === undefined) { - additionalFeatures.push(key) + additionalFeatures.push(key); } else { // Don't allow webPreferences to be set since it must be an object // that cannot be directly overridden - if (key === 'webPreferences') return + if (key === 'webPreferences') return; if (webPreferences.includes(key)) { if (options.webPreferences == null) { - options.webPreferences = {} + options.webPreferences = {}; } - options.webPreferences[key] = value + options.webPreferences[key] = value; } else { - options[key] = value + options[key] = value; } } - }) + }); if (options.left) { if (options.x == null) { - options.x = options.left + options.x = options.left; } } if (options.top) { if (options.y == null) { - options.y = options.top + options.y = options.top; } } if (options.title == null) { - options.title = frameName + options.title = frameName; } if (options.width == null) { - options.width = 800 + options.width = 800; } if (options.height == null) { - options.height = 600 + options.height = 600; } for (const name of ints) { if (options[name] != null) { - options[name] = parseInt(options[name], 10) + options[name] = parseInt(options[name], 10); } } - const referrer = { url: '', policy: 'default' } - internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures) -}) + const referrer = { url: '', policy: 'default' }; + internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures); +}); // Routed window.open messages with fully parsed options function internalWindowOpen (event, url, referrer, frameName, disposition, options, additionalFeatures, postData) { - options = mergeBrowserWindowOptions(event.sender, options) - event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer) - const { newGuest } = event + options = mergeBrowserWindowOptions(event.sender, options); + event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer); + const { newGuest } = event; if ((event.sender.getType() === 'webview' && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) { if (newGuest != null) { if (options.webContents === newGuest.webContents) { // the webContents is not changed, so set defaultPrevented to false to // stop the callers of this event from destroying the webContents. - event.defaultPrevented = false + event.defaultPrevented = false; } - event.returnValue = setupGuest(event.sender, frameName, newGuest, options) + event.returnValue = setupGuest(event.sender, frameName, newGuest, options); } else { - event.returnValue = null + event.returnValue = null; } } else { - event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData) + event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData); } } const makeSafeHandler = function (handler) { return (event, guestId, ...args) => { // Access webContents via electron to prevent circular require. - const guestContents = electron.webContents.fromId(guestId) + const guestContents = electron.webContents.fromId(guestId); if (!guestContents) { - throw new Error(`Invalid guestId: ${guestId}`) + throw new Error(`Invalid guestId: ${guestId}`); } - return handler(event, guestContents, ...args) - } -} + return handler(event, guestContents, ...args); + }; +}; const handleMessage = function (channel, handler) { - ipcMainInternal.handle(channel, makeSafeHandler(handler)) -} + ipcMainInternal.handle(channel, makeSafeHandler(handler)); +}; const handleMessageSync = function (channel, handler) { - ipcMainUtils.handleSync(channel, makeSafeHandler(handler)) -} + ipcMainUtils.handleSync(channel, makeSafeHandler(handler)); +}; const securityCheck = function (contents, guestContents, check) { if (!check(contents, guestContents)) { - console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`) - throw new Error(`Access denied to guestId: ${guestContents.id}`) + console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`); + throw new Error(`Access denied to guestId: ${guestContents.id}`); } -} +}; const windowMethods = new Set([ 'destroy', 'focus', 'blur' -]) +]); handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => { - securityCheck(event.sender, guestContents, canAccessWindow) + securityCheck(event.sender, guestContents, canAccessWindow); if (!windowMethods.has(method)) { - console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) - throw new Error(`Invalid method: ${method}`) + console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`); + throw new Error(`Invalid method: ${method}`); } - return getGuestWindow(guestContents)[method](...args) -}) + return getGuestWindow(guestContents)[method](...args); +}); handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestContents, message, targetOrigin, sourceOrigin) => { if (targetOrigin == null) { - targetOrigin = '*' + targetOrigin = '*'; } // The W3C does not seem to have word on how postMessage should work when the // origins do not match, so we do not do |canAccessWindow| check here since // postMessage across origins is useful and not harmful. - securityCheck(event.sender, guestContents, isRelatedWindow) + securityCheck(event.sender, guestContents, isRelatedWindow); if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) { - const sourceId = event.sender.id - guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) + const sourceId = event.sender.id; + guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin); } -}) +}); const webContentsMethodsAsync = new Set([ 'loadURL', 'executeJavaScript', 'print' -]) +]); handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => { - securityCheck(event.sender, guestContents, canAccessWindow) + securityCheck(event.sender, guestContents, canAccessWindow); if (!webContentsMethodsAsync.has(method)) { - console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) - throw new Error(`Invalid method: ${method}`) + console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`); + throw new Error(`Invalid method: ${method}`); } - return guestContents[method](...args) -}) + return guestContents[method](...args); +}); const webContentsMethodsSync = new Set([ 'getURL' -]) +]); handleMessageSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => { - securityCheck(event.sender, guestContents, canAccessWindow) + securityCheck(event.sender, guestContents, canAccessWindow); if (!webContentsMethodsSync.has(method)) { - console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) - throw new Error(`Invalid method: ${method}`) + console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`); + throw new Error(`Invalid method: ${method}`); } - return guestContents[method](...args) -}) + return guestContents[method](...args); +}); -exports.internalWindowOpen = internalWindowOpen +exports.internalWindowOpen = internalWindowOpen; diff --git a/lib/browser/init.ts b/lib/browser/init.ts index cbd5f7f2f0a73..2210761b2eae8 100644 --- a/lib/browser/init.ts +++ b/lib/browser/init.ts @@ -1,23 +1,23 @@ -import { Buffer } from 'buffer' -import { EventEmitter } from 'events' -import * as fs from 'fs' -import { Socket } from 'net' -import * as path from 'path' -import * as util from 'util' +import { Buffer } from 'buffer'; +import { EventEmitter } from 'events'; +import * as fs from 'fs'; +import { Socket } from 'net'; +import * as path from 'path'; +import * as util from 'util'; -const Module = require('module') +const Module = require('module'); // We modified the original process.argv to let node.js load the init.js, // we need to restore it here. -process.argv.splice(1, 1) +process.argv.splice(1, 1); // Clear search paths. -require('../common/reset-search-paths') +require('../common/reset-search-paths'); // Import common settings. -require('@electron/internal/common/init') +require('@electron/internal/common/init'); -process.electronBinding('event_emitter').setEventEmitterPrototype(EventEmitter.prototype) +process.electronBinding('event_emitter').setEventEmitterPrototype(EventEmitter.prototype); if (process.platform === 'win32') { // Redirect node's console to use our own implementations, since node can not @@ -25,27 +25,27 @@ if (process.platform === 'win32') { const consoleLog = (...args: any[]) => { // @ts-ignore this typing is incorrect; 'format' is an optional parameter // See https://nodejs.org/api/util.html#util_util_format_format_args - return process.log(util.format(...args) + '\n') - } + return process.log(util.format(...args) + '\n'); + }; const streamWrite: Socket['write'] = function (chunk: Buffer | string, encoding?: any, callback?: Function) { if (Buffer.isBuffer(chunk)) { - chunk = chunk.toString(encoding) + chunk = chunk.toString(encoding); } - process.log(chunk) + process.log(chunk); if (callback) { - callback() + callback(); } - return true - } - console.log = console.error = console.warn = consoleLog - process.stdout.write = process.stderr.write = streamWrite + return true; + }; + console.log = console.error = console.warn = consoleLog; + process.stdout.write = process.stderr.write = streamWrite; } // Don't quit on fatal error. process.on('uncaughtException', function (error) { // Do nothing if the user has a custom uncaught exception handler. if (process.listenerCount('uncaughtException') > 1) { - return + return; } // Show error in GUI. @@ -54,18 +54,18 @@ process.on('uncaughtException', function (error) { // so we import it inside the handler down here import('electron') .then(({ dialog }) => { - const stack = error.stack ? error.stack : `${error.name}: ${error.message}` - const message = 'Uncaught Exception:\n' + stack - dialog.showErrorBox('A JavaScript error occurred in the main process', message) - }) -}) + const stack = error.stack ? error.stack : `${error.name}: ${error.message}`; + const message = 'Uncaught Exception:\n' + stack; + dialog.showErrorBox('A JavaScript error occurred in the main process', message); + }); +}); // Emit 'exit' event on quit. -const { app } = require('electron') +const { app } = require('electron'); app.on('quit', function (event, exitCode) { - process.emit('exit', exitCode) -}) + process.emit('exit', exitCode); +}); if (process.platform === 'win32') { // If we are a Squirrel.Windows-installed app, set app user model ID @@ -82,141 +82,141 @@ if (process.platform === 'win32') { // form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call // app.setAppUserModelId with a matching identifier so that renderer processes // will inherit this value. - const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe') + const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe'); if (fs.existsSync(updateDotExe)) { - const packageDir = path.dirname(path.resolve(updateDotExe)) - const packageName = path.basename(packageDir).replace(/\s/g, '') - const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '') + const packageDir = path.dirname(path.resolve(updateDotExe)); + const packageName = path.basename(packageDir).replace(/\s/g, ''); + const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, ''); - app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`) + app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`); } } // Map process.exit to app.exit, which quits gracefully. -process.exit = app.exit as () => never +process.exit = app.exit as () => never; // Load the RPC server. -require('@electron/internal/browser/rpc-server') +require('@electron/internal/browser/rpc-server'); // Load the guest view manager. -require('@electron/internal/browser/guest-view-manager') -require('@electron/internal/browser/guest-window-manager') +require('@electron/internal/browser/guest-view-manager'); +require('@electron/internal/browser/guest-window-manager'); // Now we try to load app's package.json. -let packagePath = null -let packageJson = null -const searchPaths = ['app', 'app.asar', 'default_app.asar'] +let packagePath = null; +let packageJson = null; +const searchPaths = ['app', 'app.asar', 'default_app.asar']; if (process.resourcesPath) { for (packagePath of searchPaths) { try { - packagePath = path.join(process.resourcesPath, packagePath) - packageJson = Module._load(path.join(packagePath, 'package.json')) - break + packagePath = path.join(process.resourcesPath, packagePath); + packageJson = Module._load(path.join(packagePath, 'package.json')); + break; } catch { - continue + continue; } } } if (packageJson == null) { process.nextTick(function () { - return process.exit(1) - }) - throw new Error('Unable to find a valid app') + 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) + app.setVersion(packageJson.version); } // Set application's name. if (packageJson.productName != null) { - app.name = `${packageJson.productName}`.trim() + app.name = `${packageJson.productName}`.trim(); } else if (packageJson.name != null) { - app.name = `${packageJson.name}`.trim() + app.name = `${packageJson.name}`.trim(); } // Set application's desktop name. if (packageJson.desktopName != null) { - app.setDesktopName(packageJson.desktopName) + app.setDesktopName(packageJson.desktopName); } else { - app.setDesktopName(`${app.name}.desktop`) + app.setDesktopName(`${app.name}.desktop`); } // Set v8 flags, delibrately lazy load so that apps that do not use this // feature do not pay the price if (packageJson.v8Flags != null) { - require('v8').setFlagsFromString(packageJson.v8Flags) + require('v8').setFlagsFromString(packageJson.v8Flags); } -app._setDefaultAppPaths(packagePath) +app._setDefaultAppPaths(packagePath); // Load the chrome devtools support. -require('@electron/internal/browser/devtools') +require('@electron/internal/browser/devtools'); -const features = process.electronBinding('features') +const features = process.electronBinding('features'); // Load the chrome extension support. if (features.isExtensionsEnabled()) { - require('@electron/internal/browser/chrome-extension-shim') + require('@electron/internal/browser/chrome-extension-shim'); } else { - require('@electron/internal/browser/chrome-extension') + require('@electron/internal/browser/chrome-extension'); } if (features.isRemoteModuleEnabled()) { - require('@electron/internal/browser/remote/server') + require('@electron/internal/browser/remote/server'); } // Load protocol module to ensure it is populated on app ready -require('@electron/internal/browser/api/protocol') +require('@electron/internal/browser/api/protocol'); // Set main startup script of the app. -const mainStartupScript = packageJson.main || 'index.js' +const mainStartupScript = packageJson.main || 'index.js'; -const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME'] +const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME']; function currentPlatformSupportsAppIndicator () { - if (process.platform !== 'linux') return false - const currentDesktop = process.env.XDG_CURRENT_DESKTOP + if (process.platform !== 'linux') return false; + const currentDesktop = process.env.XDG_CURRENT_DESKTOP; - if (!currentDesktop) return false - if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true + if (!currentDesktop) return false; + if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true; // ubuntu based or derived session (default ubuntu one, communitheme…) supports // indicator too. - if (/ubuntu/ig.test(currentDesktop)) return true + if (/ubuntu/ig.test(currentDesktop)) return true; - return false + return false; } // Workaround for electron/electron#5050 and electron/electron#9046 if (currentPlatformSupportsAppIndicator()) { - process.env.XDG_CURRENT_DESKTOP = 'Unity' + process.env.XDG_CURRENT_DESKTOP = 'Unity'; } // Quit when all windows are closed and no other one is listening to this. app.on('window-all-closed', () => { if (app.listenerCount('window-all-closed') === 1) { - app.quit() + app.quit(); } -}) +}); -const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu') +const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu'); // Create default menu. // // Note that the task must be added before loading any app, so we can make sure // the call is maded before any user window is created, otherwise the default // menu may show even when user explicitly hides the menu. -app.whenReady().then(setDefaultApplicationMenu) +app.whenReady().then(setDefaultApplicationMenu); if (packagePath) { // Finally load app's main.js and transfer control to C++. - process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false) - Module._load(path.join(packagePath, mainStartupScript), Module, true) + process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false); + Module._load(path.join(packagePath, mainStartupScript), Module, true); } else { - console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)') - console.error('This normally means you\'ve damaged the Electron package somehow') + console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)'); + console.error('This normally means you\'ve damaged the Electron package somehow'); } diff --git a/lib/browser/ipc-main-impl.ts b/lib/browser/ipc-main-impl.ts index 8f3fc9acd6243..8d6c91bbbaaf1 100644 --- a/lib/browser/ipc-main-impl.ts +++ b/lib/browser/ipc-main-impl.ts @@ -1,33 +1,33 @@ -import { EventEmitter } from 'events' -import { IpcMainInvokeEvent } from 'electron' +import { EventEmitter } from 'events'; +import { IpcMainInvokeEvent } from 'electron'; export class IpcMainImpl extends EventEmitter { private _invokeHandlers: Map void> = new Map(); handle: Electron.IpcMain['handle'] = (method, fn) => { if (this._invokeHandlers.has(method)) { - throw new Error(`Attempted to register a second handler for '${method}'`) + throw new Error(`Attempted to register a second handler for '${method}'`); } if (typeof fn !== 'function') { - throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`) + throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`); } this._invokeHandlers.set(method, async (e, ...args) => { try { - (e as any)._reply(await Promise.resolve(fn(e, ...args))) + (e as any)._reply(await Promise.resolve(fn(e, ...args))); } catch (err) { - (e as any)._throw(err) + (e as any)._throw(err); } - }) + }); } handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => { this.handle(method, (e, ...args) => { - this.removeHandler(method) - return fn(e, ...args) - }) + this.removeHandler(method); + return fn(e, ...args); + }); } removeHandler (method: string) { - this._invokeHandlers.delete(method) + this._invokeHandlers.delete(method); } } diff --git a/lib/browser/ipc-main-internal-utils.ts b/lib/browser/ipc-main-internal-utils.ts index 3c2e4322a74b6..34063d923f4b8 100644 --- a/lib/browser/ipc-main-internal-utils.ts +++ b/lib/browser/ipc-main-internal-utils.ts @@ -1,44 +1,44 @@ -import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal' +import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'; type IPCHandler = (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any export const handleSync = function (channel: string, handler: T) { ipcMainInternal.on(channel, async (event, ...args) => { try { - event.returnValue = [null, await handler(event, ...args)] + event.returnValue = [null, await handler(event, ...args)]; } catch (error) { - event.returnValue = [error] + event.returnValue = [error]; } - }) -} + }); +}; -let nextId = 0 +let nextId = 0; export function invokeInWebContents (sender: Electron.WebContentsInternal, sendToAll: boolean, command: string, ...args: any[]) { return new Promise((resolve, reject) => { - const requestId = ++nextId - const channel = `${command}_RESPONSE_${requestId}` + const requestId = ++nextId; + const channel = `${command}_RESPONSE_${requestId}`; ipcMainInternal.on(channel, function handler ( event, error: Electron.SerializedError, result: any ) { if (event.sender !== sender) { - console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`) - return + console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`); + return; } - ipcMainInternal.removeListener(channel, handler) + ipcMainInternal.removeListener(channel, handler); if (error) { - reject(error) + reject(error); } else { - resolve(result) + resolve(result); } - }) + }); if (sendToAll) { - sender._sendInternalToAll(command, requestId, ...args) + sender._sendInternalToAll(command, requestId, ...args); } else { - sender._sendInternal(command, requestId, ...args) + sender._sendInternal(command, requestId, ...args); } - }) + }); } diff --git a/lib/browser/ipc-main-internal.ts b/lib/browser/ipc-main-internal.ts index 9f296453b4279..f6c24537d324c 100644 --- a/lib/browser/ipc-main-internal.ts +++ b/lib/browser/ipc-main-internal.ts @@ -1,6 +1,6 @@ -import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl' +import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'; -export const ipcMainInternal = new IpcMainImpl() as ElectronInternal.IpcMainInternal +export const ipcMainInternal = new IpcMainImpl() as ElectronInternal.IpcMainInternal; // Do not throw exception when channel name is "error". -ipcMainInternal.on('error', () => {}) +ipcMainInternal.on('error', () => {}); diff --git a/lib/browser/message-port-main.ts b/lib/browser/message-port-main.ts index 0034677fa545f..10cb42ad5a63c 100644 --- a/lib/browser/message-port-main.ts +++ b/lib/browser/message-port-main.ts @@ -1,28 +1,28 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'events'; export class MessagePortMain extends EventEmitter { _internalPort: any constructor (internalPort: any) { - super() - this._internalPort = internalPort + super(); + this._internalPort = internalPort; this._internalPort.emit = (channel: string, event: {ports: any[]}) => { - if (channel === 'message') { event = { ...event, ports: event.ports.map(p => new MessagePortMain(p)) } } - this.emit(channel, event) - } + if (channel === 'message') { event = { ...event, ports: event.ports.map(p => new MessagePortMain(p)) }; } + this.emit(channel, event); + }; } start () { - return this._internalPort.start() + return this._internalPort.start(); } close () { - return this._internalPort.close() + return this._internalPort.close(); } postMessage (...args: any[]) { if (Array.isArray(args[1])) { - args[1] = args[1].map((o: any) => o instanceof MessagePortMain ? o._internalPort : o) + args[1] = args[1].map((o: any) => o instanceof MessagePortMain ? o._internalPort : o); } - return this._internalPort.postMessage(...args) + return this._internalPort.postMessage(...args); } } diff --git a/lib/browser/navigation-controller.js b/lib/browser/navigation-controller.js index 9b4989881f2ff..b80e8cf656936 100644 --- a/lib/browser/navigation-controller.js +++ b/lib/browser/navigation-controller.js @@ -1,23 +1,23 @@ -'use strict' +'use strict'; -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); // The history operation in renderer is redirected to browser. ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) { - event.sender.goBack() -}) + event.sender.goBack(); +}); ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD', function (event) { - event.sender.goForward() -}) + event.sender.goForward(); +}); ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (event, offset) { - event.sender.goToOffset(offset) -}) + event.sender.goToOffset(offset); +}); ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) { - event.returnValue = event.sender.length() -}) + event.returnValue = event.sender.length(); +}); // JavaScript implementation of Chromium's NavigationController. // Instead of relying on Chromium for history control, we compeletely do history @@ -26,64 +26,64 @@ ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) { // process is restarted everytime. const NavigationController = (function () { function NavigationController (webContents) { - this.webContents = webContents - this.clearHistory() + 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.currentIndex++; + this.history.push(this.webContents._getURL()); } this.webContents.on('navigation-entry-committed', (event, url, inPage, replaceEntry) => { if (this.inPageIndex > -1 && !inPage) { // Navigated to a new page, clear in-page mark. - this.inPageIndex = -1 + this.inPageIndex = -1; } else if (this.inPageIndex === -1 && inPage && !replaceEntry) { // Started in-page navigations. - this.inPageIndex = this.currentIndex + this.inPageIndex = this.currentIndex; } if (this.pendingIndex >= 0) { // Go to index. - this.currentIndex = this.pendingIndex - this.pendingIndex = -1 - this.history[this.currentIndex] = url + this.currentIndex = this.pendingIndex; + this.pendingIndex = -1; + this.history[this.currentIndex] = url; } else if (replaceEntry) { // Non-user initialized navigation. - this.history[this.currentIndex] = url + this.history[this.currentIndex] = url; } else { // Normal navigation. Clear history. - this.history = this.history.slice(0, this.currentIndex + 1) - this.currentIndex++ - this.history.push(url) + this.history = this.history.slice(0, this.currentIndex + 1); + this.currentIndex++; + this.history.push(url); } - }) + }); } NavigationController.prototype.loadURL = function (url, options) { if (options == null) { - options = {} + options = {}; } const p = new Promise((resolve, reject) => { const resolveAndCleanup = () => { - removeListeners() - resolve() - } + removeListeners(); + resolve(); + }; const rejectAndCleanup = (errorCode, errorDescription, url) => { - const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`) - Object.assign(err, { errno: errorCode, code: errorDescription, url }) - removeListeners() - reject(err) - } + const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`); + Object.assign(err, { errno: errorCode, code: errorDescription, url }); + removeListeners(); + reject(err); + }; const finishListener = () => { - resolveAndCleanup() - } + resolveAndCleanup(); + }; const failListener = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => { if (isMainFrame) { - rejectAndCleanup(errorCode, errorDescription, validatedURL) + rejectAndCleanup(errorCode, errorDescription, validatedURL); } - } + }; - let navigationStarted = false + let navigationStarted = false; const navigationListener = (event, url, isSameDocument, isMainFrame, frameProcessId, frameRoutingId, navigationId) => { if (isMainFrame) { if (navigationStarted && !isSameDocument) { @@ -96,11 +96,11 @@ const NavigationController = (function () { // considered navigation events but are triggered with isSameDocument. // We can ignore these to allow virtual routing on page load as long // as the routing does not leave the document - return rejectAndCleanup(-3, 'ERR_ABORTED', url) + return rejectAndCleanup(-3, 'ERR_ABORTED', url); } - navigationStarted = true + navigationStarted = true; } - } + }; const stopLoadingListener = () => { // By the time we get here, either 'finish' or 'fail' should have fired // if the navigation occurred. However, in some situations (e.g. when @@ -110,134 +110,134 @@ const NavigationController = (function () { // TODO(jeremy): enumerate all the cases in which this can happen. If // the only one is with a bad scheme, perhaps ERR_INVALID_ARGUMENT // would be more appropriate. - rejectAndCleanup(-2, 'ERR_FAILED', url) - } + rejectAndCleanup(-2, 'ERR_FAILED', url); + }; const removeListeners = () => { - this.webContents.removeListener('did-finish-load', finishListener) - this.webContents.removeListener('did-fail-load', failListener) - this.webContents.removeListener('did-start-navigation', navigationListener) - this.webContents.removeListener('did-stop-loading', stopLoadingListener) - } - this.webContents.on('did-finish-load', finishListener) - this.webContents.on('did-fail-load', failListener) - this.webContents.on('did-start-navigation', navigationListener) - this.webContents.on('did-stop-loading', stopLoadingListener) - }) + this.webContents.removeListener('did-finish-load', finishListener); + this.webContents.removeListener('did-fail-load', failListener); + this.webContents.removeListener('did-start-navigation', navigationListener); + this.webContents.removeListener('did-stop-loading', stopLoadingListener); + }; + this.webContents.on('did-finish-load', finishListener); + this.webContents.on('did-fail-load', failListener); + this.webContents.on('did-start-navigation', navigationListener); + this.webContents.on('did-stop-loading', stopLoadingListener); + }); // Add a no-op rejection handler to silence the unhandled rejection error. - p.catch(() => {}) - this.pendingIndex = -1 - this.webContents._loadURL(url, options) - this.webContents.emit('load-url', url, options) - return p - } + p.catch(() => {}); + this.pendingIndex = -1; + this.webContents._loadURL(url, options); + this.webContents.emit('load-url', url, options); + return p; + }; NavigationController.prototype.getURL = function () { if (this.currentIndex === -1) { - return '' + return ''; } else { - return this.history[this.currentIndex] + return this.history[this.currentIndex]; } - } + }; NavigationController.prototype.stop = function () { - this.pendingIndex = -1 - return this.webContents._stop() - } + this.pendingIndex = -1; + return this.webContents._stop(); + }; NavigationController.prototype.reload = function () { - this.pendingIndex = this.currentIndex - return this.webContents._loadURL(this.getURL(), {}) - } + this.pendingIndex = this.currentIndex; + return this.webContents._loadURL(this.getURL(), {}); + }; NavigationController.prototype.reloadIgnoringCache = function () { - this.pendingIndex = this.currentIndex + this.pendingIndex = this.currentIndex; return this.webContents._loadURL(this.getURL(), { extraHeaders: 'pragma: no-cache\n', reloadIgnoringCache: true - }) - } + }); + }; NavigationController.prototype.canGoBack = function () { - return this.getActiveIndex() > 0 - } + return this.getActiveIndex() > 0; + }; NavigationController.prototype.canGoForward = function () { - return this.getActiveIndex() < this.history.length - 1 - } + return this.getActiveIndex() < this.history.length - 1; + }; NavigationController.prototype.canGoToIndex = function (index) { - return index >= 0 && index < this.history.length - } + return index >= 0 && index < this.history.length; + }; NavigationController.prototype.canGoToOffset = function (offset) { - return this.canGoToIndex(this.currentIndex + offset) - } + return this.canGoToIndex(this.currentIndex + offset); + }; NavigationController.prototype.clearHistory = function () { - this.history = [] - this.currentIndex = -1 - this.pendingIndex = -1 - this.inPageIndex = -1 - } + this.history = []; + this.currentIndex = -1; + this.pendingIndex = -1; + this.inPageIndex = -1; + }; NavigationController.prototype.goBack = function () { if (!this.canGoBack()) { - return + return; } - this.pendingIndex = this.getActiveIndex() - 1 + this.pendingIndex = this.getActiveIndex() - 1; if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) { - return this.webContents._goBack() + return this.webContents._goBack(); } else { - return this.webContents._loadURL(this.history[this.pendingIndex], {}) + return this.webContents._loadURL(this.history[this.pendingIndex], {}); } - } + }; NavigationController.prototype.goForward = function () { if (!this.canGoForward()) { - return + return; } - this.pendingIndex = this.getActiveIndex() + 1 + this.pendingIndex = this.getActiveIndex() + 1; if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) { - return this.webContents._goForward() + return this.webContents._goForward(); } else { - return this.webContents._loadURL(this.history[this.pendingIndex], {}) + return this.webContents._loadURL(this.history[this.pendingIndex], {}); } - } + }; NavigationController.prototype.goToIndex = function (index) { if (!this.canGoToIndex(index)) { - return + return; } - this.pendingIndex = index - return this.webContents._loadURL(this.history[this.pendingIndex], {}) - } + this.pendingIndex = index; + return this.webContents._loadURL(this.history[this.pendingIndex], {}); + }; NavigationController.prototype.goToOffset = function (offset) { if (!this.canGoToOffset(offset)) { - return + return; } - const pendingIndex = this.currentIndex + offset + const pendingIndex = this.currentIndex + offset; if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) { - this.pendingIndex = pendingIndex - return this.webContents._goToOffset(offset) + this.pendingIndex = pendingIndex; + return this.webContents._goToOffset(offset); } else { - return this.goToIndex(pendingIndex) + return this.goToIndex(pendingIndex); } - } + }; NavigationController.prototype.getActiveIndex = function () { if (this.pendingIndex === -1) { - return this.currentIndex + return this.currentIndex; } else { - return this.pendingIndex + return this.pendingIndex; } - } + }; NavigationController.prototype.length = function () { - return this.history.length - } + return this.history.length; + }; - return NavigationController -})() + return NavigationController; +})(); -module.exports = NavigationController +module.exports = NavigationController; diff --git a/lib/browser/remote/objects-registry.ts b/lib/browser/remote/objects-registry.ts index 0de48c3960d6f..1142c8416fc5e 100644 --- a/lib/browser/remote/objects-registry.ts +++ b/lib/browser/remote/objects-registry.ts @@ -1,12 +1,12 @@ -'use strict' +'use strict'; -import { WebContents } from 'electron' +import { WebContents } from 'electron'; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); const getOwnerKey = (webContents: WebContents, contextId: string) => { - return `${webContents.id}-${contextId}` -} + return `${webContents.id}-${contextId}`; +}; class ObjectsRegistry { private nextId: number = 0 @@ -23,29 +23,29 @@ class ObjectsRegistry { // registered then the already assigned ID would be returned. add (webContents: WebContents, contextId: string, obj: any) { // Get or assign an ID to the object. - const id = this.saveToStorage(obj) + const id = this.saveToStorage(obj); // Add object to the set of referenced objects. - const ownerKey = getOwnerKey(webContents, contextId) - let owner = this.owners[ownerKey] + const ownerKey = getOwnerKey(webContents, contextId); + let owner = this.owners[ownerKey]; if (!owner) { - owner = this.owners[ownerKey] = new Map() - this.registerDeleteListener(webContents, contextId) + owner = this.owners[ownerKey] = new Map(); + this.registerDeleteListener(webContents, contextId); } if (!owner.has(id)) { - owner.set(id, 0) + owner.set(id, 0); // Increase reference count if not referenced before. - this.storage[id].count++ + this.storage[id].count++; } - owner.set(id, owner.get(id)! + 1) - return id + owner.set(id, owner.get(id)! + 1); + return id; } // Get an object according to its ID. get (id: number) { - const pointer = this.storage[id] - if (pointer != null) return pointer.object + const pointer = this.storage[id]; + if (pointer != null) return pointer.object; } // Dereference an object according to its ID. @@ -60,79 +60,79 @@ class ObjectsRegistry { // For more details on why we do renderer side ref counting see // https://github.com/electron/electron/pull/17464 remove (webContents: WebContents, contextId: string, id: number, rendererSideRefCount: number) { - const ownerKey = getOwnerKey(webContents, contextId) - const owner = this.owners[ownerKey] + const ownerKey = getOwnerKey(webContents, contextId); + const owner = this.owners[ownerKey]; if (owner && owner.has(id)) { - const newRefCount = owner.get(id)! - rendererSideRefCount + const newRefCount = owner.get(id)! - rendererSideRefCount; // Only completely remove if the number of references GCed in the // renderer is the same as the number of references we sent them if (newRefCount <= 0) { // Remove the reference in owner. - owner.delete(id) + owner.delete(id); // Dereference from the storage. - this.dereference(id) + this.dereference(id); } else { - owner.set(id, newRefCount) + owner.set(id, newRefCount); } } } // Clear all references to objects refrenced by the WebContents. clear (webContents: WebContents, contextId: string) { - const ownerKey = getOwnerKey(webContents, contextId) - const owner = this.owners[ownerKey] - if (!owner) return + const ownerKey = getOwnerKey(webContents, contextId); + const owner = this.owners[ownerKey]; + if (!owner) return; - for (const id of owner.keys()) this.dereference(id) + for (const id of owner.keys()) this.dereference(id); - delete this.owners[ownerKey] + delete this.owners[ownerKey]; } // Private: Saves the object into storage and assigns an ID for it. saveToStorage (object: any) { - let id: number = v8Util.getHiddenValue(object, 'atomId') + let id: number = v8Util.getHiddenValue(object, 'atomId'); if (!id) { - id = ++this.nextId + id = ++this.nextId; this.storage[id] = { count: 0, object: object - } - v8Util.setHiddenValue(object, 'atomId', id) + }; + v8Util.setHiddenValue(object, 'atomId', id); } - return id + return id; } // Private: Dereference the object from store. dereference (id: number) { - const pointer = this.storage[id] + const pointer = this.storage[id]; if (pointer == null) { - return + return; } - pointer.count -= 1 + pointer.count -= 1; if (pointer.count === 0) { - v8Util.deleteHiddenValue(pointer.object, 'atomId') - delete this.storage[id] + v8Util.deleteHiddenValue(pointer.object, 'atomId'); + delete this.storage[id]; } } // Private: Clear the storage when renderer process is destroyed. registerDeleteListener (webContents: WebContents, contextId: string) { // contextId => ${processHostId}-${contextCount} - const processHostId = contextId.split('-')[0] + const processHostId = contextId.split('-')[0]; const listener = (_: any, deletedProcessHostId: string) => { if (deletedProcessHostId && deletedProcessHostId.toString() === processHostId) { - webContents.removeListener('render-view-deleted' as any, listener) - this.clear(webContents, contextId) + webContents.removeListener('render-view-deleted' as any, listener); + this.clear(webContents, contextId); } - } + }; // Note that the "render-view-deleted" event may not be emitted on time when // the renderer process get destroyed because of navigation, we rely on the // renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to // guard this situation. - webContents.on('render-view-deleted' as any, listener) + webContents.on('render-view-deleted' as any, listener); } } -export default new ObjectsRegistry() +export default new ObjectsRegistry(); diff --git a/lib/browser/remote/server.ts b/lib/browser/remote/server.ts index 0d01aca3ec990..2bdeed3ea4a36 100644 --- a/lib/browser/remote/server.ts +++ b/lib/browser/remote/server.ts @@ -1,29 +1,29 @@ -'use strict' +'use strict'; -import * as electron from 'electron' -import { EventEmitter } from 'events' -import objectsRegistry from './objects-registry' -import { ipcMainInternal } from '../ipc-main-internal' -import { isPromise, isSerializableObject } from '@electron/internal/common/type-utils' +import * as electron from 'electron'; +import { EventEmitter } from 'events'; +import objectsRegistry from './objects-registry'; +import { ipcMainInternal } from '../ipc-main-internal'; +import { isPromise, isSerializableObject } from '@electron/internal/common/type-utils'; -const v8Util = process.electronBinding('v8_util') -const eventBinding = process.electronBinding('event') -const features = process.electronBinding('features') +const v8Util = process.electronBinding('v8_util'); +const eventBinding = process.electronBinding('event'); +const features = process.electronBinding('features'); if (!features.isRemoteModuleEnabled()) { - throw new Error('remote module is disabled') + throw new Error('remote module is disabled'); } -const hasProp = {}.hasOwnProperty +const hasProp = {}.hasOwnProperty; // The internal properties of Function. const FUNCTION_PROPERTIES = [ 'length', 'name', 'arguments', 'caller', 'prototype' -] +]; // The remote functions in renderer processes. // id => Function -const rendererFunctions = v8Util.createDoubleIDWeakMap<(...args: any[]) => void>() +const rendererFunctions = v8Util.createDoubleIDWeakMap<(...args: any[]) => void>(); type ObjectMember = { name: string, @@ -35,28 +35,28 @@ type ObjectMember = { // Return the description of object's members: const getObjectMembers = function (object: any): ObjectMember[] { - let names = Object.getOwnPropertyNames(object) + let names = Object.getOwnPropertyNames(object); // For Function, we should not override following properties even though they // are "own" properties. if (typeof object === 'function') { names = names.filter((name) => { - return !FUNCTION_PROPERTIES.includes(name) - }) + return !FUNCTION_PROPERTIES.includes(name); + }); } // Map properties to descriptors. return names.map((name) => { - const descriptor = Object.getOwnPropertyDescriptor(object, name)! - let type: ObjectMember['type'] - let writable = false + const descriptor = Object.getOwnPropertyDescriptor(object, name)!; + let type: ObjectMember['type']; + let writable = false; if (descriptor.get === undefined && typeof object[name] === 'function') { - type = 'method' + type = 'method'; } else { - if (descriptor.set || descriptor.writable) writable = true - type = 'get' + if (descriptor.set || descriptor.writable) writable = true; + type = 'get'; } - return { name, enumerable: descriptor.enumerable, writable, type } - }) -} + return { name, enumerable: descriptor.enumerable, writable, type }; + }); +}; type ObjProtoDescriptor = { members: ObjectMember[], @@ -65,13 +65,13 @@ type ObjProtoDescriptor = { // Return the description of object's prototype. const getObjectPrototype = function (object: any): ObjProtoDescriptor { - const proto = Object.getPrototypeOf(object) - if (proto === null || proto === Object.prototype) return null + const proto = Object.getPrototypeOf(object); + if (proto === null || proto === Object.prototype) return null; return { members: getObjectMembers(proto), proto: getObjectPrototype(proto) - } -} + }; +}; type MetaType = { type: 'number', @@ -118,25 +118,25 @@ type MetaType = { // Convert a real value into meta data. const valueToMeta = function (sender: electron.WebContents, contextId: string, value: any, optimizeSimpleObject = false): MetaType { // Determine the type of value. - let type: MetaType['type'] = typeof value + let type: MetaType['type'] = typeof value; if (type === 'object') { // Recognize certain types of objects. if (value instanceof Buffer) { - type = 'buffer' + type = 'buffer'; } else if (Array.isArray(value)) { - type = 'array' + type = 'array'; } else if (value instanceof Error) { - type = 'error' + type = 'error'; } else if (isSerializableObject(value)) { - type = 'value' + type = 'value'; } else if (isPromise(value)) { - type = 'promise' + type = 'promise'; } else if (hasProp.call(value, 'callee') && value.length != null) { // Treat the arguments object as array. - type = 'array' + type = 'array'; } else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) { // Treat simple objects as value. - type = 'value' + type = 'value'; } } @@ -145,7 +145,7 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v return { type, members: value.map((el: any) => valueToMeta(sender, contextId, el, optimizeSimpleObject)) - } + }; } else if (type === 'object' || type === 'function') { return { type, @@ -156,20 +156,20 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v id: objectsRegistry.add(sender, contextId, value), members: getObjectMembers(value), proto: getObjectPrototype(value) - } + }; } else if (type === 'buffer') { - return { type, value } + return { type, value }; } else if (type === 'promise') { // Add default handler to prevent unhandled rejections in main process // Instead they should appear in the renderer process - value.then(function () {}, function () {}) + value.then(function () {}, function () {}); return { type, then: valueToMeta(sender, contextId, function (onFulfilled: Function, onRejected: Function) { - value.then(onFulfilled, onRejected) + value.then(onFulfilled, onRejected); }) - } + }; } else if (type === 'error') { return { type, @@ -178,42 +178,42 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v name, value: valueToMeta(sender, contextId, value[name]) })) - } + }; } else { return { type: 'value', value - } + }; } -} +}; const throwRPCError = function (message: string) { - const error = new Error(message) as Error & {code: string, errno: number} - error.code = 'EBADRPC' - error.errno = -72 - throw error -} + const error = new Error(message) as Error & {code: string, errno: number}; + error.code = 'EBADRPC'; + error.errno = -72; + throw error; +}; const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...args: any[]) => void) => { - const location = v8Util.getHiddenValue(callIntoRenderer, 'location') + const location = v8Util.getHiddenValue(callIntoRenderer, 'location'); let message = 'Attempting to call a function in a renderer window that has been closed or released.' + - `\nFunction provided here: ${location}` + `\nFunction provided here: ${location}`; if (sender instanceof EventEmitter) { const remoteEvents = sender.eventNames().filter((eventName) => { - return sender.listeners(eventName).includes(callIntoRenderer) - }) + return sender.listeners(eventName).includes(callIntoRenderer); + }); if (remoteEvents.length > 0) { - message += `\nRemote event names: ${remoteEvents.join(', ')}` + message += `\nRemote event names: ${remoteEvents.join(', ')}`; remoteEvents.forEach((eventName) => { - sender.removeListener(eventName as any, callIntoRenderer) - }) + sender.removeListener(eventName as any, callIntoRenderer); + }); } } - console.warn(message) -} + console.warn(message); +}; type MetaTypeFromRenderer = { type: 'value', @@ -248,301 +248,301 @@ const fakeConstructor = (constructor: Function, name: string) => new Proxy(Object, { get (target, prop, receiver) { if (prop === 'name') { - return name + return name; } else { - return Reflect.get(target, prop, receiver) + return Reflect.get(target, prop, receiver); } } - }) + }); // Convert array of meta data from renderer into array of real values. const unwrapArgs = function (sender: electron.WebContents, frameId: number, contextId: string, args: any[]) { const metaToValue = function (meta: MetaTypeFromRenderer): any { switch (meta.type) { case 'value': - return meta.value + return meta.value; case 'remote-object': - return objectsRegistry.get(meta.id) + return objectsRegistry.get(meta.id); case 'array': - return unwrapArgs(sender, frameId, contextId, meta.value) + return unwrapArgs(sender, frameId, contextId, meta.value); case 'buffer': - return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength) + return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength); case 'promise': return Promise.resolve({ then: metaToValue(meta.then) - }) + }); case 'object': { const ret: any = meta.name !== 'Object' ? Object.create({ constructor: fakeConstructor(Object, meta.name) - }) : {} + }) : {}; for (const { name, value } of meta.members) { - ret[name] = metaToValue(value) + ret[name] = metaToValue(value); } - return ret + return ret; } case 'function-with-return-value': { - const returnValue = metaToValue(meta.value) + const returnValue = metaToValue(meta.value); return function () { - return returnValue - } + return returnValue; + }; } case 'function': { // Merge contextId and meta.id, since meta.id can be the same in // different webContents. - const objectId: [string, number] = [contextId, meta.id] + const objectId: [string, number] = [contextId, meta.id]; // Cache the callbacks in renderer. if (rendererFunctions.has(objectId)) { - return rendererFunctions.get(objectId) + return rendererFunctions.get(objectId); } const callIntoRenderer = function (this: any, ...args: any[]) { - let succeed = false + let succeed = false; if (!sender.isDestroyed()) { - succeed = (sender as any)._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args)) + succeed = (sender as any)._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args)); } if (!succeed) { - removeRemoteListenersAndLogWarning(this, callIntoRenderer) + removeRemoteListenersAndLogWarning(this, callIntoRenderer); } - } - v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location) - Object.defineProperty(callIntoRenderer, 'length', { value: meta.length }) + }; + v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location); + Object.defineProperty(callIntoRenderer, 'length', { value: meta.length }); - v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender) - rendererFunctions.set(objectId, callIntoRenderer) - return callIntoRenderer + v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender); + rendererFunctions.set(objectId, callIntoRenderer); + return callIntoRenderer; } default: - throw new TypeError(`Unknown type: ${(meta as any).type}`) + throw new TypeError(`Unknown type: ${(meta as any).type}`); } - } - return args.map(metaToValue) -} + }; + return args.map(metaToValue); +}; const isRemoteModuleEnabledImpl = function (contents: electron.WebContents) { - const webPreferences = (contents as any).getLastWebPreferences() || {} - return webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false -} + const webPreferences = (contents as any).getLastWebPreferences() || {}; + return webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false; +}; -const isRemoteModuleEnabledCache = new WeakMap() +const isRemoteModuleEnabledCache = new WeakMap(); const isRemoteModuleEnabled = function (contents: electron.WebContents) { if (!isRemoteModuleEnabledCache.has(contents)) { - isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents)) + isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents)); } - return isRemoteModuleEnabledCache.get(contents) -} + return isRemoteModuleEnabledCache.get(contents); +}; const handleRemoteCommand = function (channel: string, handler: (event: ElectronInternal.IpcMainInternalEvent, contextId: string, ...args: any[]) => void) { ipcMainInternal.on(channel, (event, contextId: string, ...args: any[]) => { - let returnValue + let returnValue; if (!isRemoteModuleEnabled(event.sender)) { - event.returnValue = null - return + event.returnValue = null; + return; } try { - returnValue = handler(event, contextId, ...args) + returnValue = handler(event, contextId, ...args); } catch (error) { returnValue = { type: 'exception', value: valueToMeta(event.sender, contextId, error) - } + }; } if (returnValue !== undefined) { - event.returnValue = returnValue + event.returnValue = returnValue; } - }) -} + }); +}; const emitCustomEvent = function (contents: electron.WebContents, eventName: string, ...args: any[]) { - const event = eventBinding.createWithSender(contents) + const event = eventBinding.createWithSender(contents); - electron.app.emit(eventName, event, contents, ...args) - contents.emit(eventName, event, ...args) + electron.app.emit(eventName, event, contents, ...args); + contents.emit(eventName, event, ...args); - return event -} + return event; +}; const logStack = function (contents: electron.WebContents, code: string, stack: string | undefined) { if (stack) { - console.warn(`WebContents (${contents.id}): ${code}`, stack) + console.warn(`WebContents (${contents.id}): ${code}`, stack); } -} +}; handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) { - const objectId: [string, number] = [passedContextId, id] + const objectId: [string, number] = [passedContextId, id]; if (!rendererFunctions.has(objectId)) { // Do nothing if the error has already been reported before. - return + return; } - removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId)!) -}) + removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId)!); +}); handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName, stack) { - logStack(event.sender, `remote.require('${moduleName}')`, stack) - const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName) + logStack(event.sender, `remote.require('${moduleName}')`, stack); + const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.require('${moduleName}')`) + throw new Error(`Blocked remote.require('${moduleName}')`); } else { - customEvent.returnValue = process.mainModule!.require(moduleName) + customEvent.returnValue = process.mainModule!.require(moduleName); } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName, stack) { - logStack(event.sender, `remote.getBuiltin('${moduleName}')`, stack) - const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName) + logStack(event.sender, `remote.getBuiltin('${moduleName}')`, stack); + const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.getBuiltin('${moduleName}')`) + throw new Error(`Blocked remote.getBuiltin('${moduleName}')`); } else { - customEvent.returnValue = (electron as any)[moduleName] + customEvent.returnValue = (electron as any)[moduleName]; } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName, stack) { - logStack(event.sender, `remote.getGlobal('${globalName}')`, stack) - const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName) + logStack(event.sender, `remote.getGlobal('${globalName}')`, stack); + const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.getGlobal('${globalName}')`) + throw new Error(`Blocked remote.getGlobal('${globalName}')`); } else { - customEvent.returnValue = (global as any)[globalName] + customEvent.returnValue = (global as any)[globalName]; } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId, stack) { - logStack(event.sender, 'remote.getCurrentWindow()', stack) - const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window') + logStack(event.sender, 'remote.getCurrentWindow()', stack); + const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window'); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error('Blocked remote.getCurrentWindow()') + throw new Error('Blocked remote.getCurrentWindow()'); } else { - customEvent.returnValue = event.sender.getOwnerBrowserWindow() + customEvent.returnValue = event.sender.getOwnerBrowserWindow(); } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId, stack) { - logStack(event.sender, 'remote.getCurrentWebContents()', stack) - const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents') + logStack(event.sender, 'remote.getCurrentWebContents()', stack); + const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents'); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error('Blocked remote.getCurrentWebContents()') + throw new Error('Blocked remote.getCurrentWebContents()'); } else { - customEvent.returnValue = event.sender + customEvent.returnValue = event.sender; } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const constructor = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const constructor = objectsRegistry.get(id); if (constructor == null) { - throwRPCError(`Cannot call constructor on missing remote object ${id}`) + throwRPCError(`Cannot call constructor on missing remote object ${id}`); } - return valueToMeta(event.sender, contextId, new constructor(...args)) -}) + return valueToMeta(event.sender, contextId, new constructor(...args)); +}); handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const func = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const func = objectsRegistry.get(id); if (func == null) { - throwRPCError(`Cannot call function on missing remote object ${id}`) + throwRPCError(`Cannot call function on missing remote object ${id}`); } try { - return valueToMeta(event.sender, contextId, func(...args), true) + return valueToMeta(event.sender, contextId, func(...args), true); } catch (error) { const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`); - (err as any).cause = error - throw err + (err as any).cause = error; + throw err; } -}) +}); handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const object = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const object = objectsRegistry.get(id); if (object == null) { - throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`) + throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`); } - return valueToMeta(event.sender, contextId, new object[method](...args)) -}) + return valueToMeta(event.sender, contextId, new object[method](...args)); +}); handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const object = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const object = objectsRegistry.get(id); if (object == null) { - throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`) + throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`); } try { - return valueToMeta(event.sender, contextId, object[method](...args), true) + return valueToMeta(event.sender, contextId, object[method](...args), true); } catch (error) { const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`); - (err as any).cause = error - throw err + (err as any).cause = error; + throw err; } -}) +}); handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const obj = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const obj = objectsRegistry.get(id); if (obj == null) { - throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`) + throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`); } - obj[name] = args[0] - return null -}) + obj[name] = args[0]; + return null; +}); handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) { - const obj = objectsRegistry.get(id) + const obj = objectsRegistry.get(id); if (obj == null) { - throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`) + throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`); } - return valueToMeta(event.sender, contextId, obj[name]) -}) + return valueToMeta(event.sender, contextId, obj[name]); +}); handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id, rendererSideRefCount) { - objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount) -}) + objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount); +}); handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => { - objectsRegistry.clear(event.sender, contextId) -}) + objectsRegistry.clear(event.sender, contextId); +}); module.exports = { isRemoteModuleEnabled -} +}; diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index f2514df63e194..10133095a7ebc 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -1,118 +1,118 @@ -'use strict' +'use strict'; -const electron = require('electron') -const fs = require('fs') +const electron = require('electron'); +const fs = require('fs'); -const eventBinding = process.electronBinding('event') -const clipboard = process.electronBinding('clipboard') -const features = process.electronBinding('features') +const eventBinding = process.electronBinding('event'); +const clipboard = process.electronBinding('clipboard'); +const features = process.electronBinding('features'); -const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') -const guestViewManager = require('@electron/internal/browser/guest-view-manager') -const typeUtils = require('@electron/internal/common/type-utils') +const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); +const guestViewManager = require('@electron/internal/browser/guest-view-manager'); +const typeUtils = require('@electron/internal/common/type-utils'); const emitCustomEvent = function (contents, eventName, ...args) { - const event = eventBinding.createWithSender(contents) + const event = eventBinding.createWithSender(contents); - electron.app.emit(eventName, event, contents, ...args) - contents.emit(eventName, event, ...args) + electron.app.emit(eventName, event, contents, ...args); + contents.emit(eventName, event, ...args); - return event -} + return event; +}; const logStack = function (contents, code, stack) { if (stack) { - console.warn(`WebContents (${contents.id}): ${code}`, stack) + console.warn(`WebContents (${contents.id}): ${code}`, stack); } -} +}; // Implements window.close() ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) { - const window = event.sender.getOwnerBrowserWindow() + const window = event.sender.getOwnerBrowserWindow(); if (window) { - window.close() + window.close(); } - event.returnValue = null -}) + event.returnValue = null; +}); ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_INIT', function (event, options) { - return crashReporterInit(options) -}) + return crashReporterInit(options); +}); ipcMainInternal.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) { - return event.sender.getLastWebPreferences() -}) + return event.sender.getLastWebPreferences(); +}); // Methods not listed in this set are called directly in the renderer process. const allowedClipboardMethods = (() => { switch (process.platform) { case 'darwin': - return new Set(['readFindText', 'writeFindText']) + return new Set(['readFindText', 'writeFindText']); case 'linux': - return new Set(Object.keys(clipboard)) + return new Set(Object.keys(clipboard)); default: - return new Set() + return new Set(); } -})() +})(); ipcMainUtils.handleSync('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) { if (!allowedClipboardMethods.has(method)) { - throw new Error(`Invalid method: ${method}`) + throw new Error(`Invalid method: ${method}`); } - return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args))) -}) + return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args))); +}); if (features.isDesktopCapturerEnabled()) { - const desktopCapturer = require('@electron/internal/browser/desktop-capturer') + const desktopCapturer = require('@electron/internal/browser/desktop-capturer'); ipcMainInternal.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, options, stack) { - logStack(event.sender, 'desktopCapturer.getSources()', stack) - const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources') + logStack(event.sender, 'desktopCapturer.getSources()', stack); + const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources'); if (customEvent.defaultPrevented) { - console.error('Blocked desktopCapturer.getSources()') - return [] + console.error('Blocked desktopCapturer.getSources()'); + return []; } - return desktopCapturer.getSources(event, options) - }) + return desktopCapturer.getSources(event, options); + }); } const isRemoteModuleEnabled = features.isRemoteModuleEnabled() ? require('@electron/internal/browser/remote/server').isRemoteModuleEnabled - : () => false + : () => false; const getPreloadScript = async function (preloadPath) { - let preloadSrc = null - let preloadError = null + let preloadSrc = null; + let preloadError = null; try { - preloadSrc = (await fs.promises.readFile(preloadPath)).toString() + preloadSrc = (await fs.promises.readFile(preloadPath)).toString(); } catch (error) { - preloadError = error + preloadError = error; } - return { preloadPath, preloadSrc, preloadError } -} + return { preloadPath, preloadSrc, preloadError }; +}; if (features.isExtensionsEnabled()) { - ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => []) + ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => []); } else { - const { getContentScripts } = require('@electron/internal/browser/chrome-extension') - ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts()) + const { getContentScripts } = require('@electron/internal/browser/chrome-extension'); + ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts()); } ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) { - const preloadPaths = event.sender._getPreloadPaths() + const preloadPaths = event.sender._getPreloadPaths(); - let contentScripts = [] + let contentScripts = []; if (!features.isExtensionsEnabled()) { - const { getContentScripts } = require('@electron/internal/browser/chrome-extension') - contentScripts = getContentScripts() + const { getContentScripts } = require('@electron/internal/browser/chrome-extension'); + contentScripts = getContentScripts(); } - const webPreferences = event.sender.getLastWebPreferences() || {} + const webPreferences = event.sender.getLastWebPreferences() || {}; return { contentScripts, @@ -129,9 +129,9 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) versions: process.versions, execPath: process.helperExecPath } - } -}) + }; +}); ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) { - event.sender.emit('preload-error', event, preloadPath, error) -}) + event.sender.emit('preload-error', event, preloadPath, error); +}); diff --git a/lib/browser/utils.ts b/lib/browser/utils.ts index 75bb0394e38a7..fd2b35d58afc3 100644 --- a/lib/browser/utils.ts +++ b/lib/browser/utils.ts @@ -1,4 +1,4 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'events'; /** * Creates a lazy instance of modules that can't be required before the @@ -16,23 +16,23 @@ export function createLazyInstance ( holder: Object, isEventEmitter: Boolean ) { - let lazyModule: Object - const module: any = {} + let lazyModule: Object; + const module: any = {}; for (const method in (holder as any).prototype) { // eslint-disable-line guard-for-in module[method] = (...args: any) => { // create new instance of module at runtime if none exists if (!lazyModule) { - lazyModule = creator() - if (isEventEmitter) EventEmitter.call(lazyModule as any) + lazyModule = creator(); + if (isEventEmitter) EventEmitter.call(lazyModule as any); } // check for properties on the prototype chain that aren't functions if (typeof (lazyModule as any)[method] !== 'function') { - return (lazyModule as any)[method] + return (lazyModule as any)[method]; } - return (lazyModule as any)[method](...args) - } + return (lazyModule as any)[method](...args); + }; } - return module + return module; } diff --git a/lib/common/api/clipboard.js b/lib/common/api/clipboard.js index 50763dd5a5706..a7c49cd88488d 100644 --- a/lib/common/api/clipboard.js +++ b/lib/common/api/clipboard.js @@ -1,29 +1,29 @@ -'use strict' +'use strict'; -const clipboard = process.electronBinding('clipboard') +const clipboard = process.electronBinding('clipboard'); if (process.type === 'renderer') { - const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') - const typeUtils = require('@electron/internal/common/type-utils') + const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); + const typeUtils = require('@electron/internal/common/type-utils'); const makeRemoteMethod = function (method) { return (...args) => { - args = typeUtils.serialize(args) - const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args) - return typeUtils.deserialize(result) - } - } + args = typeUtils.serialize(args); + const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args); + return typeUtils.deserialize(result); + }; + }; if (process.platform === 'linux') { // On Linux we could not access clipboard in renderer process. for (const method of Object.keys(clipboard)) { - clipboard[method] = makeRemoteMethod(method) + clipboard[method] = makeRemoteMethod(method); } } else if (process.platform === 'darwin') { // Read/write to find pasteboard over IPC since only main process is notified of changes - clipboard.readFindText = makeRemoteMethod('readFindText') - clipboard.writeFindText = makeRemoteMethod('writeFindText') + clipboard.readFindText = makeRemoteMethod('readFindText'); + clipboard.writeFindText = makeRemoteMethod('writeFindText'); } } -module.exports = clipboard +module.exports = clipboard; diff --git a/lib/common/api/deprecate.ts b/lib/common/api/deprecate.ts index 5083e4d59e430..6a08231cae5ff 100644 --- a/lib/common/api/deprecate.ts +++ b/lib/common/api/deprecate.ts @@ -1,95 +1,95 @@ -let deprecationHandler: ElectronInternal.DeprecationHandler | null = null +let deprecationHandler: ElectronInternal.DeprecationHandler | null = null; function warnOnce (oldName: string, newName?: string) { - let warned = false + let warned = false; const msg = newName ? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.` - : `'${oldName}' is deprecated and will be removed.` + : `'${oldName}' is deprecated and will be removed.`; return () => { if (!warned && !process.noDeprecation) { - warned = true - deprecate.log(msg) + warned = true; + deprecate.log(msg); } - } + }; } const deprecate: ElectronInternal.DeprecationUtil = { warnOnce, - setHandler: (handler) => { deprecationHandler = handler }, + setHandler: (handler) => { deprecationHandler = handler; }, getHandler: () => deprecationHandler, warn: (oldName, newName) => { if (!process.noDeprecation) { - deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`) + deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`); } }, log: (message) => { if (typeof deprecationHandler === 'function') { - deprecationHandler(message) + deprecationHandler(message); } else if (process.throwDeprecation) { - throw new Error(message) + throw new Error(message); } else if (process.traceDeprecation) { - return console.trace(message) + return console.trace(message); } else { - return console.warn(`(electron) ${message}`) + return console.warn(`(electron) ${message}`); } }, // remove a function with no replacement removeFunction: (fn, removedName) => { - if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`) } + if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`); } // wrap the deprecated function to warn user - const warn = warnOnce(`${fn.name} function`) + const warn = warnOnce(`${fn.name} function`); return function (this: any) { - warn() - fn.apply(this, arguments) - } + warn(); + fn.apply(this, arguments); + }; }, // change the name of a function renameFunction: (fn, newName) => { - const warn = warnOnce(`${fn.name} function`, `${newName} function`) + const warn = warnOnce(`${fn.name} function`, `${newName} function`); return function (this: any) { - warn() - return fn.apply(this, arguments) - } + warn(); + return fn.apply(this, arguments); + }; }, moveAPI: (fn: Function, oldUsage: string, newUsage: string) => { - const warn = warnOnce(oldUsage, newUsage) + const warn = warnOnce(oldUsage, newUsage); return function (this: any) { - warn() - return fn.apply(this, arguments) - } + warn(); + return fn.apply(this, arguments); + }; }, // change the name of an event event: (emitter, oldName, newName) => { const warn = newName.startsWith('-') /* internal event */ ? warnOnce(`${oldName} event`) - : warnOnce(`${oldName} event`, `${newName} event`) + : warnOnce(`${oldName} event`, `${newName} event`); return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) { if (this.listenerCount(oldName) !== 0) { - warn() - this.emit(oldName, ...args) + warn(); + this.emit(oldName, ...args); } - }) + }); }, // deprecate a getter/setter function pair in favor of a property fnToProperty: (prototype: any, prop: string, getter: string, setter?: string) => { const withWarnOnce = function (obj: any, key: any, oldName: string, newName: string) { - const warn = warnOnce(oldName, newName) - const method = obj[key] + const warn = warnOnce(oldName, newName); + const method = obj[key]; return function (this: any, ...args: any) { - warn() - return method.apply(this, args) - } - } + warn(); + return method.apply(this, args); + }; + }; - prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`) + prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`); if (setter) { - prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`) + prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`); } }, @@ -98,55 +98,55 @@ const deprecate: ElectronInternal.DeprecationUtil = { // if the property's already been removed, warn about it const info = Object.getOwnPropertyDescriptor((o as any).__proto__, removedName) // eslint-disable-line if (!info) { - deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`) - return o + deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`); + return o; } if (!info.get || !info.set) { - deprecate.log(`Unable to remove property '${removedName}' from an object does not have a getter / setter`) - return o + deprecate.log(`Unable to remove property '${removedName}' from an object does not have a getter / setter`); + return o; } // wrap the deprecated property in an accessor to warn - const warn = warnOnce(removedName) + const warn = warnOnce(removedName); return Object.defineProperty(o, removedName, { configurable: true, get: () => { - warn() - return info.get!.call(o) + warn(); + return info.get!.call(o); }, set: newVal => { if (!onlyForValues || onlyForValues.includes(newVal)) { - warn() + warn(); } - return info.set!.call(o, newVal) + return info.set!.call(o, newVal); } - }) + }); }, // change the name of a property renameProperty: (o, oldName, newName) => { - const warn = warnOnce(oldName, newName) + const warn = warnOnce(oldName, newName); // if the new property isn't there yet, // inject it and warn about it if ((oldName in o) && !(newName in o)) { - warn() - o[newName] = (o as any)[oldName] + warn(); + o[newName] = (o as any)[oldName]; } // wrap the deprecated property in an accessor to warn // and redirect to the new property return Object.defineProperty(o, oldName, { get: () => { - warn() - return o[newName] + warn(); + return o[newName]; }, set: value => { - warn() - o[newName] = value + warn(); + o[newName] = value; } - }) + }); } -} +}; -export default deprecate +export default deprecate; diff --git a/lib/common/api/module-list.ts b/lib/common/api/module-list.ts index b329e7e469ee1..a5493e552dfa8 100644 --- a/lib/common/api/module-list.ts +++ b/lib/common/api/module-list.ts @@ -5,4 +5,4 @@ export const commonModuleList: ElectronInternal.ModuleEntry[] = [ { name: 'shell', loader: () => require('./shell') }, // The internal modules, invisible unless you know their names. { name: 'deprecate', loader: () => require('./deprecate'), private: true } -] +]; diff --git a/lib/common/api/native-image.js b/lib/common/api/native-image.js index f1f2d51d102eb..e65060b3bc9c0 100644 --- a/lib/common/api/native-image.js +++ b/lib/common/api/native-image.js @@ -1,5 +1,5 @@ -'use strict' +'use strict'; -const { nativeImage } = process.electronBinding('native_image') +const { nativeImage } = process.electronBinding('native_image'); -module.exports = nativeImage +module.exports = nativeImage; diff --git a/lib/common/api/shell.js b/lib/common/api/shell.js index 9e9b673523588..c9f7b6c0e4e23 100644 --- a/lib/common/api/shell.js +++ b/lib/common/api/shell.js @@ -1,3 +1,3 @@ -'use strict' +'use strict'; -module.exports = process.electronBinding('shell') +module.exports = process.electronBinding('shell'); diff --git a/lib/common/asar.js b/lib/common/asar.js index a02e8ac1aa539..5837a423f4981 100644 --- a/lib/common/asar.js +++ b/lib/common/asar.js @@ -1,82 +1,82 @@ 'use strict'; (function () { - const asar = process._linkedBinding('electron_common_asar') - const v8Util = process._linkedBinding('electron_common_v8_util') - const { Buffer } = require('buffer') - const Module = require('module') - const path = require('path') - const util = require('util') + const asar = process._linkedBinding('electron_common_asar'); + const v8Util = process._linkedBinding('electron_common_v8_util'); + const { Buffer } = require('buffer'); + const Module = require('module'); + const path = require('path'); + const util = require('util'); - const Promise = global.Promise + const Promise = global.Promise; const envNoAsar = process.env.ELECTRON_NO_ASAR && process.type !== 'browser' && - process.type !== 'renderer' - const isAsarDisabled = () => process.noAsar || envNoAsar + process.type !== 'renderer'; + const isAsarDisabled = () => process.noAsar || envNoAsar; - const internalBinding = process.internalBinding - delete process.internalBinding + const internalBinding = process.internalBinding; + delete process.internalBinding; /** * @param {!Function} functionToCall * @param {!Array|undefined} args */ const nextTick = (functionToCall, args = []) => { - process.nextTick(() => functionToCall(...args)) - } + process.nextTick(() => functionToCall(...args)); + }; // Cache asar archive objects. - const cachedArchives = new Map() + const cachedArchives = new Map(); const getOrCreateArchive = archivePath => { - const isCached = cachedArchives.has(archivePath) + const isCached = cachedArchives.has(archivePath); if (isCached) { - return cachedArchives.get(archivePath) + return cachedArchives.get(archivePath); } - const newArchive = asar.createArchive(archivePath) - if (!newArchive) return null + const newArchive = asar.createArchive(archivePath); + if (!newArchive) return null; - cachedArchives.set(archivePath, newArchive) - return newArchive - } + cachedArchives.set(archivePath, newArchive); + return newArchive; + }; // Separate asar package's path from full path. const splitPath = archivePathOrBuffer => { // Shortcut for disabled asar. - if (isAsarDisabled()) return { isAsar: false } + if (isAsarDisabled()) return { isAsar: false }; // Check for a bad argument type. - let archivePath = archivePathOrBuffer + let archivePath = archivePathOrBuffer; if (Buffer.isBuffer(archivePathOrBuffer)) { - archivePath = archivePathOrBuffer.toString() + archivePath = archivePathOrBuffer.toString(); } - if (typeof archivePath !== 'string') return { isAsar: false } + if (typeof archivePath !== 'string') return { isAsar: false }; - return asar.splitPath(path.normalize(archivePath)) - } + return asar.splitPath(path.normalize(archivePath)); + }; // Convert asar archive's Stats object to fs's Stats object. - let nextInode = 0 + let nextInode = 0; - const uid = process.getuid != null ? process.getuid() : 0 - const gid = process.getgid != null ? process.getgid() : 0 + const uid = process.getuid != null ? process.getuid() : 0; + const gid = process.getgid != null ? process.getgid() : 0; - const fakeTime = new Date() - const msec = (date) => (date || fakeTime).getTime() + const fakeTime = new Date(); + const msec = (date) => (date || fakeTime).getTime(); const asarStatsToFsStats = function (stats) { - const { Stats, constants } = require('fs') + const { Stats, constants } = require('fs'); - let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR + let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR; if (stats.isFile) { - mode ^= constants.S_IFREG + mode ^= constants.S_IFREG; } else if (stats.isDirectory) { - mode ^= constants.S_IFDIR + mode ^= constants.S_IFDIR; } else if (stats.isLink) { - mode ^= constants.S_IFLNK + mode ^= constants.S_IFLNK; } return new Stats( @@ -94,636 +94,636 @@ msec(stats.mtime), // mtim_msec msec(stats.ctime), // ctim_msec msec(stats.birthtime) // birthtim_msec - ) - } + ); + }; const AsarError = { NOT_FOUND: 'NOT_FOUND', NOT_DIR: 'NOT_DIR', NO_ACCESS: 'NO_ACCESS', INVALID_ARCHIVE: 'INVALID_ARCHIVE' - } + }; const createError = (errorType, { asarPath, filePath } = {}) => { - let error + let error; switch (errorType) { case AsarError.NOT_FOUND: - error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`) - error.code = 'ENOENT' - error.errno = -2 - break + error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`); + error.code = 'ENOENT'; + error.errno = -2; + break; case AsarError.NOT_DIR: - error = new Error('ENOTDIR, not a directory') - error.code = 'ENOTDIR' - error.errno = -20 - break + error = new Error('ENOTDIR, not a directory'); + error.code = 'ENOTDIR'; + error.errno = -20; + break; case AsarError.NO_ACCESS: - error = new Error(`EACCES: permission denied, access '${filePath}'`) - error.code = 'EACCES' - error.errno = -13 - break + error = new Error(`EACCES: permission denied, access '${filePath}'`); + error.code = 'EACCES'; + error.errno = -13; + break; case AsarError.INVALID_ARCHIVE: - error = new Error(`Invalid package ${asarPath}`) - break + error = new Error(`Invalid package ${asarPath}`); + break; default: - throw new Error(`Invalid error type "${errorType}" passed to createError.`) + throw new Error(`Invalid error type "${errorType}" passed to createError.`); } - return error - } + return error; + }; const overrideAPISync = function (module, name, pathArgumentIndex, fromAsync) { - if (pathArgumentIndex == null) pathArgumentIndex = 0 - const old = module[name] + if (pathArgumentIndex == null) pathArgumentIndex = 0; + const old = module[name]; const func = function () { - const pathArgument = arguments[pathArgumentIndex] - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return old.apply(this, arguments) + const pathArgument = arguments[pathArgumentIndex]; + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return old.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) - if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + const archive = getOrCreateArchive(asarPath); + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); - const newPath = archive.copyFileOut(filePath) - if (!newPath) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + const newPath = archive.copyFileOut(filePath); + if (!newPath) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); - arguments[pathArgumentIndex] = newPath - return old.apply(this, arguments) - } + arguments[pathArgumentIndex] = newPath; + return old.apply(this, arguments); + }; if (fromAsync) { - return func + return func; } - module[name] = func - } + module[name] = func; + }; const overrideAPI = function (module, name, pathArgumentIndex) { - if (pathArgumentIndex == null) pathArgumentIndex = 0 - const old = module[name] + if (pathArgumentIndex == null) pathArgumentIndex = 0; + const old = module[name]; module[name] = function () { - const pathArgument = arguments[pathArgumentIndex] - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return old.apply(this, arguments) + const pathArgument = arguments[pathArgumentIndex]; + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return old.apply(this, arguments); - const callback = arguments[arguments.length - 1] + const callback = arguments[arguments.length - 1]; if (typeof callback !== 'function') { - return overrideAPISync(module, name, pathArgumentIndex, true).apply(this, arguments) + return overrideAPISync(module, name, pathArgumentIndex, true).apply(this, arguments); } - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const newPath = archive.copyFileOut(filePath) + const newPath = archive.copyFileOut(filePath); if (!newPath) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - arguments[pathArgumentIndex] = newPath - return old.apply(this, arguments) - } + arguments[pathArgumentIndex] = newPath; + return old.apply(this, arguments); + }; if (old[util.promisify.custom]) { - module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex) + module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex); } if (module.promises && module.promises[name]) { - module.promises[name] = makePromiseFunction(module.promises[name], pathArgumentIndex) + module.promises[name] = makePromiseFunction(module.promises[name], pathArgumentIndex); } - } + }; const makePromiseFunction = function (orig, pathArgumentIndex) { return function (...args) { - const pathArgument = args[pathArgumentIndex] - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return orig.apply(this, args) + const pathArgument = args[pathArgumentIndex]; + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return orig.apply(this, args); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - return Promise.reject(createError(AsarError.INVALID_ARCHIVE, { asarPath })) + return Promise.reject(createError(AsarError.INVALID_ARCHIVE, { asarPath })); } - const newPath = archive.copyFileOut(filePath) + const newPath = archive.copyFileOut(filePath); if (!newPath) { - return Promise.reject(createError(AsarError.NOT_FOUND, { asarPath, filePath })) + return Promise.reject(createError(AsarError.NOT_FOUND, { asarPath, filePath })); } - args[pathArgumentIndex] = newPath - return orig.apply(this, args) - } - } + args[pathArgumentIndex] = newPath; + return orig.apply(this, args); + }; + }; // Override fs APIs. exports.wrapFsWithAsar = fs => { - const logFDs = {} + const logFDs = {}; const logASARAccess = (asarPath, filePath, offset) => { - if (!process.env.ELECTRON_LOG_ASAR_READS) return + if (!process.env.ELECTRON_LOG_ASAR_READS) return; if (!logFDs[asarPath]) { - const path = require('path') - const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt` - const logPath = path.join(require('os').tmpdir(), logFilename) - logFDs[asarPath] = fs.openSync(logPath, 'a') + const path = require('path'); + const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt`; + const logPath = path.join(require('os').tmpdir(), logFilename); + logFDs[asarPath] = fs.openSync(logPath, 'a'); } - fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`) - } + fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`); + }; - const { lstatSync } = fs + const { lstatSync } = fs; fs.lstatSync = (pathArgument, options) => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return lstatSync(pathArgument, options) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return lstatSync(pathArgument, options); - const archive = getOrCreateArchive(asarPath) - if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + const archive = getOrCreateArchive(asarPath); + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); - const stats = archive.stat(filePath) - if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + const stats = archive.stat(filePath); + if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); - return asarStatsToFsStats(stats) - } + return asarStatsToFsStats(stats); + }; - const { lstat } = fs + const { lstat } = fs; fs.lstat = function (pathArgument, options, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); if (typeof options === 'function') { - callback = options - options = {} + callback = options; + options = {}; } - if (!isAsar) return lstat(pathArgument, options, callback) + if (!isAsar) return lstat(pathArgument, options, callback); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const stats = archive.stat(filePath) + const stats = archive.stat(filePath); if (!stats) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - const fsStats = asarStatsToFsStats(stats) - nextTick(callback, [null, fsStats]) - } + const fsStats = asarStatsToFsStats(stats); + nextTick(callback, [null, fsStats]); + }; - fs.promises.lstat = util.promisify(fs.lstat) + fs.promises.lstat = util.promisify(fs.lstat); - const { statSync } = fs + const { statSync } = fs; fs.statSync = (pathArgument, options) => { - const { isAsar } = splitPath(pathArgument) - if (!isAsar) return statSync(pathArgument, options) + const { isAsar } = splitPath(pathArgument); + if (!isAsar) return statSync(pathArgument, options); // Do not distinguish links for now. - return fs.lstatSync(pathArgument, options) - } + return fs.lstatSync(pathArgument, options); + }; - const { stat } = fs + const { stat } = fs; fs.stat = (pathArgument, options, callback) => { - const { isAsar } = splitPath(pathArgument) + const { isAsar } = splitPath(pathArgument); if (typeof options === 'function') { - callback = options - options = {} + callback = options; + options = {}; } - if (!isAsar) return stat(pathArgument, options, callback) + if (!isAsar) return stat(pathArgument, options, callback); // Do not distinguish links for now. - process.nextTick(() => fs.lstat(pathArgument, options, callback)) - } + process.nextTick(() => fs.lstat(pathArgument, options, callback)); + }; - fs.promises.stat = util.promisify(fs.stat) + fs.promises.stat = util.promisify(fs.stat); const wrapRealpathSync = function (realpathSync) { return function (pathArgument, options) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return realpathSync.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return realpathSync.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); } - const fileRealPath = archive.realpath(filePath) + const fileRealPath = archive.realpath(filePath); if (fileRealPath === false) { - throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); } - return path.join(realpathSync(asarPath, options), fileRealPath) - } - } + return path.join(realpathSync(asarPath, options), fileRealPath); + }; + }; - const { realpathSync } = fs - fs.realpathSync = wrapRealpathSync(realpathSync) - fs.realpathSync.native = wrapRealpathSync(realpathSync.native) + const { realpathSync } = fs; + fs.realpathSync = wrapRealpathSync(realpathSync); + fs.realpathSync.native = wrapRealpathSync(realpathSync.native); const wrapRealpath = function (realpath) { return function (pathArgument, options, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return realpath.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return realpath.apply(this, arguments); if (arguments.length < 3) { - callback = options - options = {} + callback = options; + options = {}; } - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const fileRealPath = archive.realpath(filePath) + const fileRealPath = archive.realpath(filePath); if (fileRealPath === false) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } realpath(asarPath, options, (error, archiveRealPath) => { if (error === null) { - const fullPath = path.join(archiveRealPath, fileRealPath) - callback(null, fullPath) + const fullPath = path.join(archiveRealPath, fileRealPath); + callback(null, fullPath); } else { - callback(error) + callback(error); } - }) - } - } + }); + }; + }; - const { realpath } = fs - fs.realpath = wrapRealpath(realpath) - fs.realpath.native = wrapRealpath(realpath.native) + const { realpath } = fs; + fs.realpath = wrapRealpath(realpath); + fs.realpath.native = wrapRealpath(realpath.native); - fs.promises.realpath = util.promisify(fs.realpath.native) + fs.promises.realpath = util.promisify(fs.realpath.native); - const { exists } = fs + const { exists } = fs; fs.exists = (pathArgument, callback) => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return exists(pathArgument, callback) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return exists(pathArgument, callback); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const pathExists = (archive.stat(filePath) !== false) - nextTick(callback, [pathExists]) - } + const pathExists = (archive.stat(filePath) !== false); + nextTick(callback, [pathExists]); + }; fs.exists[util.promisify.custom] = pathArgument => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return exists[util.promisify.custom](pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return exists[util.promisify.custom](pathArgument); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - return Promise.reject(error) + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + return Promise.reject(error); } - return Promise.resolve(archive.stat(filePath) !== false) - } + return Promise.resolve(archive.stat(filePath) !== false); + }; - const { existsSync } = fs + const { existsSync } = fs; fs.existsSync = pathArgument => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return existsSync(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return existsSync(pathArgument); - const archive = getOrCreateArchive(asarPath) - if (!archive) return false + const archive = getOrCreateArchive(asarPath); + if (!archive) return false; - return archive.stat(filePath) !== false - } + return archive.stat(filePath) !== false; + }; - const { access } = fs + const { access } = fs; fs.access = function (pathArgument, mode, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return access.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return access.apply(this, arguments); if (typeof mode === 'function') { - callback = mode - mode = fs.constants.F_OK + callback = mode; + mode = fs.constants.F_OK; } - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const info = archive.getFileInfo(filePath) + const info = archive.getFileInfo(filePath); if (!info) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.access(realPath, mode, callback) + const realPath = archive.copyFileOut(filePath); + return fs.access(realPath, mode, callback); } - const stats = archive.stat(filePath) + const stats = archive.stat(filePath); if (!stats) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } if (mode & fs.constants.W_OK) { - const error = createError(AsarError.NO_ACCESS, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NO_ACCESS, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - nextTick(callback) - } + nextTick(callback); + }; - fs.promises.access = util.promisify(fs.access) + fs.promises.access = util.promisify(fs.access); - const { accessSync } = fs + const { accessSync } = fs; fs.accessSync = function (pathArgument, mode) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return accessSync.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return accessSync.apply(this, arguments); - if (mode == null) mode = fs.constants.F_OK + if (mode == null) mode = fs.constants.F_OK; - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); } - const info = archive.getFileInfo(filePath) + const info = archive.getFileInfo(filePath); if (!info) { - throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); } if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.accessSync(realPath, mode) + const realPath = archive.copyFileOut(filePath); + return fs.accessSync(realPath, mode); } - const stats = archive.stat(filePath) + const stats = archive.stat(filePath); if (!stats) { - throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); } if (mode & fs.constants.W_OK) { - throw createError(AsarError.NO_ACCESS, { asarPath, filePath }) + throw createError(AsarError.NO_ACCESS, { asarPath, filePath }); } - } + }; - const { readFile } = fs + const { readFile } = fs; fs.readFile = function (pathArgument, options, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return readFile.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return readFile.apply(this, arguments); if (typeof options === 'function') { - callback = options - options = { encoding: null } + callback = options; + options = { encoding: null }; } else if (typeof options === 'string') { - options = { encoding: options } + options = { encoding: options }; } else if (options === null || options === undefined) { - options = { encoding: null } + options = { encoding: null }; } else if (typeof options !== 'object') { - throw new TypeError('Bad arguments') + throw new TypeError('Bad arguments'); } - const { encoding } = options - const archive = getOrCreateArchive(asarPath) + const { encoding } = options; + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const info = archive.getFileInfo(filePath) + const info = archive.getFileInfo(filePath); if (!info) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } if (info.size === 0) { - nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]) - return + nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]); + return; } if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.readFile(realPath, options, callback) + const realPath = archive.copyFileOut(filePath); + return fs.readFile(realPath, options, callback); } - const buffer = Buffer.alloc(info.size) - const fd = archive.getFd() + const buffer = Buffer.alloc(info.size); + const fd = archive.getFd(); if (!(fd >= 0)) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - logASARAccess(asarPath, filePath, info.offset) + logASARAccess(asarPath, filePath, info.offset); fs.read(fd, buffer, 0, info.size, info.offset, error => { - callback(error, encoding ? buffer.toString(encoding) : buffer) - }) - } + callback(error, encoding ? buffer.toString(encoding) : buffer); + }); + }; - fs.promises.readFile = util.promisify(fs.readFile) + fs.promises.readFile = util.promisify(fs.readFile); - const { readFileSync } = fs + const { readFileSync } = fs; fs.readFileSync = function (pathArgument, options) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return readFileSync.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return readFileSync.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) - if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + const archive = getOrCreateArchive(asarPath); + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); - const info = archive.getFileInfo(filePath) - if (!info) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + const info = archive.getFileInfo(filePath); + if (!info) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); - if (info.size === 0) return (options) ? '' : Buffer.alloc(0) + if (info.size === 0) return (options) ? '' : Buffer.alloc(0); if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.readFileSync(realPath, options) + const realPath = archive.copyFileOut(filePath); + return fs.readFileSync(realPath, options); } if (!options) { - options = { encoding: null } + options = { encoding: null }; } else if (typeof options === 'string') { - options = { encoding: options } + options = { encoding: options }; } else if (typeof options !== 'object') { - throw new TypeError('Bad arguments') + throw new TypeError('Bad arguments'); } - const { encoding } = options - const buffer = Buffer.alloc(info.size) - const fd = archive.getFd() - if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + const { encoding } = options; + const buffer = Buffer.alloc(info.size); + const fd = archive.getFd(); + if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); - logASARAccess(asarPath, filePath, info.offset) - fs.readSync(fd, buffer, 0, info.size, info.offset) - return (encoding) ? buffer.toString(encoding) : buffer - } + logASARAccess(asarPath, filePath, info.offset); + fs.readSync(fd, buffer, 0, info.size, info.offset); + return (encoding) ? buffer.toString(encoding) : buffer; + }; - const { readdir } = fs + const { readdir } = fs; fs.readdir = function (pathArgument, options, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); if (typeof options === 'function') { - callback = options - options = {} + callback = options; + options = {}; } - if (!isAsar) return readdir.apply(this, arguments) + if (!isAsar) return readdir.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const files = archive.readdir(filePath) + const files = archive.readdir(filePath); if (!files) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - nextTick(callback, [null, files]) - } + nextTick(callback, [null, files]); + }; - fs.promises.readdir = util.promisify(fs.readdir) + fs.promises.readdir = util.promisify(fs.readdir); - const { readdirSync } = fs + const { readdirSync } = fs; fs.readdirSync = function (pathArgument, options) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return readdirSync.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return readdirSync.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); } - const files = archive.readdir(filePath) + const files = archive.readdir(filePath); if (!files) { - throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); } - return files - } + return files; + }; - const { internalModuleReadJSON } = internalBinding('fs') + const { internalModuleReadJSON } = internalBinding('fs'); internalBinding('fs').internalModuleReadJSON = pathArgument => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return internalModuleReadJSON(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return internalModuleReadJSON(pathArgument); - const archive = getOrCreateArchive(asarPath) - if (!archive) return + const archive = getOrCreateArchive(asarPath); + if (!archive) return; - const info = archive.getFileInfo(filePath) - if (!info) return - if (info.size === 0) return '' + const info = archive.getFileInfo(filePath); + if (!info) return; + if (info.size === 0) return ''; if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.readFileSync(realPath, { encoding: 'utf8' }) + const realPath = archive.copyFileOut(filePath); + return fs.readFileSync(realPath, { encoding: 'utf8' }); } - const buffer = Buffer.alloc(info.size) - const fd = archive.getFd() - if (!(fd >= 0)) return + const buffer = Buffer.alloc(info.size); + const fd = archive.getFd(); + if (!(fd >= 0)) return; - logASARAccess(asarPath, filePath, info.offset) - fs.readSync(fd, buffer, 0, info.size, info.offset) - return buffer.toString('utf8') - } + logASARAccess(asarPath, filePath, info.offset); + fs.readSync(fd, buffer, 0, info.size, info.offset); + return buffer.toString('utf8'); + }; - const { internalModuleStat } = internalBinding('fs') + const { internalModuleStat } = internalBinding('fs'); internalBinding('fs').internalModuleStat = pathArgument => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return internalModuleStat(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return internalModuleStat(pathArgument); // -ENOENT - const archive = getOrCreateArchive(asarPath) - if (!archive) return -34 + const archive = getOrCreateArchive(asarPath); + if (!archive) return -34; // -ENOENT - const stats = archive.stat(filePath) - if (!stats) return -34 + const stats = archive.stat(filePath); + if (!stats) return -34; - return (stats.isDirectory) ? 1 : 0 - } + return (stats.isDirectory) ? 1 : 0; + }; // Calling mkdir for directory inside asar archive should throw ENOTDIR // error, but on Windows it throws ENOENT. if (process.platform === 'win32') { - const { mkdir } = fs + const { mkdir } = fs; fs.mkdir = (pathArgument, options, callback) => { if (typeof options === 'function') { - callback = options - options = {} + callback = options; + options = {}; } - const { isAsar, filePath } = splitPath(pathArgument) + const { isAsar, filePath } = splitPath(pathArgument); if (isAsar && filePath.length > 0) { - const error = createError(AsarError.NOT_DIR) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_DIR); + nextTick(callback, [error]); + return; } - mkdir(pathArgument, options, callback) - } + mkdir(pathArgument, options, callback); + }; - fs.promises.mkdir = util.promisify(fs.mkdir) + fs.promises.mkdir = util.promisify(fs.mkdir); - const { mkdirSync } = fs + const { mkdirSync } = fs; fs.mkdirSync = function (pathArgument, options) { - const { isAsar, filePath } = splitPath(pathArgument) - if (isAsar && filePath.length) throw createError(AsarError.NOT_DIR) - return mkdirSync(pathArgument, options) - } + const { isAsar, filePath } = splitPath(pathArgument); + if (isAsar && filePath.length) throw createError(AsarError.NOT_DIR); + return mkdirSync(pathArgument, options); + }; } function invokeWithNoAsar (func) { return function () { - const processNoAsarOriginalValue = process.noAsar - process.noAsar = true + const processNoAsarOriginalValue = process.noAsar; + process.noAsar = true; try { - return func.apply(this, arguments) + return func.apply(this, arguments); } finally { - process.noAsar = processNoAsarOriginalValue + process.noAsar = processNoAsarOriginalValue; } - } + }; } // Strictly implementing the flags of fs.copyFile is hard, just do a simple // implementation for now. Doing 2 copies won't spend much time more as OS // has filesystem caching. - overrideAPI(fs, 'copyFile') - overrideAPISync(fs, 'copyFileSync') + overrideAPI(fs, 'copyFile'); + overrideAPISync(fs, 'copyFileSync'); - overrideAPI(fs, 'open') - overrideAPISync(process, 'dlopen', 1) - overrideAPISync(Module._extensions, '.node', 1) - overrideAPISync(fs, 'openSync') + overrideAPI(fs, 'open'); + overrideAPISync(process, 'dlopen', 1); + overrideAPISync(Module._extensions, '.node', 1); + overrideAPISync(fs, 'openSync'); const overrideChildProcess = (childProcess) => { // Executing a command string containing a path to an asar archive // confuses `childProcess.execFile`, which is internally called by // `childProcess.{exec,execSync}`, causing Electron to consider the full // command as a single path to an archive. - const { exec, execSync } = childProcess - childProcess.exec = invokeWithNoAsar(exec) - childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]) - childProcess.execSync = invokeWithNoAsar(execSync) + const { exec, execSync } = childProcess; + childProcess.exec = invokeWithNoAsar(exec); + childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]); + childProcess.execSync = invokeWithNoAsar(execSync); - overrideAPI(childProcess, 'execFile') - overrideAPISync(childProcess, 'execFileSync') - } + overrideAPI(childProcess, 'execFile'); + overrideAPISync(childProcess, 'execFileSync'); + }; // Lazily override the child_process APIs only when child_process is // fetched the first time. We will eagerly override the child_process APIs @@ -731,22 +731,22 @@ // tests will match. This env var will only slow things down in users apps // and should not be used. if (process.env.ELECTRON_EAGER_ASAR_HOOK_FOR_TESTING) { - overrideChildProcess(require('child_process')) + overrideChildProcess(require('child_process')); } else { - const originalModuleLoad = Module._load + const originalModuleLoad = Module._load; Module._load = (request, ...args) => { - const loadResult = originalModuleLoad(request, ...args) + const loadResult = originalModuleLoad(request, ...args); if (request === 'child_process') { if (!v8Util.getHiddenValue(loadResult, 'asar-ready')) { - v8Util.setHiddenValue(loadResult, 'asar-ready', true) + v8Util.setHiddenValue(loadResult, 'asar-ready', true); // Just to make it obvious what we are dealing with here - const childProcess = loadResult + const childProcess = loadResult; - overrideChildProcess(childProcess) + overrideChildProcess(childProcess); } } - return loadResult - } + return loadResult; + }; } - } -})() + }; +})(); diff --git a/lib/common/asar_init.js b/lib/common/asar_init.js index 0d6d145b1104e..ae00669bf5f8f 100644 --- a/lib/common/asar_init.js +++ b/lib/common/asar_init.js @@ -1,6 +1,6 @@ -'use strict' +'use strict'; /* global require */ // Monkey-patch the fs module. -require('electron/js2c/asar').wrapFsWithAsar(require('fs')) +require('electron/js2c/asar').wrapFsWithAsar(require('fs')); diff --git a/lib/common/crash-reporter.js b/lib/common/crash-reporter.js index 9557219166038..36df08928cf62 100644 --- a/lib/common/crash-reporter.js +++ b/lib/common/crash-reporter.js @@ -1,19 +1,19 @@ -'use strict' +'use strict'; -const binding = process.electronBinding('crash_reporter') +const binding = process.electronBinding('crash_reporter'); class CrashReporter { constructor () { - this.productName = null - this.crashesDirectory = null + this.productName = null; + this.crashesDirectory = null; } init (options) { - throw new Error('Not implemented') + throw new Error('Not implemented'); } start (options) { - if (options == null) options = {} + if (options == null) options = {}; const { productName, @@ -22,77 +22,77 @@ class CrashReporter { ignoreSystemCrashHandler = false, submitURL, uploadToServer = true - } = options + } = options; - if (companyName == null) throw new Error('companyName is a required option to crashReporter.start') - if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start') + if (companyName == null) throw new Error('companyName is a required option to crashReporter.start'); + if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start'); const ret = this.init({ submitURL, productName - }) + }); - this.productName = ret.productName - this.crashesDirectory = ret.crashesDirectory + this.productName = ret.productName; + this.crashesDirectory = ret.crashesDirectory; - if (extra._productName == null) extra._productName = ret.productName - if (extra._companyName == null) extra._companyName = companyName - if (extra._version == null) extra._version = ret.appVersion + if (extra._productName == null) extra._productName = ret.productName; + if (extra._companyName == null) extra._companyName = companyName; + if (extra._version == null) extra._version = ret.appVersion; - binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra) + binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra); } getLastCrashReport () { const reports = this.getUploadedReports() .sort((a, b) => { - const ats = (a && a.date) ? new Date(a.date).getTime() : 0 - const bts = (b && b.date) ? new Date(b.date).getTime() : 0 - return bts - ats - }) + const ats = (a && a.date) ? new Date(a.date).getTime() : 0; + const bts = (b && b.date) ? new Date(b.date).getTime() : 0; + return bts - ats; + }); - return (reports.length > 0) ? reports[0] : null + return (reports.length > 0) ? reports[0] : null; } getUploadedReports () { - const crashDir = this.getCrashesDirectory() + const crashDir = this.getCrashesDirectory(); if (!crashDir) { - throw new Error('crashReporter has not been started') + throw new Error('crashReporter has not been started'); } - return binding.getUploadedReports(crashDir) + return binding.getUploadedReports(crashDir); } getCrashesDirectory () { - return this.crashesDirectory + return this.crashesDirectory; } getUploadToServer () { if (process.type === 'browser') { - return binding.getUploadToServer() + return binding.getUploadToServer(); } else { - throw new Error('getUploadToServer can only be called from the main process') + throw new Error('getUploadToServer can only be called from the main process'); } } setUploadToServer (uploadToServer) { if (process.type === 'browser') { - return binding.setUploadToServer(uploadToServer) + return binding.setUploadToServer(uploadToServer); } else { - throw new Error('setUploadToServer can only be called from the main process') + throw new Error('setUploadToServer can only be called from the main process'); } } addExtraParameter (key, value) { - binding.addExtraParameter(key, value) + binding.addExtraParameter(key, value); } removeExtraParameter (key) { - binding.removeExtraParameter(key) + binding.removeExtraParameter(key); } getParameters () { - return binding.getParameters() + return binding.getParameters(); } } -module.exports = CrashReporter +module.exports = CrashReporter; diff --git a/lib/common/define-properties.ts b/lib/common/define-properties.ts index 7f2ddf5a46aed..add72cd846656 100644 --- a/lib/common/define-properties.ts +++ b/lib/common/define-properties.ts @@ -1,17 +1,17 @@ const handleESModule = (loader: ElectronInternal.ModuleLoader) => () => { - const value = loader() - if (value.__esModule && value.default) return value.default - return value -} + const value = loader(); + if (value.__esModule && value.default) return value.default; + return value; +}; // Attaches properties to |targetExports|. export function defineProperties (targetExports: Object, moduleList: ElectronInternal.ModuleEntry[]) { - const descriptors: PropertyDescriptorMap = {} + const descriptors: PropertyDescriptorMap = {}; for (const module of moduleList) { descriptors[module.name] = { enumerable: !module.private, get: handleESModule(module.loader) - } + }; } - return Object.defineProperties(targetExports, descriptors) + return Object.defineProperties(targetExports, descriptors); } diff --git a/lib/common/electron-binding-setup.ts b/lib/common/electron-binding-setup.ts index 8f9aba78ddd27..4a001d9fd0156 100644 --- a/lib/common/electron-binding-setup.ts +++ b/lib/common/electron-binding-setup.ts @@ -1,13 +1,13 @@ export function electronBindingSetup (binding: typeof process['_linkedBinding'], processType: typeof process['type']): typeof process['electronBinding'] { return function electronBinding (name: string) { try { - return binding(`electron_${processType}_${name}`) + return binding(`electron_${processType}_${name}`); } catch (error) { if (/No such module/.test(error.message)) { - return binding(`electron_common_${name}`) + return binding(`electron_common_${name}`); } else { - throw error + throw error; } } - } + }; } diff --git a/lib/common/init.ts b/lib/common/init.ts index 425efac85d9c7..cabdfd8b4a11f 100644 --- a/lib/common/init.ts +++ b/lib/common/init.ts @@ -1,10 +1,10 @@ -import * as util from 'util' +import * as util from 'util'; -import { electronBindingSetup } from '@electron/internal/common/electron-binding-setup' +import { electronBindingSetup } from '@electron/internal/common/electron-binding-setup'; -const timers = require('timers') +const timers = require('timers'); -process.electronBinding = electronBindingSetup(process._linkedBinding, process.type) +process.electronBinding = electronBindingSetup(process._linkedBinding, process.type); type AnyFn = (...args: any[]) => any @@ -17,11 +17,11 @@ type AnyFn = (...args: any[]) => any const wrapWithActivateUvLoop = function (func: T): T { return wrap(func, function (func) { return function (this: any, ...args: any[]) { - process.activateUvLoop() - return func.apply(this, args) - } - }) as T -} + process.activateUvLoop(); + return func.apply(this, args); + }; + }) as T; +}; /** * Casts to any below for func are due to Typescript not supporting symbols @@ -30,41 +30,41 @@ const wrapWithActivateUvLoop = function (func: T): T { * Refs: https://github.com/Microsoft/TypeScript/issues/1863 */ function wrap (func: T, wrapper: (fn: AnyFn) => T) { - const wrapped = wrapper(func) + const wrapped = wrapper(func); if ((func as any)[util.promisify.custom]) { - (wrapped as any)[util.promisify.custom] = wrapper((func as any)[util.promisify.custom]) + (wrapped as any)[util.promisify.custom] = wrapper((func as any)[util.promisify.custom]); } - return wrapped + return wrapped; } -process.nextTick = wrapWithActivateUvLoop(process.nextTick) +process.nextTick = wrapWithActivateUvLoop(process.nextTick); -global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate) -global.clearImmediate = timers.clearImmediate +global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate); +global.clearImmediate = timers.clearImmediate; // setTimeout needs to update the polling timeout of the event loop, when // called under Chromium's event loop the node's event loop won't get a chance // to update the timeout, so we have to force the node's event loop to // recalculate the timeout in browser process. -timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout) -timers.setInterval = wrapWithActivateUvLoop(timers.setInterval) +timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout); +timers.setInterval = wrapWithActivateUvLoop(timers.setInterval); // Only override the global setTimeout/setInterval impls in the browser process if (process.type === 'browser') { - global.setTimeout = timers.setTimeout - global.setInterval = timers.setInterval + global.setTimeout = timers.setTimeout; + global.setInterval = timers.setInterval; } if (process.platform === 'win32') { // Always returns EOF for stdin stream. - const { Readable } = require('stream') - const stdin = new Readable() - stdin.push(null) + const { Readable } = require('stream'); + const stdin = new Readable(); + stdin.push(null); Object.defineProperty(process, 'stdin', { configurable: false, enumerable: true, get () { - return stdin + return stdin; } - }) + }); } diff --git a/lib/common/parse-features-string.js b/lib/common/parse-features-string.js index 1ef51092fdd3f..90c6f7ad140a5 100644 --- a/lib/common/parse-features-string.js +++ b/lib/common/parse-features-string.js @@ -1,21 +1,21 @@ -'use strict' +'use strict'; // parses a feature string that has the format used in window.open() // - `features` input string // - `emit` function(key, value) - called for each parsed KV module.exports = function parseFeaturesString (features, emit) { - features = `${features}`.trim() + features = `${features}`.trim(); // split the string by ',' features.split(/\s*,\s*/).forEach((feature) => { // expected form is either a key by itself or a key/value pair in the form of // 'key=value' - let [key, value] = feature.split(/\s*=\s*/) - if (!key) return + let [key, value] = feature.split(/\s*=\s*/); + if (!key) return; // interpret the value as a boolean, if possible - value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value + value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value; // emit the parsed pair - emit(key, value) - }) -} + emit(key, value); + }); +}; diff --git a/lib/common/reset-search-paths.ts b/lib/common/reset-search-paths.ts index c1ffabe8f6935..7cdd9c31ec6e6 100644 --- a/lib/common/reset-search-paths.ts +++ b/lib/common/reset-search-paths.ts @@ -1,42 +1,42 @@ -import * as path from 'path' +import * as path from 'path'; -const Module = require('module') +const Module = require('module'); // Clear Node's global search paths. -Module.globalPaths.length = 0 +Module.globalPaths.length = 0; // Prevent Node from adding paths outside this app to search paths. -const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep -const originalNodeModulePaths = Module._nodeModulePaths +const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep; +const originalNodeModulePaths = Module._nodeModulePaths; Module._nodeModulePaths = function (from: string) { - const paths: string[] = originalNodeModulePaths(from) - const fromPath = path.resolve(from) + path.sep + const paths: string[] = originalNodeModulePaths(from); + const fromPath = path.resolve(from) + path.sep; // If "from" is outside the app then we do nothing. if (fromPath.startsWith(resourcesPathWithTrailingSlash)) { return paths.filter(function (candidate) { - return candidate.startsWith(resourcesPathWithTrailingSlash) - }) + return candidate.startsWith(resourcesPathWithTrailingSlash); + }); } else { - return paths + return paths; } -} +}; // Make a fake Electron module that we will insert into the module cache -const electronModule = new Module('electron', null) -electronModule.id = 'electron' -electronModule.loaded = true -electronModule.filename = 'electron' +const electronModule = new Module('electron', null); +electronModule.id = 'electron'; +electronModule.loaded = true; +electronModule.filename = 'electron'; Object.defineProperty(electronModule, 'exports', { get: () => require('electron') -}) +}); -Module._cache.electron = electronModule +Module._cache.electron = electronModule; -const originalResolveFilename = Module._resolveFilename +const originalResolveFilename = Module._resolveFilename; Module._resolveFilename = function (request: string, parent: NodeModule, isMain: boolean) { if (request === 'electron') { - return 'electron' + return 'electron'; } else { - return originalResolveFilename(request, parent, isMain) + return originalResolveFilename(request, parent, isMain); } -} +}; diff --git a/lib/common/type-utils.ts b/lib/common/type-utils.ts index d5536db080afa..4d244b40ae65b 100644 --- a/lib/common/type-utils.ts +++ b/lib/common/type-utils.ts @@ -1,4 +1,4 @@ -const { nativeImage, NativeImage } = process.electronBinding('native_image') +const { nativeImage, NativeImage } = process.electronBinding('native_image'); export function isPromise (val: any) { return ( @@ -10,7 +10,7 @@ export function isPromise (val: any) { val.constructor.reject instanceof Function && val.constructor.resolve && val.constructor.resolve instanceof Function - ) + ); } const serializableTypes = [ @@ -21,17 +21,17 @@ const serializableTypes = [ Error, RegExp, ArrayBuffer -] +]; export function isSerializableObject (value: any) { - return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type) + return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type); } const objectMap = function (source: Object, mapper: (value: any) => any) { - const sourceEntries = Object.entries(source) - const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]) - return Object.fromEntries(targetEntries) -} + const sourceEntries = Object.entries(source); + const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]); + return Object.fromEntries(targetEntries); +}; export function serialize (value: any): any { if (value instanceof NativeImage) { @@ -39,28 +39,28 @@ export function serialize (value: any): any { buffer: value.toBitmap(), size: value.getSize(), __ELECTRON_SERIALIZED_NativeImage__: true - } + }; } else if (Array.isArray(value)) { - return value.map(serialize) + return value.map(serialize); } else if (isSerializableObject(value)) { - return value + return value; } else if (value instanceof Object) { - return objectMap(value, serialize) + return objectMap(value, serialize); } else { - return value + return value; } } export function deserialize (value: any): any { if (value && value.__ELECTRON_SERIALIZED_NativeImage__) { - return nativeImage.createFromBitmap(value.buffer, value.size) + return nativeImage.createFromBitmap(value.buffer, value.size); } else if (Array.isArray(value)) { - return value.map(deserialize) + return value.map(deserialize); } else if (isSerializableObject(value)) { - return value + return value; } else if (value instanceof Object) { - return objectMap(value, deserialize) + return objectMap(value, deserialize); } else { - return value + return value; } } diff --git a/lib/common/web-view-methods.ts b/lib/common/web-view-methods.ts index 8018c7a87bbbe..449ee9d3915c4 100644 --- a/lib/common/web-view-methods.ts +++ b/lib/common/web-view-methods.ts @@ -48,7 +48,7 @@ export const syncMethods = new Set([ 'getZoomLevel', 'setZoomFactor', 'setZoomLevel' -]) +]); export const properties = new Set([ 'audioMuted', @@ -56,7 +56,7 @@ export const properties = new Set([ 'zoomLevel', 'zoomFactor', 'frameRate' -]) +]); export const asyncMethods = new Set([ 'loadURL', @@ -70,4 +70,4 @@ export const asyncMethods = new Set([ 'setVisualZoomLevelLimits', 'print', 'printToPDF' -]) +]); diff --git a/lib/common/webpack-globals-provider.ts b/lib/common/webpack-globals-provider.ts index f7d64270776ae..8271118eea1c9 100644 --- a/lib/common/webpack-globals-provider.ts +++ b/lib/common/webpack-globals-provider.ts @@ -5,4 +5,4 @@ // // Will mutate this captured one as well and that is OK. -export const Promise = global.Promise +export const Promise = global.Promise; diff --git a/lib/content_script/init.js b/lib/content_script/init.js index c8a1373b8a090..b2fa8b8f12675 100644 --- a/lib/content_script/init.js +++ b/lib/content_script/init.js @@ -1,37 +1,37 @@ -'use strict' +'use strict'; /* global nodeProcess, isolatedWorld, worldId */ -const { EventEmitter } = require('events') +const { EventEmitter } = require('events'); -process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer') +process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer'); -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); // The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the // "ipc-internal" hidden value -v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal')) +v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal')); // The process object created by webpack is not an event emitter, fix it so // the API is more compatible with non-sandboxed renderers. for (const prop of Object.keys(EventEmitter.prototype)) { if (Object.prototype.hasOwnProperty.call(process, prop)) { - delete process[prop] + delete process[prop]; } } -Object.setPrototypeOf(process, EventEmitter.prototype) +Object.setPrototypeOf(process, EventEmitter.prototype); -const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args') +const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args'); if (isolatedWorldArgs) { - const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled } = isolatedWorldArgs - const { windowSetup } = require('@electron/internal/renderer/window-setup') - windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled) + const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled } = isolatedWorldArgs; + const { windowSetup } = require('@electron/internal/renderer/window-setup'); + windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled); } -const extensionId = v8Util.getHiddenValue(isolatedWorld, `extension-${worldId}`) +const extensionId = v8Util.getHiddenValue(isolatedWorld, `extension-${worldId}`); if (extensionId) { - const chromeAPI = require('@electron/internal/renderer/chrome-api') - chromeAPI.injectTo(extensionId, window) + const chromeAPI = require('@electron/internal/renderer/chrome-api'); + chromeAPI.injectTo(extensionId, window); } diff --git a/lib/isolated_renderer/init.js b/lib/isolated_renderer/init.js index 94aef18d874d2..1eacc239083a4 100644 --- a/lib/isolated_renderer/init.js +++ b/lib/isolated_renderer/init.js @@ -1,27 +1,27 @@ -'use strict' +'use strict'; /* global nodeProcess, isolatedWorld */ -process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer') +process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer'); -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); // The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the // "ipc-internal" hidden value -v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal')) +v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal')); -const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl') +const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl'); if (webViewImpl) { // Must setup the WebView element in main world. - const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') - setupWebView(v8Util, webViewImpl) + const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element'); + setupWebView(v8Util, webViewImpl); } -const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args') +const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args'); if (isolatedWorldArgs) { - const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled } = isolatedWorldArgs - const { windowSetup } = require('@electron/internal/renderer/window-setup') - windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled) + const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled } = isolatedWorldArgs; + const { windowSetup } = require('@electron/internal/renderer/window-setup'); + windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled); } diff --git a/lib/renderer/api/context-bridge.ts b/lib/renderer/api/context-bridge.ts index b435be60d7be1..ff72c672ff74a 100644 --- a/lib/renderer/api/context-bridge.ts +++ b/lib/renderer/api/context-bridge.ts @@ -1,20 +1,20 @@ -const { hasSwitch } = process.electronBinding('command_line') -const binding = process.electronBinding('context_bridge') +const { hasSwitch } = process.electronBinding('command_line'); +const binding = process.electronBinding('context_bridge'); -const contextIsolationEnabled = hasSwitch('context-isolation') +const contextIsolationEnabled = hasSwitch('context-isolation'); const checkContextIsolationEnabled = () => { - if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled') -} + if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled'); +}; const contextBridge = { exposeInMainWorld: (key: string, api: Record) => { - checkContextIsolationEnabled() - return binding.exposeAPIInMainWorld(key, api) + checkContextIsolationEnabled(); + return binding.exposeAPIInMainWorld(key, api); }, debugGC: () => binding._debugGCMaps({}) -} +}; -if (!binding._debugGCMaps) delete contextBridge.debugGC +if (!binding._debugGCMaps) delete contextBridge.debugGC; -export default contextBridge +export default contextBridge; diff --git a/lib/renderer/api/crash-reporter.js b/lib/renderer/api/crash-reporter.js index 5931befb3fee1..48212ab170fab 100644 --- a/lib/renderer/api/crash-reporter.js +++ b/lib/renderer/api/crash-reporter.js @@ -1,12 +1,12 @@ -'use strict' +'use strict'; -const CrashReporter = require('@electron/internal/common/crash-reporter') -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') +const CrashReporter = require('@electron/internal/common/crash-reporter'); +const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); class CrashReporterRenderer extends CrashReporter { init (options) { - return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options) + return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options); } } -module.exports = new CrashReporterRenderer() +module.exports = new CrashReporterRenderer(); diff --git a/lib/renderer/api/desktop-capturer.ts b/lib/renderer/api/desktop-capturer.ts index 538f6be1322b2..30838f070d230 100644 --- a/lib/renderer/api/desktop-capturer.ts +++ b/lib/renderer/api/desktop-capturer.ts @@ -1,39 +1,39 @@ -import { nativeImage } from 'electron' -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { nativeImage } from 'electron'; +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; -const { hasSwitch } = process.electronBinding('command_line') +const { hasSwitch } = process.electronBinding('command_line'); // |options.types| can't be empty and must be an array function isValid (options: Electron.SourcesOptions) { - const types = options ? options.types : undefined - return Array.isArray(types) + const types = options ? options.types : undefined; + return Array.isArray(types); } -const enableStacks = hasSwitch('enable-api-filtering-logging') +const enableStacks = hasSwitch('enable-api-filtering-logging'); function getCurrentStack () { - const target = {} + const target = {}; if (enableStacks) { - Error.captureStackTrace(target, getCurrentStack) + Error.captureStackTrace(target, getCurrentStack); } - return (target as any).stack + return (target as any).stack; } export async function getSources (options: Electron.SourcesOptions) { - if (!isValid(options)) throw new Error('Invalid options') + if (!isValid(options)) throw new Error('Invalid options'); - const captureWindow = options.types.includes('window') - const captureScreen = options.types.includes('screen') + const captureWindow = options.types.includes('window'); + const captureScreen = options.types.includes('screen'); - const { thumbnailSize = { width: 150, height: 150 } } = options - const { fetchWindowIcons = false } = options + const { thumbnailSize = { width: 150, height: 150 } } = options; + const { fetchWindowIcons = false } = options; const sources = await ipcRendererInternal.invoke('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', { captureWindow, captureScreen, thumbnailSize, fetchWindowIcons - } as ElectronInternal.GetSourcesOptions, getCurrentStack()) + } as ElectronInternal.GetSourcesOptions, getCurrentStack()); return sources.map(source => ({ id: source.id, @@ -41,5 +41,5 @@ export async function getSources (options: Electron.SourcesOptions) { thumbnail: nativeImage.createFromDataURL(source.thumbnail), display_id: source.display_id, appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null - })) + })); } diff --git a/lib/renderer/api/exports/electron.ts b/lib/renderer/api/exports/electron.ts index 138bd8a95a936..13ea2fc57a20a 100644 --- a/lib/renderer/api/exports/electron.ts +++ b/lib/renderer/api/exports/electron.ts @@ -1,8 +1,8 @@ -import { defineProperties } from '@electron/internal/common/define-properties' -import { commonModuleList } from '@electron/internal/common/api/module-list' -import { rendererModuleList } from '@electron/internal/renderer/api/module-list' +import { defineProperties } from '@electron/internal/common/define-properties'; +import { commonModuleList } from '@electron/internal/common/api/module-list'; +import { rendererModuleList } from '@electron/internal/renderer/api/module-list'; -module.exports = {} +module.exports = {}; -defineProperties(module.exports, commonModuleList) -defineProperties(module.exports, rendererModuleList) +defineProperties(module.exports, commonModuleList); +defineProperties(module.exports, rendererModuleList); diff --git a/lib/renderer/api/ipc-renderer.ts b/lib/renderer/api/ipc-renderer.ts index c7ba2addde18c..045cf47d1c5ce 100644 --- a/lib/renderer/api/ipc-renderer.ts +++ b/lib/renderer/api/ipc-renderer.ts @@ -1,36 +1,36 @@ -const { ipc } = process.electronBinding('ipc') -const v8Util = process.electronBinding('v8_util') +const { ipc } = process.electronBinding('ipc'); +const v8Util = process.electronBinding('v8_util'); // Created by init.js. -const ipcRenderer = v8Util.getHiddenValue(global, 'ipc') -const internal = false +const ipcRenderer = v8Util.getHiddenValue(global, 'ipc'); +const internal = false; ipcRenderer.send = function (channel, ...args) { - return ipc.send(internal, channel, args) -} + return ipc.send(internal, channel, args); +}; ipcRenderer.sendSync = function (channel, ...args) { - return ipc.sendSync(internal, channel, args)[0] -} + return ipc.sendSync(internal, channel, args)[0]; +}; ipcRenderer.sendToHost = function (channel, ...args) { - return ipc.sendToHost(channel, args) -} + return ipc.sendToHost(channel, args); +}; ipcRenderer.sendTo = function (webContentsId, channel, ...args) { - return ipc.sendTo(internal, false, webContentsId, channel, args) -} + return ipc.sendTo(internal, false, webContentsId, channel, args); +}; ipcRenderer.invoke = async function (channel, ...args) { - const { error, result } = await ipc.invoke(internal, channel, args) + const { error, result } = await ipc.invoke(internal, channel, args); if (error) { - throw new Error(`Error invoking remote method '${channel}': ${error}`) + throw new Error(`Error invoking remote method '${channel}': ${error}`); } - return result -} + return result; +}; ipcRenderer.postMessage = function (channel: string, message: any, transferables: any) { - return ipc.postMessage(channel, message, transferables) -} + return ipc.postMessage(channel, message, transferables); +}; -export default ipcRenderer +export default ipcRenderer; diff --git a/lib/renderer/api/module-list.ts b/lib/renderer/api/module-list.ts index f35f66f6ae449..d11e1020b520c 100644 --- a/lib/renderer/api/module-list.ts +++ b/lib/renderer/api/module-list.ts @@ -1,7 +1,7 @@ -const features = process.electronBinding('features') -const v8Util = process.electronBinding('v8_util') +const features = process.electronBinding('features'); +const v8Util = process.electronBinding('v8_util'); -const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule') +const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule'); // Renderer side modules, please sort alphabetically. export const rendererModuleList: ElectronInternal.ModuleEntry[] = [ @@ -9,12 +9,12 @@ export const rendererModuleList: ElectronInternal.ModuleEntry[] = [ { name: 'crashReporter', loader: () => require('./crash-reporter') }, { name: 'ipcRenderer', loader: () => require('./ipc-renderer') }, { name: 'webFrame', loader: () => require('./web-frame') } -] +]; if (features.isDesktopCapturerEnabled()) { - rendererModuleList.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') }) + rendererModuleList.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') }); } if (features.isRemoteModuleEnabled() && enableRemoteModule) { - rendererModuleList.push({ name: 'remote', loader: () => require('./remote') }) + rendererModuleList.push({ name: 'remote', loader: () => require('./remote') }); } diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index 884c54486a5ac..a6e9b7ce82730 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -1,26 +1,26 @@ -'use strict' +'use strict'; -const v8Util = process.electronBinding('v8_util') -const { hasSwitch } = process.electronBinding('command_line') +const v8Util = process.electronBinding('v8_util'); +const { hasSwitch } = process.electronBinding('command_line'); -const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry') -const { isPromise, isSerializableObject } = require('@electron/internal/common/type-utils') -const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') +const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry'); +const { isPromise, isSerializableObject } = require('@electron/internal/common/type-utils'); +const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); -const callbacksRegistry = new CallbacksRegistry() -const remoteObjectCache = v8Util.createIDWeakMap() +const callbacksRegistry = new CallbacksRegistry(); +const remoteObjectCache = v8Util.createIDWeakMap(); // An unique ID that can represent current context. -const contextId = v8Util.getHiddenValue(global, 'contextId') +const contextId = v8Util.getHiddenValue(global, 'contextId'); // Notify the main process when current context is going to be released. // Note that when the renderer process is destroyed, the message may not be // sent, we also listen to the "render-view-deleted" event in the main process // to guard that situation. process.on('exit', () => { - const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE' - ipcRendererInternal.send(command, contextId) -}) + const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'; + ipcRendererInternal.send(command, contextId); +}); // Convert the arguments object into an array of meta data. function wrapArgs (args, visited = new Set()) { @@ -30,182 +30,182 @@ function wrapArgs (args, visited = new Set()) { return { type: 'value', value: null - } + }; } if (Array.isArray(value)) { - visited.add(value) + visited.add(value); const meta = { type: 'array', value: wrapArgs(value, visited) - } - visited.delete(value) - return meta + }; + visited.delete(value); + return meta; } else if (value instanceof Buffer) { return { type: 'buffer', value - } + }; } else if (isSerializableObject(value)) { return { type: 'value', value - } + }; } else if (typeof value === 'object') { if (isPromise(value)) { return { type: 'promise', then: valueToMeta(function (onFulfilled, onRejected) { - value.then(onFulfilled, onRejected) + value.then(onFulfilled, onRejected); }) - } + }; } else if (v8Util.getHiddenValue(value, 'atomId')) { return { type: 'remote-object', id: v8Util.getHiddenValue(value, 'atomId') - } + }; } const meta = { type: 'object', name: value.constructor ? value.constructor.name : '', members: [] - } - visited.add(value) + }; + visited.add(value); for (const prop in value) { // eslint-disable-line guard-for-in meta.members.push({ name: prop, value: valueToMeta(value[prop]) - }) + }); } - visited.delete(value) - return meta + visited.delete(value); + return meta; } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) { return { type: 'function-with-return-value', value: valueToMeta(value()) - } + }; } else if (typeof value === 'function') { return { type: 'function', id: callbacksRegistry.add(value), location: v8Util.getHiddenValue(value, 'location'), length: value.length - } + }; } else { return { type: 'value', value - } + }; } - } - return args.map(valueToMeta) + }; + return args.map(valueToMeta); } // Populate object's members from descriptors. // The |ref| will be kept referenced by |members|. // This matches |getObjectMemebers| in rpc-server. function setObjectMembers (ref, object, metaId, members) { - if (!Array.isArray(members)) return + if (!Array.isArray(members)) return; for (const member of members) { - if (Object.prototype.hasOwnProperty.call(object, member.name)) continue + if (Object.prototype.hasOwnProperty.call(object, member.name)) continue; - const descriptor = { enumerable: member.enumerable } + const descriptor = { enumerable: member.enumerable }; if (member.type === 'method') { const remoteMemberFunction = function (...args) { - let command + let command; if (this && this.constructor === remoteMemberFunction) { - command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR' + command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'; } else { - command = 'ELECTRON_BROWSER_MEMBER_CALL' + command = 'ELECTRON_BROWSER_MEMBER_CALL'; } - const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args)) - return metaToValue(ret) - } + const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args)); + return metaToValue(ret); + }; - let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name) + let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name); descriptor.get = () => { - descriptorFunction.ref = ref // The member should reference its object. - return descriptorFunction - } + descriptorFunction.ref = ref; // The member should reference its object. + return descriptorFunction; + }; // Enable monkey-patch the method descriptor.set = (value) => { - descriptorFunction = value - return value - } - descriptor.configurable = true + descriptorFunction = value; + return value; + }; + descriptor.configurable = true; } else if (member.type === 'get') { descriptor.get = () => { - const command = 'ELECTRON_BROWSER_MEMBER_GET' - const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name) - return metaToValue(meta) - } + const command = 'ELECTRON_BROWSER_MEMBER_GET'; + const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name); + return metaToValue(meta); + }; if (member.writable) { descriptor.set = (value) => { - const args = wrapArgs([value]) - const command = 'ELECTRON_BROWSER_MEMBER_SET' - const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args) - if (meta != null) metaToValue(meta) - return value - } + const args = wrapArgs([value]); + const command = 'ELECTRON_BROWSER_MEMBER_SET'; + const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args); + if (meta != null) metaToValue(meta); + return value; + }; } } - Object.defineProperty(object, member.name, descriptor) + Object.defineProperty(object, member.name, descriptor); } } // Populate object's prototype from descriptor. // This matches |getObjectPrototype| in rpc-server. function setObjectPrototype (ref, object, metaId, descriptor) { - if (descriptor === null) return - const proto = {} - setObjectMembers(ref, proto, metaId, descriptor.members) - setObjectPrototype(ref, proto, metaId, descriptor.proto) - Object.setPrototypeOf(object, proto) + if (descriptor === null) return; + const proto = {}; + setObjectMembers(ref, proto, metaId, descriptor.members); + setObjectPrototype(ref, proto, metaId, descriptor.proto); + Object.setPrototypeOf(object, proto); } // Wrap function in Proxy for accessing remote properties function proxyFunctionProperties (remoteMemberFunction, metaId, name) { - let loaded = false + let loaded = false; // Lazily load function properties const loadRemoteProperties = () => { - if (loaded) return - loaded = true - const command = 'ELECTRON_BROWSER_MEMBER_GET' - const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name) - setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members) - } + if (loaded) return; + loaded = true; + const command = 'ELECTRON_BROWSER_MEMBER_GET'; + const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name); + setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members); + }; return new Proxy(remoteMemberFunction, { set: (target, property, value, receiver) => { - if (property !== 'ref') loadRemoteProperties() - target[property] = value - return true + if (property !== 'ref') loadRemoteProperties(); + target[property] = value; + return true; }, get: (target, property, receiver) => { - if (!Object.prototype.hasOwnProperty.call(target, property)) loadRemoteProperties() - const value = target[property] + if (!Object.prototype.hasOwnProperty.call(target, property)) loadRemoteProperties(); + const value = target[property]; if (property === 'toString' && typeof value === 'function') { - return value.bind(target) + return value.bind(target); } - return value + return value; }, ownKeys: (target) => { - loadRemoteProperties() - return Object.getOwnPropertyNames(target) + loadRemoteProperties(); + return Object.getOwnPropertyNames(target); }, getOwnPropertyDescriptor: (target, property) => { - const descriptor = Object.getOwnPropertyDescriptor(target, property) - if (descriptor) return descriptor - loadRemoteProperties() - return Object.getOwnPropertyDescriptor(target, property) + const descriptor = Object.getOwnPropertyDescriptor(target, property); + if (descriptor) return descriptor; + loadRemoteProperties(); + return Object.getOwnPropertyDescriptor(target, property); } - }) + }); } // Convert meta data from browser into real value. @@ -216,143 +216,143 @@ function metaToValue (meta) { buffer: () => Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength), promise: () => Promise.resolve({ then: metaToValue(meta.then) }), error: () => metaToError(meta), - exception: () => { throw metaToError(meta.value) } - } + exception: () => { throw metaToError(meta.value); } + }; if (Object.prototype.hasOwnProperty.call(types, meta.type)) { - return types[meta.type]() + return types[meta.type](); } else { - let ret + let ret; if (remoteObjectCache.has(meta.id)) { - v8Util.addRemoteObjectRef(contextId, meta.id) - return remoteObjectCache.get(meta.id) + v8Util.addRemoteObjectRef(contextId, meta.id); + return remoteObjectCache.get(meta.id); } // A shadow class to represent the remote function object. if (meta.type === 'function') { const remoteFunction = function (...args) { - let command + let command; if (this && this.constructor === remoteFunction) { - command = 'ELECTRON_BROWSER_CONSTRUCTOR' + command = 'ELECTRON_BROWSER_CONSTRUCTOR'; } else { - command = 'ELECTRON_BROWSER_FUNCTION_CALL' + command = 'ELECTRON_BROWSER_FUNCTION_CALL'; } - const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args)) - return metaToValue(obj) - } - ret = remoteFunction + const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args)); + return metaToValue(obj); + }; + ret = remoteFunction; } else { - ret = {} + ret = {}; } - setObjectMembers(ret, ret, meta.id, meta.members) - setObjectPrototype(ret, ret, meta.id, meta.proto) - Object.defineProperty(ret.constructor, 'name', { value: meta.name }) + setObjectMembers(ret, ret, meta.id, meta.members); + setObjectPrototype(ret, ret, meta.id, meta.proto); + Object.defineProperty(ret.constructor, 'name', { value: meta.name }); // Track delegate obj's lifetime & tell browser to clean up when object is GCed. - v8Util.setRemoteObjectFreer(ret, contextId, meta.id) - v8Util.setHiddenValue(ret, 'atomId', meta.id) - v8Util.addRemoteObjectRef(contextId, meta.id) - remoteObjectCache.set(meta.id, ret) - return ret + v8Util.setRemoteObjectFreer(ret, contextId, meta.id); + v8Util.setHiddenValue(ret, 'atomId', meta.id); + v8Util.addRemoteObjectRef(contextId, meta.id); + remoteObjectCache.set(meta.id, ret); + return ret; } } function metaToError (meta) { - const obj = meta.value + const obj = meta.value; for (const { name, value } of meta.members) { - obj[name] = metaToValue(value) + obj[name] = metaToValue(value); } - return obj + return obj; } function handleMessage (channel, handler) { ipcRendererInternal.on(channel, (event, passedContextId, id, ...args) => { if (passedContextId === contextId) { - handler(id, ...args) + handler(id, ...args); } else { // Message sent to an un-exist context, notify the error to main process. - ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id) + ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id); } - }) + }); } -const enableStacks = hasSwitch('enable-api-filtering-logging') +const enableStacks = hasSwitch('enable-api-filtering-logging'); function getCurrentStack () { - const target = {} + const target = {}; if (enableStacks) { - Error.captureStackTrace(target, getCurrentStack) + Error.captureStackTrace(target, getCurrentStack); } - return target.stack + return target.stack; } // Browser calls a callback in renderer. handleMessage('ELECTRON_RENDERER_CALLBACK', (id, args) => { - callbacksRegistry.apply(id, metaToValue(args)) -}) + callbacksRegistry.apply(id, metaToValue(args)); +}); // A callback in browser is released. handleMessage('ELECTRON_RENDERER_RELEASE_CALLBACK', (id) => { - callbacksRegistry.remove(id) -}) + callbacksRegistry.remove(id); +}); exports.require = (module) => { - const command = 'ELECTRON_BROWSER_REQUIRE' - const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_REQUIRE'; + const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack()); + return metaToValue(meta); +}; // Alias to remote.require('electron').xxx. exports.getBuiltin = (module) => { - const command = 'ELECTRON_BROWSER_GET_BUILTIN' - const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_GET_BUILTIN'; + const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack()); + return metaToValue(meta); +}; exports.getCurrentWindow = () => { - const command = 'ELECTRON_BROWSER_CURRENT_WINDOW' - const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'; + const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack()); + return metaToValue(meta); +}; // Get current WebContents object. exports.getCurrentWebContents = () => { - const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS' - const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'; + const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack()); + return metaToValue(meta); +}; // Get a global object in browser. exports.getGlobal = (name) => { - const command = 'ELECTRON_BROWSER_GLOBAL' - const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_GLOBAL'; + const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack()); + return metaToValue(meta); +}; // Get the process object in browser. Object.defineProperty(exports, 'process', { get: () => exports.getGlobal('process') -}) +}); // Create a function that will return the specified value when called in browser. exports.createFunctionWithReturnValue = (returnValue) => { - const func = () => returnValue - v8Util.setHiddenValue(func, 'returnValue', true) - return func -} + const func = () => returnValue; + v8Util.setHiddenValue(func, 'returnValue', true); + return func; +}; const addBuiltinProperty = (name) => { Object.defineProperty(exports, name, { get: () => exports.getBuiltin(name) - }) -} + }); +}; -const { commonModuleList } = require('@electron/internal/common/api/module-list') -const browserModules = commonModuleList.concat(require('@electron/internal/browser/api/module-keys')) +const { commonModuleList } = require('@electron/internal/common/api/module-list'); +const browserModules = commonModuleList.concat(require('@electron/internal/browser/api/module-keys')); // And add a helper receiver for each one. browserModules .filter((m) => !m.private) .map((m) => m.name) - .forEach(addBuiltinProperty) + .forEach(addBuiltinProperty); diff --git a/lib/renderer/api/web-frame.ts b/lib/renderer/api/web-frame.ts index 7b34e87229d1e..b8dd1fe37ca9d 100644 --- a/lib/renderer/api/web-frame.ts +++ b/lib/renderer/api/web-frame.ts @@ -1,49 +1,49 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'events'; -const binding = process.electronBinding('web_frame') +const binding = process.electronBinding('web_frame'); class WebFrame extends EventEmitter { constructor (public context: Window) { - super() + super(); // Lots of webview would subscribe to webFrame's events. - this.setMaxListeners(0) + this.setMaxListeners(0); } findFrameByRoutingId (...args: Array) { - return getWebFrame(binding._findFrameByRoutingId(this.context, ...args)) + return getWebFrame(binding._findFrameByRoutingId(this.context, ...args)); } getFrameForSelector (...args: Array) { - return getWebFrame(binding._getFrameForSelector(this.context, ...args)) + return getWebFrame(binding._getFrameForSelector(this.context, ...args)); } findFrameByName (...args: Array) { - return getWebFrame(binding._findFrameByName(this.context, ...args)) + return getWebFrame(binding._findFrameByName(this.context, ...args)); } get opener () { - return getWebFrame(binding._getOpener(this.context)) + return getWebFrame(binding._getOpener(this.context)); } get parent () { - return getWebFrame(binding._getParent(this.context)) + return getWebFrame(binding._getParent(this.context)); } get top () { - return getWebFrame(binding._getTop(this.context)) + return getWebFrame(binding._getTop(this.context)); } get firstChild () { - return getWebFrame(binding._getFirstChild(this.context)) + return getWebFrame(binding._getFirstChild(this.context)); } get nextSibling () { - return getWebFrame(binding._getNextSibling(this.context)) + return getWebFrame(binding._getNextSibling(this.context)); } get routingId () { - return binding._getRoutingId(this.context) + return binding._getRoutingId(this.context); } } @@ -53,17 +53,17 @@ for (const name in binding) { // TODO(felixrieseberg): Once we can type web_frame natives, we could // use a neat `keyof` here (WebFrame as any).prototype[name] = function (...args: Array) { - return binding[name](this.context, ...args) - } + return binding[name](this.context, ...args); + }; } } // Helper to return WebFrame or null depending on context. // TODO(zcbenz): Consider returning same WebFrame for the same frame. function getWebFrame (context: Window) { - return context ? new WebFrame(context) : null + return context ? new WebFrame(context) : null; } -const _webFrame = new WebFrame(window) +const _webFrame = new WebFrame(window); -export default _webFrame +export default _webFrame; diff --git a/lib/renderer/chrome-api.ts b/lib/renderer/chrome-api.ts index 1f121e5e795f5..cdd9db6395279 100644 --- a/lib/renderer/chrome-api.ts +++ b/lib/renderer/chrome-api.ts @@ -1,14 +1,14 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' -import * as url from 'url' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; +import * as url from 'url'; -import { Event } from '@electron/internal/renderer/extensions/event' +import { Event } from '@electron/internal/renderer/extensions/event'; class Tab { public id: number constructor (tabId: number) { - this.id = tabId + this.id = tabId; } } @@ -18,9 +18,9 @@ class MessageSender { public url: string constructor (tabId: number, extensionId: string) { - this.tab = tabId ? new Tab(tabId) : null - this.id = extensionId - this.url = `chrome-extension://${extensionId}` + this.tab = tabId ? new Tab(tabId) : null; + this.id = extensionId; + this.url = `chrome-extension://${extensionId}`; } } @@ -31,69 +31,69 @@ class Port { public sender: MessageSender constructor (public tabId: number, public portId: number, extensionId: string, public name: string) { - this.onDisconnect = new Event() - this.onMessage = new Event() - this.sender = new MessageSender(tabId, extensionId) + this.onDisconnect = new Event(); + this.onMessage = new Event(); + this.sender = new MessageSender(tabId, extensionId); ipcRendererInternal.once(`CHROME_PORT_DISCONNECT_${portId}`, () => { - this._onDisconnect() - }) + this._onDisconnect(); + }); ipcRendererInternal.on(`CHROME_PORT_POSTMESSAGE_${portId}`, ( _event: Electron.Event, message: any ) => { - const sendResponse = function () { console.error('sendResponse is not implemented') } - this.onMessage.emit(JSON.parse(message), this.sender, sendResponse) - }) + const sendResponse = function () { console.error('sendResponse is not implemented'); }; + this.onMessage.emit(JSON.parse(message), this.sender, sendResponse); + }); } disconnect () { - if (this.disconnected) return + if (this.disconnected) return; - ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`) - this._onDisconnect() + ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`); + this._onDisconnect(); } postMessage (message: any) { - ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, JSON.stringify(message)) + ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, JSON.stringify(message)); } _onDisconnect () { - this.disconnected = true - ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`) - this.onDisconnect.emit() + this.disconnected = true; + ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`); + this.onDisconnect.emit(); } } // Inject chrome API to the |context| export function injectTo (extensionId: string, context: any) { if (process.electronBinding('features').isExtensionsEnabled()) { - throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled') + throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled'); } - const chrome = context.chrome = context.chrome || {} + const chrome = context.chrome = context.chrome || {}; ipcRendererInternal.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, ( _event: Electron.Event, tabId: number, portId: number, connectInfo: { name: string } ) => { - chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name)) - }) + chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name)); + }); ipcRendererUtils.handle(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, ( _event: Electron.Event, tabId: number, message: string ) => { return new Promise(resolve => { - chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve) - }) - }) + chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve); + }); + }); ipcRendererInternal.on('CHROME_TABS_ONCREATED', (_event: Electron.Event, tabId: number) => { - chrome.tabs.onCreated.emit(new Tab(tabId)) - }) + chrome.tabs.onCreated.emit(new Tab(tabId)); + }); ipcRendererInternal.on('CHROME_TABS_ONREMOVED', (_event: Electron.Event, tabId: number) => { - chrome.tabs.onRemoved.emit(tabId) - }) + chrome.tabs.onRemoved.emit(tabId); + }); chrome.runtime = { id: extensionId, @@ -105,69 +105,69 @@ export function injectTo (extensionId: string, context: any) { slashes: true, hostname: extensionId, pathname: path - }) + }); }, // https://developer.chrome.com/extensions/runtime#method-getManifest getManifest: function () { - const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId) - return manifest + const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId); + return manifest; }, // https://developer.chrome.com/extensions/runtime#method-connect connect (...args: Array) { // Parse the optional args. - let targetExtensionId = extensionId - let connectInfo = { name: '' } + let targetExtensionId = extensionId; + let connectInfo = { name: '' }; if (args.length === 1) { if (typeof args[0] === 'string') { - targetExtensionId = args[0] + targetExtensionId = args[0]; } else { - connectInfo = args[0] + connectInfo = args[0]; } } else if (args.length === 2) { - [targetExtensionId, connectInfo] = args + [targetExtensionId, connectInfo] = args; } - const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo) - return new Port(tabId, portId, extensionId, connectInfo.name) + const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo); + return new Port(tabId, portId, extensionId, connectInfo.name); }, // https://developer.chrome.com/extensions/runtime#method-sendMessage sendMessage (...args: Array) { // Parse the optional args. - const targetExtensionId = extensionId - let message: string - let options: Object | undefined - let responseCallback: Chrome.Tabs.SendMessageCallback = () => {} + const targetExtensionId = extensionId; + let message: string; + let options: Object | undefined; + let responseCallback: Chrome.Tabs.SendMessageCallback = () => {}; if (typeof args[args.length - 1] === 'function') { - responseCallback = args.pop() + responseCallback = args.pop(); } if (args.length === 1) { - [message] = args + [message] = args; } else if (args.length === 2) { if (typeof args[0] === 'string') { - [extensionId, message] = args + [extensionId, message] = args; } else { - [message, options] = args + [message, options] = args; } } else { - [extensionId, message, options] = args + [extensionId, message, options] = args; } if (options) { - console.error('options are not supported') + console.error('options are not supported'); } - ipcRendererInternal.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback) + ipcRendererInternal.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback); }, onConnect: new Event(), onMessage: new Event(), onInstalled: new Event() - } + }; chrome.tabs = { // https://developer.chrome.com/extensions/tabs#method-executeScript @@ -177,7 +177,7 @@ export function injectTo (extensionId: string, context: any) { resultCallback: Chrome.Tabs.ExecuteScriptCallback = () => {} ) { ipcRendererInternal.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details) - .then((result: any) => resultCallback([result])) + .then((result: any) => resultCallback([result])); }, // https://developer.chrome.com/extensions/tabs#method-sendMessage @@ -187,13 +187,13 @@ export function injectTo (extensionId: string, context: any) { _options: Chrome.Tabs.SendMessageDetails, responseCallback: Chrome.Tabs.SendMessageCallback = () => {} ) { - ipcRendererInternal.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback) + ipcRendererInternal.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback); }, onUpdated: new Event(), onCreated: new Event(), onRemoved: new Event() - } + }; chrome.extension = { getURL: chrome.runtime.getURL, @@ -201,9 +201,9 @@ export function injectTo (extensionId: string, context: any) { onConnect: chrome.runtime.onConnect, sendMessage: chrome.runtime.sendMessage, onMessage: chrome.runtime.onMessage - } + }; - chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId) + chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId); chrome.pageAction = { show () {}, @@ -213,14 +213,14 @@ export function injectTo (extensionId: string, context: any) { setIcon () {}, setPopup () {}, getPopup () {} - } + }; - chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId) - chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup() + chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId); + chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup(); // Electron has no concept of a browserAction but we should stub these APIs for compatibility chrome.browserAction = { setIcon () {}, setPopup () {} - } + }; } diff --git a/lib/renderer/content-scripts-injector.ts b/lib/renderer/content-scripts-injector.ts index 2d2dbb714f5e8..f353d0bb8f5d3 100644 --- a/lib/renderer/content-scripts-injector.ts +++ b/lib/renderer/content-scripts-injector.ts @@ -1,8 +1,8 @@ -import { webFrame } from 'electron' +import { webFrame } from 'electron'; -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); const IsolatedWorldIDs = { /** @@ -10,93 +10,93 @@ const IsolatedWorldIDs = { * electron_render_frame_observer.h */ ISOLATED_WORLD_EXTENSIONS: 1 << 20 -} +}; -let isolatedWorldIds = IsolatedWorldIDs.ISOLATED_WORLD_EXTENSIONS -const extensionWorldId: {[key: string]: number | undefined} = {} +let isolatedWorldIds = IsolatedWorldIDs.ISOLATED_WORLD_EXTENSIONS; +const extensionWorldId: {[key: string]: number | undefined} = {}; // https://cs.chromium.org/chromium/src/extensions/renderer/script_injection.cc?type=cs&sq=package:chromium&g=0&l=52 const getIsolatedWorldIdForInstance = () => { // TODO(samuelmaddock): allocate and cleanup IDs - return isolatedWorldIds++ -} + return isolatedWorldIds++; +}; const escapePattern = function (pattern: string) { - return pattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&') -} + return pattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&'); +}; // Check whether pattern matches. // https://developer.chrome.com/extensions/match_patterns const matchesPattern = function (pattern: string) { - if (pattern === '') return true - const regexp = new RegExp(`^${pattern.split('*').map(escapePattern).join('.*')}$`) - const url = `${location.protocol}//${location.host}${location.pathname}` - return url.match(regexp) -} + if (pattern === '') return true; + const regexp = new RegExp(`^${pattern.split('*').map(escapePattern).join('.*')}$`); + const url = `${location.protocol}//${location.host}${location.pathname}`; + return url.match(regexp); +}; // Run the code with chrome API integrated. const runContentScript = function (this: any, extensionId: string, url: string, code: string) { // Assign unique world ID to each extension const worldId = extensionWorldId[extensionId] || - (extensionWorldId[extensionId] = getIsolatedWorldIdForInstance()) + (extensionWorldId[extensionId] = getIsolatedWorldIdForInstance()); // store extension ID for content script to read in isolated world - v8Util.setHiddenValue(global, `extension-${worldId}`, extensionId) + v8Util.setHiddenValue(global, `extension-${worldId}`, extensionId); webFrame.setIsolatedWorldInfo(worldId, { name: `${extensionId} [${worldId}]` // TODO(samuelmaddock): read `content_security_policy` from extension manifest // csp: manifest.content_security_policy, - }) + }); - const sources = [{ code, url }] - return webFrame.executeJavaScriptInIsolatedWorld(worldId, sources) -} + const sources = [{ code, url }]; + return webFrame.executeJavaScriptInIsolatedWorld(worldId, sources); +}; const runAllContentScript = function (scripts: Array, extensionId: string) { for (const { url, code } of scripts) { - runContentScript.call(window, extensionId, url, code) + runContentScript.call(window, extensionId, url, code); } -} +}; const runStylesheet = function (this: any, url: string, code: string) { - webFrame.insertCSS(code) -} + webFrame.insertCSS(code); +}; const runAllStylesheet = function (css: Array) { for (const { url, code } of css) { - runStylesheet.call(window, url, code) + runStylesheet.call(window, url, code); } -} +}; // Run injected scripts. // https://developer.chrome.com/extensions/content_scripts const injectContentScript = function (extensionId: string, script: Electron.ContentScript) { - if (!process.isMainFrame && !script.allFrames) return - if (!script.matches.some(matchesPattern)) return + if (!process.isMainFrame && !script.allFrames) return; + if (!script.matches.some(matchesPattern)) return; if (script.js) { - const fire = runAllContentScript.bind(window, script.js, extensionId) + const fire = runAllContentScript.bind(window, script.js, extensionId); if (script.runAt === 'document_start') { - process.once('document-start', fire) + process.once('document-start', fire); } else if (script.runAt === 'document_end') { - process.once('document-end', fire) + process.once('document-end', fire); } else { - document.addEventListener('DOMContentLoaded', fire) + document.addEventListener('DOMContentLoaded', fire); } } if (script.css) { - const fire = runAllStylesheet.bind(window, script.css) + const fire = runAllStylesheet.bind(window, script.css); if (script.runAt === 'document_start') { - process.once('document-start', fire) + process.once('document-start', fire); } else if (script.runAt === 'document_end') { - process.once('document-end', fire) + process.once('document-end', fire); } else { - document.addEventListener('DOMContentLoaded', fire) + document.addEventListener('DOMContentLoaded', fire); } } -} +}; // Handle the request of chrome.tabs.executeJavaScript. ipcRendererUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', function ( @@ -105,15 +105,15 @@ ipcRendererUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', function ( url: string, code: string ) { - return runContentScript.call(window, extensionId, url, code) -}) + return runContentScript.call(window, extensionId, url, code); +}); module.exports = (entries: Electron.ContentScriptEntry[]) => { for (const entry of entries) { if (entry.contentScripts) { for (const script of entry.contentScripts) { - injectContentScript(entry.extensionId, script) + injectContentScript(entry.extensionId, script); } } } -} +}; diff --git a/lib/renderer/extensions/event.ts b/lib/renderer/extensions/event.ts index d1fc5fb723e5f..d88800c4f93c7 100644 --- a/lib/renderer/extensions/event.ts +++ b/lib/renderer/extensions/event.ts @@ -2,19 +2,19 @@ export class Event { private listeners: Function[] = [] addListener (callback: Function) { - this.listeners.push(callback) + this.listeners.push(callback); } removeListener (callback: Function) { - const index = this.listeners.indexOf(callback) + const index = this.listeners.indexOf(callback); if (index !== -1) { - this.listeners.splice(index, 1) + this.listeners.splice(index, 1); } } emit (...args: any[]) { for (const listener of this.listeners) { - listener(...args) + listener(...args); } } } diff --git a/lib/renderer/extensions/i18n.ts b/lib/renderer/extensions/i18n.ts index 8e3c7edaa118d..b979a5a06fa0d 100644 --- a/lib/renderer/extensions/i18n.ts +++ b/lib/renderer/extensions/i18n.ts @@ -4,7 +4,7 @@ // Does not implement predefined messages: // https://developer.chrome.com/extensions/i18n#overview-predefined -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; interface Placeholder { content: string; @@ -13,48 +13,48 @@ interface Placeholder { const getMessages = (extensionId: number) => { try { - const data = ipcRendererUtils.invokeSync('CHROME_GET_MESSAGES', extensionId) - return JSON.parse(data) || {} + const data = ipcRendererUtils.invokeSync('CHROME_GET_MESSAGES', extensionId); + return JSON.parse(data) || {}; } catch { - return {} + return {}; } -} +}; const replaceNumberedSubstitutions = (message: string, substitutions: string[]) => { return message.replace(/\$(\d+)/, (_, number) => { - const index = parseInt(number, 10) - 1 - return substitutions[index] || '' - }) -} + const index = parseInt(number, 10) - 1; + return substitutions[index] || ''; + }); +}; const replacePlaceholders = (message: string, placeholders: Record, substitutions: string[] | string) => { - if (typeof substitutions === 'string') substitutions = [substitutions] - if (!Array.isArray(substitutions)) substitutions = [] + if (typeof substitutions === 'string') substitutions = [substitutions]; + if (!Array.isArray(substitutions)) substitutions = []; if (placeholders) { Object.keys(placeholders).forEach((name: string) => { - let { content } = placeholders[name] - const substitutionsArray = Array.isArray(substitutions) ? substitutions : [] - content = replaceNumberedSubstitutions(content, substitutionsArray) - message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content) - }) + let { content } = placeholders[name]; + const substitutionsArray = Array.isArray(substitutions) ? substitutions : []; + content = replaceNumberedSubstitutions(content, substitutionsArray); + message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content); + }); } - return replaceNumberedSubstitutions(message, substitutions) -} + return replaceNumberedSubstitutions(message, substitutions); +}; const getMessage = (extensionId: number, messageName: string, substitutions: string[]) => { - const messages = getMessages(extensionId) + const messages = getMessages(extensionId); if (Object.prototype.hasOwnProperty.call(messages, messageName)) { - const { message, placeholders } = messages[messageName] - return replacePlaceholders(message, placeholders, substitutions) + const { message, placeholders } = messages[messageName]; + return replacePlaceholders(message, placeholders, substitutions); } -} +}; exports.setup = (extensionId: number) => { return { getMessage (messageName: string, substitutions: string[]) { - return getMessage(extensionId, messageName, substitutions) + return getMessage(extensionId, messageName, substitutions); } - } -} + }; +}; diff --git a/lib/renderer/extensions/storage.ts b/lib/renderer/extensions/storage.ts index f5ed8b69cdab1..6dbafb6e5bd81 100644 --- a/lib/renderer/extensions/storage.ts +++ b/lib/renderer/extensions/storage.ts @@ -1,86 +1,86 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; const getStorage = (storageType: string, extensionId: number, callback: Function) => { - if (typeof callback !== 'function') throw new TypeError('No callback provided') + if (typeof callback !== 'function') throw new TypeError('No callback provided'); ipcRendererInternal.invoke('CHROME_STORAGE_READ', storageType, extensionId) .then(data => { if (data !== null) { - callback(JSON.parse(data)) + callback(JSON.parse(data)); } else { // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal - callback({}) + callback({}); } - }) -} + }); +}; const setStorage = (storageType: string, extensionId: number, storage: Record, callback: Function) => { - const json = JSON.stringify(storage) + const json = JSON.stringify(storage); ipcRendererInternal.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json) .then(() => { - if (callback) callback() - }) -} + if (callback) callback(); + }); +}; const getStorageManager = (storageType: string, extensionId: number) => { return { get (keys: string[], callback: Function) { getStorage(storageType, extensionId, (storage: Record) => { - if (keys == null) return callback(storage) + if (keys == null) return callback(storage); - let defaults: Record = {} + let defaults: Record = {}; switch (typeof keys) { case 'string': - keys = [keys] - break + keys = [keys]; + break; case 'object': if (!Array.isArray(keys)) { - defaults = keys - keys = Object.keys(keys) + defaults = keys; + keys = Object.keys(keys); } - break + break; } // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal - if (keys.length === 0) return callback({}) + if (keys.length === 0) return callback({}); - const items: Record = {} + const items: Record = {}; keys.forEach((key: string) => { - let value = storage[key] - if (value == null) value = defaults[key] - items[key] = value - }) - callback(items) - }) + let value = storage[key]; + if (value == null) value = defaults[key]; + items[key] = value; + }); + callback(items); + }); }, set (items: Record, callback: Function) { getStorage(storageType, extensionId, (storage: Record) => { - Object.keys(items).forEach(name => { storage[name] = items[name] }) - setStorage(storageType, extensionId, storage, callback) - }) + Object.keys(items).forEach(name => { storage[name] = items[name]; }); + setStorage(storageType, extensionId, storage, callback); + }); }, remove (keys: string[], callback: Function) { getStorage(storageType, extensionId, (storage: Record) => { - if (!Array.isArray(keys)) keys = [keys] + if (!Array.isArray(keys)) keys = [keys]; keys.forEach((key: string) => { - delete storage[key] - }) + delete storage[key]; + }); - setStorage(storageType, extensionId, storage, callback) - }) + setStorage(storageType, extensionId, storage, callback); + }); }, clear (callback: Function) { - setStorage(storageType, extensionId, {}, callback) + setStorage(storageType, extensionId, {}, callback); } - } -} + }; +}; export const setup = (extensionId: number) => ({ sync: getStorageManager('sync', extensionId), local: getStorageManager('local', extensionId) -}) +}); diff --git a/lib/renderer/extensions/web-navigation.ts b/lib/renderer/extensions/web-navigation.ts index fb39f11af0424..768eb342804b1 100644 --- a/lib/renderer/extensions/web-navigation.ts +++ b/lib/renderer/extensions/web-navigation.ts @@ -1,5 +1,5 @@ -import { Event } from '@electron/internal/renderer/extensions/event' -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { Event } from '@electron/internal/renderer/extensions/event'; +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; class WebNavigation { private onBeforeNavigate = new Event() @@ -7,13 +7,13 @@ class WebNavigation { constructor () { ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event: Electron.IpcRendererEvent, details: any) => { - this.onBeforeNavigate.emit(details) - }) + this.onBeforeNavigate.emit(details); + }); ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event: Electron.IpcRendererEvent, details: any) => { - this.onCompleted.emit(details) - }) + this.onCompleted.emit(details); + }); } } -export const setup = () => new WebNavigation() +export const setup = () => new WebNavigation(); diff --git a/lib/renderer/init.ts b/lib/renderer/init.ts index 5ef9a02c4d8b0..5f03d9911adad 100644 --- a/lib/renderer/init.ts +++ b/lib/renderer/init.ts @@ -1,7 +1,7 @@ -import { EventEmitter } from 'events' -import * as path from 'path' +import { EventEmitter } from 'events'; +import * as path from 'path'; -const Module = require('module') +const Module = require('module'); // Make sure globals like "process" and "global" are always available in preload // scripts even after they are deleted in "loaded" script. @@ -23,42 +23,42 @@ Module.wrapper = [ // code to override "process" and "Buffer" with local variables. 'return function (exports, require, module, __filename, __dirname) { ', '\n}.call(this, exports, require, module, __filename, __dirname); });' -] +]; // We modified the original process.argv to let node.js load the // init.js, we need to restore it here. -process.argv.splice(1, 1) +process.argv.splice(1, 1); // Clear search paths. -require('../common/reset-search-paths') +require('../common/reset-search-paths'); // Import common settings. -require('@electron/internal/common/init') +require('@electron/internal/common/init'); // The global variable will be used by ipc for event dispatching -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); -const ipcEmitter = new EventEmitter() -const ipcInternalEmitter = new EventEmitter() -v8Util.setHiddenValue(global, 'ipc', ipcEmitter) -v8Util.setHiddenValue(global, 'ipc-internal', ipcInternalEmitter) +const ipcEmitter = new EventEmitter(); +const ipcInternalEmitter = new EventEmitter(); +v8Util.setHiddenValue(global, 'ipc', ipcEmitter); +v8Util.setHiddenValue(global, 'ipc-internal', ipcInternalEmitter); v8Util.setHiddenValue(global, 'ipcNative', { onMessage (internal: boolean, channel: string, ports: any[], args: any[], senderId: number) { - const sender = internal ? ipcInternalEmitter : ipcEmitter - sender.emit(channel, { sender, senderId, ports }, ...args) + const sender = internal ? ipcInternalEmitter : ipcEmitter; + sender.emit(channel, { sender, senderId, ports }, ...args); } -}) +}); // Use electron module after everything is ready. -const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') -const { webFrameInit } = require('@electron/internal/renderer/web-frame-init') -webFrameInit() +const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); +const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); +const { webFrameInit } = require('@electron/internal/renderer/web-frame-init'); +webFrameInit(); // Process command line arguments. -const { hasSwitch, getSwitchValue } = process.electronBinding('command_line') +const { hasSwitch, getSwitchValue } = process.electronBinding('command_line'); const parseOption = function ( name: string, defaultValue: T, converter?: (value: string) => T @@ -69,105 +69,105 @@ const parseOption = function ( ? converter(getSwitchValue(name)) : getSwitchValue(name) ) - : defaultValue -} - -const contextIsolation = hasSwitch('context-isolation') -const nodeIntegration = hasSwitch('node-integration') -const webviewTag = hasSwitch('webview-tag') -const isHiddenPage = hasSwitch('hidden-page') -const usesNativeWindowOpen = hasSwitch('native-window-open') -const rendererProcessReuseEnabled = hasSwitch('disable-electron-site-instance-overrides') - -const preloadScript = parseOption('preload', null) -const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter)) as string[] -const appPath = parseOption('app-path', null) -const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value)) -const openerId = parseOption('opener-id', null, value => parseInt(value)) + : defaultValue; +}; + +const contextIsolation = hasSwitch('context-isolation'); +const nodeIntegration = hasSwitch('node-integration'); +const webviewTag = hasSwitch('webview-tag'); +const isHiddenPage = hasSwitch('hidden-page'); +const usesNativeWindowOpen = hasSwitch('native-window-open'); +const rendererProcessReuseEnabled = hasSwitch('disable-electron-site-instance-overrides'); + +const preloadScript = parseOption('preload', null); +const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter)) as string[]; +const appPath = parseOption('app-path', null); +const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value)); +const openerId = parseOption('opener-id', null, value => parseInt(value)); // The arguments to be passed to isolated world. -const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled } +const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled }; // The webContents preload script is loaded after the session preload scripts. if (preloadScript) { - preloadScripts.push(preloadScript) + preloadScripts.push(preloadScript); } switch (window.location.protocol) { case 'devtools:': { // Override some inspector APIs. - require('@electron/internal/renderer/inspector') - break + require('@electron/internal/renderer/inspector'); + break; } case 'chrome-extension:': { // Inject the chrome.* APIs that chrome extensions require if (!process.electronBinding('features').isExtensionsEnabled()) { - require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window) + require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window); } - break + break; } case 'chrome:': - break + break; default: { // Override default web functions. - const { windowSetup } = require('@electron/internal/renderer/window-setup') - windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled) + const { windowSetup } = require('@electron/internal/renderer/window-setup'); + windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled); // Inject content scripts. if (!process.electronBinding('features').isExtensionsEnabled()) { - const contentScripts = ipcRendererUtils.invokeSync('ELECTRON_GET_CONTENT_SCRIPTS') as Electron.ContentScriptEntry[] - require('@electron/internal/renderer/content-scripts-injector')(contentScripts) + const contentScripts = ipcRendererUtils.invokeSync('ELECTRON_GET_CONTENT_SCRIPTS') as Electron.ContentScriptEntry[]; + require('@electron/internal/renderer/content-scripts-injector')(contentScripts); } } } // Load webview tag implementation. if (process.isMainFrame) { - const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') - webViewInit(contextIsolation, webviewTag, guestInstanceId) + const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init'); + webViewInit(contextIsolation, webviewTag, guestInstanceId); } // Pass the arguments to isolatedWorld. if (contextIsolation) { - v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs) + v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs); } if (nodeIntegration) { // Export node bindings to global. const { makeRequireFunction } = __non_webpack_require__('internal/modules/cjs/helpers') // eslint-disable-line - global.module = new Module('electron/js2c/renderer_init') - global.require = makeRequireFunction(global.module) + global.module = new Module('electron/js2c/renderer_init'); + global.require = makeRequireFunction(global.module); // Set the __filename to the path of html file if it is file: protocol. if (window.location.protocol === 'file:') { - const location = window.location - let pathname = location.pathname + const location = window.location; + let pathname = location.pathname; if (process.platform === 'win32') { - if (pathname[0] === '/') pathname = pathname.substr(1) + if (pathname[0] === '/') pathname = pathname.substr(1); - const isWindowsNetworkSharePath = location.hostname.length > 0 && process.resourcesPath.startsWith('\\') + const isWindowsNetworkSharePath = location.hostname.length > 0 && process.resourcesPath.startsWith('\\'); if (isWindowsNetworkSharePath) { - pathname = `//${location.host}/${pathname}` + pathname = `//${location.host}/${pathname}`; } } - global.__filename = path.normalize(decodeURIComponent(pathname)) - global.__dirname = path.dirname(global.__filename) + global.__filename = path.normalize(decodeURIComponent(pathname)); + global.__dirname = path.dirname(global.__filename); // Set module's filename so relative require can work as expected. - global.module.filename = global.__filename + global.module.filename = global.__filename; // Also search for module under the html file. - global.module.paths = Module._nodeModulePaths(global.__dirname) + global.module.paths = Module._nodeModulePaths(global.__dirname); } else { // For backwards compatibility we fake these two paths here - global.__filename = path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js') - global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'renderer') + global.__filename = path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js'); + global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'renderer'); if (appPath) { // Search for module under the app directory - global.module.paths = Module._nodeModulePaths(appPath) + global.module.paths = Module._nodeModulePaths(appPath); } } @@ -177,42 +177,42 @@ if (nodeIntegration) { // We do not want to add `uncaughtException` to our definitions // because we don't want anyone else (anywhere) to throw that kind // of error. - global.process.emit('uncaughtException' as any, error as any) - return true + global.process.emit('uncaughtException' as any, error as any); + return true; } else { - return false + return false; } - } + }; } else { // Delete Node's symbols after the Environment has been loaded in a // non context-isolated environment if (!contextIsolation) { process.once('loaded', function () { - delete global.process - delete global.Buffer - delete global.setImmediate - delete global.clearImmediate - delete global.global - delete global.root - delete global.GLOBAL - }) + delete global.process; + delete global.Buffer; + delete global.setImmediate; + delete global.clearImmediate; + delete global.global; + delete global.root; + delete global.GLOBAL; + }); } } // Load the preload scripts. for (const preloadScript of preloadScripts) { try { - Module._load(preloadScript) + Module._load(preloadScript); } catch (error) { - console.error(`Unable to load preload script: ${preloadScript}`) - console.error(error) + console.error(`Unable to load preload script: ${preloadScript}`); + console.error(error); - ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, error) + ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, error); } } // Warn about security issues if (process.isMainFrame) { - const { securityWarnings } = require('@electron/internal/renderer/security-warnings') - securityWarnings(nodeIntegration) + const { securityWarnings } = require('@electron/internal/renderer/security-warnings'); + securityWarnings(nodeIntegration); } diff --git a/lib/renderer/inspector.ts b/lib/renderer/inspector.ts index 0f4927040cb25..ebb76cc8a2f52 100644 --- a/lib/renderer/inspector.ts +++ b/lib/renderer/inspector.ts @@ -1,61 +1,61 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; window.onload = function () { // Use menu API to show context menu. - window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu + window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu; // correct for Chromium returning undefined for filesystem - window.Persistence!.FileSystemWorkspaceBinding.completeURL = completeURL + window.Persistence!.FileSystemWorkspaceBinding.completeURL = completeURL; // Use dialog API to override file chooser dialog. - window.UI!.createFileSelectorElement = createFileSelectorElement -} + window.UI!.createFileSelectorElement = createFileSelectorElement; +}; // Extra / is needed as a result of MacOS requiring absolute paths function completeURL (project: string, path: string) { - project = 'file:///' - return `${project}${path}` + project = 'file:///'; + return `${project}${path}`; } // The DOM implementation expects (message?: string) => boolean (window.confirm as any) = function (message: string, title: string) { - return ipcRendererUtils.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean -} + return ipcRendererUtils.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean; +}; const useEditMenuItems = function (x: number, y: number, items: ContextMenuItem[]) { return items.length === 0 && document.elementsFromPoint(x, y).some(function (element) { return element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA' || - (element as HTMLElement).isContentEditable - }) -} + (element as HTMLElement).isContentEditable; + }); +}; const createMenu = function (x: number, y: number, items: ContextMenuItem[]) { - const isEditMenu = useEditMenuItems(x, y, items) + const isEditMenu = useEditMenuItems(x, y, items); ipcRendererInternal.invoke('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => { if (typeof id === 'number') { - window.DevToolsAPI!.contextMenuItemSelected(id) + window.DevToolsAPI!.contextMenuItemSelected(id); } - window.DevToolsAPI!.contextMenuCleared() - }) -} + window.DevToolsAPI!.contextMenuCleared(); + }); +}; const showFileChooserDialog = function (callback: (blob: File) => void) { ipcRendererInternal.invoke<[ string, any ]>('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => { if (path && data) { - callback(dataToHtml5FileObject(path, data)) + callback(dataToHtml5FileObject(path, data)); } - }) -} + }); +}; const dataToHtml5FileObject = function (path: string, data: any) { - return new File([data], path) -} + return new File([data], path); +}; const createFileSelectorElement = function (this: any, callback: () => void) { - const fileSelectorElement = document.createElement('span') - fileSelectorElement.style.display = 'none' - fileSelectorElement.click = showFileChooserDialog.bind(this, callback) - return fileSelectorElement -} + const fileSelectorElement = document.createElement('span'); + fileSelectorElement.style.display = 'none'; + fileSelectorElement.click = showFileChooserDialog.bind(this, callback); + return fileSelectorElement; +}; diff --git a/lib/renderer/ipc-renderer-internal-utils.ts b/lib/renderer/ipc-renderer-internal-utils.ts index 8c34140148c61..709880ffe5cb1 100644 --- a/lib/renderer/ipc-renderer-internal-utils.ts +++ b/lib/renderer/ipc-renderer-internal-utils.ts @@ -1,24 +1,24 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; type IPCHandler = (event: Electron.IpcRendererEvent, ...args: any[]) => any export const handle = function (channel: string, handler: T) { ipcRendererInternal.on(channel, async (event, requestId, ...args) => { - const replyChannel = `${channel}_RESPONSE_${requestId}` + const replyChannel = `${channel}_RESPONSE_${requestId}`; try { - event.sender.send(replyChannel, null, await handler(event, ...args)) + event.sender.send(replyChannel, null, await handler(event, ...args)); } catch (error) { - event.sender.send(replyChannel, error) + event.sender.send(replyChannel, error); } - }) -} + }); +}; export function invokeSync (command: string, ...args: any[]): T { - const [error, result] = ipcRendererInternal.sendSync(command, ...args) + const [error, result] = ipcRendererInternal.sendSync(command, ...args); if (error) { - throw error + throw error; } else { - return result + return result; } } diff --git a/lib/renderer/ipc-renderer-internal.ts b/lib/renderer/ipc-renderer-internal.ts index 661f7059d6aa1..97fe16b4ad2a5 100644 --- a/lib/renderer/ipc-renderer-internal.ts +++ b/lib/renderer/ipc-renderer-internal.ts @@ -1,30 +1,30 @@ -const { ipc } = process.electronBinding('ipc') -const v8Util = process.electronBinding('v8_util') +const { ipc } = process.electronBinding('ipc'); +const v8Util = process.electronBinding('v8_util'); // Created by init.js. -export const ipcRendererInternal = v8Util.getHiddenValue(global, 'ipc-internal') -const internal = true +export const ipcRendererInternal = v8Util.getHiddenValue(global, 'ipc-internal'); +const internal = true; ipcRendererInternal.send = function (channel, ...args) { - return ipc.send(internal, channel, args) -} + return ipc.send(internal, channel, args); +}; ipcRendererInternal.sendSync = function (channel, ...args) { - return ipc.sendSync(internal, channel, args)[0] -} + return ipc.sendSync(internal, channel, args)[0]; +}; ipcRendererInternal.sendTo = function (webContentsId, channel, ...args) { - return ipc.sendTo(internal, false, webContentsId, channel, args) -} + return ipc.sendTo(internal, false, webContentsId, channel, args); +}; ipcRendererInternal.sendToAll = function (webContentsId, channel, ...args) { - return ipc.sendTo(internal, true, webContentsId, channel, args) -} + return ipc.sendTo(internal, true, webContentsId, channel, args); +}; ipcRendererInternal.invoke = async function (channel: string, ...args: any[]) { - const { error, result } = await ipc.invoke(internal, channel, args) + const { error, result } = await ipc.invoke(internal, channel, args); if (error) { - throw new Error(`Error invoking remote method '${channel}': ${error}`) + throw new Error(`Error invoking remote method '${channel}': ${error}`); } - return result -} + return result; +}; diff --git a/lib/renderer/remote/callbacks-registry.ts b/lib/renderer/remote/callbacks-registry.ts index f2f164fa6fa09..61d3d3a07ca99 100644 --- a/lib/renderer/remote/callbacks-registry.ts +++ b/lib/renderer/remote/callbacks-registry.ts @@ -1,4 +1,4 @@ -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); export class CallbacksRegistry { private nextId: number = 0 @@ -6,50 +6,50 @@ export class CallbacksRegistry { add (callback: Function) { // The callback is already added. - let id = v8Util.getHiddenValue(callback, 'callbackId') - if (id != null) return id + let id = v8Util.getHiddenValue(callback, 'callbackId'); + if (id != null) return id; - id = this.nextId += 1 + id = this.nextId += 1; // Capture the location of the function and put it in the ID string, // so that release errors can be tracked down easily. - const regexp = /at (.*)/gi - const stackString = (new Error()).stack - if (!stackString) return + const regexp = /at (.*)/gi; + const stackString = (new Error()).stack; + if (!stackString) return; - let filenameAndLine - let match + let filenameAndLine; + let match; while ((match = regexp.exec(stackString)) !== null) { - const location = match[1] - if (location.includes('(native)')) continue - if (location.includes('()')) continue - if (location.includes('electron/js2c')) continue - - const ref = /([^/^)]*)\)?$/gi.exec(location) - if (ref) filenameAndLine = ref![1] - break + const location = match[1]; + if (location.includes('(native)')) continue; + if (location.includes('()')) continue; + if (location.includes('electron/js2c')) continue; + + const ref = /([^/^)]*)\)?$/gi.exec(location); + if (ref) filenameAndLine = ref![1]; + break; } - this.callbacks.set(id, callback) - v8Util.setHiddenValue(callback, 'callbackId', id) - v8Util.setHiddenValue(callback, 'location', filenameAndLine) - return id + this.callbacks.set(id, callback); + v8Util.setHiddenValue(callback, 'callbackId', id); + v8Util.setHiddenValue(callback, 'location', filenameAndLine); + return id; } get (id: number) { - return this.callbacks.get(id) || function () {} + return this.callbacks.get(id) || function () {}; } apply (id: number, ...args: any[]) { - return this.get(id).apply(global, ...args) + return this.get(id).apply(global, ...args); } remove (id: number) { - const callback = this.callbacks.get(id) + const callback = this.callbacks.get(id); if (callback) { - v8Util.deleteHiddenValue(callback, 'callbackId') - this.callbacks.delete(id) + v8Util.deleteHiddenValue(callback, 'callbackId'); + this.callbacks.delete(id); } } } diff --git a/lib/renderer/security-warnings.ts b/lib/renderer/security-warnings.ts index 4e78a304fcef6..accbbc927979b 100644 --- a/lib/renderer/security-warnings.ts +++ b/lib/renderer/security-warnings.ts @@ -1,9 +1,9 @@ -import { webFrame } from 'electron' -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { webFrame } from 'electron'; +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; -let shouldLog: boolean | null = null +let shouldLog: boolean | null = null; -const { platform, execPath, env } = process +const { platform, execPath, env } = process; /** * This method checks if a security message should be logged. @@ -15,37 +15,37 @@ const { platform, execPath, env } = process */ const shouldLogSecurityWarnings = function (): boolean { if (shouldLog !== null) { - return shouldLog + return shouldLog; } switch (platform) { case 'darwin': shouldLog = execPath.endsWith('MacOS/Electron') || - execPath.includes('Electron.app/Contents/Frameworks/') - break + execPath.includes('Electron.app/Contents/Frameworks/'); + break; case 'freebsd': case 'linux': - shouldLog = execPath.endsWith('/electron') - break + shouldLog = execPath.endsWith('/electron'); + break; case 'win32': - shouldLog = execPath.endsWith('\\electron.exe') - break + shouldLog = execPath.endsWith('\\electron.exe'); + break; default: - shouldLog = false + shouldLog = false; } if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) || (window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) { - shouldLog = false + shouldLog = false; } if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) || (window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) { - shouldLog = true + shouldLog = true; } - return shouldLog -} + return shouldLog; +}; /** * Checks if the current window is remote. @@ -54,9 +54,9 @@ const shouldLogSecurityWarnings = function (): boolean { */ const getIsRemoteProtocol = function () { if (window && window.location && window.location.protocol) { - return /^(http|ftp)s?/gi.test(window.location.protocol) + return /^(http|ftp)s?/gi.test(window.location.protocol); } -} +}; /** * Checks if the current window is from localhost. @@ -65,11 +65,11 @@ const getIsRemoteProtocol = function () { */ const isLocalhost = function () { if (!window || !window.location) { - return false + return false; } - return window.location.hostname === 'localhost' -} + return window.location.hostname === 'localhost'; +}; /** * Tries to determine whether a CSP without `unsafe-eval` is set. @@ -79,17 +79,17 @@ const isLocalhost = function () { const isUnsafeEvalEnabled = function () { return webFrame.executeJavaScript(`(${(() => { try { - new Function('') // eslint-disable-line no-new,no-new-func + new Function(''); // eslint-disable-line no-new,no-new-func } catch { - return false + return false; } - return true - }).toString()})()`, false) -} + return true; + }).toString()})()`, false); +}; const moreInformation = `\nFor more information and help, consult https://electronjs.org/docs/tutorial/security.\nThis warning will not show up -once the app is packaged.` +once the app is packaged.`; /** * #1 Only load secure content @@ -99,7 +99,7 @@ once the app is packaged.` */ const warnAboutInsecureResources = function () { if (!window || !window.performance || !window.performance.getEntriesByType) { - return + return; } const resources = window.performance @@ -107,20 +107,20 @@ const warnAboutInsecureResources = function () { .filter(({ name }) => /^(http|ftp):/gi.test(name || '')) .filter(({ name }) => new URL(name).hostname !== 'localhost') .map(({ name }) => `- ${name}`) - .join('\n') + .join('\n'); if (!resources || resources.length === 0) { - return + return; } const warning = `This renderer process loads resources using insecure protocols. This exposes users of this app to unnecessary security risks. Consider loading the following resources over HTTPS or FTPS. \n${resources} - \n${moreInformation}` + \n${moreInformation}`; console.warn('%cElectron Security Warning (Insecure Resources)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #2 on the checklist: Disable the Node.js integration in all renderers that @@ -129,17 +129,17 @@ const warnAboutInsecureResources = function () { * Logs a warning message about Node integration. */ const warnAboutNodeWithRemoteContent = function (nodeIntegration: boolean) { - if (!nodeIntegration || isLocalhost()) return + if (!nodeIntegration || isLocalhost()) return; if (getIsRemoteProtocol()) { const warning = `This renderer process has Node.js integration enabled and attempted to load remote content from '${window.location}'. This - exposes users of this app to severe security risks.\n${moreInformation}` + exposes users of this app to severe security risks.\n${moreInformation}`; console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)', - 'font-weight: bold;', warning) + 'font-weight: bold;', warning); } -} +}; // Currently missing since it has ramifications and is still experimental: // #3 Enable context isolation in all renderers that display remote content @@ -153,14 +153,14 @@ const warnAboutNodeWithRemoteContent = function (nodeIntegration: boolean) { * Logs a warning message about disabled webSecurity. */ const warnAboutDisabledWebSecurity = function (webPreferences?: Electron.WebPreferences) { - if (!webPreferences || webPreferences.webSecurity !== false) return + if (!webPreferences || webPreferences.webSecurity !== false) return; const warning = `This renderer process has "webSecurity" disabled. This - exposes users of this app to severe security risks.\n${moreInformation}` + exposes users of this app to severe security risks.\n${moreInformation}`; console.warn('%cElectron Security Warning (Disabled webSecurity)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #6 on the checklist: Define a Content-Security-Policy and use restrictive @@ -170,16 +170,16 @@ const warnAboutDisabledWebSecurity = function (webPreferences?: Electron.WebPref */ const warnAboutInsecureCSP = function () { isUnsafeEvalEnabled().then((enabled) => { - if (!enabled) return + if (!enabled) return; const warning = `This renderer process has either no Content Security Policy set or a policy with "unsafe-eval" enabled. This exposes users of - this app to unnecessary security risks.\n${moreInformation}` + this app to unnecessary security risks.\n${moreInformation}`; console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)', - 'font-weight: bold;', warning) - }) -} + 'font-weight: bold;', warning); + }); +}; /** * #7 on the checklist: Do not set allowRunningInsecureContent to true @@ -187,15 +187,15 @@ const warnAboutInsecureCSP = function () { * Logs a warning message about disabled webSecurity. */ const warnAboutInsecureContentAllowed = function (webPreferences?: Electron.WebPreferences) { - if (!webPreferences || !webPreferences.allowRunningInsecureContent) return + if (!webPreferences || !webPreferences.allowRunningInsecureContent) return; const warning = `This renderer process has "allowRunningInsecureContent" enabled. This exposes users of this app to severe security risks.\n - ${moreInformation}` + ${moreInformation}`; console.warn('%cElectron Security Warning (allowRunningInsecureContent)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #8 on the checklist: Do not enable experimental features @@ -204,16 +204,16 @@ const warnAboutInsecureContentAllowed = function (webPreferences?: Electron.WebP */ const warnAboutExperimentalFeatures = function (webPreferences?: Electron.WebPreferences) { if (!webPreferences || (!webPreferences.experimentalFeatures)) { - return + return; } const warning = `This renderer process has "experimentalFeatures" enabled. This exposes users of this app to some security risk. If you do not need - this feature, you should disable it.\n${moreInformation}` + this feature, you should disable it.\n${moreInformation}`; console.warn('%cElectron Security Warning (experimentalFeatures)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #9 on the checklist: Do not use enableBlinkFeatures @@ -224,16 +224,16 @@ const warnAboutEnableBlinkFeatures = function (webPreferences?: Electron.WebPref if (!webPreferences || !Object.prototype.hasOwnProperty.call(webPreferences, 'enableBlinkFeatures') || (webPreferences.enableBlinkFeatures && webPreferences.enableBlinkFeatures.length === 0)) { - return + return; } const warning = `This renderer process has additional "enableBlinkFeatures" enabled. This exposes users of this app to some security risk. If you do not - need this feature, you should disable it.\n${moreInformation}` + need this feature, you should disable it.\n${moreInformation}`; console.warn('%cElectron Security Warning (enableBlinkFeatures)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #10 on the checklist: Do Not Use allowpopups @@ -242,21 +242,21 @@ const warnAboutEnableBlinkFeatures = function (webPreferences?: Electron.WebPref */ const warnAboutAllowedPopups = function () { if (document && document.querySelectorAll) { - const domElements = document.querySelectorAll('[allowpopups]') + const domElements = document.querySelectorAll('[allowpopups]'); if (!domElements || domElements.length === 0) { - return + return; } const warning = `A has "allowpopups" set to true. This exposes users of this app to some security risk, since popups are just BrowserWindows. If you do not need this feature, you should disable it.\n - ${moreInformation}` + ${moreInformation}`; console.warn('%cElectron Security Warning (allowpopups)', - 'font-weight: bold;', warning) + 'font-weight: bold;', warning); } -} +}; // Currently missing since we can't easily programmatically check for it: // #11 Verify WebView Options Before Creation @@ -268,19 +268,19 @@ const warnAboutAllowedPopups = function () { // Logs a warning message about the remote module const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) { - if (!webPreferences || isLocalhost()) return - const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true - if (!remoteModuleEnabled) return + if (!webPreferences || isLocalhost()) return; + const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true; + if (!remoteModuleEnabled) return; if (getIsRemoteProtocol()) { const warning = `This renderer process has "enableRemoteModule" enabled and attempted to load remote content from '${window.location}'. This - exposes users of this app to unnecessary security risks.\n${moreInformation}` + exposes users of this app to unnecessary security risks.\n${moreInformation}`; console.warn('%cElectron Security Warning (enableRemoteModule)', - 'font-weight: bold;', warning) + 'font-weight: bold;', warning); } -} +}; // Currently missing since we can't easily programmatically check for it: // #16 Filter the `remote` module @@ -288,31 +288,31 @@ const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electr const logSecurityWarnings = function ( webPreferences: Electron.WebPreferences | undefined, nodeIntegration: boolean ) { - warnAboutNodeWithRemoteContent(nodeIntegration) - warnAboutDisabledWebSecurity(webPreferences) - warnAboutInsecureResources() - warnAboutInsecureContentAllowed(webPreferences) - warnAboutExperimentalFeatures(webPreferences) - warnAboutEnableBlinkFeatures(webPreferences) - warnAboutInsecureCSP() - warnAboutAllowedPopups() - warnAboutRemoteModuleWithRemoteContent(webPreferences) -} + warnAboutNodeWithRemoteContent(nodeIntegration); + warnAboutDisabledWebSecurity(webPreferences); + warnAboutInsecureResources(); + warnAboutInsecureContentAllowed(webPreferences); + warnAboutExperimentalFeatures(webPreferences); + warnAboutEnableBlinkFeatures(webPreferences); + warnAboutInsecureCSP(); + warnAboutAllowedPopups(); + warnAboutRemoteModuleWithRemoteContent(webPreferences); +}; const getWebPreferences = async function () { try { - return ipcRendererInternal.invoke('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES') + return ipcRendererInternal.invoke('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES'); } catch (error) { - console.warn(`getLastWebPreferences() failed: ${error}`) + console.warn(`getLastWebPreferences() failed: ${error}`); } -} +}; export function securityWarnings (nodeIntegration: boolean) { const loadHandler = async function () { if (shouldLogSecurityWarnings()) { - const webPreferences = await getWebPreferences() - logSecurityWarnings(webPreferences, nodeIntegration) + const webPreferences = await getWebPreferences(); + logSecurityWarnings(webPreferences, nodeIntegration); } - } - window.addEventListener('load', loadHandler, { once: true }) + }; + window.addEventListener('load', loadHandler, { once: true }); } diff --git a/lib/renderer/web-frame-init.ts b/lib/renderer/web-frame-init.ts index 743dba3adb3cf..b4e8604b05b13 100644 --- a/lib/renderer/web-frame-init.ts +++ b/lib/renderer/web-frame-init.ts @@ -1,5 +1,5 @@ -import { webFrame, WebFrame } from 'electron' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import { webFrame, WebFrame } from 'electron'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; // All keys of WebFrame that extend Function type WebFrameMethod = { @@ -15,6 +15,6 @@ export const webFrameInit = () => { // The TypeScript compiler cannot handle the sheer number of // call signatures here and simply gives up. Incorrect invocations // will be caught by "keyof WebFrameMethod" though. - return (webFrame[method] as any)(...args) - }) -} + return (webFrame[method] as any)(...args); + }); +}; diff --git a/lib/renderer/web-view/guest-view-internal.ts b/lib/renderer/web-view/guest-view-internal.ts index cbb2798283bf8..2d26d1f25a21d 100644 --- a/lib/renderer/web-view/guest-view-internal.ts +++ b/lib/renderer/web-view/guest-view-internal.ts @@ -1,7 +1,7 @@ -import { webFrame, IpcMessageEvent } from 'electron' -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { webFrame, IpcMessageEvent } from 'electron'; +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; -import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl' +import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'; const WEB_VIEW_EVENTS: Record> = { 'load-commit': ['url', 'isMainFrame'], @@ -37,76 +37,76 @@ const WEB_VIEW_EVENTS: Record> = { 'found-in-page': ['result'], 'did-change-theme-color': ['themeColor'], 'update-target-url': ['url'] -} +}; const DEPRECATED_EVENTS: Record = { 'page-title-updated': 'page-title-set' -} +}; const dispatchEvent = function ( webView: WebViewImpl, eventName: string, eventKey: string, ...args: Array ) { if (DEPRECATED_EVENTS[eventName] != null) { - dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args) + dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args); } - const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent + const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent; WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => { - (domEvent as any)[prop] = args[index] - }) + (domEvent as any)[prop] = args[index]; + }); - webView.dispatchEvent(domEvent) + webView.dispatchEvent(domEvent); if (eventName === 'load-commit') { - webView.onLoadCommit(domEvent) + webView.onLoadCommit(domEvent); } else if (eventName === 'focus-change') { - webView.onFocusChange() + webView.onFocusChange(); } -} +}; export function registerEvents (webView: WebViewImpl, viewInstanceId: number) { ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () { - webView.guestInstanceId = undefined - webView.reset() - const domEvent = new Event('destroyed') - webView.dispatchEvent(domEvent) - }) + webView.guestInstanceId = undefined; + webView.reset(); + const domEvent = new Event('destroyed'); + webView.dispatchEvent(domEvent); + }); ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) { - dispatchEvent(webView, eventName, eventName, ...args) - }) + dispatchEvent(webView, eventName, eventName, ...args); + }); ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) { - const domEvent = new Event('ipc-message') as IpcMessageEvent - domEvent.channel = channel - domEvent.args = args + const domEvent = new Event('ipc-message') as IpcMessageEvent; + domEvent.channel = channel; + domEvent.args = args; - webView.dispatchEvent(domEvent) - }) + webView.dispatchEvent(domEvent); + }); } export function deregisterEvents (viewInstanceId: number) { - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`) - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`) - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`) + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`); + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`); + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`); } export function createGuest (params: Record): Promise { - return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params) + return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params); } export function attachGuest ( elementInstanceId: number, guestInstanceId: number, params: Record, contentWindow: Window ) { - const embedderFrameId = (webFrame as ElectronInternal.WebFrameInternal).getWebFrameId(contentWindow) + const embedderFrameId = (webFrame as ElectronInternal.WebFrameInternal).getWebFrameId(contentWindow); if (embedderFrameId < 0) { // this error should not happen. - throw new Error('Invalid embedder frame') + throw new Error('Invalid embedder frame'); } - ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params) + ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params); } export const guestViewInternalModule = { deregisterEvents, createGuest, attachGuest -} +}; diff --git a/lib/renderer/web-view/web-view-attributes.ts b/lib/renderer/web-view/web-view-attributes.ts index 4a6844acea05d..4ce18e9a9d296 100644 --- a/lib/renderer/web-view/web-view-attributes.ts +++ b/lib/renderer/web-view/web-view-attributes.ts @@ -1,15 +1,15 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl' -import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'; +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'; // Helper function to resolve url set in attribute. -const a = document.createElement('a') +const a = document.createElement('a'); const resolveURL = function (url?: string | null) { - if (!url) return '' - a.href = url - return a.href -} + if (!url) return ''; + a.href = url; + return a.href; +}; interface MutationHandler { handleMutation (_oldValue: any, _newValue: any): any; @@ -22,40 +22,40 @@ class WebViewAttribute implements MutationHandler { public ignoreMutation = false; constructor (public name: string, public webViewImpl: WebViewImpl) { - this.name = name - this.value = (webViewImpl.webviewNode as Record)[name] || '' - this.webViewImpl = webViewImpl - this.defineProperty() + this.name = name; + this.value = (webViewImpl.webviewNode as Record)[name] || ''; + this.webViewImpl = webViewImpl; + this.defineProperty(); } // Retrieves and returns the attribute's value. public getValue () { - return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value + return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value; } // Sets the attribute's value. public setValue (value: any) { - this.webViewImpl.webviewNode.setAttribute(this.name, value || '') + this.webViewImpl.webviewNode.setAttribute(this.name, value || ''); } // Changes the attribute's value without triggering its mutation handler. public setValueIgnoreMutation (value: any) { - this.ignoreMutation = true - this.setValue(value) - this.ignoreMutation = false + this.ignoreMutation = true; + this.setValue(value); + this.ignoreMutation = false; } // Defines this attribute as a property on the webview node. public defineProperty () { return Object.defineProperty(this.webViewImpl.webviewNode, this.name, { get: () => { - return this.getValue() + return this.getValue(); }, set: (value) => { - return this.setValue(value) + return this.setValue(value); }, enumerable: true - }) + }); } // Called when the attribute's value changes. @@ -65,14 +65,14 @@ class WebViewAttribute implements MutationHandler { // An attribute that is treated as a Boolean. class BooleanAttribute extends WebViewAttribute { getValue () { - return this.webViewImpl.webviewNode.hasAttribute(this.name) + return this.webViewImpl.webviewNode.hasAttribute(this.name); } setValue (value: boolean) { if (value) { - this.webViewImpl.webviewNode.setAttribute(this.name, '') + this.webViewImpl.webviewNode.setAttribute(this.name, ''); } else { - this.webViewImpl.webviewNode.removeAttribute(this.name) + this.webViewImpl.webviewNode.removeAttribute(this.name); } } } @@ -82,21 +82,21 @@ class PartitionAttribute extends WebViewAttribute { public validPartitionId = true constructor (public webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, webViewImpl); } public handleMutation = (oldValue: any, newValue: any) => { - newValue = newValue || '' + newValue = newValue || ''; // The partition cannot change if the webview has already navigated. if (!this.webViewImpl.beforeFirstNavigation) { - console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_ALREADY_NAVIGATED) - this.setValueIgnoreMutation(oldValue) - return + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_ALREADY_NAVIGATED); + this.setValueIgnoreMutation(oldValue); + return; } if (newValue === 'persist:') { - this.validPartitionId = false - console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE) + this.validPartitionId = false; + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE); } } } @@ -106,26 +106,26 @@ class SrcAttribute extends WebViewAttribute { public observer!: MutationObserver; constructor (public webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, webViewImpl) - this.setupMutationObserver() + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, webViewImpl); + this.setupMutationObserver(); } public getValue () { if (this.webViewImpl.webviewNode.hasAttribute(this.name)) { - return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)) + return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)); } else { - return this.value + return this.value; } } public setValueIgnoreMutation (value: any) { - super.setValueIgnoreMutation(value) + super.setValueIgnoreMutation(value); // takeRecords() is needed to clear queued up src mutations. Without it, it // is possible for this change to get picked up asyncronously by src's // mutation observer |observer|, and then get handled even though we do not // want to handle this mutation. - this.observer.takeRecords() + this.observer.takeRecords(); } public handleMutation = (oldValue: any, newValue: any) => { @@ -136,10 +136,10 @@ class SrcAttribute extends WebViewAttribute { // src attribute changes normally initiate a navigation. We suppress // the next src attribute handler call to avoid reloading the page // on every guest-initiated navigation. - this.setValueIgnoreMutation(oldValue) - return + this.setValueIgnoreMutation(oldValue); + return; } - this.parse() + this.parse(); } // The purpose of this mutation observer is to catch assignment to the src @@ -149,144 +149,144 @@ class SrcAttribute extends WebViewAttribute { public setupMutationObserver () { this.observer = new MutationObserver((mutations) => { for (const mutation of mutations) { - const { oldValue } = mutation - const newValue = this.getValue() + const { oldValue } = mutation; + const newValue = this.getValue(); if (oldValue !== newValue) { - return + return; } - this.handleMutation(oldValue, newValue) + this.handleMutation(oldValue, newValue); } - }) + }); const params = { attributes: true, attributeOldValue: true, attributeFilter: [this.name] - } + }; - this.observer.observe(this.webViewImpl.webviewNode, params) + this.observer.observe(this.webViewImpl.webviewNode, params); } public parse () { if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) { - return + return; } if (this.webViewImpl.guestInstanceId == null) { if (this.webViewImpl.beforeFirstNavigation) { - this.webViewImpl.beforeFirstNavigation = false - this.webViewImpl.createGuest() + this.webViewImpl.beforeFirstNavigation = false; + this.webViewImpl.createGuest(); } - return + return; } // Navigate to |this.src|. - const opts: Record = {} + const opts: Record = {}; - const httpreferrer = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER].getValue() + const httpreferrer = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER].getValue(); if (httpreferrer) { - opts.httpReferrer = httpreferrer + opts.httpReferrer = httpreferrer; } - const useragent = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT].getValue() + const useragent = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT].getValue(); if (useragent) { - opts.userAgent = useragent + opts.userAgent = useragent; } - const guestInstanceId = this.webViewImpl.guestInstanceId - const method = 'loadURL' - const args = [this.getValue(), opts] + const guestInstanceId = this.webViewImpl.guestInstanceId; + const method = 'loadURL'; + const args = [this.getValue(), opts]; - ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args) + ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args); } } // Attribute specifies HTTP referrer. class HttpReferrerAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, webViewImpl); } } // Attribute specifies user agent class UserAgentAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, webViewImpl); } } // Attribute that set preload script. class PreloadAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, webViewImpl); } public getValue () { if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) { - return this.value + return this.value; } - let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)) - const protocol = preload.substr(0, 5) + let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)); + const protocol = preload.substr(0, 5); if (protocol !== 'file:') { - console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE) - preload = '' + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE); + preload = ''; } - return preload + return preload; } } // Attribute that specifies the blink features to be enabled. class BlinkFeaturesAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, webViewImpl); } } // Attribute that specifies the blink features to be disabled. class DisableBlinkFeaturesAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl); } } // Attribute that specifies the web preferences to be enabled. class WebPreferencesAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, webViewImpl); } } class EnableRemoteModuleAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl); } public getValue () { - return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false' + return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false'; } public setValue (value: any) { - this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false') + this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false'); } } // Sets up all of the webview attributes. WebViewImpl.prototype.setupWebViewAttributes = function () { - this.attributes = {} - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION] = new PartitionAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC] = new SrcAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this) -} + this.attributes = {}; + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION] = new PartitionAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC] = new SrcAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this); +}; diff --git a/lib/renderer/web-view/web-view-element.ts b/lib/renderer/web-view/web-view-element.ts index cdb81308e22d2..b885a26da5fa9 100644 --- a/lib/renderer/web-view/web-view-element.ts +++ b/lib/renderer/web-view/web-view-element.ts @@ -8,12 +8,12 @@ // which runs in browserify environment instead of Node environment, all native // modules must be passed from outside, all included files must be plain JS. -import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' -import { WebViewImpl as IWebViewImpl, webViewImplModule } from '@electron/internal/renderer/web-view/web-view-impl' +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'; +import { WebViewImpl as IWebViewImpl, webViewImplModule } from '@electron/internal/renderer/web-view/web-view-impl'; // Return a WebViewElement class that is defined in this context. const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { - const { guestViewInternal, WebViewImpl } = webViewImpl + const { guestViewInternal, WebViewImpl } = webViewImpl; return class WebViewElement extends HTMLElement { public internalInstanceId?: number; @@ -33,45 +33,45 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES - ] + ]; } constructor () { - super() - v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this)) + super(); + v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this)); } connectedCallback () { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (!internal) { - return + return; } if (!internal.elementAttached) { - guestViewInternal.registerEvents(internal, internal.viewInstanceId) - internal.elementAttached = true - internal.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].parse() + guestViewInternal.registerEvents(internal, internal.viewInstanceId); + internal.elementAttached = true; + internal.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].parse(); } } attributeChangedCallback (name: string, oldValue: any, newValue: any) { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (internal) { - internal.handleWebviewAttributeMutation(name, oldValue, newValue) + internal.handleWebviewAttributeMutation(name, oldValue, newValue); } } disconnectedCallback () { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (!internal) { - return + return; } - guestViewInternal.deregisterEvents(internal.viewInstanceId) - internal.elementAttached = false - this.internalInstanceId = 0 - internal.reset() + guestViewInternal.deregisterEvents(internal.viewInstanceId); + internal.elementAttached = false; + this.internalInstanceId = 0; + internal.reset(); } - } -} + }; +}; // Register custom element. const registerWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { @@ -79,40 +79,40 @@ const registerWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeo // eslint-disable-next-line const WebViewElement = defineWebViewElement(v8Util, webViewImpl) as unknown as typeof ElectronInternal.WebViewElement - webViewImpl.setupMethods(WebViewElement) + webViewImpl.setupMethods(WebViewElement); // The customElements.define has to be called in a special scope. - const webFrame = webViewImpl.webFrame as ElectronInternal.WebFrameInternal + const webFrame = webViewImpl.webFrame as ElectronInternal.WebFrameInternal; webFrame.allowGuestViewElementDefinition(window, () => { window.customElements.define('webview', WebViewElement); - (window as any).WebView = WebViewElement + (window as any).WebView = WebViewElement; // Delete the callbacks so developers cannot call them and produce unexpected // behavior. - delete WebViewElement.prototype.connectedCallback - delete WebViewElement.prototype.disconnectedCallback - delete WebViewElement.prototype.attributeChangedCallback + delete WebViewElement.prototype.connectedCallback; + delete WebViewElement.prototype.disconnectedCallback; + delete WebViewElement.prototype.attributeChangedCallback; // Now that |observedAttributes| has been retrieved, we can hide it from // user code as well. // TypeScript is concerned that we're deleting a read-only attribute - delete (WebViewElement as any).observedAttributes - }) -} + delete (WebViewElement as any).observedAttributes; + }); +}; // Prepare to register the element. export const setupWebView = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { - const useCapture = true + const useCapture = true; const listener = (event: Event) => { if (document.readyState === 'loading') { - return + return; } - webViewImpl.setupAttributes() - registerWebViewElement(v8Util, webViewImpl) + webViewImpl.setupAttributes(); + registerWebViewElement(v8Util, webViewImpl); - window.removeEventListener(event.type, listener, useCapture) - } + window.removeEventListener(event.type, listener, useCapture); + }; - window.addEventListener('readystatechange', listener, useCapture) -} + window.addEventListener('readystatechange', listener, useCapture); +}; diff --git a/lib/renderer/web-view/web-view-impl.ts b/lib/renderer/web-view/web-view-impl.ts index e614578278de9..ea26bcbc36677 100644 --- a/lib/renderer/web-view/web-view-impl.ts +++ b/lib/renderer/web-view/web-view-impl.ts @@ -1,21 +1,21 @@ -import * as electron from 'electron' +import * as electron from 'electron'; -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' -import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal' -import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' -import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods' -import { deserialize } from '@electron/internal/common/type-utils' -const { webFrame } = electron +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; +import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal'; +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'; +import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods'; +import { deserialize } from '@electron/internal/common/type-utils'; +const { webFrame } = electron; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); // ID generator. -let nextId = 0 +let nextId = 0; const getNextId = function () { - return ++nextId -} + return ++nextId; +}; // Represents the internal state of the WebView node. export class WebViewImpl { @@ -38,29 +38,29 @@ export class WebViewImpl { constructor (public webviewNode: HTMLElement) { // Create internal iframe element. - this.internalElement = this.createInternalElement() - const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' }) - shadowRoot.innerHTML = '' - this.setupWebViewAttributes() - this.viewInstanceId = getNextId() - shadowRoot.appendChild(this.internalElement) + this.internalElement = this.createInternalElement(); + const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' }); + shadowRoot.innerHTML = ''; + this.setupWebViewAttributes(); + this.viewInstanceId = getNextId(); + shadowRoot.appendChild(this.internalElement); // Provide access to contentWindow. Object.defineProperty(this.webviewNode, 'contentWindow', { get: () => { - return this.internalElement.contentWindow + return this.internalElement.contentWindow; }, enumerable: true - }) + }); } createInternalElement () { - const iframeElement = document.createElement('iframe') - iframeElement.style.flex = '1 1 auto' - iframeElement.style.width = '100%' - iframeElement.style.border = '0' - v8Util.setHiddenValue(iframeElement, 'internal', this) - return iframeElement + const iframeElement = document.createElement('iframe'); + iframeElement.style.flex = '1 1 auto'; + iframeElement.style.width = '100%'; + iframeElement.style.border = '0'; + v8Util.setHiddenValue(iframeElement, 'internal', this); + return iframeElement; } // Resets some state upon reattaching element to the DOM. @@ -72,20 +72,20 @@ export class WebViewImpl { // 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) { - this.guestInstanceId = undefined + this.guestInstanceId = undefined; } - this.beforeFirstNavigation = true - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId = true + this.beforeFirstNavigation = true; + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId = true; // Since attachment swaps a local frame for a remote frame, we need our // internal iframe element to be local again before we can reattach. - const newFrame = this.createInternalElement() - const oldFrame = this.internalElement - this.internalElement = newFrame + const newFrame = this.createInternalElement(); + const oldFrame = this.internalElement; + this.internalElement = newFrame; if (oldFrame && oldFrame.parentNode) { - oldFrame.parentNode.replaceChild(newFrame, oldFrame) + oldFrame.parentNode.replaceChild(newFrame, oldFrame); } } @@ -96,179 +96,179 @@ export class WebViewImpl { // details. handleWebviewAttributeMutation (attributeName: string, oldValue: any, newValue: any) { if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) { - return + return; } // Let the changed attribute handle its own mutation - this.attributes[attributeName].handleMutation(oldValue, newValue) + this.attributes[attributeName].handleMutation(oldValue, newValue); } onElementResize () { - const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent - resizeEvent.newWidth = this.webviewNode.clientWidth - resizeEvent.newHeight = this.webviewNode.clientHeight - this.dispatchEvent(resizeEvent) + const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent; + resizeEvent.newWidth = this.webviewNode.clientWidth; + resizeEvent.newHeight = this.webviewNode.clientHeight; + this.dispatchEvent(resizeEvent); } createGuest () { guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => { - this.attachGuestInstance(guestInstanceId) - }) + this.attachGuestInstance(guestInstanceId); + }); } dispatchEvent (webViewEvent: Electron.Event) { - this.webviewNode.dispatchEvent(webViewEvent) + this.webviewNode.dispatchEvent(webViewEvent); } // Adds an 'on' property on the webview, which can be used to set/unset // an event handler. setupEventProperty (eventName: string) { - const propertyName = `on${eventName.toLowerCase()}` + const propertyName = `on${eventName.toLowerCase()}`; return Object.defineProperty(this.webviewNode, propertyName, { get: () => { - return this.on[propertyName] + return this.on[propertyName]; }, set: (value) => { if (this.on[propertyName]) { - this.webviewNode.removeEventListener(eventName, this.on[propertyName]) + this.webviewNode.removeEventListener(eventName, this.on[propertyName]); } - this.on[propertyName] = value + this.on[propertyName] = value; if (value) { - return this.webviewNode.addEventListener(eventName, value) + return this.webviewNode.addEventListener(eventName, value); } }, enumerable: true - }) + }); } // Updates state upon loadcommit. onLoadCommit (webViewEvent: ElectronInternal.WebViewEvent) { - const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC) - const newValue = webViewEvent.url + const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC); + const newValue = webViewEvent.url; if (webViewEvent.isMainFrame && (oldValue !== newValue)) { // Touching the src attribute triggers a navigation. To avoid // triggering a page reload on every guest-initiated navigation, // we do not handle this mutation. - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue); } } // Emits focus/blur events. onFocusChange () { - const hasFocus = document.activeElement === this.webviewNode + const hasFocus = document.activeElement === this.webviewNode; if (hasFocus !== this.hasFocus) { - this.hasFocus = hasFocus - this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur')) + this.hasFocus = hasFocus; + this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur')); } } onAttach (storagePartitionId: number) { - return this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].setValue(storagePartitionId) + return this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].setValue(storagePartitionId); } buildParams () { const params: Record = { instanceId: this.viewInstanceId, userAgentOverride: this.userAgentOverride - } + }; for (const attributeName in this.attributes) { if (Object.prototype.hasOwnProperty.call(this.attributes, attributeName)) { - params[attributeName] = this.attributes[attributeName].getValue() + params[attributeName] = this.attributes[attributeName].getValue(); } } - return params + return params; } attachGuestInstance (guestInstanceId: number) { if (!this.elementAttached) { // The element could be detached before we got response from browser. - return + return; } - this.internalInstanceId = getNextId() - this.guestInstanceId = guestInstanceId + this.internalInstanceId = getNextId(); + this.guestInstanceId = guestInstanceId; guestViewInternal.attachGuest( this.internalInstanceId, this.guestInstanceId, this.buildParams(), this.internalElement.contentWindow! - ) + ); // ResizeObserver is a browser global not recognized by "standard". /* globals ResizeObserver */ // TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not // even documented. - this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this)) - this.resizeObserver.observe(this.internalElement) + this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this)); + this.resizeObserver.observe(this.internalElement); } } export const setupAttributes = () => { - require('@electron/internal/renderer/web-view/web-view-attributes') -} + require('@electron/internal/renderer/web-view/web-view-attributes'); +}; // I wish eslint wasn't so stupid, but it is // eslint-disable-next-line export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElement) => { WebViewElement.prototype.getWebContentsId = function () { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (!internal.guestInstanceId) { - throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.') + throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.'); } - return internal.guestInstanceId - } + return internal.guestInstanceId; + }; // Focusing the webview should move page focus to the underlying iframe. WebViewElement.prototype.focus = function () { - this.contentWindow.focus() - } + this.contentWindow.focus(); + }; // Forward proto.foo* method calls to WebViewImpl.foo*. const createBlockHandler = function (method: string) { return function (this: ElectronInternal.WebViewElement, ...args: Array) { - return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args) - } - } + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args); + }; + }; for (const method of syncMethods) { - (WebViewElement.prototype as Record)[method] = createBlockHandler(method) + (WebViewElement.prototype as Record)[method] = createBlockHandler(method); } const createNonBlockHandler = function (method: string) { return function (this: ElectronInternal.WebViewElement, ...args: Array) { - return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args) - } - } + return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args); + }; + }; for (const method of asyncMethods) { - (WebViewElement.prototype as Record)[method] = createNonBlockHandler(method) + (WebViewElement.prototype as Record)[method] = createNonBlockHandler(method); } WebViewElement.prototype.capturePage = async function (...args) { - return deserialize(await ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', this.getWebContentsId(), args)) - } + return deserialize(await ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', this.getWebContentsId(), args)); + }; const createPropertyGetter = function (property: string) { return function (this: ElectronInternal.WebViewElement) { - return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', this.getWebContentsId(), property) - } - } + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', this.getWebContentsId(), property); + }; + }; const createPropertySetter = function (property: string) { return function (this: ElectronInternal.WebViewElement, arg: any) { - return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', this.getWebContentsId(), property, arg) - } - } + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', this.getWebContentsId(), property, arg); + }; + }; for (const property of properties) { Object.defineProperty(WebViewElement.prototype, property, { get: createPropertyGetter(property) as any, set: createPropertySetter(property) - }) + }); } -} +}; export const webViewImplModule = { setupAttributes, @@ -276,4 +276,4 @@ export const webViewImplModule = { guestViewInternal, webFrame, WebViewImpl -} +}; diff --git a/lib/renderer/web-view/web-view-init.ts b/lib/renderer/web-view/web-view-init.ts index 621ba569845df..95d334bafd724 100644 --- a/lib/renderer/web-view/web-view-init.ts +++ b/lib/renderer/web-view/web-view-init.ts @@ -1,18 +1,18 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); function handleFocusBlur (guestInstanceId: number) { // Note that while Chromium content APIs have observer for focus/blur, they // unfortunately do not work for webview. window.addEventListener('focus', () => { - ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, guestInstanceId) - }) + ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, guestInstanceId); + }); window.addEventListener('blur', () => { - ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, guestInstanceId) - }) + ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, guestInstanceId); + }); } export function webViewInit ( @@ -20,17 +20,17 @@ export function webViewInit ( ) { // Don't allow recursive ``. if (webviewTag && guestInstanceId == null) { - const { webViewImplModule } = require('@electron/internal/renderer/web-view/web-view-impl') + const { webViewImplModule } = require('@electron/internal/renderer/web-view/web-view-impl'); if (contextIsolation) { - v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule) + v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule); } else { - const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') - setupWebView(v8Util, webViewImplModule) + const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element'); + setupWebView(v8Util, webViewImplModule); } } if (guestInstanceId) { // Report focus/blur events of webview to browser. - handleFocusBlur(guestInstanceId) + handleFocusBlur(guestInstanceId); } } diff --git a/lib/renderer/webpack-provider.ts b/lib/renderer/webpack-provider.ts index 6ed060145e077..829320cd756e4 100644 --- a/lib/renderer/webpack-provider.ts +++ b/lib/renderer/webpack-provider.ts @@ -7,12 +7,12 @@ // Rip global off of window (which is also global) so that webpack doesn't // auto replace it with a looped reference to this file -const _global = (self as any || window as any).global as NodeJS.Global -const process = _global.process -const Buffer = _global.Buffer +const _global = (self as any || window as any).global as NodeJS.Global; +const process = _global.process; +const Buffer = _global.Buffer; export { _global, process, Buffer -} +}; diff --git a/lib/renderer/window-setup.ts b/lib/renderer/window-setup.ts index 184d6263946ea..0ed0fadd4686b 100644 --- a/lib/renderer/window-setup.ts +++ b/lib/renderer/window-setup.ts @@ -1,5 +1,5 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; // This file implements the following APIs: // - window.history.back() @@ -19,29 +19,29 @@ import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-inte // - document.visibilityState // Helper function to resolve relative url. -const resolveURL = (url: string, base: string) => new URL(url, base).href +const resolveURL = (url: string, base: string) => new URL(url, base).href; // Use this method to ensure values expected as strings in the main process // are convertible to strings in the renderer process. This ensures exceptions // converting values to strings are thrown in this process. const toString = (value: any) => { - return value != null ? `${value}` : value -} + return value != null ? `${value}` : value; +}; -const windowProxies = new Map() +const windowProxies = new Map(); const getOrCreateProxy = (guestId: number) => { - let proxy = windowProxies.get(guestId) + let proxy = windowProxies.get(guestId); if (proxy == null) { - proxy = new BrowserWindowProxy(guestId) - windowProxies.set(guestId, proxy) + proxy = new BrowserWindowProxy(guestId); + windowProxies.set(guestId, proxy); } - return proxy -} + return proxy; +}; const removeProxy = (guestId: number) => { - windowProxies.delete(guestId) -} + windowProxies.delete(guestId); +}; type LocationProperties = 'hash' | 'href' | 'host' | 'hostname' | 'origin' | 'pathname' | 'port' | 'protocol' | 'search' @@ -65,52 +65,52 @@ class LocationProxy { private static ProxyProperty (target: LocationProxy, propertyKey: LocationProperties) { Object.defineProperty(target, propertyKey, { get: function (this: LocationProxy): T | string { - const guestURL = this.getGuestURL() - const value = guestURL ? guestURL[propertyKey] : '' - return value === undefined ? '' : value + const guestURL = this.getGuestURL(); + const value = guestURL ? guestURL[propertyKey] : ''; + return value === undefined ? '' : value; }, set: function (this: LocationProxy, newVal: T) { - const guestURL = this.getGuestURL() + const guestURL = this.getGuestURL(); if (guestURL) { // TypeScript doesn't want us to assign to read-only variables. // It's right, that's bad, but we're doing it anway. - (guestURL as any)[propertyKey] = newVal + (guestURL as any)[propertyKey] = newVal; - return this._invokeWebContentsMethod('loadURL', guestURL.toString()) + return this._invokeWebContentsMethod('loadURL', guestURL.toString()); } } - }) + }); } constructor (guestId: number) { // eslint will consider the constructor "useless" // unless we assign them in the body. It's fine, that's what // TS would do anyway. - this.guestId = guestId - this.getGuestURL = this.getGuestURL.bind(this) + this.guestId = guestId; + this.getGuestURL = this.getGuestURL.bind(this); } public toString (): string { - return this.href + return this.href; } private getGuestURL (): URL | null { - const urlString = this._invokeWebContentsMethodSync('getURL') as string + const urlString = this._invokeWebContentsMethodSync('getURL') as string; try { - return new URL(urlString) + return new URL(urlString); } catch (e) { - console.error('LocationProxy: failed to parse string', urlString, e) + console.error('LocationProxy: failed to parse string', urlString, e); } - return null + return null; } private _invokeWebContentsMethod (method: string, ...args: any[]) { - return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args) + return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args); } private _invokeWebContentsMethodSync (method: string, ...args: any[]) { - return ipcRendererUtils.invokeSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args) + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args); } } @@ -124,54 +124,54 @@ class BrowserWindowProxy { // so for now, we'll have to make do with an "any" in the mix. // https://github.com/Microsoft/TypeScript/issues/2521 public get location (): LocationProxy | any { - return this._location + return this._location; } public set location (url: string | any) { - url = resolveURL(url, this.location.href) - this._invokeWebContentsMethod('loadURL', url) + url = resolveURL(url, this.location.href); + this._invokeWebContentsMethod('loadURL', url); } constructor (guestId: number) { - this.guestId = guestId - this._location = new LocationProxy(guestId) + this.guestId = guestId; + this._location = new LocationProxy(guestId); ipcRendererInternal.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => { - removeProxy(guestId) - this.closed = true - }) + removeProxy(guestId); + this.closed = true; + }); } public close () { - this._invokeWindowMethod('destroy') + this._invokeWindowMethod('destroy'); } public focus () { - this._invokeWindowMethod('focus') + this._invokeWindowMethod('focus'); } public blur () { - this._invokeWindowMethod('blur') + this._invokeWindowMethod('blur'); } public print () { - this._invokeWebContentsMethod('print') + this._invokeWebContentsMethod('print'); } public postMessage (message: any, targetOrigin: string) { - ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin) + ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin); } public eval (code: string) { - this._invokeWebContentsMethod('executeJavaScript', code) + this._invokeWebContentsMethod('executeJavaScript', code); } private _invokeWindowMethod (method: string, ...args: any[]) { - return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, method, ...args) + return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, method, ...args); } private _invokeWebContentsMethod (method: string, ...args: any[]) { - return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args) + return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args); } } @@ -181,33 +181,33 @@ export const windowSetup = ( if (!process.sandboxed && guestInstanceId == null) { // Override default window.close. window.close = function () { - ipcRendererInternal.send('ELECTRON_BROWSER_WINDOW_CLOSE') - } + ipcRendererInternal.send('ELECTRON_BROWSER_WINDOW_CLOSE'); + }; } if (!usesNativeWindowOpen) { // Make the browser window or guest view emit "new-window" event. (window as any).open = function (url?: string, frameName?: string, features?: string) { if (url != null && url !== '') { - url = resolveURL(url, location.href) + url = resolveURL(url, location.href); } - const guestId = ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features)) + const guestId = ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features)); if (guestId != null) { - return getOrCreateProxy(guestId) + return getOrCreateProxy(guestId); } else { - return null + return null; } - } + }; } if (openerId != null) { - window.opener = getOrCreateProxy(openerId) + window.opener = getOrCreateProxy(openerId); } // But we do not support prompt(). window.prompt = function () { - throw new Error('prompt() is and will not be supported.') - } + throw new Error('prompt() is and will not be supported.'); + }; if (!usesNativeWindowOpen || openerId != null) { ipcRendererInternal.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function ( @@ -219,36 +219,36 @@ export const windowSetup = ( // Why any? We can't construct a MessageEvent and we can't // use `as MessageEvent` because you're not supposed to override // data, origin, and source - const event: any = document.createEvent('Event') - event.initEvent('message', false, false) + const event: any = document.createEvent('Event'); + event.initEvent('message', false, false); - event.data = message - event.origin = sourceOrigin - event.source = getOrCreateProxy(sourceId) + event.data = message; + event.origin = sourceOrigin; + event.source = getOrCreateProxy(sourceId); - window.dispatchEvent(event as MessageEvent) - }) + window.dispatchEvent(event as MessageEvent); + }); } if (!process.sandboxed && !rendererProcessReuseEnabled) { window.history.back = function () { - ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK') - } + ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK'); + }; window.history.forward = function () { - ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD') - } + ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD'); + }; window.history.go = function (offset: number) { - ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset) - } + ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset); + }; Object.defineProperty(window.history, 'length', { get: function () { - return ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH') + return ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH'); }, set () {} - }) + }); } if (guestInstanceId != null) { @@ -259,27 +259,27 @@ export const windowSetup = ( // Note that this results in duplicate visibilitychange events (since // Chromium also fires them) and potentially incorrect visibility change. // We should reconsider this decision for Electron 2.0. - let cachedVisibilityState = isHiddenPage ? 'hidden' : 'visible' + let cachedVisibilityState = isHiddenPage ? 'hidden' : 'visible'; // Subscribe to visibilityState changes. ipcRendererInternal.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (_event, visibilityState: VisibilityState) { if (cachedVisibilityState !== visibilityState) { - cachedVisibilityState = visibilityState - document.dispatchEvent(new Event('visibilitychange')) + cachedVisibilityState = visibilityState; + document.dispatchEvent(new Event('visibilitychange')); } - }) + }); // Make document.hidden and document.visibilityState return the correct value. Object.defineProperty(document, 'hidden', { get: function () { - return cachedVisibilityState !== 'visible' + return cachedVisibilityState !== 'visible'; } - }) + }); Object.defineProperty(document, 'visibilityState', { get: function () { - return cachedVisibilityState + return cachedVisibilityState; } - }) + }); } -} +}; diff --git a/lib/sandboxed_renderer/api/exports/electron.ts b/lib/sandboxed_renderer/api/exports/electron.ts index fd0695852c359..f63029878eb06 100644 --- a/lib/sandboxed_renderer/api/exports/electron.ts +++ b/lib/sandboxed_renderer/api/exports/electron.ts @@ -1,6 +1,6 @@ -import { defineProperties } from '@electron/internal/common/define-properties' -import { moduleList } from '@electron/internal/sandboxed_renderer/api/module-list' +import { defineProperties } from '@electron/internal/common/define-properties'; +import { moduleList } from '@electron/internal/sandboxed_renderer/api/module-list'; -module.exports = {} +module.exports = {}; -defineProperties(module.exports, moduleList) +defineProperties(module.exports, moduleList); diff --git a/lib/sandboxed_renderer/api/module-list.ts b/lib/sandboxed_renderer/api/module-list.ts index 228d4bc7651ae..85c0919538357 100644 --- a/lib/sandboxed_renderer/api/module-list.ts +++ b/lib/sandboxed_renderer/api/module-list.ts @@ -1,4 +1,4 @@ -const features = process.electronBinding('features') +const features = process.electronBinding('features'); export const moduleList: ElectronInternal.ModuleEntry[] = [ { @@ -27,18 +27,18 @@ export const moduleList: ElectronInternal.ModuleEntry[] = [ loader: () => require('@electron/internal/common/api/deprecate'), private: true } -] +]; if (features.isDesktopCapturerEnabled()) { moduleList.push({ name: 'desktopCapturer', loader: () => require('@electron/internal/renderer/api/desktop-capturer') - }) + }); } if (features.isRemoteModuleEnabled() && process.isRemoteModuleEnabled) { moduleList.push({ name: 'remote', loader: () => require('@electron/internal/renderer/api/remote') - }) + }); } diff --git a/lib/sandboxed_renderer/init.js b/lib/sandboxed_renderer/init.js index b49831a64ce29..d5c39674f0b2c 100644 --- a/lib/sandboxed_renderer/init.js +++ b/lib/sandboxed_renderer/init.js @@ -1,33 +1,33 @@ -'use strict' +'use strict'; /* eslint no-eval: "off" */ /* global binding, Buffer */ -const events = require('events') -const { EventEmitter } = events +const events = require('events'); +const { EventEmitter } = events; -process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(binding.get, 'renderer') +process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(binding.get, 'renderer'); -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); // Expose Buffer shim as a hidden value. This is used by C++ code to // deserialize Buffer instances sent from browser process. -v8Util.setHiddenValue(global, 'Buffer', Buffer) +v8Util.setHiddenValue(global, 'Buffer', Buffer); // The `lib/renderer/api/ipc-renderer.ts` module looks for the ipc object in the // "ipc" hidden value -v8Util.setHiddenValue(global, 'ipc', new EventEmitter()) +v8Util.setHiddenValue(global, 'ipc', new EventEmitter()); // The `lib/renderer/ipc-renderer-internal.ts` module looks for the ipc object in the // "ipc-internal" hidden value -v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter()) +v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter()); // The process object created by webpack is not an event emitter, fix it so // the API is more compatible with non-sandboxed renderers. for (const prop of Object.keys(EventEmitter.prototype)) { if (Object.prototype.hasOwnProperty.call(process, prop)) { - delete process[prop] + delete process[prop]; } } -Object.setPrototypeOf(process, EventEmitter.prototype) +Object.setPrototypeOf(process, EventEmitter.prototype); -const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') +const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); +const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); const { contentScripts, @@ -37,128 +37,128 @@ const { guestInstanceId, openerId, process: processProps -} = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_SANDBOX_LOAD') +} = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_SANDBOX_LOAD'); -process.isRemoteModuleEnabled = isRemoteModuleEnabled +process.isRemoteModuleEnabled = isRemoteModuleEnabled; // The electron module depends on process.electronBinding -const electron = require('electron') +const electron = require('electron'); const loadedModules = new Map([ ['electron', electron], ['events', events], ['timers', require('timers')], ['url', require('url')] -]) +]); // ElectronApiServiceImpl will look for the "ipcNative" hidden object when // invoking the 'onMessage' callback. v8Util.setHiddenValue(global, 'ipcNative', { onMessage (internal, channel, ports, args, senderId) { - const sender = internal ? ipcRendererInternal : electron.ipcRenderer - sender.emit(channel, { sender, senderId, ports }, ...args) + const sender = internal ? ipcRendererInternal : electron.ipcRenderer; + sender.emit(channel, { sender, senderId, ports }, ...args); } -}) +}); // AtomSandboxedRendererClient will look for the "lifecycle" hidden object when v8Util.setHiddenValue(global, 'lifecycle', { onLoaded () { - process.emit('loaded') + process.emit('loaded'); }, onExit () { - process.emit('exit') + process.emit('exit'); }, onDocumentStart () { - process.emit('document-start') + process.emit('document-start'); }, onDocumentEnd () { - process.emit('document-end') + process.emit('document-end'); } -}) +}); -const { webFrameInit } = require('@electron/internal/renderer/web-frame-init') -webFrameInit() +const { webFrameInit } = require('@electron/internal/renderer/web-frame-init'); +webFrameInit(); // Pass different process object to the preload script(which should not have // access to things like `process.electronBinding`). -const preloadProcess = new EventEmitter() +const preloadProcess = new EventEmitter(); -Object.assign(preloadProcess, binding.process) -Object.assign(preloadProcess, processProps) +Object.assign(preloadProcess, binding.process); +Object.assign(preloadProcess, processProps); -Object.assign(process, binding.process) -Object.assign(process, processProps) +Object.assign(process, binding.process); +Object.assign(process, processProps); Object.defineProperty(preloadProcess, 'noDeprecation', { get () { - return process.noDeprecation + return process.noDeprecation; }, set (value) { - process.noDeprecation = value + process.noDeprecation = value; } -}) +}); -process.on('loaded', () => preloadProcess.emit('loaded')) -process.on('exit', () => preloadProcess.emit('exit')) -process.on('document-start', () => preloadProcess.emit('document-start')) -process.on('document-end', () => preloadProcess.emit('document-end')) +process.on('loaded', () => preloadProcess.emit('loaded')); +process.on('exit', () => preloadProcess.emit('exit')); +process.on('document-start', () => preloadProcess.emit('document-start')); +process.on('document-end', () => preloadProcess.emit('document-end')); // This is the `require` function that will be visible to the preload script function preloadRequire (module) { if (loadedModules.has(module)) { - return loadedModules.get(module) + return loadedModules.get(module); } - throw new Error(`module not found: ${module}`) + throw new Error(`module not found: ${module}`); } // Process command line arguments. -const { hasSwitch } = process.electronBinding('command_line') +const { hasSwitch } = process.electronBinding('command_line'); -const contextIsolation = hasSwitch('context-isolation') -const isHiddenPage = hasSwitch('hidden-page') -const rendererProcessReuseEnabled = hasSwitch('disable-electron-site-instance-overrides') -const usesNativeWindowOpen = true +const contextIsolation = hasSwitch('context-isolation'); +const isHiddenPage = hasSwitch('hidden-page'); +const rendererProcessReuseEnabled = hasSwitch('disable-electron-site-instance-overrides'); +const usesNativeWindowOpen = true; // The arguments to be passed to isolated world. -const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled } +const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled }; switch (window.location.protocol) { case 'devtools:': { // Override some inspector APIs. - require('@electron/internal/renderer/inspector') - break + require('@electron/internal/renderer/inspector'); + break; } case 'chrome-extension:': { // Inject the chrome.* APIs that chrome extensions require if (!process.electronBinding('features').isExtensionsEnabled()) { - require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window) + require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window); } - break + break; } case 'chrome': { - break + break; } default: { // Override default web functions. - const { windowSetup } = require('@electron/internal/renderer/window-setup') - windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled) + const { windowSetup } = require('@electron/internal/renderer/window-setup'); + windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled); // Inject content scripts. if (!process.electronBinding('features').isExtensionsEnabled()) { - require('@electron/internal/renderer/content-scripts-injector')(contentScripts) + require('@electron/internal/renderer/content-scripts-injector')(contentScripts); } } } // Load webview tag implementation. if (process.isMainFrame) { - const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') - webViewInit(contextIsolation, isWebViewTagEnabled, guestInstanceId) + const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init'); + webViewInit(contextIsolation, isWebViewTagEnabled, guestInstanceId); } // Pass the arguments to isolatedWorld. if (contextIsolation) { - v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs) + v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs); } // Wrap the script into a function executed in global scope. It won't have @@ -171,32 +171,32 @@ if (contextIsolation) { function runPreloadScript (preloadSrc) { const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate, clearImmediate, exports) { ${preloadSrc} - })` + })`; // eval in window scope - const preloadFn = binding.createPreloadScript(preloadWrapperSrc) - const { setImmediate, clearImmediate } = require('timers') + const preloadFn = binding.createPreloadScript(preloadWrapperSrc); + const { setImmediate, clearImmediate } = require('timers'); - preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate, {}) + preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate, {}); } for (const { preloadPath, preloadSrc, preloadError } of preloadScripts) { try { if (preloadSrc) { - runPreloadScript(preloadSrc) + runPreloadScript(preloadSrc); } else if (preloadError) { - throw preloadError + throw preloadError; } } catch (error) { - console.error(`Unable to load preload script: ${preloadPath}`) - console.error(error) + console.error(`Unable to load preload script: ${preloadPath}`); + console.error(error); - ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadPath, error) + ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadPath, error); } } // Warn about security issues if (process.isMainFrame) { - const { securityWarnings } = require('@electron/internal/renderer/security-warnings') - securityWarnings() + const { securityWarnings } = require('@electron/internal/renderer/security-warnings'); + securityWarnings(); } diff --git a/lib/worker/init.js b/lib/worker/init.js index 3a6f2eb2bee29..05a6443693804 100644 --- a/lib/worker/init.js +++ b/lib/worker/init.js @@ -1,36 +1,36 @@ -'use strict' +'use strict'; -const path = require('path') -const Module = require('module') +const path = require('path'); +const Module = require('module'); // We modified the original process.argv to let node.js load the // init.js, we need to restore it here. -process.argv.splice(1, 1) +process.argv.splice(1, 1); // Clear search paths. -require('../common/reset-search-paths') +require('../common/reset-search-paths'); // Import common settings. -require('@electron/internal/common/init') +require('@electron/internal/common/init'); // Export node bindings to global. const { makeRequireFunction } = __non_webpack_require__('internal/modules/cjs/helpers') // eslint-disable-line -global.module = new Module('electron/js2c/worker_init') -global.require = makeRequireFunction(global.module) +global.module = new Module('electron/js2c/worker_init'); +global.require = makeRequireFunction(global.module); // Set the __filename to the path of html file if it is file: protocol. if (self.location.protocol === 'file:') { - const pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.pathname - global.__filename = path.normalize(decodeURIComponent(pathname)) - global.__dirname = path.dirname(global.__filename) + const pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.pathname; + global.__filename = path.normalize(decodeURIComponent(pathname)); + global.__dirname = path.dirname(global.__filename); // Set module's filename so relative require can work as expected. - global.module.filename = global.__filename + global.module.filename = global.__filename; // Also search for module under the html file. - global.module.paths = Module._nodeModulePaths(global.__dirname) + global.module.paths = Module._nodeModulePaths(global.__dirname); } else { // For backwards compatibility we fake these two paths here - global.__filename = path.join(process.resourcesPath, 'electron.asar', 'worker', 'init.js') - global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'worker') + global.__filename = path.join(process.resourcesPath, 'electron.asar', 'worker', 'init.js'); + global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'worker'); } diff --git a/script/codesign/gen-trust.js b/script/codesign/gen-trust.js index ed3ab1f85bc6d..9dca60aab0b89 100644 --- a/script/codesign/gen-trust.js +++ b/script/codesign/gen-trust.js @@ -1,38 +1,38 @@ -const cp = require('child_process') -const fs = require('fs') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); -const certificatePath = process.argv[2] -const outPath = process.argv[3] -const templatePath = path.resolve(__dirname, 'trust.xml') +const certificatePath = process.argv[2]; +const outPath = process.argv[3]; +const templatePath = path.resolve(__dirname, 'trust.xml'); -const template = fs.readFileSync(templatePath, 'utf8') +const template = fs.readFileSync(templatePath, 'utf8'); -const fingerprintResult = cp.spawnSync('openssl', ['x509', '-noout', '-fingerprint', '-sha1', '-in', certificatePath]) +const fingerprintResult = cp.spawnSync('openssl', ['x509', '-noout', '-fingerprint', '-sha1', '-in', certificatePath]); if (fingerprintResult.status !== 0) { - console.error(fingerprintResult.stderr.toString()) - process.exit(1) + console.error(fingerprintResult.stderr.toString()); + process.exit(1); } -const fingerprint = fingerprintResult.stdout.toString().replace(/^SHA1 Fingerprint=/, '').replace(/:/g, '').trim() +const fingerprint = fingerprintResult.stdout.toString().replace(/^SHA1 Fingerprint=/, '').replace(/:/g, '').trim(); -const serialResult = cp.spawnSync('openssl', ['x509', '-serial', '-noout', '-in', certificatePath]) +const serialResult = cp.spawnSync('openssl', ['x509', '-serial', '-noout', '-in', certificatePath]); if (serialResult.status !== 0) { - console.error(serialResult.stderr.toString()) - process.exit(1) + console.error(serialResult.stderr.toString()); + process.exit(1); } -let serialHex = serialResult.stdout.toString().replace(/^serial=/, '').trim() +let serialHex = serialResult.stdout.toString().replace(/^serial=/, '').trim(); // Pad the serial number out to 18 hex chars while (serialHex.length < 18) { - serialHex = `0${serialHex}` + serialHex = `0${serialHex}`; } -const serialB64 = Buffer.from(serialHex, 'hex').toString('base64') +const serialB64 = Buffer.from(serialHex, 'hex').toString('base64'); const trust = template .replace(/{{FINGERPRINT}}/g, fingerprint) - .replace(/{{SERIAL_BASE64}}/g, serialB64) + .replace(/{{SERIAL_BASE64}}/g, serialB64); -fs.writeFileSync(outPath, trust) +fs.writeFileSync(outPath, trust); -console.log('Generated Trust Settings') +console.log('Generated Trust Settings'); diff --git a/script/doc-only-change.js b/script/doc-only-change.js index 418e9fb3b06ac..13174f513d019 100644 --- a/script/doc-only-change.js +++ b/script/doc-only-change.js @@ -1,18 +1,18 @@ -const args = require('minimist')(process.argv.slice(2)) -const octokit = require('@octokit/rest')() -const path = require('path') +const args = require('minimist')(process.argv.slice(2)); +const octokit = require('@octokit/rest')(); +const path = require('path'); -const SOURCE_ROOT = path.normalize(path.dirname(__dirname)) +const SOURCE_ROOT = path.normalize(path.dirname(__dirname)); async function checkIfDocOnlyChange () { if (args.prNumber || args.prBranch || args.prURL) { try { - let pullRequestNumber = args.prNumber + let pullRequestNumber = args.prNumber; if (!pullRequestNumber || isNaN(pullRequestNumber)) { if (args.prURL) { // CircleCI doesn't provide the PR number for branch builds, but it does provide the PR URL - const pullRequestParts = args.prURL.split('/') - pullRequestNumber = pullRequestParts[pullRequestParts.length - 1] + const pullRequestParts = args.prURL.split('/'); + pullRequestNumber = pullRequestParts[pullRequestParts.length - 1]; } else if (args.prBranch) { // AppVeyor doesn't provide a PR number for branch builds - figure it out from the branch const prsForBranch = await octokit.pulls.list({ @@ -20,39 +20,39 @@ async function checkIfDocOnlyChange () { repo: 'electron', state: 'open', head: `electron:${args.prBranch}` - }) + }); if (prsForBranch.data.length === 1) { - pullRequestNumber = prsForBranch.data[0].number + pullRequestNumber = prsForBranch.data[0].number; } else { // If there are 0 PRs or more than one PR on a branch, just assume that this is more than a doc change - process.exit(1) + process.exit(1); } } } const filesChanged = await octokit.pulls.listFiles({ owner: 'electron', repo: 'electron', pull_number: pullRequestNumber - }) + }); const nonDocChange = filesChanged.data.find((fileInfo) => { - const fileDirs = fileInfo.filename.split('/') + const fileDirs = fileInfo.filename.split('/'); if (fileDirs[0] !== 'docs') { - return true + return true; } - }) + }); if (nonDocChange) { - process.exit(1) + process.exit(1); } else { - process.exit(0) + process.exit(0); } } catch (ex) { - console.error('Error getting list of files changed: ', ex) - process.exit(-1) + console.error('Error getting list of files changed: ', ex); + process.exit(-1); } } else { console.error(`Check if only the docs were changed for a commit. - Usage: doc-only-change.js --prNumber=PR_NUMBER || --prBranch=PR_BRANCH || --prURL=PR_URL`) - process.exit(-1) + Usage: doc-only-change.js --prNumber=PR_NUMBER || --prBranch=PR_BRANCH || --prURL=PR_URL`); + process.exit(-1); } } -checkIfDocOnlyChange() +checkIfDocOnlyChange(); diff --git a/script/download-circleci-artifacts.js b/script/download-circleci-artifacts.js index 2266bfd2e6c89..9771d9661ca54 100644 --- a/script/download-circleci-artifacts.js +++ b/script/download-circleci-artifacts.js @@ -1,38 +1,38 @@ -const args = require('minimist')(process.argv.slice(2)) -const nugget = require('nugget') -const request = require('request') +const args = require('minimist')(process.argv.slice(2)); +const nugget = require('nugget'); +const request = require('request'); async function makeRequest (requestOptions, parseResponse) { return new Promise((resolve, reject) => { request(requestOptions, (err, res, body) => { if (!err && res.statusCode >= 200 && res.statusCode < 300) { if (parseResponse) { - const build = JSON.parse(body) - resolve(build) + const build = JSON.parse(body); + resolve(build); } else { - resolve(body) + resolve(body); } } else { if (args.verbose) { - console.error('Error occurred while requesting:', requestOptions.url) + console.error('Error occurred while requesting:', requestOptions.url); if (parseResponse) { try { - console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions) + console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions); } catch (err) { - console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions) + console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions); } } else { - console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions) + console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions); } } - reject(err) + reject(err); } - }) - }) + }); + }); } async function downloadArtifact (name, buildNum, dest) { - const circleArtifactUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/${args.buildNum}/artifacts?circle-token=${process.env.CIRCLE_TOKEN}` + const circleArtifactUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/${args.buildNum}/artifacts?circle-token=${process.env.CIRCLE_TOKEN}`; const artifacts = await makeRequest({ method: 'GET', url: circleArtifactUrl, @@ -42,47 +42,47 @@ async function downloadArtifact (name, buildNum, dest) { } }, true).catch(err => { if (args.verbose) { - console.log('Error calling CircleCI:', err) + console.log('Error calling CircleCI:', err); } else { - console.error('Error calling CircleCI to get artifact details') + console.error('Error calling CircleCI to get artifact details'); } - }) + }); const artifactToDownload = artifacts.find(artifact => { - return (artifact.path === name) - }) + return (artifact.path === name); + }); if (!artifactToDownload) { - console.log(`Could not find artifact called ${name} to download for build #${buildNum}.`) - process.exit(1) + console.log(`Could not find artifact called ${name} to download for build #${buildNum}.`); + process.exit(1); } else { - console.log(`Downloading ${artifactToDownload.url}.`) - let downloadError = false + console.log(`Downloading ${artifactToDownload.url}.`); + let downloadError = false; await downloadWithRetry(artifactToDownload.url, dest).catch(err => { if (args.verbose) { - console.log(`${artifactToDownload.url} could not be successfully downloaded. Error was:`, err) + console.log(`${artifactToDownload.url} could not be successfully downloaded. Error was:`, err); } else { - console.log(`${artifactToDownload.url} could not be successfully downloaded.`) + console.log(`${artifactToDownload.url} could not be successfully downloaded.`); } - downloadError = true - }) + downloadError = true; + }); if (!downloadError) { - console.log(`Successfully downloaded ${name}.`) + console.log(`Successfully downloaded ${name}.`); } } } async function downloadWithRetry (url, directory) { - let lastError - const downloadURL = `${url}?circle-token=${process.env.CIRCLE_TOKEN}` + let lastError; + const downloadURL = `${url}?circle-token=${process.env.CIRCLE_TOKEN}`; for (let i = 0; i < 5; i++) { - console.log(`Attempting to download ${url} - attempt #${(i + 1)}`) + console.log(`Attempting to download ${url} - attempt #${(i + 1)}`); try { - return await downloadFile(downloadURL, directory) + return await downloadFile(downloadURL, directory); } catch (err) { - lastError = err - await new Promise((resolve, reject) => setTimeout(resolve, 30000)) + lastError = err; + await new Promise((resolve, reject) => setTimeout(resolve, 30000)); } } - throw lastError + throw lastError; } function downloadFile (url, directory) { @@ -90,21 +90,21 @@ function downloadFile (url, directory) { const nuggetOpts = { dir: directory, quiet: args.verbose - } + }; nugget(url, nuggetOpts, (err) => { if (err) { - reject(err) + reject(err); } else { - resolve() + resolve(); } - }) - }) + }); + }); } if (!args.name || !args.buildNum || !args.dest) { console.log(`Download CircleCI artifacts. - Usage: download-circleci-artifacts.js [--buildNum=CIRCLE_BUILD_NUMBER] [--name=artifactName] [--dest] [--verbose]`) - process.exit(0) + Usage: download-circleci-artifacts.js [--buildNum=CIRCLE_BUILD_NUMBER] [--name=artifactName] [--dest] [--verbose]`); + process.exit(0); } else { - downloadArtifact(args.name, args.buildNum, args.dest) + downloadArtifact(args.name, args.buildNum, args.dest); } diff --git a/script/gen-filenames.js b/script/gen-filenames.js index 1fec22facd97b..f306ba83f0017 100644 --- a/script/gen-filenames.js +++ b/script/gen-filenames.js @@ -1,17 +1,17 @@ -const cp = require('child_process') -const fs = require('fs-extra') -const os = require('os') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs-extra'); +const os = require('os'); +const path = require('path'); -const rootPath = path.resolve(__dirname, '..') -const gniPath = path.resolve(__dirname, '../filenames.auto.gni') +const rootPath = path.resolve(__dirname, '..'); +const gniPath = path.resolve(__dirname, '../filenames.auto.gni'); const allDocs = fs.readdirSync(path.resolve(__dirname, '../docs/api')) .map(doc => `docs/api/${doc}`) .concat( fs.readdirSync(path.resolve(__dirname, '../docs/api/structures')) .map(doc => `docs/api/structures/${doc}`) - ) + ); const main = async () => { const webpackTargets = [ @@ -39,30 +39,30 @@ const main = async () => { name: 'worker_bundle_deps', config: 'webpack.config.worker.js' } - ] + ]; await Promise.all(webpackTargets.map(async webpackTarget => { - const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-filenames-')) + const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-filenames-')); const child = cp.spawn('node', [ 'build/webpack/get-outputs.js', `./${webpackTarget.config}`, path.resolve(tmpDir, `${webpackTarget.name}.measure.js`) ], { cwd: path.resolve(__dirname, '..') - }) - let output = '' + }); + let output = ''; child.stdout.on('data', chunk => { - output += chunk.toString() - }) - child.stderr.on('data', chunk => console.error(chunk.toString())) + output += chunk.toString(); + }); + child.stderr.on('data', chunk => console.error(chunk.toString())); await new Promise((resolve, reject) => child.on('exit', (code) => { if (code !== 0) { - console.error(output) - return reject(new Error(`Failed to list webpack dependencies for entry: ${webpackTarget.name}`)) + console.error(output); + return reject(new Error(`Failed to list webpack dependencies for entry: ${webpackTarget.name}`)); } - resolve() - })) + resolve(); + })); webpackTarget.dependencies = JSON.parse(output) // Remove whitespace @@ -76,9 +76,9 @@ const main = async () => { // All webpack builds depend on the tsconfig and package json files .concat(['tsconfig.json', 'tsconfig.electron.json', 'package.json']) // Make the generated list easier to read - .sort() - await fs.remove(tmpDir) - })) + .sort(); + await fs.remove(tmpDir); + })); fs.writeFileSync( gniPath, @@ -92,12 +92,12 @@ ${webpackTargets.map(target => ` ${target.name} = [ ${target.dependencies.map(dep => ` "${dep}",`).join('\n')} ]`).join('\n\n')} } -`) -} +`); +}; if (process.mainModule === module) { main().catch((err) => { - console.error(err) - process.exit(1) - }) + console.error(err); + process.exit(1); + }); } diff --git a/script/gen-hunspell-filenames.js b/script/gen-hunspell-filenames.js index e91f935018e27..4e9b9d2a01052 100644 --- a/script/gen-hunspell-filenames.js +++ b/script/gen-hunspell-filenames.js @@ -1,18 +1,18 @@ -const fs = require('fs') -const path = require('path') +const fs = require('fs'); +const path = require('path'); -const check = process.argv.includes('--check') +const check = process.argv.includes('--check'); -const dictsPath = path.resolve(__dirname, '..', '..', 'third_party', 'hunspell_dictionaries') -const gclientPath = 'third_party/hunspell_dictionaries' +const dictsPath = path.resolve(__dirname, '..', '..', 'third_party', 'hunspell_dictionaries'); +const gclientPath = 'third_party/hunspell_dictionaries'; -const allFiles = fs.readdirSync(dictsPath) +const allFiles = fs.readdirSync(dictsPath); const dictionaries = allFiles - .filter(file => path.extname(file) === '.bdic') + .filter(file => path.extname(file) === '.bdic'); const licenses = allFiles - .filter(file => file.startsWith('LICENSE') || file.startsWith('COPYING')) + .filter(file => file.startsWith('LICENSE') || file.startsWith('COPYING')); const content = `hunspell_dictionaries = [ ${dictionaries.map(f => `"//${path.posix.join(gclientPath, f)}"`).join(',\n ')}, @@ -21,15 +21,15 @@ const content = `hunspell_dictionaries = [ hunspell_licenses = [ ${licenses.map(f => `"//${path.posix.join(gclientPath, f)}"`).join(',\n ')}, ] -` +`; -const filenamesPath = path.resolve(__dirname, '..', 'filenames.hunspell.gni') +const filenamesPath = path.resolve(__dirname, '..', 'filenames.hunspell.gni'); if (check) { - const currentContent = fs.readFileSync(filenamesPath, 'utf8') + const currentContent = fs.readFileSync(filenamesPath, 'utf8'); if (currentContent !== content) { - throw new Error('hunspell filenames need to be regenerated, latest generation does not match current file. Please run node gen-hunspell-filenames.js') + throw new Error('hunspell filenames need to be regenerated, latest generation does not match current file. Please run node gen-hunspell-filenames.js'); } } else { - fs.writeFileSync(filenamesPath, content) + fs.writeFileSync(filenamesPath, content); } diff --git a/script/generate-deps-hash.js b/script/generate-deps-hash.js index e1991af07d8c7..3c090eceac559 100644 --- a/script/generate-deps-hash.js +++ b/script/generate-deps-hash.js @@ -1,9 +1,9 @@ -const crypto = require('crypto') -const fs = require('fs') -const path = require('path') +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); // Fallback to blow away old cache keys -const HASH_VERSION = 3 +const HASH_VERSION = 3; // Base files to hash const filesToHash = [ @@ -11,42 +11,42 @@ const filesToHash = [ path.resolve(__dirname, '../yarn.lock'), path.resolve(__dirname, '../script/external-binaries.json'), path.resolve(__dirname, '../script/sysroots.json') -] +]; const addAllFiles = (dir) => { for (const child of fs.readdirSync(dir).sort()) { - const childPath = path.resolve(dir, child) + const childPath = path.resolve(dir, child); if (fs.statSync(childPath).isDirectory()) { - addAllFiles(childPath) + addAllFiles(childPath); } else { - filesToHash.push(childPath) + filesToHash.push(childPath); } } -} +}; // Add all patch files to the hash -addAllFiles(path.resolve(__dirname, '../patches')) +addAllFiles(path.resolve(__dirname, '../patches')); // Create Hash -const hasher = crypto.createHash('SHA256') -hasher.update(`HASH_VERSION:${HASH_VERSION}`) +const hasher = crypto.createHash('SHA256'); +hasher.update(`HASH_VERSION:${HASH_VERSION}`); for (const file of filesToHash) { - hasher.update(fs.readFileSync(file)) + hasher.update(fs.readFileSync(file)); } // Add the GCLIENT_EXTRA_ARGS variable to the hash -const extraArgs = process.env.GCLIENT_EXTRA_ARGS || 'no_extra_args' -hasher.update(extraArgs) +const extraArgs = process.env.GCLIENT_EXTRA_ARGS || 'no_extra_args'; +hasher.update(extraArgs); -const effectivePlatform = extraArgs.includes('host_os=mac') ? 'darwin' : process.platform +const effectivePlatform = extraArgs.includes('host_os=mac') ? 'darwin' : process.platform; // Write the hash to disk -fs.writeFileSync(path.resolve(__dirname, '../.depshash'), hasher.digest('hex')) +fs.writeFileSync(path.resolve(__dirname, '../.depshash'), hasher.digest('hex')); -let targetContent = `${effectivePlatform}\n${process.env.TARGET_ARCH}\n${process.env.GN_CONFIG}\n${undefined}\n${process.env.GN_EXTRA_ARGS}\n${process.env.GN_BUILDFLAG_ARGS}` -const argsDir = path.resolve(__dirname, '../build/args') +let targetContent = `${effectivePlatform}\n${process.env.TARGET_ARCH}\n${process.env.GN_CONFIG}\n${undefined}\n${process.env.GN_EXTRA_ARGS}\n${process.env.GN_BUILDFLAG_ARGS}`; +const argsDir = path.resolve(__dirname, '../build/args'); for (const argFile of fs.readdirSync(argsDir).sort()) { - targetContent += `\n${argFile}--${crypto.createHash('SHA1').update(fs.readFileSync(path.resolve(argsDir, argFile))).digest('hex')}` + targetContent += `\n${argFile}--${crypto.createHash('SHA1').update(fs.readFileSync(path.resolve(argsDir, argFile))).digest('hex')}`; } -fs.writeFileSync(path.resolve(__dirname, '../.depshash-target'), targetContent) +fs.writeFileSync(path.resolve(__dirname, '../.depshash-target'), targetContent); diff --git a/script/generate-version-json.js b/script/generate-version-json.js index 442fbc53dce08..5c41d0543394d 100644 --- a/script/generate-version-json.js +++ b/script/generate-version-json.js @@ -1,23 +1,23 @@ -const fs = require('fs') -const path = require('path') -const semver = require('semver') +const fs = require('fs'); +const path = require('path'); +const semver = require('semver'); -const outputPath = process.argv[2] +const outputPath = process.argv[2]; -const currentVersion = fs.readFileSync(path.resolve(__dirname, '../ELECTRON_VERSION'), 'utf8').trim() +const currentVersion = fs.readFileSync(path.resolve(__dirname, '../ELECTRON_VERSION'), 'utf8').trim(); -const parsed = semver.parse(currentVersion) +const parsed = semver.parse(currentVersion); -let prerelease = '' +let prerelease = ''; if (parsed.prerelease && parsed.prerelease.length > 0) { - prerelease = parsed.prerelease.join('.') + prerelease = parsed.prerelease.join('.'); } const { major, minor, patch -} = parsed +} = parsed; fs.writeFileSync(outputPath, JSON.stringify({ major, @@ -25,4 +25,4 @@ fs.writeFileSync(outputPath, JSON.stringify({ patch, prerelease, has_prerelease: prerelease === '' ? 0 : 1 -}, null, 2)) +}, null, 2)); diff --git a/script/gn-asar.js b/script/gn-asar.js index aeb69e7fddd7e..80e2cdb34f4c8 100644 --- a/script/gn-asar.js +++ b/script/gn-asar.js @@ -1,63 +1,63 @@ -const asar = require('asar') -const assert = require('assert') -const fs = require('fs-extra') -const os = require('os') -const path = require('path') +const asar = require('asar'); +const assert = require('assert'); +const fs = require('fs-extra'); +const os = require('os'); +const path = require('path'); const getArgGroup = (name) => { - const group = [] - let inGroup = false + const group = []; + let inGroup = false; for (const arg of process.argv) { // At the next flag we stop being in the current group - if (arg.startsWith('--')) inGroup = false + if (arg.startsWith('--')) inGroup = false; // Push all args in the group - if (inGroup) group.push(arg) + if (inGroup) group.push(arg); // If we find the start flag, start pushing - if (arg === `--${name}`) inGroup = true + if (arg === `--${name}`) inGroup = true; } - return group -} + return group; +}; -const base = getArgGroup('base') -const files = getArgGroup('files') -const out = getArgGroup('out') +const base = getArgGroup('base'); +const files = getArgGroup('files'); +const out = getArgGroup('out'); -assert(base.length === 1, 'should have a single base dir') -assert(files.length >= 1, 'should have at least one input file') -assert(out.length === 1, 'should have a single out path') +assert(base.length === 1, 'should have a single base dir'); +assert(files.length >= 1, 'should have at least one input file'); +assert(out.length === 1, 'should have a single out path'); // Ensure all files are inside the base dir for (const file of files) { if (!file.startsWith(base[0])) { - console.error(`Expected all files to be inside the base dir but "${file}" was not in "${base[0]}"`) - process.exit(1) + console.error(`Expected all files to be inside the base dir but "${file}" was not in "${base[0]}"`); + process.exit(1); } } -const tmpPath = fs.mkdtempSync(path.resolve(os.tmpdir(), 'electron-gn-asar-')) +const tmpPath = fs.mkdtempSync(path.resolve(os.tmpdir(), 'electron-gn-asar-')); try { // Copy all files to a tmp dir to avoid including scrap files in the ASAR for (const file of files) { - const newLocation = path.resolve(tmpPath, path.relative(base[0], file)) - fs.mkdirsSync(path.dirname(newLocation)) - fs.writeFileSync(newLocation, fs.readFileSync(file)) + const newLocation = path.resolve(tmpPath, path.relative(base[0], file)); + fs.mkdirsSync(path.dirname(newLocation)); + fs.writeFileSync(newLocation, fs.readFileSync(file)); } } catch (err) { - console.error('Unexpected error while generating ASAR', err) + console.error('Unexpected error while generating ASAR', err); fs.remove(tmpPath) .then(() => process.exit(1)) - .catch(() => process.exit(1)) - return + .catch(() => process.exit(1)); + return; } // Create the ASAR archive asar.createPackageWithOptions(tmpPath, out[0], {}) .catch(err => { const exit = () => { - console.error('Unexpected error while generating ASAR', err) - process.exit(1) - } - fs.remove(tmpPath).then(exit).catch(exit) - }).then(() => fs.remove(tmpPath)) + console.error('Unexpected error while generating ASAR', err); + process.exit(1); + }; + fs.remove(tmpPath).then(exit).catch(exit); + }).then(() => fs.remove(tmpPath)); diff --git a/script/gn-check.js b/script/gn-check.js index 899e728f00655..58c80e8a1e4e4 100644 --- a/script/gn-check.js +++ b/script/gn-check.js @@ -4,38 +4,38 @@ Usage: $ node ./script/gn-check.js [--outDir=dirName] */ -const cp = require('child_process') -const path = require('path') -const args = require('minimist')(process.argv.slice(2), { string: ['outDir'] }) +const cp = require('child_process'); +const path = require('path'); +const args = require('minimist')(process.argv.slice(2), { string: ['outDir'] }); -const { getOutDir } = require('./lib/utils') +const { getOutDir } = require('./lib/utils'); -const SOURCE_ROOT = path.normalize(path.dirname(__dirname)) -const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools') +const SOURCE_ROOT = path.normalize(path.dirname(__dirname)); +const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools'); -const OUT_DIR = getOutDir({ outDir: args.outDir }) +const OUT_DIR = getOutDir({ outDir: args.outDir }); if (!OUT_DIR) { - throw new Error('No viable out dir: one of Debug, Testing, or Release must exist.') + throw new Error('No viable out dir: one of Debug, Testing, or Release must exist.'); } const env = Object.assign({ CHROMIUM_BUILDTOOLS_PATH: path.resolve(SOURCE_ROOT, '..', 'buildtools'), DEPOT_TOOLS_WIN_TOOLCHAIN: '0' -}, process.env) +}, process.env); // Users may not have depot_tools in PATH. -env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}` +env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}`; const gnCheckDirs = [ '//electron:electron_lib', '//electron:electron_app', '//electron:manifests', '//electron/shell/common/api:mojo' -] +]; for (const dir of gnCheckDirs) { - const args = ['check', `../out/${OUT_DIR}`, dir] - const result = cp.spawnSync('gn', args, { env, stdio: 'inherit' }) - if (result.status !== 0) process.exit(result.status) + const args = ['check', `../out/${OUT_DIR}`, dir]; + const result = cp.spawnSync('gn', args, { env, stdio: 'inherit' }); + if (result.status !== 0) process.exit(result.status); } -process.exit(0) +process.exit(0); diff --git a/script/lib/utils.js b/script/lib/utils.js index e4ad5dca4f075..a0c869530d083 100644 --- a/script/lib/utils.js +++ b/script/lib/utils.js @@ -1,95 +1,95 @@ -const { GitProcess } = require('dugite') -const fs = require('fs') -const path = require('path') +const { GitProcess } = require('dugite'); +const fs = require('fs'); +const path = require('path'); -const ELECTRON_DIR = path.resolve(__dirname, '..', '..') -const SRC_DIR = path.resolve(ELECTRON_DIR, '..') +const ELECTRON_DIR = path.resolve(__dirname, '..', '..'); +const SRC_DIR = path.resolve(ELECTRON_DIR, '..'); -const RELEASE_BRANCH_PATTERN = /(\d)+-(?:(?:[0-9]+-x$)|(?:x+-y$))/ +const RELEASE_BRANCH_PATTERN = /(\d)+-(?:(?:[0-9]+-x$)|(?:x+-y$))/; -require('colors') -const pass = '✓'.green -const fail = '✗'.red +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; function getElectronExec () { - const OUT_DIR = getOutDir() + const OUT_DIR = getOutDir(); switch (process.platform) { case 'darwin': - return `out/${OUT_DIR}/Electron.app/Contents/MacOS/Electron` + return `out/${OUT_DIR}/Electron.app/Contents/MacOS/Electron`; case 'win32': - return `out/${OUT_DIR}/electron.exe` + return `out/${OUT_DIR}/electron.exe`; case 'linux': - return `out/${OUT_DIR}/electron` + return `out/${OUT_DIR}/electron`; default: - throw new Error('Unknown platform') + throw new Error('Unknown platform'); } } function getOutDir (options = {}) { - const shouldLog = options.shouldLog || false - const presetDirs = ['Testing', 'Release', 'Default', 'Debug'] + const shouldLog = options.shouldLog || false; + const presetDirs = ['Testing', 'Release', 'Default', 'Debug']; if (options.outDir || process.env.ELECTRON_OUT_DIR) { - const outDir = options.outDir || process.env.ELECTRON_OUT_DIR - const outPath = path.resolve(SRC_DIR, 'out', outDir) + const outDir = options.outDir || process.env.ELECTRON_OUT_DIR; + const outPath = path.resolve(SRC_DIR, 'out', outDir); // Check that user-set variable is a valid/existing directory if (fs.existsSync(outPath)) { - if (shouldLog) console.log(`OUT_DIR is: ${outDir}`) - return outDir + if (shouldLog) console.log(`OUT_DIR is: ${outDir}`); + return outDir; } // Throw error if user passed/set nonexistent directory. - throw new Error(`${outDir} directory not configured on your machine.`) + throw new Error(`${outDir} directory not configured on your machine.`); } else { for (const buildType of presetDirs) { - const outPath = path.resolve(SRC_DIR, 'out', buildType) + const outPath = path.resolve(SRC_DIR, 'out', buildType); if (fs.existsSync(outPath)) { - if (shouldLog) console.log(`OUT_DIR is: ${buildType}`) - return buildType + if (shouldLog) console.log(`OUT_DIR is: ${buildType}`); + return buildType; } } } // If we got here, it means process.env.ELECTRON_OUT_DIR was not // set and none of the preset options could be found in /out, so throw - throw new Error(`No valid out directory found; use one of ${presetDirs.join(',')} or set process.env.ELECTRON_OUT_DIR`) + throw new Error(`No valid out directory found; use one of ${presetDirs.join(',')} or set process.env.ELECTRON_OUT_DIR`); } function getAbsoluteElectronExec () { - return path.resolve(SRC_DIR, getElectronExec()) + return path.resolve(SRC_DIR, getElectronExec()); } async function handleGitCall (args, gitDir) { - const details = await GitProcess.exec(args, gitDir) + const details = await GitProcess.exec(args, gitDir); if (details.exitCode === 0) { - return details.stdout.replace(/^\*|\s+|\s+$/, '') + return details.stdout.replace(/^\*|\s+|\s+$/, ''); } else { - const error = GitProcess.parseError(details.stderr) - console.log(`${fail} couldn't parse git process call: `, error) - process.exit(1) + const error = GitProcess.parseError(details.stderr); + console.log(`${fail} couldn't parse git process call: `, error); + process.exit(1); } } async function getCurrentBranch (gitDir) { - let branch = await handleGitCall(['rev-parse', '--abbrev-ref', 'HEAD'], gitDir) + let branch = await handleGitCall(['rev-parse', '--abbrev-ref', 'HEAD'], gitDir); if (branch !== 'master' && !RELEASE_BRANCH_PATTERN.test(branch)) { - const lastCommit = await handleGitCall(['rev-parse', 'HEAD'], gitDir) + const lastCommit = await handleGitCall(['rev-parse', 'HEAD'], gitDir); const branches = (await handleGitCall([ 'branch', '--contains', lastCommit, '--remote' - ], gitDir)).split('\n') + ], gitDir)).split('\n'); - branch = branches.filter(b => b.trim() === 'master' || b.trim() === 'origin/master' || RELEASE_BRANCH_PATTERN.test(b.trim()))[0] + branch = branches.filter(b => b.trim() === 'master' || b.trim() === 'origin/master' || RELEASE_BRANCH_PATTERN.test(b.trim()))[0]; if (!branch) { - console.log(`${fail} no release branch exists for this ref`) - process.exit(1) + console.log(`${fail} no release branch exists for this ref`); + process.exit(1); } - if (branch.startsWith('origin/')) branch = branch.substr('origin/'.length) + if (branch.startsWith('origin/')) branch = branch.substr('origin/'.length); } - return branch.trim() + return branch.trim(); } module.exports = { @@ -99,4 +99,4 @@ module.exports = { getAbsoluteElectronExec, ELECTRON_DIR, SRC_DIR -} +}; diff --git a/script/lint.js b/script/lint.js index 3a86d8f23dc75..80c94c547f514 100755 --- a/script/lint.js +++ b/script/lint.js @@ -1,14 +1,14 @@ #!/usr/bin/env node -const { GitProcess } = require('dugite') -const childProcess = require('child_process') -const fs = require('fs') -const klaw = require('klaw') -const minimist = require('minimist') -const path = require('path') +const { GitProcess } = require('dugite'); +const childProcess = require('child_process'); +const fs = require('fs'); +const klaw = require('klaw'); +const minimist = require('minimist'); +const path = require('path'); -const SOURCE_ROOT = path.normalize(path.dirname(__dirname)) -const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools') +const SOURCE_ROOT = path.normalize(path.dirname(__dirname)); +const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools'); const BLACKLIST = new Set([ ['shell', 'browser', 'mac', 'atom_application.h'], @@ -30,31 +30,31 @@ const BLACKLIST = new Set([ ['spec', 'ts-smoke', 'electron', 'main.ts'], ['spec', 'ts-smoke', 'electron', 'renderer.ts'], ['spec', 'ts-smoke', 'runner.js'] -].map(tokens => path.join(SOURCE_ROOT, ...tokens))) +].map(tokens => path.join(SOURCE_ROOT, ...tokens))); function spawnAndCheckExitCode (cmd, args, opts) { - opts = Object.assign({ stdio: 'inherit' }, opts) - const status = childProcess.spawnSync(cmd, args, opts).status - if (status) process.exit(status) + opts = Object.assign({ stdio: 'inherit' }, opts); + const status = childProcess.spawnSync(cmd, args, opts).status; + if (status) process.exit(status); } function cpplint (args) { - const result = childProcess.spawnSync('cpplint.py', args, { encoding: 'utf8' }) + const result = childProcess.spawnSync('cpplint.py', args, { encoding: 'utf8' }); // cpplint.py writes EVERYTHING to stderr, including status messages if (result.stderr) { for (const line of result.stderr.split(/[\r\n]+/)) { if (line.length && !line.startsWith('Done processing ') && line !== 'Total errors found: 0') { - console.warn(line) + console.warn(line); } } } if (result.status) { - process.exit(result.status) + process.exit(result.status); } } function isObjCHeader (filename) { - return /\/(mac|cocoa)\//.test(filename) + return /\/(mac|cocoa)\//.test(filename); } const LINTERS = [{ @@ -63,11 +63,11 @@ const LINTERS = [{ test: filename => filename.endsWith('.cc') || (filename.endsWith('.h') && !isObjCHeader(filename)), run: (opts, filenames) => { if (opts.fix) { - spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]) + spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]); } else { - spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]) + spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]); } - cpplint(filenames) + cpplint(filenames); } }, { key: 'objc', @@ -75,27 +75,27 @@ const LINTERS = [{ test: filename => filename.endsWith('.mm'), run: (opts, filenames) => { if (opts.fix) { - spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]) + spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]); } else { - spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]) + spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]); } const filter = [ '-readability/casting', '-whitespace/braces', '-whitespace/indent', '-whitespace/parens' - ] - cpplint(['--extensions=mm', `--filter=${filter.join(',')}`, ...filenames]) + ]; + cpplint(['--extensions=mm', `--filter=${filter.join(',')}`, ...filenames]); } }, { key: 'python', roots: ['script'], test: filename => filename.endsWith('.py'), run: (opts, filenames) => { - const rcfile = path.join(DEPOT_TOOLS, 'pylintrc') - const args = ['--rcfile=' + rcfile, ...filenames] - const env = Object.assign({ PYTHONPATH: path.join(SOURCE_ROOT, 'script') }, process.env) - spawnAndCheckExitCode('pylint.py', args, { env }) + const rcfile = path.join(DEPOT_TOOLS, 'pylintrc'); + const args = ['--rcfile=' + rcfile, ...filenames]; + const env = Object.assign({ PYTHONPATH: path.join(SOURCE_ROOT, 'script') }, process.env); + spawnAndCheckExitCode('pylint.py', args, { env }); } }, { key: 'javascript', @@ -103,10 +103,10 @@ const LINTERS = [{ ignoreRoots: ['spec/node_modules', 'spec-main/node_modules'], test: filename => filename.endsWith('.js') || filename.endsWith('.ts'), run: (opts, filenames) => { - const cmd = path.join(SOURCE_ROOT, 'node_modules', '.bin', 'eslint') - const args = ['--cache', '--ext', '.js,.ts', ...filenames] - if (opts.fix) args.unshift('--fix') - spawnAndCheckExitCode(cmd, args, { cwd: SOURCE_ROOT }) + const cmd = path.join(SOURCE_ROOT, 'node_modules', '.bin', 'eslint'); + const args = ['--cache', '--ext', '.js,.ts', ...filenames]; + if (opts.fix) args.unshift('--fix'); + spawnAndCheckExitCode(cmd, args, { cwd: SOURCE_ROOT }); } }, { key: 'gn', @@ -117,24 +117,24 @@ const LINTERS = [{ const env = Object.assign({ CHROMIUM_BUILDTOOLS_PATH: path.resolve(SOURCE_ROOT, '..', 'buildtools'), DEPOT_TOOLS_WIN_TOOLCHAIN: '0' - }, process.env) + }, process.env); // Users may not have depot_tools in PATH. - env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}` - const args = ['format', filename] - if (!opts.fix) args.push('--dry-run') - const result = childProcess.spawnSync('gn', args, { env, stdio: 'inherit', shell: true }) + env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}`; + const args = ['format', filename]; + if (!opts.fix) args.push('--dry-run'); + const result = childProcess.spawnSync('gn', args, { env, stdio: 'inherit', shell: true }); if (result.status === 0) { - return true + return true; } else if (result.status === 2) { - console.log(`GN format errors in "${filename}". Run 'gn format "${filename}"' or rerun with --fix to fix them.`) - return false + console.log(`GN format errors in "${filename}". Run 'gn format "${filename}"' or rerun with --fix to fix them.`); + return false; } else { - console.log(`Error running 'gn format --dry-run "${filename}"': exit code ${result.status}`) - return false + console.log(`Error running 'gn format --dry-run "${filename}"': exit code ${result.status}`); + return false; } - }).every(x => x) + }).every(x => x); if (!allOk) { - process.exit(1) + process.exit(1); } } }, { @@ -142,167 +142,167 @@ const LINTERS = [{ roots: ['patches'], test: () => true, run: (opts, filenames) => { - const patchesDir = path.resolve(__dirname, '../patches') + const patchesDir = path.resolve(__dirname, '../patches'); for (const patchTarget of fs.readdirSync(patchesDir)) { - const targetDir = path.resolve(patchesDir, patchTarget) + const targetDir = path.resolve(patchesDir, patchTarget); // If the config does not exist that is OK, we just skip this dir - const targetConfig = path.resolve(targetDir, 'config.json') - if (!fs.existsSync(targetConfig)) continue + const targetConfig = path.resolve(targetDir, 'config.json'); + if (!fs.existsSync(targetConfig)) continue; - const config = JSON.parse(fs.readFileSync(targetConfig, 'utf8')) + const config = JSON.parse(fs.readFileSync(targetConfig, 'utf8')); for (const key of Object.keys(config)) { // The directory the config points to should exist - const targetPatchesDir = path.resolve(__dirname, '../../..', key) - if (!fs.existsSync(targetPatchesDir)) throw new Error(`target patch directory: "${targetPatchesDir}" does not exist`) + const targetPatchesDir = path.resolve(__dirname, '../../..', key); + if (!fs.existsSync(targetPatchesDir)) throw new Error(`target patch directory: "${targetPatchesDir}" does not exist`); // We need a .patches file - const dotPatchesPath = path.resolve(targetPatchesDir, '.patches') - if (!fs.existsSync(dotPatchesPath)) throw new Error(`.patches file: "${dotPatchesPath}" does not exist`) + const dotPatchesPath = path.resolve(targetPatchesDir, '.patches'); + if (!fs.existsSync(dotPatchesPath)) throw new Error(`.patches file: "${dotPatchesPath}" does not exist`); // Read the patch list - const patchFileList = fs.readFileSync(dotPatchesPath, 'utf8').trim().split('\n') - const patchFileSet = new Set(patchFileList) + const patchFileList = fs.readFileSync(dotPatchesPath, 'utf8').trim().split('\n'); + const patchFileSet = new Set(patchFileList); patchFileList.reduce((seen, file) => { if (seen.has(file)) { - throw new Error(`'${file}' is listed in ${dotPatchesPath} more than once`) + throw new Error(`'${file}' is listed in ${dotPatchesPath} more than once`); } - return seen.add(file) - }, new Set()) - if (patchFileList.length !== patchFileSet.size) throw new Error('each patch file should only be in the .patches file once') + return seen.add(file); + }, new Set()); + if (patchFileList.length !== patchFileSet.size) throw new Error('each patch file should only be in the .patches file once'); for (const file of fs.readdirSync(targetPatchesDir)) { // Ignore the .patches file and READMEs - if (file === '.patches' || file === 'README.md') continue + if (file === '.patches' || file === 'README.md') continue; if (!patchFileSet.has(file)) { - throw new Error(`Expected the .patches file at "${dotPatchesPath}" to contain a patch file ("${file}") present in the directory but it did not`) + throw new Error(`Expected the .patches file at "${dotPatchesPath}" to contain a patch file ("${file}") present in the directory but it did not`); } - patchFileSet.delete(file) + patchFileSet.delete(file); } // If anything is left in this set, it means it did not exist on disk if (patchFileSet.size > 0) { - throw new Error(`Expected all the patch files listed in the .patches file at "${dotPatchesPath}" to exist but some did not:\n${JSON.stringify([...patchFileSet.values()], null, 2)}`) + throw new Error(`Expected all the patch files listed in the .patches file at "${dotPatchesPath}" to exist but some did not:\n${JSON.stringify([...patchFileSet.values()], null, 2)}`); } } } - let ok = true + let ok = true; filenames.filter(f => f.endsWith('.patch')).forEach(f => { - const patchText = fs.readFileSync(f, 'utf8') + const patchText = fs.readFileSync(f, 'utf8'); if (/^Subject: .*$\s+^diff/m.test(patchText)) { - console.warn(`Patch file '${f}' has no description. Every patch must contain a justification for why the patch exists and the plan for its removal.`) - ok = false + console.warn(`Patch file '${f}' has no description. Every patch must contain a justification for why the patch exists and the plan for its removal.`); + ok = false; } - }) + }); if (!ok) { - process.exit(1) + process.exit(1); } } -}] +}]; function parseCommandLine () { - let help + let help; const opts = minimist(process.argv.slice(2), { boolean: ['c++', 'objc', 'javascript', 'python', 'gn', 'patches', 'help', 'changed', 'fix', 'verbose', 'only'], alias: { 'c++': ['cc', 'cpp', 'cxx'], javascript: ['js', 'es'], python: 'py', changed: 'c', help: 'h', verbose: 'v' }, - unknown: arg => { help = true } - }) + unknown: arg => { help = true; } + }); if (help || opts.help) { - console.log('Usage: script/lint.js [--cc] [--js] [--py] [-c|--changed] [-h|--help] [-v|--verbose] [--fix] [--only -- file1 file2]') - process.exit(0) + console.log('Usage: script/lint.js [--cc] [--js] [--py] [-c|--changed] [-h|--help] [-v|--verbose] [--fix] [--only -- file1 file2]'); + process.exit(0); } - return opts + return opts; } async function findChangedFiles (top) { - const result = await GitProcess.exec(['diff', '--name-only', '--cached'], top) + const result = await GitProcess.exec(['diff', '--name-only', '--cached'], top); if (result.exitCode !== 0) { - console.log('Failed to find changed files', GitProcess.parseError(result.stderr)) - process.exit(1) + console.log('Failed to find changed files', GitProcess.parseError(result.stderr)); + process.exit(1); } - const relativePaths = result.stdout.split(/\r\n|\r|\n/g) - const absolutePaths = relativePaths.map(x => path.join(top, x)) - return new Set(absolutePaths) + const relativePaths = result.stdout.split(/\r\n|\r|\n/g); + const absolutePaths = relativePaths.map(x => path.join(top, x)); + return new Set(absolutePaths); } async function findMatchingFiles (top, test) { return new Promise((resolve, reject) => { - const matches = [] + const matches = []; klaw(top, { filter: f => path.basename(f) !== '.bin' }) .on('end', () => resolve(matches)) .on('data', item => { if (test(item.path)) { - matches.push(item.path) + matches.push(item.path); } - }) - }) + }); + }); } async function findFiles (args, linter) { - let filenames = [] - let whitelist = null + let filenames = []; + let whitelist = null; // build the whitelist if (args.changed) { - whitelist = await findChangedFiles(SOURCE_ROOT) + whitelist = await findChangedFiles(SOURCE_ROOT); if (!whitelist.size) { - return [] + return []; } } else if (args.only) { - whitelist = new Set(args._.map(p => path.resolve(p))) + whitelist = new Set(args._.map(p => path.resolve(p))); } // accumulate the raw list of files for (const root of linter.roots) { - const files = await findMatchingFiles(path.join(SOURCE_ROOT, root), linter.test) - filenames.push(...files) + const files = await findMatchingFiles(path.join(SOURCE_ROOT, root), linter.test); + filenames.push(...files); } for (const ignoreRoot of (linter.ignoreRoots) || []) { - const ignorePath = path.join(SOURCE_ROOT, ignoreRoot) - if (!fs.existsSync(ignorePath)) continue + const ignorePath = path.join(SOURCE_ROOT, ignoreRoot); + if (!fs.existsSync(ignorePath)) continue; - const ignoreFiles = new Set(await findMatchingFiles(ignorePath, linter.test)) - filenames = filenames.filter(fileName => !ignoreFiles.has(fileName)) + const ignoreFiles = new Set(await findMatchingFiles(ignorePath, linter.test)); + filenames = filenames.filter(fileName => !ignoreFiles.has(fileName)); } // remove blacklisted files - filenames = filenames.filter(x => !BLACKLIST.has(x)) + filenames = filenames.filter(x => !BLACKLIST.has(x)); // if a whitelist exists, remove anything not in it if (whitelist) { - filenames = filenames.filter(x => whitelist.has(x)) + filenames = filenames.filter(x => whitelist.has(x)); } // it's important that filenames be relative otherwise clang-format will // produce patches with absolute paths in them, which `git apply` will refuse // to apply. - return filenames.map(x => path.relative(SOURCE_ROOT, x)) + return filenames.map(x => path.relative(SOURCE_ROOT, x)); } async function main () { - const opts = parseCommandLine() + const opts = parseCommandLine(); // no mode specified? run 'em all if (!opts['c++'] && !opts.javascript && !opts.objc && !opts.python && !opts.gn && !opts.patches) { - opts['c++'] = opts.javascript = opts.objc = opts.python = opts.gn = opts.patches = true + opts['c++'] = opts.javascript = opts.objc = opts.python = opts.gn = opts.patches = true; } - const linters = LINTERS.filter(x => opts[x.key]) + const linters = LINTERS.filter(x => opts[x.key]); for (const linter of linters) { - const filenames = await findFiles(opts, linter) + const filenames = await findFiles(opts, linter); if (filenames.length) { - if (opts.verbose) { console.log(`linting ${filenames.length} ${linter.key} ${filenames.length === 1 ? 'file' : 'files'}`) } - linter.run(opts, filenames) + if (opts.verbose) { console.log(`linting ${filenames.length} ${linter.key} ${filenames.length === 1 ? 'file' : 'files'}`); } + linter.run(opts, filenames); } } } if (process.mainModule === module) { main().catch((error) => { - console.error(error) - process.exit(1) - }) + console.error(error); + process.exit(1); + }); } diff --git a/script/nan-spec-runner.js b/script/nan-spec-runner.js index 75510936b68fe..fbc6630f41baf 100644 --- a/script/nan-spec-runner.js +++ b/script/nan-spec-runner.js @@ -1,49 +1,49 @@ -const cp = require('child_process') -const fs = require('fs') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); -const BASE = path.resolve(__dirname, '../..') -const NAN_DIR = path.resolve(BASE, 'third_party', 'nan') -const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' +const BASE = path.resolve(__dirname, '../..'); +const NAN_DIR = path.resolve(BASE, 'third_party', 'nan'); +const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; -const utils = require('./lib/utils') -const { YARN_VERSION } = require('./yarn') +const utils = require('./lib/utils'); +const { YARN_VERSION } = require('./yarn'); if (!process.mainModule) { - throw new Error('Must call the nan spec runner directly') + throw new Error('Must call the nan spec runner directly'); } async function main () { - const nodeDir = path.resolve(BASE, `out/${utils.getOutDir({ shouldLog: true })}/gen/node_headers`) + const nodeDir = path.resolve(BASE, `out/${utils.getOutDir({ shouldLog: true })}/gen/node_headers`); const env = Object.assign({}, process.env, { npm_config_nodedir: nodeDir, npm_config_msvs_version: '2019', npm_config_arch: process.env.NPM_CONFIG_ARCH - }) + }); const { status: buildStatus } = cp.spawnSync(NPX_CMD, ['node-gyp', 'rebuild', '--directory', 'test'], { env, cwd: NAN_DIR, stdio: 'inherit' - }) + }); if (buildStatus !== 0) { - console.error('Failed to build nan test modules') - return process.exit(buildStatus) + console.error('Failed to build nan test modules'); + return process.exit(buildStatus); } const { status: installStatus } = cp.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install'], { env, cwd: NAN_DIR, stdio: 'inherit' - }) + }); if (installStatus !== 0) { - console.error('Failed to install nan node_modules') - return process.exit(installStatus) + console.error('Failed to install nan node_modules'); + return process.exit(installStatus); } - const DISABLED_TESTS = ['nannew-test.js'] + const DISABLED_TESTS = ['nannew-test.js']; const testsToRun = fs.readdirSync(path.resolve(NAN_DIR, 'test', 'js')) .filter(test => !DISABLED_TESTS.includes(test)) - .map(test => `test/js/${test}`) + .map(test => `test/js/${test}`); const testChild = cp.spawn(utils.getAbsoluteElectronExec(), ['node_modules/.bin/tap', ...testsToRun], { env: { @@ -52,13 +52,13 @@ async function main () { }, cwd: NAN_DIR, stdio: 'inherit' - }) + }); testChild.on('exit', (testCode) => { - process.exit(testCode) - }) + process.exit(testCode); + }); } main().catch((err) => { - console.error('An unhandled error occurred in the nan spec runner', err) - process.exit(1) -}) + console.error('An unhandled error occurred in the nan spec runner', err); + process.exit(1); +}); diff --git a/script/node-spec-runner.js b/script/node-spec-runner.js index 487038b4513dd..a81d4ec65d7a4 100644 --- a/script/node-spec-runner.js +++ b/script/node-spec-runner.js @@ -1,24 +1,24 @@ -const cp = require('child_process') -const fs = require('fs') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); const args = require('minimist')(process.argv.slice(2), { boolean: ['default'], string: ['jUnitDir'] -}) +}); -const BASE = path.resolve(__dirname, '../..') -const DISABLED_TESTS = require('./node-disabled-tests.json') -const NODE_DIR = path.resolve(BASE, 'third_party', 'electron_node') -const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' -const JUNIT_DIR = args.jUnitDir ? path.resolve(args.jUnitDir) : null -const TAP_FILE_NAME = 'test.tap' +const BASE = path.resolve(__dirname, '../..'); +const DISABLED_TESTS = require('./node-disabled-tests.json'); +const NODE_DIR = path.resolve(BASE, 'third_party', 'electron_node'); +const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; +const JUNIT_DIR = args.jUnitDir ? path.resolve(args.jUnitDir) : null; +const TAP_FILE_NAME = 'test.tap'; -const utils = require('./lib/utils') -const { YARN_VERSION } = require('./yarn') +const utils = require('./lib/utils'); +const { YARN_VERSION } = require('./yarn'); if (!process.mainModule) { - throw new Error('Must call the node spec runner directly') + throw new Error('Must call the node spec runner directly'); } const defaultOptions = [ @@ -33,15 +33,15 @@ const defaultOptions = [ '--shell', utils.getAbsoluteElectronExec(), '-J' -] +]; const getCustomOptions = () => { - let customOptions = ['tools/test.py'] + let customOptions = ['tools/test.py']; // Add all custom arguments. - const extra = process.argv.slice(2) + const extra = process.argv.slice(2); if (extra) { - customOptions = customOptions.concat(extra) + customOptions = customOptions.concat(extra); } // We need this unilaterally or Node.js will try @@ -49,13 +49,13 @@ const getCustomOptions = () => { customOptions = customOptions.concat([ '--shell', utils.getAbsoluteElectronExec() - ]) + ]); - return customOptions -} + return customOptions; +}; async function main () { - const options = args.default ? defaultOptions : getCustomOptions() + const options = args.default ? defaultOptions : getCustomOptions(); const testChild = cp.spawn('python', options, { env: { @@ -65,23 +65,23 @@ async function main () { }, cwd: NODE_DIR, stdio: 'inherit' - }) + }); testChild.on('exit', (testCode) => { if (JUNIT_DIR) { - fs.mkdirSync(JUNIT_DIR) - const converterStream = require('tap-xunit')() + fs.mkdirSync(JUNIT_DIR); + const converterStream = require('tap-xunit')(); fs.createReadStream( path.resolve(NODE_DIR, TAP_FILE_NAME) ).pipe(converterStream).pipe( fs.createWriteStream(path.resolve(JUNIT_DIR, 'nodejs.xml')) ).on('close', () => { - process.exit(testCode) - }) + process.exit(testCode); + }); } - }) + }); } main().catch((err) => { - console.error('An unhandled error occurred in the node spec runner', err) - process.exit(1) -}) + console.error('An unhandled error occurred in the node spec runner', err); + process.exit(1); +}); diff --git a/script/release/ci-release-build.js b/script/release/ci-release-build.js index 6c2b36f26fa29..9a875ebb00e52 100644 --- a/script/release/ci-release-build.js +++ b/script/release/ci-release-build.js @@ -1,18 +1,18 @@ -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); -const assert = require('assert') -const request = require('request') +const assert = require('assert'); +const request = require('request'); -const BUILD_APPVEYOR_URL = 'https://ci.appveyor.com/api/builds' -const CIRCLECI_PIPELINE_URL = 'https://circleci.com/api/v2/project/gh/electron/electron/pipeline' -const VSTS_URL = 'https://github.visualstudio.com/electron/_apis/build' -const CIRCLECI_WAIT_TIME = process.env.CIRCLECI_WAIT_TIME || 30000 +const BUILD_APPVEYOR_URL = 'https://ci.appveyor.com/api/builds'; +const CIRCLECI_PIPELINE_URL = 'https://circleci.com/api/v2/project/gh/electron/electron/pipeline'; +const VSTS_URL = 'https://github.visualstudio.com/electron/_apis/build'; +const CIRCLECI_WAIT_TIME = process.env.CIRCLECI_WAIT_TIME || 30000; const appVeyorJobs = { 'electron-x64': 'electron-x64-release', 'electron-ia32': 'electron-ia32-release', 'electron-woa': 'electron-woa-release' -} +}; const circleCIJobs = [ 'linux-arm-publish', @@ -21,50 +21,50 @@ const circleCIJobs = [ 'linux-x64-publish', 'mas-publish', 'osx-publish' -] +]; const circleCIPublishWorkflows = [ 'linux-publish', 'macos-publish' -] +]; const vstsArmJobs = [ 'electron-arm-testing', 'electron-arm64-testing', 'electron-woa-testing' -] +]; -let jobRequestedCount = 0 +let jobRequestedCount = 0; async function makeRequest (requestOptions, parseResponse) { return new Promise((resolve, reject) => { request(requestOptions, (err, res, body) => { if (!err && res.statusCode >= 200 && res.statusCode < 300) { if (parseResponse) { - const build = JSON.parse(body) - resolve(build) + const build = JSON.parse(body); + resolve(build); } else { - resolve(body) + resolve(body); } } else { - console.error('Error occurred while requesting:', requestOptions.url) + console.error('Error occurred while requesting:', requestOptions.url); if (parseResponse) { try { - console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body)) + console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body)); } catch (err) { - console.log('Error: ', `(status ${res.statusCode})`, res.body) + console.log('Error: ', `(status ${res.statusCode})`, res.body); } } else { - console.log('Error: ', `(status ${res.statusCode})`, err || res.body) + console.log('Error: ', `(status ${res.statusCode})`, err || res.body); } - reject(err) + reject(err); } - }) - }) + }); + }); } async function circleCIcall (targetBranch, job, options) { - console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`) + console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`); const buildRequest = { branch: targetBranch, parameters: { @@ -72,82 +72,82 @@ async function circleCIcall (targetBranch, job, options) { 'run-build-linux': false, 'run-build-mac': false } - } + }; if (options.ghRelease) { - buildRequest.parameters['upload-to-s3'] = '0' + buildRequest.parameters['upload-to-s3'] = '0'; } else { - buildRequest.parameters['upload-to-s3'] = '1' + buildRequest.parameters['upload-to-s3'] = '1'; } - buildRequest.parameters[`run-${job}`] = true - jobRequestedCount++ + buildRequest.parameters[`run-${job}`] = true; + jobRequestedCount++; // The logic below expects that the CircleCI workflows for releases each // contain only one job in order to maintain compatibility with sudowoodo. // If the workflows are changed in the CircleCI config.yml, this logic will // also need to be changed as well as possibly changing sudowoodo. try { - const circleResponse = await circleCIRequest(CIRCLECI_PIPELINE_URL, 'POST', buildRequest) - console.log(`CircleCI release build pipeline ${circleResponse.id} for ${job} triggered.`) - const pipelineInfoUrl = `https://circleci.com/api/v2/pipeline/${circleResponse.id}` - const workflowId = await getCircleCIWorkflowId(circleResponse.id) + const circleResponse = await circleCIRequest(CIRCLECI_PIPELINE_URL, 'POST', buildRequest); + console.log(`CircleCI release build pipeline ${circleResponse.id} for ${job} triggered.`); + const pipelineInfoUrl = `https://circleci.com/api/v2/pipeline/${circleResponse.id}`; + const workflowId = await getCircleCIWorkflowId(circleResponse.id); if (workflowId === -1) { - return + return; } - const workFlowUrl = `https://circleci.com/workflow-run/${workflowId}` + const workFlowUrl = `https://circleci.com/workflow-run/${workflowId}`; if (options.runningPublishWorkflows) { - console.log(`CircleCI release workflow request for ${job} successful. Check ${workFlowUrl} for status.`) + console.log(`CircleCI release workflow request for ${job} successful. Check ${workFlowUrl} for status.`); } else { - console.log(`CircleCI release build workflow running at https://circleci.com/workflow-run/${workflowId} for ${job}.`) - const jobNumber = await getCircleCIJobNumber(workflowId) + console.log(`CircleCI release build workflow running at https://circleci.com/workflow-run/${workflowId} for ${job}.`); + const jobNumber = await getCircleCIJobNumber(workflowId); if (jobNumber === -1) { - return + return; } - const jobUrl = `https://circleci.com/gh/electron/electron/${jobNumber}` - console.log(`CircleCI release build request for ${job} successful. Check ${jobUrl} for status.`) + const jobUrl = `https://circleci.com/gh/electron/electron/${jobNumber}`; + console.log(`CircleCI release build request for ${job} successful. Check ${jobUrl} for status.`); } } catch (err) { - console.log('Error calling CircleCI: ', err) + console.log('Error calling CircleCI: ', err); } } async function getCircleCIWorkflowId (pipelineId) { - const pipelineInfoUrl = `https://circleci.com/api/v2/pipeline/${pipelineId}` - let workflowId = 0 + const pipelineInfoUrl = `https://circleci.com/api/v2/pipeline/${pipelineId}`; + let workflowId = 0; while (workflowId === 0) { - const pipelineInfo = await circleCIRequest(pipelineInfoUrl, 'GET') + const pipelineInfo = await circleCIRequest(pipelineInfoUrl, 'GET'); switch (pipelineInfo.state) { case 'created': { - const workflows = await circleCIRequest(`${pipelineInfoUrl}/workflow`, 'GET') + const workflows = await circleCIRequest(`${pipelineInfoUrl}/workflow`, 'GET'); if (workflows.items.length === 1) { - workflowId = workflows.items[0].id - break + workflowId = workflows.items[0].id; + break; } - console.log('Unxpected number of workflows, response was:', pipelineInfo) - workflowId = -1 - break + console.log('Unxpected number of workflows, response was:', pipelineInfo); + workflowId = -1; + break; } case 'error': { - console.log('Error retrieving workflows, response was:', pipelineInfo) - workflowId = -1 - break + console.log('Error retrieving workflows, response was:', pipelineInfo); + workflowId = -1; + break; } } - await new Promise(resolve => setTimeout(resolve, CIRCLECI_WAIT_TIME)) + await new Promise(resolve => setTimeout(resolve, CIRCLECI_WAIT_TIME)); } - return workflowId + return workflowId; } async function getCircleCIJobNumber (workflowId) { - const jobInfoUrl = `https://circleci.com/api/v2/workflow/${workflowId}/job` - let jobNumber = 0 + const jobInfoUrl = `https://circleci.com/api/v2/workflow/${workflowId}/job`; + let jobNumber = 0; while (jobNumber === 0) { - const jobInfo = await circleCIRequest(jobInfoUrl, 'GET') + const jobInfo = await circleCIRequest(jobInfoUrl, 'GET'); if (!jobInfo.items) { - continue + continue; } if (jobInfo.items.length !== 1) { - console.log('Unxpected number of jobs, response was:', jobInfo) - jobNumber = -1 - break + console.log('Unxpected number of jobs, response was:', jobInfo); + jobNumber = -1; + break; } switch (jobInfo.items[0].status) { @@ -155,9 +155,9 @@ async function getCircleCIJobNumber (workflowId) { case 'queued': case 'running': { if (jobInfo.items[0].job_number && !isNaN(jobInfo.items[0].job_number)) { - jobNumber = jobInfo.items[0].job_number + jobNumber = jobInfo.items[0].job_number; } - break + break; } case 'canceled': case 'error': @@ -165,14 +165,14 @@ async function getCircleCIJobNumber (workflowId) { case 'timedout': case 'not_run': case 'failed': { - console.log(`Error job returned a status of ${jobInfo.items[0].status}, response was:`, jobInfo) - jobNumber = -1 - break + console.log(`Error job returned a status of ${jobInfo.items[0].status}, response was:`, jobInfo); + jobNumber = -1; + break; } } - await new Promise(resolve => setTimeout(resolve, CIRCLECI_WAIT_TIME)) + await new Promise(resolve => setTimeout(resolve, CIRCLECI_WAIT_TIME)); } - return jobNumber + return jobNumber; } async function circleCIRequest (url, method, requestBody) { @@ -189,28 +189,28 @@ async function circleCIRequest (url, method, requestBody) { }, body: requestBody ? JSON.stringify(requestBody) : null }, true).catch(err => { - console.log('Error calling CircleCI:', err) - }) + console.log('Error calling CircleCI:', err); + }); } function buildAppVeyor (targetBranch, options) { - const validJobs = Object.keys(appVeyorJobs) + const validJobs = Object.keys(appVeyorJobs); if (options.job) { - assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`) - callAppVeyor(targetBranch, options.job, options) + assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`); + callAppVeyor(targetBranch, options.job, options); } else { - validJobs.forEach((job) => callAppVeyor(targetBranch, job, options)) + validJobs.forEach((job) => callAppVeyor(targetBranch, job, options)); } } async function callAppVeyor (targetBranch, job, options) { - console.log(`Triggering AppVeyor to run build job: ${job} on branch: ${targetBranch} with release flag.`) + console.log(`Triggering AppVeyor to run build job: ${job} on branch: ${targetBranch} with release flag.`); const environmentVariables = { ELECTRON_RELEASE: 1 - } + }; if (!options.ghRelease) { - environmentVariables.UPLOAD_TO_S3 = 1 + environmentVariables.UPLOAD_TO_S3 = 1; } const requestOpts = { @@ -228,44 +228,44 @@ async function callAppVeyor (targetBranch, job, options) { environmentVariables }), method: 'POST' - } - jobRequestedCount++ + }; + jobRequestedCount++; const appVeyorResponse = await makeRequest(requestOpts, true).catch(err => { - console.log('Error calling AppVeyor:', err) - }) - const buildUrl = `https://ci.appveyor.com/project/electron-bot/${appVeyorJobs[job]}/build/${appVeyorResponse.version}` - console.log(`AppVeyor release build request for ${job} successful. Check build status at ${buildUrl}`) + console.log('Error calling AppVeyor:', err); + }); + const buildUrl = `https://ci.appveyor.com/project/electron-bot/${appVeyorJobs[job]}/build/${appVeyorResponse.version}`; + console.log(`AppVeyor release build request for ${job} successful. Check build status at ${buildUrl}`); } function buildCircleCI (targetBranch, options) { if (options.job) { - assert(circleCIJobs.includes(options.job), `Unknown CircleCI job name: ${options.job}. Valid values are: ${circleCIJobs}.`) - circleCIcall(targetBranch, options.job, options) + assert(circleCIJobs.includes(options.job), `Unknown CircleCI job name: ${options.job}. Valid values are: ${circleCIJobs}.`); + circleCIcall(targetBranch, options.job, options); } else { - options.runningPublishWorkflows = true - circleCIPublishWorkflows.forEach((job) => circleCIcall(targetBranch, job, options)) + options.runningPublishWorkflows = true; + circleCIPublishWorkflows.forEach((job) => circleCIcall(targetBranch, job, options)); } } async function buildVSTS (targetBranch, options) { if (options.armTest) { - assert(vstsArmJobs.includes(options.job), `Unknown VSTS CI arm test job name: ${options.job}. Valid values are: ${vstsArmJobs}.`) + assert(vstsArmJobs.includes(options.job), `Unknown VSTS CI arm test job name: ${options.job}. Valid values are: ${vstsArmJobs}.`); } - console.log(`Triggering VSTS to run build on branch: ${targetBranch} with release flag.`) + console.log(`Triggering VSTS to run build on branch: ${targetBranch} with release flag.`); const environmentVariables = { ELECTRON_RELEASE: 1 - } + }; if (options.armTest) { if (options.circleBuildNum) { - environmentVariables.CIRCLE_BUILD_NUM = options.circleBuildNum + environmentVariables.CIRCLE_BUILD_NUM = options.circleBuildNum; } else if (options.appveyorJobId) { - environmentVariables.APPVEYOR_JOB_ID = options.appveyorJobId + environmentVariables.APPVEYOR_JOB_ID = options.appveyorJobId; } } else { if (!options.ghRelease) { - environmentVariables.UPLOAD_TO_S3 = 1 + environmentVariables.UPLOAD_TO_S3 = 1; } } @@ -278,12 +278,12 @@ async function buildVSTS (targetBranch, options) { headers: { 'Content-Type': 'application/json' } - } + }; const vstsResponse = await makeRequest(requestOpts, true).catch(err => { - console.log('Error calling VSTS to get build definitions:', err) - }) - const buildsToRun = vstsResponse.value.filter(build => build.name === options.job) - buildsToRun.forEach((build) => callVSTSBuild(build, targetBranch, environmentVariables)) + console.log('Error calling VSTS to get build definitions:', err); + }); + const buildsToRun = vstsResponse.value.filter(build => build.name === options.job); + buildsToRun.forEach((build) => callVSTSBuild(build, targetBranch, environmentVariables)); } async function callVSTSBuild (build, targetBranch, environmentVariables) { @@ -291,9 +291,9 @@ async function callVSTSBuild (build, targetBranch, environmentVariables) { definition: build, sourceBranch: targetBranch, priority: 'high' - } + }; if (Object.keys(environmentVariables).length !== 0) { - buildBody.parameters = JSON.stringify(environmentVariables) + buildBody.parameters = JSON.stringify(environmentVariables); } const requestOpts = { url: `${VSTS_URL}/builds?api-version=4.1`, @@ -306,54 +306,54 @@ async function callVSTSBuild (build, targetBranch, environmentVariables) { }, body: JSON.stringify(buildBody), method: 'POST' - } - jobRequestedCount++ + }; + jobRequestedCount++; const vstsResponse = await makeRequest(requestOpts, true).catch(err => { - console.log(`Error calling VSTS for job ${build.name}`, err) - }) - console.log(`VSTS release build request for ${build.name} successful. Check ${vstsResponse._links.web.href} for status.`) + console.log(`Error calling VSTS for job ${build.name}`, err); + }); + console.log(`VSTS release build request for ${build.name} successful. Check ${vstsResponse._links.web.href} for status.`); } function runRelease (targetBranch, options) { if (options.ci) { switch (options.ci) { case 'CircleCI': { - buildCircleCI(targetBranch, options) - break + buildCircleCI(targetBranch, options); + break; } case 'AppVeyor': { - buildAppVeyor(targetBranch, options) - break + buildAppVeyor(targetBranch, options); + break; } case 'VSTS': { - buildVSTS(targetBranch, options) - break + buildVSTS(targetBranch, options); + break; } default: { - console.log(`Error! Unknown CI: ${options.ci}.`) - process.exit(1) + console.log(`Error! Unknown CI: ${options.ci}.`); + process.exit(1); } } } else { - buildCircleCI(targetBranch, options) - buildAppVeyor(targetBranch, options) + buildCircleCI(targetBranch, options); + buildAppVeyor(targetBranch, options); } - console.log(`${jobRequestedCount} jobs were requested.`) + console.log(`${jobRequestedCount} jobs were requested.`); } -module.exports = runRelease +module.exports = runRelease; if (require.main === module) { const args = require('minimist')(process.argv.slice(2), { boolean: ['ghRelease', 'armTest'] - }) - const targetBranch = args._[0] + }); + const targetBranch = args._[0]; if (args._.length < 1) { console.log(`Trigger CI to build release builds of electron. Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor|VSTS] [--ghRelease] [--armTest] [--circleBuildNum=xxx] [--appveyorJobId=xxx] TARGET_BRANCH - `) - process.exit(0) + `); + process.exit(0); } - runRelease(targetBranch, args) + runRelease(targetBranch, args); } diff --git a/script/release/find-github-release.js b/script/release/find-github-release.js index 4551133c1d693..0b47c8692ebfc 100644 --- a/script/release/find-github-release.js +++ b/script/release/find-github-release.js @@ -1,38 +1,38 @@ -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) +}); if (process.argv.length < 3) { - console.log('Usage: find-release version') - process.exit(1) + console.log('Usage: find-release version'); + process.exit(1); } -const version = process.argv[2] +const version = process.argv[2]; async function findRelease () { const releases = await octokit.repos.listReleases({ owner: 'electron', repo: version.indexOf('nightly') > 0 ? 'nightlies' : 'electron' - }) + }); - const targetRelease = releases.data.find(release => release.tag_name === version) - let returnObject = {} + const targetRelease = releases.data.find(release => release.tag_name === version); + let returnObject = {}; if (targetRelease) { returnObject = { id: targetRelease.id, draft: targetRelease.draft, exists: true - } + }; } else { returnObject = { exists: false, draft: false - } + }; } - console.log(JSON.stringify(returnObject)) + console.log(JSON.stringify(returnObject)); } -findRelease() +findRelease(); diff --git a/script/release/notes/index.js b/script/release/notes/index.js index 0d43ff69975be..45334243bf36e 100755 --- a/script/release/notes/index.js +++ b/script/release/notes/index.js @@ -1,53 +1,53 @@ #!/usr/bin/env node -const { GitProcess } = require('dugite') -const minimist = require('minimist') -const path = require('path') -const semver = require('semver') +const { GitProcess } = require('dugite'); +const minimist = require('minimist'); +const path = require('path'); +const semver = require('semver'); -const { ELECTRON_DIR } = require('../../lib/utils') -const notesGenerator = require('./notes.js') +const { ELECTRON_DIR } = require('../../lib/utils'); +const notesGenerator = require('./notes.js'); -const semverify = version => version.replace(/^origin\//, '').replace('x', '0').replace(/-/g, '.') +const semverify = version => version.replace(/^origin\//, '').replace('x', '0').replace(/-/g, '.'); const runGit = async (args) => { - const response = await GitProcess.exec(args, ELECTRON_DIR) + const response = await GitProcess.exec(args, ELECTRON_DIR); if (response.exitCode !== 0) { - throw new Error(response.stderr.trim()) + throw new Error(response.stderr.trim()); } - return response.stdout.trim() -} + return response.stdout.trim(); +}; -const tagIsSupported = tag => tag && !tag.includes('nightly') && !tag.includes('unsupported') -const tagIsBeta = tag => tag.includes('beta') -const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag) +const tagIsSupported = tag => tag && !tag.includes('nightly') && !tag.includes('unsupported'); +const tagIsBeta = tag => tag.includes('beta'); +const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag); const getTagsOf = async (point) => { return (await runGit(['tag', '--merged', point])) .split('\n') .map(tag => tag.trim()) .filter(tag => semver.valid(tag)) - .sort(semver.compare) -} + .sort(semver.compare); +}; const getTagsOnBranch = async (point) => { - const masterTags = await getTagsOf('master') + const masterTags = await getTagsOf('master'); if (point === 'master') { - return masterTags + return masterTags; } - const masterTagsSet = new Set(masterTags) - return (await getTagsOf(point)).filter(tag => !masterTagsSet.has(tag)) -} + const masterTagsSet = new Set(masterTags); + return (await getTagsOf(point)).filter(tag => !masterTagsSet.has(tag)); +}; const getBranchOf = async (point) => { const branches = (await runGit(['branch', '-a', '--contains', point])) .split('\n') .map(branch => branch.trim()) - .filter(branch => !!branch) - const current = branches.find(branch => branch.startsWith('* ')) - return current ? current.slice(2) : branches.shift() -} + .filter(branch => !!branch); + const current = branches.find(branch => branch.startsWith('* ')); + return current ? current.slice(2) : branches.shift(); +}; const getAllBranches = async () => { return (await runGit(['branch', '--remote'])) @@ -55,101 +55,101 @@ const getAllBranches = async () => { .map(branch => branch.trim()) .filter(branch => !!branch) .filter(branch => branch !== 'origin/HEAD -> origin/master') - .sort() -} + .sort(); +}; const getStabilizationBranches = async () => { return (await getAllBranches()) - .filter(branch => /^origin\/\d+-\d+-x$/.test(branch)) -} + .filter(branch => /^origin\/\d+-\d+-x$/.test(branch)); +}; const getPreviousStabilizationBranch = async (current) => { const stabilizationBranches = (await getStabilizationBranches()) - .filter(branch => branch !== current && branch !== `origin/${current}`) + .filter(branch => branch !== current && branch !== `origin/${current}`); if (!semver.valid(current)) { // since we don't seem to be on a stabilization branch right now, // pick a placeholder name that will yield the newest branch // as a comparison point. - current = 'v999.999.999' + current = 'v999.999.999'; } - let newestMatch = null + let newestMatch = null; for (const branch of stabilizationBranches) { if (semver.gte(semverify(branch), semverify(current))) { - continue + continue; } if (newestMatch && semver.lte(semverify(branch), semverify(newestMatch))) { - continue + continue; } - newestMatch = branch + newestMatch = branch; } - return newestMatch -} + return newestMatch; +}; const getPreviousPoint = async (point) => { - const currentBranch = await getBranchOf(point) - const currentTag = (await getTagsOf(point)).filter(tag => tagIsSupported(tag)).pop() - const currentIsStable = tagIsStable(currentTag) + const currentBranch = await getBranchOf(point); + const currentTag = (await getTagsOf(point)).filter(tag => tagIsSupported(tag)).pop(); + const currentIsStable = tagIsStable(currentTag); try { // First see if there's an earlier tag on the same branch // that can serve as a reference point. - let tags = (await getTagsOnBranch(`${point}^`)).filter(tag => tagIsSupported(tag)) + let tags = (await getTagsOnBranch(`${point}^`)).filter(tag => tagIsSupported(tag)); if (currentIsStable) { - tags = tags.filter(tag => tagIsStable(tag)) + tags = tags.filter(tag => tagIsStable(tag)); } if (tags.length) { - return tags.pop() + return tags.pop(); } } catch (error) { - console.log('error', error) + console.log('error', error); } // Otherwise, use the newest stable release that preceeds this branch. // To reach that you may have to walk past >1 branch, e.g. to get past // 2-1-x which never had a stable release. - let branch = currentBranch + let branch = currentBranch; while (branch) { - const prevBranch = await getPreviousStabilizationBranch(branch) - const tags = (await getTagsOnBranch(prevBranch)).filter(tag => tagIsStable(tag)) + const prevBranch = await getPreviousStabilizationBranch(branch); + const tags = (await getTagsOnBranch(prevBranch)).filter(tag => tagIsStable(tag)); if (tags.length) { - return tags.pop() + return tags.pop(); } - branch = prevBranch + branch = prevBranch; } -} +}; async function getReleaseNotes (range, newVersion, explicitLinks) { - const rangeList = range.split('..') || ['HEAD'] - const to = rangeList.pop() - const from = rangeList.pop() || (await getPreviousPoint(to)) + const rangeList = range.split('..') || ['HEAD']; + const to = rangeList.pop(); + const from = rangeList.pop() || (await getPreviousPoint(to)); if (!newVersion) { - newVersion = to + newVersion = to; } - console.log(`Generating release notes between ${from} and ${to} for version ${newVersion}`) - const notes = await notesGenerator.get(from, to, newVersion) + console.log(`Generating release notes between ${from} and ${to} for version ${newVersion}`); + const notes = await notesGenerator.get(from, to, newVersion); const ret = { text: notesGenerator.render(notes, explicitLinks) - } + }; if (notes.unknown.length) { - ret.warning = `You have ${notes.unknown.length} unknown release notes. Please fix them before releasing.` + ret.warning = `You have ${notes.unknown.length} unknown release notes. Please fix them before releasing.`; } - return ret + return ret; } async function main () { const opts = minimist(process.argv.slice(2), { boolean: ['explicit-links', 'help'], string: ['version'] - }) - opts.range = opts._.shift() + }); + opts.range = opts._.shift(); if (opts.help || !opts.range) { - const name = path.basename(process.argv[1]) + const name = path.basename(process.argv[1]); console.log(` easy usage: ${name} version @@ -165,22 +165,22 @@ full usage: ${name} [begin..]end [--version version] [--explicit-links] For example, these invocations are equivalent: ${process.argv[1]} v4.0.1 ${process.argv[1]} v4.0.0..v4.0.1 --version v4.0.1 -`) - return 0 +`); + return 0; } - const notes = await getReleaseNotes(opts.range, opts.version, opts['explicit-links']) - console.log(notes.text) + const notes = await getReleaseNotes(opts.range, opts.version, opts['explicit-links']); + console.log(notes.text); if (notes.warning) { - throw new Error(notes.warning) + throw new Error(notes.warning); } } if (process.mainModule === module) { main().catch((err) => { - console.error('Error Occurred:', err) - process.exit(1) - }) + console.error('Error Occurred:', err); + process.exit(1); + }); } -module.exports = getReleaseNotes +module.exports = getReleaseNotes; diff --git a/script/release/notes/notes.js b/script/release/notes/notes.js index 014c184bb0d50..0be22e599dd6e 100644 --- a/script/release/notes/notes.js +++ b/script/release/notes/notes.js @@ -1,75 +1,75 @@ #!/usr/bin/env node -const childProcess = require('child_process') -const fs = require('fs') -const os = require('os') -const path = require('path') +const childProcess = require('child_process'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); -const { GitProcess } = require('dugite') +const { GitProcess } = require('dugite'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) -const semver = require('semver') +}); +const semver = require('semver'); -const { ELECTRON_VERSION, SRC_DIR } = require('../../lib/utils') +const { ELECTRON_VERSION, SRC_DIR } = require('../../lib/utils'); -const MAX_FAIL_COUNT = 3 -const CHECK_INTERVAL = 5000 +const MAX_FAIL_COUNT = 3; +const CHECK_INTERVAL = 5000; -const CACHE_DIR = path.resolve(__dirname, '.cache') -const NO_NOTES = 'No notes' -const FOLLOW_REPOS = ['electron/electron', 'electron/node'] +const CACHE_DIR = path.resolve(__dirname, '.cache'); +const NO_NOTES = 'No notes'; +const FOLLOW_REPOS = ['electron/electron', 'electron/node']; -const breakTypes = new Set(['breaking-change']) -const docTypes = new Set(['doc', 'docs']) -const featTypes = new Set(['feat', 'feature']) -const fixTypes = new Set(['fix']) -const otherTypes = new Set(['spec', 'build', 'test', 'chore', 'deps', 'refactor', 'tools', 'vendor', 'perf', 'style', 'ci']) -const knownTypes = new Set([...breakTypes.keys(), ...docTypes.keys(), ...featTypes.keys(), ...fixTypes.keys(), ...otherTypes.keys()]) +const breakTypes = new Set(['breaking-change']); +const docTypes = new Set(['doc', 'docs']); +const featTypes = new Set(['feat', 'feature']); +const fixTypes = new Set(['fix']); +const otherTypes = new Set(['spec', 'build', 'test', 'chore', 'deps', 'refactor', 'tools', 'vendor', 'perf', 'style', 'ci']); +const knownTypes = new Set([...breakTypes.keys(), ...docTypes.keys(), ...featTypes.keys(), ...fixTypes.keys(), ...otherTypes.keys()]); const runGit = async (dir, args) => { - const response = await GitProcess.exec(args, dir) + const response = await GitProcess.exec(args, dir); if (response.exitCode !== 0) { - throw new Error(response.stderr.trim()) + throw new Error(response.stderr.trim()); } - return response.stdout.trim() -} + return response.stdout.trim(); +}; const getCommonAncestor = async (dir, point1, point2) => { - return runGit(dir, ['merge-base', point1, point2]) -} + return runGit(dir, ['merge-base', point1, point2]); +}; const setPullRequest = (commit, owner, repo, number) => { if (!owner || !repo || !number) { - throw new Error(JSON.stringify({ owner, repo, number }, null, 2)) + throw new Error(JSON.stringify({ owner, repo, number }, null, 2)); } if (!commit.originalPr) { - commit.originalPr = commit.pr + commit.originalPr = commit.pr; } - commit.pr = { owner, repo, number } + commit.pr = { owner, repo, number }; if (!commit.originalPr) { - commit.originalPr = commit.pr + commit.originalPr = commit.pr; } -} +}; const getNoteFromClerk = async (number, owner, repo) => { - const comments = await getComments(number, owner, repo) - if (!comments || !comments.data) return + const comments = await getComments(number, owner, repo); + if (!comments || !comments.data) return; - const CLERK_LOGIN = 'release-clerk[bot]' - const CLERK_NO_NOTES = '**No Release Notes**' - const PERSIST_LEAD = '**Release Notes Persisted**\n\n' - const QUOTE_LEAD = '> ' + const CLERK_LOGIN = 'release-clerk[bot]'; + const CLERK_NO_NOTES = '**No Release Notes**'; + const PERSIST_LEAD = '**Release Notes Persisted**\n\n'; + const QUOTE_LEAD = '> '; for (const comment of comments.data.reverse()) { if (comment.user.login !== CLERK_LOGIN) { - continue + continue; } if (comment.body === CLERK_NO_NOTES) { - return NO_NOTES + return NO_NOTES; } if (comment.body.startsWith(PERSIST_LEAD)) { return comment.body @@ -79,10 +79,10 @@ const getNoteFromClerk = async (number, owner, repo) => { .filter(line => line.startsWith(QUOTE_LEAD)) // notes are quoted .map(line => line.slice(QUOTE_LEAD.length)) // unquote the lines .join(' ') // join the note lines - .trim() + .trim(); } } -} +}; // copied from https://github.com/electron/clerk/blob/master/src/index.ts#L4-L13 const OMIT_FROM_RELEASE_NOTES_KEYS = [ @@ -94,36 +94,36 @@ const OMIT_FROM_RELEASE_NOTES_KEYS = [ 'nothing', 'empty', 'blank' -] +]; const getNoteFromBody = body => { if (!body) { - return null + return null; } - const NOTE_PREFIX = 'Notes: ' - const NOTE_HEADER = '#### Release Notes' + const NOTE_PREFIX = 'Notes: '; + const NOTE_HEADER = '#### Release Notes'; let note = body .split(/\r?\n\r?\n/) // split into paragraphs .map(paragraph => paragraph.trim()) .map(paragraph => paragraph.startsWith(NOTE_HEADER) ? paragraph.slice(NOTE_HEADER.length).trim() : paragraph) - .find(paragraph => paragraph.startsWith(NOTE_PREFIX)) + .find(paragraph => paragraph.startsWith(NOTE_PREFIX)); if (note) { note = note .slice(NOTE_PREFIX.length) .replace(//, '') // '' .replace(/\r?\n/, ' ') // remove newlines - .trim() + .trim(); } if (note && OMIT_FROM_RELEASE_NOTES_KEYS.includes(note.toLowerCase())) { - return NO_NOTES + return NO_NOTES; } - return note -} + return note; +}; /** * Looks for our project's conventions in the commit message: @@ -138,71 +138,71 @@ const getNoteFromBody = body => { */ const parseCommitMessage = (commitMessage, owner, repo, commit = {}) => { // split commitMessage into subject & body - let subject = commitMessage - let body = '' - const pos = subject.indexOf('\n') + let subject = commitMessage; + let body = ''; + const pos = subject.indexOf('\n'); if (pos !== -1) { - body = subject.slice(pos).trim() - subject = subject.slice(0, pos).trim() + body = subject.slice(pos).trim(); + subject = subject.slice(0, pos).trim(); } if (!commit.originalSubject) { - commit.originalSubject = subject + commit.originalSubject = subject; } if (body) { - commit.body = body + commit.body = body; - const note = getNoteFromBody(body) - if (note) { commit.note = note } + const note = getNoteFromBody(body); + if (note) { commit.note = note; } } // if the subject ends in ' (#dddd)', treat it as a pull request id - let match + let match; if ((match = subject.match(/^(.*)\s\(#(\d+)\)$/))) { - setPullRequest(commit, owner, repo, parseInt(match[2])) - subject = match[1] + setPullRequest(commit, owner, repo, parseInt(match[2])); + subject = match[1]; } // if the subject begins with 'word:', treat it as a semantic commit if ((match = subject.match(/^(\w+):\s(.*)$/))) { - const type = match[1].toLocaleLowerCase() + const type = match[1].toLocaleLowerCase(); if (knownTypes.has(type)) { - commit.type = type - subject = match[2] + commit.type = type; + subject = match[2]; } } // Check for GitHub commit message that indicates a PR if ((match = subject.match(/^Merge pull request #(\d+) from (.*)$/))) { - setPullRequest(commit, owner, repo, parseInt(match[1])) - commit.pr.branch = match[2].trim() + setPullRequest(commit, owner, repo, parseInt(match[1])); + commit.pr.branch = match[2].trim(); } // Check for a trop comment that indicates a PR if ((match = commitMessage.match(/\bBackport of #(\d+)\b/))) { - setPullRequest(commit, owner, repo, parseInt(match[1])) + setPullRequest(commit, owner, repo, parseInt(match[1])); } // https://help.github.com/articles/closing-issues-using-keywords/ if ((match = subject.match(/\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved|for)\s#(\d+)\b/))) { - commit.issueNumber = parseInt(match[1]) + commit.issueNumber = parseInt(match[1]); if (!commit.type) { - commit.type = 'fix' + commit.type = 'fix'; } } // look for 'fixes' in markdown; e.g. 'Fixes [#8952](https://github.com/electron/electron/issues/8952)' if (!commit.issueNumber && ((match = commitMessage.match(/Fixes \[#(\d+)\]\(https:\/\/github.com\/(\w+)\/(\w+)\/issues\/(\d+)\)/)))) { - commit.issueNumber = parseInt(match[1]) + commit.issueNumber = parseInt(match[1]); if (commit.pr && commit.pr.number === commit.issueNumber) { - commit.pr = null + commit.pr = null; } if (commit.originalPr && commit.originalPr.number === commit.issueNumber) { - commit.originalPr = null + commit.originalPr = null; } if (!commit.type) { - commit.type = 'fix' + commit.type = 'fix'; } } @@ -211,55 +211,55 @@ const parseCommitMessage = (commitMessage, owner, repo, commit = {}) => { .split(/\r?\n/) // split into lines .map(line => line.trim()) .some(line => line.startsWith('BREAKING CHANGE'))) { - commit.type = 'breaking-change' + commit.type = 'breaking-change'; } // Check for a reversion commit if ((match = body.match(/This reverts commit ([a-f0-9]{40})\./))) { - commit.revertHash = match[1] + commit.revertHash = match[1]; } // Edge case: manual backport where commit has `owner/repo#pull` notation if (commitMessage.toLowerCase().includes('backport') && ((match = commitMessage.match(/\b(\w+)\/(\w+)#(\d+)\b/)))) { - const [, owner, repo, number] = match + const [, owner, repo, number] = match; if (FOLLOW_REPOS.includes(`${owner}/${repo}`)) { - setPullRequest(commit, owner, repo, number) + setPullRequest(commit, owner, repo, number); } } // Edge case: manual backport where commit has a link to the backport PR if (commitMessage.includes('ackport') && ((match = commitMessage.match(/https:\/\/github\.com\/(\w+)\/(\w+)\/pull\/(\d+)/)))) { - const [, owner, repo, number] = match + const [, owner, repo, number] = match; if (FOLLOW_REPOS.includes(`${owner}/${repo}`)) { - setPullRequest(commit, owner, repo, number) + setPullRequest(commit, owner, repo, number); } } // Legacy commits: pre-semantic commits if (!commit.type || commit.type === 'chore') { - const commitMessageLC = commitMessage.toLocaleLowerCase() + const commitMessageLC = commitMessage.toLocaleLowerCase(); if ((match = commitMessageLC.match(/\bchore\((\w+)\):/))) { // example: 'Chore(docs): description' - commit.type = knownTypes.has(match[1]) ? match[1] : 'chore' + commit.type = knownTypes.has(match[1]) ? match[1] : 'chore'; } else if (commitMessageLC.match(/\b(?:fix|fixes|fixed)/)) { // example: 'fix a bug' - commit.type = 'fix' + commit.type = 'fix'; } else if (commitMessageLC.match(/\[(?:docs|doc)\]/)) { // example: '[docs] - commit.type = 'doc' + commit.type = 'doc'; } } - commit.subject = subject.trim() - return commit -} + commit.subject = subject.trim(); + return commit; +}; const getLocalCommitHashes = async (dir, ref) => { - const args = ['log', '-z', '--format=%H', ref] - return (await runGit(dir, args)).split('\0').map(hash => hash.trim()) -} + const args = ['log', '-z', '--format=%H', ref]; + return (await runGit(dir, args)).split('\0').map(hash => hash.trim()); +}; /* * possible properties: @@ -267,75 +267,75 @@ const getLocalCommitHashes = async (dir, ref) => { * pr { owner, repo, number, branch }, revertHash, subject, type */ const getLocalCommitDetails = async (module, point1, point2) => { - const { owner, repo, dir } = module + const { owner, repo, dir } = module; - const fieldSep = '||' - const format = ['%H', '%P', '%aE', '%B'].join(fieldSep) - const args = ['log', '-z', '--cherry-pick', '--right-only', '--first-parent', `--format=${format}`, `${point1}..${point2}`] - const commits = (await runGit(dir, args)).split('\0').map(field => field.trim()) - const details = [] + const fieldSep = '||'; + const format = ['%H', '%P', '%aE', '%B'].join(fieldSep); + const args = ['log', '-z', '--cherry-pick', '--right-only', '--first-parent', `--format=${format}`, `${point1}..${point2}`]; + const commits = (await runGit(dir, args)).split('\0').map(field => field.trim()); + const details = []; for (const commit of commits) { if (!commit) { - continue + continue; } - const [hash, parentHashes, email, commitMessage] = commit.split(fieldSep, 4).map(field => field.trim()) + const [hash, parentHashes, email, commitMessage] = commit.split(fieldSep, 4).map(field => field.trim()); details.push(parseCommitMessage(commitMessage, owner, repo, { email, hash, owner, repo, parentHashes: parentHashes.split() - })) + })); } - return details -} + return details; +}; const checkCache = async (name, operation) => { - const filename = path.resolve(CACHE_DIR, name) + const filename = path.resolve(CACHE_DIR, name); if (fs.existsSync(filename)) { - return JSON.parse(fs.readFileSync(filename, 'utf8')) + return JSON.parse(fs.readFileSync(filename, 'utf8')); } - const response = await operation() + const response = await operation(); if (response) { - fs.writeFileSync(filename, JSON.stringify(response)) + fs.writeFileSync(filename, JSON.stringify(response)); } - return response -} + return response; +}; // helper function to add some resiliency to volatile GH api endpoints async function runRetryable (fn, maxRetries) { - let lastError + let lastError; for (let i = 0; i < maxRetries; i++) { try { - return await fn() + return await fn(); } catch (error) { - await new Promise((resolve, reject) => setTimeout(resolve, CHECK_INTERVAL)) - lastError = error + await new Promise((resolve, reject) => setTimeout(resolve, CHECK_INTERVAL)); + lastError = error; } } // Silently eat 404s. - if (lastError.status !== 404) throw lastError + if (lastError.status !== 404) throw lastError; } const getPullRequest = async (number, owner, repo) => { - const name = `${owner}-${repo}-pull-${number}` - const retryableFunc = () => octokit.pulls.get({ pull_number: number, owner, repo }) - return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT)) -} + const name = `${owner}-${repo}-pull-${number}`; + const retryableFunc = () => octokit.pulls.get({ pull_number: number, owner, repo }); + return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT)); +}; const getComments = async (number, owner, repo) => { - const name = `${owner}-${repo}-issue-${number}-comments` - const retryableFunc = () => octokit.issues.listComments({ issue_number: number, owner, repo, per_page: 100 }) - return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT)) -} + const name = `${owner}-${repo}-issue-${number}-comments`; + const retryableFunc = () => octokit.issues.listComments({ issue_number: number, owner, repo, per_page: 100 }); + return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT)); +}; const addRepoToPool = async (pool, repo, from, to) => { - const commonAncestor = await getCommonAncestor(repo.dir, from, to) - const oldHashes = await getLocalCommitHashes(repo.dir, from) - oldHashes.forEach(hash => { pool.processedHashes.add(hash) }) - const commits = await getLocalCommitDetails(repo, commonAncestor, to) - pool.commits.push(...commits) -} + const commonAncestor = await getCommonAncestor(repo.dir, from, to); + const oldHashes = await getLocalCommitHashes(repo.dir, from); + oldHashes.forEach(hash => { pool.processedHashes.add(hash); }); + const commits = await getLocalCommitDetails(repo, commonAncestor, to); + pool.commits.push(...commits); +}; /*** **** Other Repos @@ -345,21 +345,21 @@ const addRepoToPool = async (pool, repo, from, to) => { const getDepsVariable = async (ref, key) => { // get a copy of that reference point's DEPS file - const deps = await runGit(ELECTRON_VERSION, ['show', `${ref}:DEPS`]) - const filename = path.resolve(os.tmpdir(), 'DEPS') - fs.writeFileSync(filename, deps) + const deps = await runGit(ELECTRON_VERSION, ['show', `${ref}:DEPS`]); + const filename = path.resolve(os.tmpdir(), 'DEPS'); + fs.writeFileSync(filename, deps); // query the DEPS file const response = childProcess.spawnSync( 'gclient', ['getdep', '--deps-file', filename, '--var', key], { encoding: 'utf8' } - ) + ); // cleanup - fs.unlinkSync(filename) - return response.stdout.trim() -} + fs.unlinkSync(filename); + return response.stdout.trim(); +}; const getDependencyCommitsGN = async (pool, fromRef, toRef) => { const repos = [{ // just node @@ -367,16 +367,16 @@ const getDependencyCommitsGN = async (pool, fromRef, toRef) => { repo: 'node', dir: path.resolve(SRC_DIR, 'third_party', 'electron_node'), deps_variable_name: 'node_version' - }] + }]; for (const repo of repos) { // the 'DEPS' file holds the dependency reference point - const key = repo.deps_variable_name - const from = await getDepsVariable(fromRef, key) - const to = await getDepsVariable(toRef, key) - await addRepoToPool(pool, repo, from, to) + const key = repo.deps_variable_name; + const from = await getDepsVariable(fromRef, key); + const to = await getDepsVariable(toRef, key); + await addRepoToPool(pool, repo, from, to); } -} +}; // Changes are interesting if they make a change relative to a previous // release in the same series. For example if you fix a Y.0.0 bug, that @@ -388,17 +388,17 @@ const getDependencyCommitsGN = async (pool, fromRef, toRef) => { // branches' changes. Otherwise we will have an overwhelmingly long // list of mostly-irrelevant changes. const shouldIncludeMultibranchChanges = (version) => { - let show = true + let show = true; if (semver.valid(version)) { - const prerelease = semver.prerelease(version) + const prerelease = semver.prerelease(version); show = prerelease ? parseInt(prerelease.pop()) > 1 - : semver.patch(version) > 0 + : semver.patch(version) > 0; } - return show -} + return show; +}; /*** **** Main @@ -406,131 +406,131 @@ const shouldIncludeMultibranchChanges = (version) => { const getNotes = async (fromRef, toRef, newVersion) => { if (!fs.existsSync(CACHE_DIR)) { - fs.mkdirSync(CACHE_DIR) + fs.mkdirSync(CACHE_DIR); } const pool = { processedHashes: new Set(), commits: [] - } + }; // get the electron/electron commits - const electron = { owner: 'electron', repo: 'electron', dir: ELECTRON_VERSION } - await addRepoToPool(pool, electron, fromRef, toRef) + const electron = { owner: 'electron', repo: 'electron', dir: ELECTRON_VERSION }; + await addRepoToPool(pool, electron, fromRef, toRef); // Don't include submodules if comparing across major versions; // there's just too much churn otherwise. const includeDeps = semver.valid(fromRef) && semver.valid(toRef) && - semver.major(fromRef) === semver.major(toRef) + semver.major(fromRef) === semver.major(toRef); if (includeDeps) { - await getDependencyCommitsGN(pool, fromRef, toRef) + await getDependencyCommitsGN(pool, fromRef, toRef); } // remove any old commits - pool.commits = pool.commits.filter(commit => !pool.processedHashes.has(commit.hash)) + pool.commits = pool.commits.filter(commit => !pool.processedHashes.has(commit.hash)); // if a commmit _and_ revert occurred in the unprocessed set, skip them both for (const commit of pool.commits) { - const revertHash = commit.revertHash + const revertHash = commit.revertHash; if (!revertHash) { - continue + continue; } - const revert = pool.commits.find(commit => commit.hash === revertHash) + const revert = pool.commits.find(commit => commit.hash === revertHash); if (!revert) { - continue + continue; } - commit.note = NO_NOTES - revert.note = NO_NOTES - pool.processedHashes.add(commit.hash) - pool.processedHashes.add(revertHash) + commit.note = NO_NOTES; + revert.note = NO_NOTES; + pool.processedHashes.add(commit.hash); + pool.processedHashes.add(revertHash); } // scrape PRs for release note 'Notes:' comments for (const commit of pool.commits) { - let pr = commit.pr + let pr = commit.pr; - let prSubject + let prSubject; while (pr && !commit.note) { - const note = await getNoteFromClerk(pr.number, pr.owner, pr.repo) + const note = await getNoteFromClerk(pr.number, pr.owner, pr.repo); if (note) { - commit.note = note + commit.note = note; } // if we already have all the data we need, stop scraping the PRs if (commit.note && commit.type && prSubject) { - break + break; } - const prData = await getPullRequest(pr.number, pr.owner, pr.repo) + const prData = await getPullRequest(pr.number, pr.owner, pr.repo); if (!prData || !prData.data) { - break + break; } // try to pull a release note from the pull comment - const prParsed = parseCommitMessage(`${prData.data.title}\n\n${prData.data.body}`, pr.owner, pr.repo) + const prParsed = parseCommitMessage(`${prData.data.title}\n\n${prData.data.body}`, pr.owner, pr.repo); if (!commit.note) { - commit.note = prParsed.note + commit.note = prParsed.note; } if (!commit.type || prParsed.type === 'breaking-change') { - commit.type = prParsed.type + commit.type = prParsed.type; } - prSubject = prSubject || prParsed.subject + prSubject = prSubject || prParsed.subject; - pr = prParsed.pr && (prParsed.pr.number !== pr.number) ? prParsed.pr : null + pr = prParsed.pr && (prParsed.pr.number !== pr.number) ? prParsed.pr : null; } // if we still don't have a note, it's because someone missed a 'Notes: // comment in a PR somewhere... use the PR subject as a fallback. - commit.note = commit.note || prSubject + commit.note = commit.note || prSubject; } // remove non-user-facing commits pool.commits = pool.commits .filter(commit => commit.note !== NO_NOTES) - .filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/))) + .filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/))); if (!shouldIncludeMultibranchChanges(newVersion)) { // load all the prDatas await Promise.all( pool.commits.map(commit => (async () => { - const { pr } = commit + const { pr } = commit; if (typeof pr === 'object') { - const prData = await getPullRequest(pr.number, pr.owner, pr.repo) + const prData = await getPullRequest(pr.number, pr.owner, pr.repo); if (prData) { - commit.prData = prData + commit.prData = prData; } } })()) - ) + ); // remove items that already landed in a previous major/minor series pool.commits = pool.commits .filter(commit => { if (!commit.prData) { - return true + return true; } const reducer = (accumulator, current) => { - if (!semver.valid(accumulator)) { return current } - if (!semver.valid(current)) { return accumulator } - return semver.lt(accumulator, current) ? accumulator : current - } + if (!semver.valid(accumulator)) { return current; } + if (!semver.valid(current)) { return accumulator; } + return semver.lt(accumulator, current) ? accumulator : current; + }; const earliestRelease = commit.prData.data.labels .map(label => label.name.match(/merged\/(\d+)-(\d+)-x/)) .filter(label => !!label) .map(label => `${label[1]}.${label[2]}.0`) - .reduce(reducer, null) + .reduce(reducer, null); if (!semver.valid(earliestRelease)) { - return true + return true; } - return semver.diff(earliestRelease, newVersion).includes('patch') - }) + return semver.diff(earliestRelease, newVersion).includes('patch'); + }); } - pool.commits = removeSupercededChromiumUpdates(pool.commits) + pool.commits = removeSupercededChromiumUpdates(pool.commits); const notes = { breaking: [], @@ -540,78 +540,78 @@ const getNotes = async (fromRef, toRef, newVersion) => { other: [], unknown: [], name: newVersion - } + }; pool.commits.forEach(commit => { - const str = commit.type + const str = commit.type; if (!str) { - notes.unknown.push(commit) + notes.unknown.push(commit); } else if (breakTypes.has(str)) { - notes.breaking.push(commit) + notes.breaking.push(commit); } else if (docTypes.has(str)) { - notes.docs.push(commit) + notes.docs.push(commit); } else if (featTypes.has(str)) { - notes.feat.push(commit) + notes.feat.push(commit); } else if (fixTypes.has(str)) { - notes.fix.push(commit) + notes.fix.push(commit); } else if (otherTypes.has(str)) { - notes.other.push(commit) + notes.other.push(commit); } else { - notes.unknown.push(commit) + notes.unknown.push(commit); } - }) + }); - return notes -} + return notes; +}; const removeSupercededChromiumUpdates = (commits) => { - const chromiumRegex = /^Updated Chromium to \d+\.\d+\.\d+\.\d+/ - const updates = commits.filter(commit => (commit.note || commit.subject).match(chromiumRegex)) - const keepers = commits.filter(commit => !updates.includes(commit)) + const chromiumRegex = /^Updated Chromium to \d+\.\d+\.\d+\.\d+/; + const updates = commits.filter(commit => (commit.note || commit.subject).match(chromiumRegex)); + const keepers = commits.filter(commit => !updates.includes(commit)); // keep the newest update. if (updates.length) { - updates.sort((a, b) => a.originalPr.number - b.originalPr.number) - keepers.push(updates.pop()) + updates.sort((a, b) => a.originalPr.number - b.originalPr.number); + keepers.push(updates.pop()); } - return keepers -} + return keepers; +}; /*** **** Render ***/ const renderLink = (commit, explicitLinks) => { - let link - const pr = commit.originalPr + let link; + const pr = commit.originalPr; if (pr) { - const { owner, repo, number } = pr - const url = `https://github.com/${owner}/${repo}/pull/${number}` + const { owner, repo, number } = pr; + const url = `https://github.com/${owner}/${repo}/pull/${number}`; const text = owner === 'electron' && repo === 'electron' ? `#${number}` - : `${owner}/${repo}#${number}` - link = explicitLinks ? `[${text}](${url})` : text + : `${owner}/${repo}#${number}`; + link = explicitLinks ? `[${text}](${url})` : text; } else { - const { owner, repo, hash } = commit - const url = `https://github.com/${owner}/${repo}/commit/${hash}` + const { owner, repo, hash } = commit; + const url = `https://github.com/${owner}/${repo}/commit/${hash}`; const text = owner === 'electron' && repo === 'electron' ? `${hash.slice(0, 8)}` - : `${owner}/${repo}@${hash.slice(0, 8)}` - link = explicitLinks ? `[${text}](${url})` : text + : `${owner}/${repo}@${hash.slice(0, 8)}`; + link = explicitLinks ? `[${text}](${url})` : text; } - return link -} + return link; +}; const renderCommit = (commit, explicitLinks) => { // clean up the note - let note = commit.note || commit.subject - note = note.trim() + let note = commit.note || commit.subject; + note = note.trim(); if (note.length !== 0) { - note = note[0].toUpperCase() + note.substr(1) + note = note[0].toUpperCase() + note.substr(1); if (!note.endsWith('.')) { - note = note + '.' + note = note + '.'; } const commonVerbs = { @@ -631,57 +631,57 @@ const renderCommit = (commit, explicitLinks) => { Stopped: ['Stop'], Updated: ['Update'], Upgraded: ['Upgrade'] - } + }; for (const [key, values] of Object.entries(commonVerbs)) { for (const value of values) { - const start = `${value} ` + const start = `${value} `; if (note.startsWith(start)) { - note = `${key} ${note.slice(start.length)}` + note = `${key} ${note.slice(start.length)}`; } } } } - const link = renderLink(commit, explicitLinks) + const link = renderLink(commit, explicitLinks); - return { note, link } -} + return { note, link }; +}; const renderNotes = (notes, explicitLinks) => { - const rendered = [`# Release Notes for ${notes.name}\n\n`] + const rendered = [`# Release Notes for ${notes.name}\n\n`]; const renderSection = (title, commits) => { if (commits.length === 0) { - return + return; } - const notes = new Map() + const notes = new Map(); for (const note of commits.map(commit => renderCommit(commit, explicitLinks))) { if (!notes.has(note.note)) { - notes.set(note.note, [note.link]) + notes.set(note.note, [note.link]); } else { - notes.get(note.note).push(note.link) + notes.get(note.note).push(note.link); } } - rendered.push(`## ${title}\n\n`) - const lines = [] - notes.forEach((links, key) => lines.push(` * ${key} ${links.map(link => link.toString()).sort().join(', ')}\n`)) - rendered.push(...lines.sort(), '\n') - } + rendered.push(`## ${title}\n\n`); + const lines = []; + notes.forEach((links, key) => lines.push(` * ${key} ${links.map(link => link.toString()).sort().join(', ')}\n`)); + rendered.push(...lines.sort(), '\n'); + }; - renderSection('Breaking Changes', notes.breaking) - renderSection('Features', notes.feat) - renderSection('Fixes', notes.fix) - renderSection('Other Changes', notes.other) + renderSection('Breaking Changes', notes.breaking); + renderSection('Features', notes.feat); + renderSection('Fixes', notes.fix); + renderSection('Other Changes', notes.other); if (notes.docs.length) { - const docs = notes.docs.map(commit => renderLink(commit, explicitLinks)).sort() - rendered.push('## Documentation\n\n', ` * Documentation changes: ${docs.join(', ')}\n`, '\n') + const docs = notes.docs.map(commit => renderLink(commit, explicitLinks)).sort(); + rendered.push('## Documentation\n\n', ` * Documentation changes: ${docs.join(', ')}\n`, '\n'); } - renderSection('Unknown', notes.unknown) + renderSection('Unknown', notes.unknown); - return rendered.join('') -} + return rendered.join(''); +}; /*** **** Module @@ -690,4 +690,4 @@ const renderNotes = (notes, explicitLinks) => { module.exports = { get: getNotes, render: renderNotes -} +}; diff --git a/script/release/prepare-release.js b/script/release/prepare-release.js index 0831d6d992733..91e9a4fc87666 100755 --- a/script/release/prepare-release.js +++ b/script/release/prepare-release.js @@ -1,105 +1,105 @@ #!/usr/bin/env node -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); const args = require('minimist')(process.argv.slice(2), { boolean: ['automaticRelease', 'notesOnly', 'stable'] -}) -const ciReleaseBuild = require('./ci-release-build') +}); +const ciReleaseBuild = require('./ci-release-build'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) -const { execSync } = require('child_process') -const { GitProcess } = require('dugite') +}); +const { execSync } = require('child_process'); +const { GitProcess } = require('dugite'); -const path = require('path') -const readline = require('readline') -const releaseNotesGenerator = require('./notes/index.js') -const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js') -const bumpType = args._[0] -const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron' +const path = require('path'); +const readline = require('readline'); +const releaseNotesGenerator = require('./notes/index.js'); +const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js'); +const bumpType = args._[0]; +const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron'; -require('colors') -const pass = '✓'.green -const fail = '✗'.red +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; if (!bumpType && !args.notesOnly) { console.log('Usage: prepare-release [stable | minor | beta | nightly]' + - ' (--stable) (--notesOnly) (--automaticRelease) (--branch)') - process.exit(1) + ' (--stable) (--notesOnly) (--automaticRelease) (--branch)'); + process.exit(1); } async function getNewVersion (dryRun) { if (!dryRun) { - console.log(`Bumping for new "${bumpType}" version.`) + console.log(`Bumping for new "${bumpType}" version.`); } - const bumpScript = path.join(__dirname, 'version-bumper.js') - const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`] - if (dryRun) scriptArgs.push('--dryRun') + const bumpScript = path.join(__dirname, 'version-bumper.js'); + const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`]; + if (dryRun) scriptArgs.push('--dryRun'); try { - let bumpVersion = execSync(scriptArgs.join(' '), { encoding: 'UTF-8' }) - bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim() - const newVersion = `v${bumpVersion}` + let bumpVersion = execSync(scriptArgs.join(' '), { encoding: 'UTF-8' }); + bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim(); + const newVersion = `v${bumpVersion}`; if (!dryRun) { - console.log(`${pass} Successfully bumped version to ${newVersion}`) + console.log(`${pass} Successfully bumped version to ${newVersion}`); } - return newVersion + return newVersion; } catch (err) { - console.log(`${fail} Could not bump version, error was:`, err) - throw err + console.log(`${fail} Could not bump version, error was:`, err); + throw err; } } async function getReleaseNotes (currentBranch, newVersion) { if (bumpType === 'nightly') { - return { text: 'Nightlies do not get release notes, please compare tags for info.' } + return { text: 'Nightlies do not get release notes, please compare tags for info.' }; } - console.log(`Generating release notes for ${currentBranch}.`) - const releaseNotes = await releaseNotesGenerator(currentBranch, newVersion) + console.log(`Generating release notes for ${currentBranch}.`); + const releaseNotes = await releaseNotesGenerator(currentBranch, newVersion); if (releaseNotes.warning) { - console.warn(releaseNotes.warning) + console.warn(releaseNotes.warning); } - return releaseNotes + return releaseNotes; } async function createRelease (branchToTarget, isBeta) { - const newVersion = await getNewVersion() - const releaseNotes = await getReleaseNotes(branchToTarget, newVersion) - await tagRelease(newVersion) + const newVersion = await getNewVersion(); + const releaseNotes = await getReleaseNotes(branchToTarget, newVersion); + await tagRelease(newVersion); - console.log('Checking for existing draft release.') + console.log('Checking for existing draft release.'); const releases = await octokit.repos.listReleases({ owner: 'electron', repo: targetRepo }).catch(err => { - console.log(`${fail} Could not get releases. Error was: `, err) - }) + console.log(`${fail} Could not get releases. Error was: `, err); + }); const drafts = releases.data.filter(release => release.draft && - release.tag_name === newVersion) + release.tag_name === newVersion); if (drafts.length > 0) { console.log(`${fail} Aborting because draft release for - ${drafts[0].tag_name} already exists.`) - process.exit(1) + ${drafts[0].tag_name} already exists.`); + process.exit(1); } - console.log(`${pass} A draft release does not exist; creating one.`) + console.log(`${pass} A draft release does not exist; creating one.`); - let releaseBody - let releaseIsPrelease = false + let releaseBody; + let releaseIsPrelease = false; if (isBeta) { if (newVersion.indexOf('nightly') > 0) { releaseBody = 'Note: This is a nightly release. Please file new issues ' + 'for any bugs you find in it.\n \n This release is published to npm ' + 'under the nightly tag and can be installed via npm install electron@nightly, ' + - `or npm i electron-nightly@${newVersion.substr(1)}.\n \n ${releaseNotes.text}` + `or npm i electron-nightly@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`; } else { releaseBody = 'Note: This is a beta release. Please file new issues ' + 'for any bugs you find in it.\n \n This release is published to npm ' + 'under the beta tag and can be installed via npm install electron@beta, ' + - `or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}` + `or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`; } - releaseIsPrelease = true + releaseIsPrelease = true; } else { - releaseBody = releaseNotes.text + releaseBody = releaseNotes.text; } const release = await octokit.repos.createRelease({ @@ -112,22 +112,22 @@ async function createRelease (branchToTarget, isBeta) { prerelease: releaseIsPrelease, target_commitish: newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget }).catch(err => { - console.log(`${fail} Error creating new release: `, err) - process.exit(1) - }) + console.log(`${fail} Error creating new release: `, err); + process.exit(1); + }); - console.log(`Release has been created with id: ${release.data.id}.`) - console.log(`${pass} Draft release for ${newVersion} successful.`) + console.log(`Release has been created with id: ${release.data.id}.`); + console.log(`${pass} Draft release for ${newVersion} successful.`); } async function pushRelease (branch) { - const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR) + const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR); if (pushDetails.exitCode === 0) { console.log(`${pass} Successfully pushed the release. Wait for ` + - 'release builds to finish before running "npm run release".') + 'release builds to finish before running "npm run release".'); } else { - console.log(`${fail} Error pushing the release: ${pushDetails.stderr}`) - process.exit(1) + console.log(`${fail} Error pushing the release: ${pushDetails.stderr}`); + process.exit(1); } } @@ -135,34 +135,34 @@ async function runReleaseBuilds (branch) { await ciReleaseBuild(branch, { ghRelease: true, automaticRelease: args.automaticRelease - }) + }); } async function tagRelease (version) { - console.log(`Tagging release ${version}.`) - const checkoutDetails = await GitProcess.exec(['tag', '-a', '-m', version, version], ELECTRON_DIR) + console.log(`Tagging release ${version}.`); + const checkoutDetails = await GitProcess.exec(['tag', '-a', '-m', version, version], ELECTRON_DIR); if (checkoutDetails.exitCode === 0) { - console.log(`${pass} Successfully tagged ${version}.`) + console.log(`${pass} Successfully tagged ${version}.`); } else { console.log(`${fail} Error tagging ${version}: ` + - `${checkoutDetails.stderr}`) - process.exit(1) + `${checkoutDetails.stderr}`); + process.exit(1); } } async function verifyNewVersion () { - const newVersion = await getNewVersion(true) - let response + const newVersion = await getNewVersion(true); + let response; if (args.automaticRelease) { - response = 'y' + response = 'y'; } else { - response = await promptForVersion(newVersion) + response = await promptForVersion(newVersion); } if (response.match(/^y/i)) { - console.log(`${pass} Starting release of ${newVersion}`) + console.log(`${pass} Starting release of ${newVersion}`); } else { - console.log(`${fail} Aborting release of ${newVersion}`) - process.exit() + console.log(`${fail} Aborting release of ${newVersion}`); + process.exit(); } } @@ -171,44 +171,44 @@ async function promptForVersion (version) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout - }) + }); rl.question(`Do you want to create the release ${version.green} (y/N)? `, (answer) => { - rl.close() - resolve(answer) - }) - }) + rl.close(); + resolve(answer); + }); + }); } // function to determine if there have been commits to master since the last release async function changesToRelease () { - const lastCommitWasRelease = new RegExp('^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$', 'g') - const lastCommit = await GitProcess.exec(['log', '-n', '1', '--pretty=format:\'%s\''], ELECTRON_DIR) - return !lastCommitWasRelease.test(lastCommit.stdout) + const lastCommitWasRelease = new RegExp('^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$', 'g'); + const lastCommit = await GitProcess.exec(['log', '-n', '1', '--pretty=format:\'%s\''], ELECTRON_DIR); + return !lastCommitWasRelease.test(lastCommit.stdout); } async function prepareRelease (isBeta, notesOnly) { if (args.dryRun) { - const newVersion = await getNewVersion(true) - console.log(newVersion) + const newVersion = await getNewVersion(true); + console.log(newVersion); } else { - const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(ELECTRON_DIR) + const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(ELECTRON_DIR); if (notesOnly) { - const newVersion = await getNewVersion(true) - const releaseNotes = await getReleaseNotes(currentBranch, newVersion) - console.log(`Draft release notes are: \n${releaseNotes.text}`) + const newVersion = await getNewVersion(true); + const releaseNotes = await getReleaseNotes(currentBranch, newVersion); + console.log(`Draft release notes are: \n${releaseNotes.text}`); } else { - const changes = await changesToRelease(currentBranch) + const changes = await changesToRelease(currentBranch); if (changes) { - await verifyNewVersion() - await createRelease(currentBranch, isBeta) - await pushRelease(currentBranch) - await runReleaseBuilds(currentBranch) + await verifyNewVersion(); + await createRelease(currentBranch, isBeta); + await pushRelease(currentBranch); + await runReleaseBuilds(currentBranch); } else { - console.log('There are no new changes to this branch since the last release, aborting release.') - process.exit(1) + console.log('There are no new changes to this branch since the last release, aborting release.'); + process.exit(1); } } } } -prepareRelease(!args.stable, args.notesOnly) +prepareRelease(!args.stable, args.notesOnly); diff --git a/script/release/publish-to-npm.js b/script/release/publish-to-npm.js index d884241550f37..1ec37ad62f5aa 100644 --- a/script/release/publish-to-npm.js +++ b/script/release/publish-to-npm.js @@ -1,22 +1,22 @@ -const temp = require('temp') -const fs = require('fs') -const path = require('path') -const childProcess = require('child_process') -const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils') -const request = require('request') -const semver = require('semver') -const rootPackageJson = require('../../package.json') +const temp = require('temp'); +const fs = require('fs'); +const path = require('path'); +const childProcess = require('child_process'); +const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils'); +const request = require('request'); +const semver = require('semver'); +const rootPackageJson = require('../../package.json'); const octokit = require('@octokit/rest')({ headers: { 'User-Agent': 'electron-npm-publisher' } -}) +}); if (!process.env.ELECTRON_NPM_OTP) { - console.error('Please set ELECTRON_NPM_OTP') - process.exit(1) + console.error('Please set ELECTRON_NPM_OTP'); + process.exit(1); } -let tempDir -temp.track() // track and cleanup files at exit +let tempDir; +temp.track(); // track and cleanup files at exit const files = [ 'cli.js', @@ -25,7 +25,7 @@ const files = [ 'package.json', 'README.md', 'LICENSE' -] +]; const jsonFields = [ 'name', @@ -35,58 +35,58 @@ const jsonFields = [ 'license', 'author', 'keywords' -] +]; -let npmTag = '' +let npmTag = ''; new Promise((resolve, reject) => { temp.mkdir('electron-npm', (err, dirPath) => { if (err) { - reject(err) + reject(err); } else { - resolve(dirPath) + resolve(dirPath); } - }) + }); }) .then((dirPath) => { - tempDir = dirPath + tempDir = dirPath; // copy files from `/npm` to temp directory files.forEach((name) => { - const noThirdSegment = name === 'README.md' || name === 'LICENSE' + const noThirdSegment = name === 'README.md' || name === 'LICENSE'; fs.writeFileSync( path.join(tempDir, name), fs.readFileSync(path.join(ELECTRON_DIR, noThirdSegment ? '' : 'npm', name)) - ) - }) + ); + }); // copy from root package.json to temp/package.json - const packageJson = require(path.join(tempDir, 'package.json')) + const packageJson = require(path.join(tempDir, 'package.json')); jsonFields.forEach((fieldName) => { - packageJson[fieldName] = rootPackageJson[fieldName] - }) + packageJson[fieldName] = rootPackageJson[fieldName]; + }); fs.writeFileSync( path.join(tempDir, 'package.json'), JSON.stringify(packageJson, null, 2) - ) + ); return octokit.repos.listReleases({ owner: 'electron', repo: rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron' - }) + }); }) .then((releases) => { // download electron.d.ts from release const release = releases.data.find( (release) => release.tag_name === `v${rootPackageJson.version}` - ) + ); if (!release) { - throw new Error(`cannot find release with tag v${rootPackageJson.version}`) + throw new Error(`cannot find release with tag v${rootPackageJson.version}`); } - return release + return release; }) .then((release) => { - const tsdAsset = release.assets.find((asset) => asset.name === 'electron.d.ts') + const tsdAsset = release.assets.find((asset) => asset.name === 'electron.d.ts'); if (!tsdAsset) { - throw new Error(`cannot find electron.d.ts from v${rootPackageJson.version} release assets`) + throw new Error(`cannot find electron.d.ts from v${rootPackageJson.version} release assets`); } return new Promise((resolve, reject) => { request.get({ @@ -97,78 +97,78 @@ new Promise((resolve, reject) => { } }, (err, response, body) => { if (err || response.statusCode !== 200) { - reject(err || new Error('Cannot download electron.d.ts')) + reject(err || new Error('Cannot download electron.d.ts')); } else { - fs.writeFileSync(path.join(tempDir, 'electron.d.ts'), body) - resolve(release) + fs.writeFileSync(path.join(tempDir, 'electron.d.ts'), body); + resolve(release); } - }) - }) + }); + }); }) .then(async (release) => { - const currentBranch = await getCurrentBranch() + const currentBranch = await getCurrentBranch(); if (release.tag_name.indexOf('nightly') > 0) { if (currentBranch === 'master') { // Nightlies get published to their own module, so master nightlies should be tagged as latest - npmTag = 'latest' + npmTag = 'latest'; } else { - npmTag = `nightly-${currentBranch}` + npmTag = `nightly-${currentBranch}`; } - const currentJson = JSON.parse(fs.readFileSync(path.join(tempDir, 'package.json'), 'utf8')) - currentJson.name = 'electron-nightly' - rootPackageJson.name = 'electron-nightly' + const currentJson = JSON.parse(fs.readFileSync(path.join(tempDir, 'package.json'), 'utf8')); + currentJson.name = 'electron-nightly'; + rootPackageJson.name = 'electron-nightly'; fs.writeFileSync( path.join(tempDir, 'package.json'), JSON.stringify(currentJson, null, 2) - ) + ); } else { if (currentBranch === 'master') { // This should never happen, master releases should be nightly releases // this is here just-in-case - npmTag = 'master' + npmTag = 'master'; } else if (!release.prerelease) { // Tag the release with a `2-0-x` style tag - npmTag = currentBranch + npmTag = currentBranch; } else { // Tag the release with a `beta-3-0-x` style tag - npmTag = `beta-${currentBranch}` + npmTag = `beta-${currentBranch}`; } } }) .then(() => childProcess.execSync('npm pack', { cwd: tempDir })) .then(() => { // test that the package can install electron prebuilt from github release - const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${rootPackageJson.version}.tgz`) + const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${rootPackageJson.version}.tgz`); return new Promise((resolve, reject) => { childProcess.execSync(`npm install ${tarballPath} --force --silent`, { env: Object.assign({}, process.env, { electron_config_cache: tempDir }), cwd: tempDir - }) - resolve(tarballPath) - }) + }); + resolve(tarballPath); + }); }) .then((tarballPath) => childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag} --otp=${process.env.ELECTRON_NPM_OTP}`)) .then(() => { - const currentTags = JSON.parse(childProcess.execSync('npm show electron dist-tags --json').toString()) - const localVersion = rootPackageJson.version - const parsedLocalVersion = semver.parse(localVersion) + const currentTags = JSON.parse(childProcess.execSync('npm show electron dist-tags --json').toString()); + const localVersion = rootPackageJson.version; + const parsedLocalVersion = semver.parse(localVersion); if (rootPackageJson.name === 'electron') { // We should only customly add dist tags for non-nightly releases where the package name is still // "electron" if (parsedLocalVersion.prerelease.length === 0 && semver.gt(localVersion, currentTags.latest)) { - childProcess.execSync(`npm dist-tag add electron@${localVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`) + childProcess.execSync(`npm dist-tag add electron@${localVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`); } if (parsedLocalVersion.prerelease[0] === 'beta' && semver.gt(localVersion, currentTags.beta)) { - childProcess.execSync(`npm dist-tag add electron@${localVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`) + childProcess.execSync(`npm dist-tag add electron@${localVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`); } } }) .catch((err) => { - console.error(`Error: ${err}`) - process.exit(1) - }) + console.error(`Error: ${err}`); + process.exit(1); + }); diff --git a/script/release/release-artifact-cleanup.js b/script/release/release-artifact-cleanup.js index d68354b089b5a..57bbf2c5e6503 100755 --- a/script/release/release-artifact-cleanup.js +++ b/script/release/release-artifact-cleanup.js @@ -1,39 +1,39 @@ #!/usr/bin/env node -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); const args = require('minimist')(process.argv.slice(2), { string: ['tag', 'releaseID'], default: { releaseID: '' } -}) -const path = require('path') -const { execSync } = require('child_process') -const { GitProcess } = require('dugite') -const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js') +}); +const path = require('path'); +const { execSync } = require('child_process'); +const { GitProcess } = require('dugite'); +const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) +}); -require('colors') -const pass = '✓'.green -const fail = '✗'.red +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; function getLastBumpCommit (tag) { - const data = execSync(`git log -n1 --grep "Bump ${tag}" --format='format:{"hash": "%H", "message": "%s"}'`).toString() - return JSON.parse(data) + const data = execSync(`git log -n1 --grep "Bump ${tag}" --format='format:{"hash": "%H", "message": "%s"}'`).toString(); + return JSON.parse(data); } async function revertBumpCommit (tag) { - const branch = await getCurrentBranch() - const commitToRevert = getLastBumpCommit(tag).hash - await GitProcess.exec(['revert', commitToRevert], ELECTRON_DIR) - const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR) + const branch = await getCurrentBranch(); + const commitToRevert = getLastBumpCommit(tag).hash; + await GitProcess.exec(['revert', commitToRevert], ELECTRON_DIR); + const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR); if (pushDetails.exitCode === 0) { - console.log(`${pass} successfully reverted release commit.`) + console.log(`${pass} successfully reverted release commit.`); } else { - const error = GitProcess.parseError(pushDetails.stderr) - console.error(`${fail} could not push release commit: `, error) - process.exit(1) + const error = GitProcess.parseError(pushDetails.stderr); + console.error(`${fail} could not push release commit: `, error); + process.exit(1); } } @@ -43,22 +43,22 @@ async function deleteDraft (releaseId, targetRepo) { owner: 'electron', repo: targetRepo, release_id: parseInt(releaseId, 10) - }) + }); if (!result.data.draft) { - console.log(`${fail} published releases cannot be deleted.`) - return false + console.log(`${fail} published releases cannot be deleted.`); + return false; } else { await octokit.repos.deleteRelease({ owner: 'electron', repo: targetRepo, release_id: result.data.id - }) + }); } - console.log(`${pass} successfully deleted draft with id ${releaseId} from ${targetRepo}`) - return true + console.log(`${pass} successfully deleted draft with id ${releaseId} from ${targetRepo}`); + return true; } catch (err) { - console.error(`${fail} couldn't delete draft with id ${releaseId} from ${targetRepo}: `, err) - return false + console.error(`${fail} couldn't delete draft with id ${releaseId} from ${targetRepo}: `, err); + return false; } } @@ -68,42 +68,42 @@ async function deleteTag (tag, targetRepo) { owner: 'electron', repo: targetRepo, ref: `tags/${tag}` - }) - console.log(`${pass} successfully deleted tag ${tag} from ${targetRepo}`) + }); + console.log(`${pass} successfully deleted tag ${tag} from ${targetRepo}`); } catch (err) { - console.log(`${fail} couldn't delete tag ${tag} from ${targetRepo}: `, err) + console.log(`${fail} couldn't delete tag ${tag} from ${targetRepo}: `, err); } } async function cleanReleaseArtifacts () { - const releaseId = args.releaseID.length > 0 ? args.releaseID : null - const isNightly = args.tag.includes('nightly') + const releaseId = args.releaseID.length > 0 ? args.releaseID : null; + const isNightly = args.tag.includes('nightly'); // try to revert commit regardless of tag and draft deletion status - await revertBumpCommit(args.tag) + await revertBumpCommit(args.tag); if (releaseId) { if (isNightly) { - await deleteDraft(releaseId, 'nightlies') + await deleteDraft(releaseId, 'nightlies'); // We only need to delete the Electron tag since the // nightly tag is only created at publish-time. - await deleteTag(args.tag, 'electron') + await deleteTag(args.tag, 'electron'); } else { - const deletedElectronDraft = await deleteDraft(releaseId, 'electron') + const deletedElectronDraft = await deleteDraft(releaseId, 'electron'); // don't delete tag unless draft deleted successfully if (deletedElectronDraft) { - await deleteTag(args.tag, 'electron') + await deleteTag(args.tag, 'electron'); } } } else { await Promise.all([ deleteTag(args.tag, 'electron'), deleteTag(args.tag, 'nightlies') - ]) + ]); } - console.log(`${pass} failed release artifact cleanup complete`) + console.log(`${pass} failed release artifact cleanup complete`); } -cleanReleaseArtifacts() +cleanReleaseArtifacts(); diff --git a/script/release/release.js b/script/release/release.js index c5577e3062805..12b93f5b7832b 100755 --- a/script/release/release.js +++ b/script/release/release.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); const args = require('minimist')(process.argv.slice(2), { boolean: [ @@ -10,87 +10,87 @@ const args = require('minimist')(process.argv.slice(2), { 'verboseNugget' ], default: { verboseNugget: false } -}) -const fs = require('fs') -const { execSync } = require('child_process') -const nugget = require('nugget') -const got = require('got') -const pkg = require('../../package.json') -const pkgVersion = `v${pkg.version}` -const path = require('path') -const sumchecker = require('sumchecker') -const temp = require('temp').track() -const { URL } = require('url') - -require('colors') -const pass = '✓'.green -const fail = '✗'.red - -const { ELECTRON_DIR } = require('../lib/utils') +}); +const fs = require('fs'); +const { execSync } = require('child_process'); +const nugget = require('nugget'); +const got = require('got'); +const pkg = require('../../package.json'); +const pkgVersion = `v${pkg.version}`; +const path = require('path'); +const sumchecker = require('sumchecker'); +const temp = require('temp').track(); +const { URL } = require('url'); + +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; + +const { ELECTRON_DIR } = require('../lib/utils'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) +}); -const targetRepo = pkgVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron' -let failureCount = 0 +const targetRepo = pkgVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron'; +let failureCount = 0; async function getDraftRelease (version, skipValidation) { const releaseInfo = await octokit.repos.listReleases({ owner: 'electron', repo: targetRepo - }) + }); - const versionToCheck = version || pkgVersion + const versionToCheck = version || pkgVersion; const drafts = releaseInfo.data.filter(release => { - return release.tag_name === versionToCheck && release.draft === true - }) + return release.tag_name === versionToCheck && release.draft === true; + }); - const draft = drafts[0] + const draft = drafts[0]; if (!skipValidation) { - failureCount = 0 - check(drafts.length === 1, 'one draft exists', true) + failureCount = 0; + check(drafts.length === 1, 'one draft exists', true); if (versionToCheck.indexOf('beta') > -1) { - check(draft.prerelease, 'draft is a prerelease') + check(draft.prerelease, 'draft is a prerelease'); } - check(draft.body.length > 50 && !draft.body.includes('(placeholder)'), 'draft has release notes') - check((failureCount === 0), 'Draft release looks good to go.', true) + check(draft.body.length > 50 && !draft.body.includes('(placeholder)'), 'draft has release notes'); + check((failureCount === 0), 'Draft release looks good to go.', true); } - return draft + return draft; } async function validateReleaseAssets (release, validatingRelease) { - const requiredAssets = assetsForVersion(release.tag_name, validatingRelease).sort() - const extantAssets = release.assets.map(asset => asset.name).sort() - const downloadUrls = release.assets.map(asset => asset.browser_download_url).sort() + const requiredAssets = assetsForVersion(release.tag_name, validatingRelease).sort(); + const extantAssets = release.assets.map(asset => asset.name).sort(); + const downloadUrls = release.assets.map(asset => asset.browser_download_url).sort(); - failureCount = 0 + failureCount = 0; requiredAssets.forEach(asset => { - check(extantAssets.includes(asset), asset) - }) - check((failureCount === 0), 'All required GitHub assets exist for release', true) + check(extantAssets.includes(asset), asset); + }); + check((failureCount === 0), 'All required GitHub assets exist for release', true); if (!validatingRelease || !release.draft) { if (release.draft) { - await verifyAssets(release) + await verifyAssets(release); } else { await verifyShasums(downloadUrls) .catch(err => { - console.log(`${fail} error verifyingShasums`, err) - }) + console.log(`${fail} error verifyingShasums`, err); + }); } - const s3Urls = s3UrlsForVersion(release.tag_name) - await verifyShasums(s3Urls, true) + const s3Urls = s3UrlsForVersion(release.tag_name); + await verifyShasums(s3Urls, true); } } function check (condition, statement, exitIfFail = false) { if (condition) { - console.log(`${pass} ${statement}`) + console.log(`${pass} ${statement}`); } else { - failureCount++ - console.log(`${fail} ${statement}`) - if (exitIfFail) process.exit(1) + failureCount++; + console.log(`${fail} ${statement}`); + if (exitIfFail) process.exit(1); } } @@ -153,15 +153,15 @@ function assetsForVersion (version, validatingRelease) { `electron-${version}-win32-ia32-toolchain-profile.zip`, `electron-${version}-win32-x64-toolchain-profile.zip`, `electron-${version}-win32-arm64-toolchain-profile.zip` - ] + ]; if (!validatingRelease) { - patterns.push('SHASUMS256.txt') + patterns.push('SHASUMS256.txt'); } - return patterns + return patterns; } function s3UrlsForVersion (version) { - const bucket = 'https://gh-contractor-zcbenz.s3.amazonaws.com/' + const bucket = 'https://gh-contractor-zcbenz.s3.amazonaws.com/'; const patterns = [ `${bucket}atom-shell/dist/${version}/iojs-${version}-headers.tar.gz`, `${bucket}atom-shell/dist/${version}/iojs-${version}.tar.gz`, @@ -173,66 +173,66 @@ function s3UrlsForVersion (version) { `${bucket}atom-shell/dist/${version}/SHASUMS.txt`, `${bucket}atom-shell/dist/${version}/SHASUMS256.txt`, `${bucket}atom-shell/dist/index.json` - ] - return patterns + ]; + return patterns; } function runScript (scriptName, scriptArgs, cwd) { - const scriptCommand = `${scriptName} ${scriptArgs.join(' ')}` + const scriptCommand = `${scriptName} ${scriptArgs.join(' ')}`; const scriptOptions = { encoding: 'UTF-8' - } - if (cwd) scriptOptions.cwd = cwd + }; + if (cwd) scriptOptions.cwd = cwd; try { - return execSync(scriptCommand, scriptOptions) + return execSync(scriptCommand, scriptOptions); } catch (err) { - console.log(`${fail} Error running ${scriptName}`, err) - process.exit(1) + console.log(`${fail} Error running ${scriptName}`, err); + process.exit(1); } } function uploadNodeShasums () { - console.log('Uploading Node SHASUMS file to S3.') - const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-node-checksums.py') - runScript(scriptPath, ['-v', pkgVersion]) - console.log(`${pass} Done uploading Node SHASUMS file to S3.`) + console.log('Uploading Node SHASUMS file to S3.'); + const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-node-checksums.py'); + runScript(scriptPath, ['-v', pkgVersion]); + console.log(`${pass} Done uploading Node SHASUMS file to S3.`); } function uploadIndexJson () { - console.log('Uploading index.json to S3.') - const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-index-json.py') - runScript(scriptPath, [pkgVersion]) - console.log(`${pass} Done uploading index.json to S3.`) + console.log('Uploading index.json to S3.'); + const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-index-json.py'); + runScript(scriptPath, [pkgVersion]); + console.log(`${pass} Done uploading index.json to S3.`); } async function createReleaseShasums (release) { - const fileName = 'SHASUMS256.txt' - const existingAssets = release.assets.filter(asset => asset.name === fileName) + const fileName = 'SHASUMS256.txt'; + const existingAssets = release.assets.filter(asset => asset.name === fileName); if (existingAssets.length > 0) { - console.log(`${fileName} already exists on GitHub; deleting before creating new file.`) + console.log(`${fileName} already exists on GitHub; deleting before creating new file.`); await octokit.repos.deleteReleaseAsset({ owner: 'electron', repo: targetRepo, asset_id: existingAssets[0].id }).catch(err => { - console.log(`${fail} Error deleting ${fileName} on GitHub:`, err) - }) + console.log(`${fail} Error deleting ${fileName} on GitHub:`, err); + }); } - console.log(`Creating and uploading the release ${fileName}.`) - const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'merge-electron-checksums.py') - const checksums = runScript(scriptPath, ['-v', pkgVersion]) + console.log(`Creating and uploading the release ${fileName}.`); + const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'merge-electron-checksums.py'); + const checksums = runScript(scriptPath, ['-v', pkgVersion]); - console.log(`${pass} Generated release SHASUMS.`) - const filePath = await saveShaSumFile(checksums, fileName) + console.log(`${pass} Generated release SHASUMS.`); + const filePath = await saveShaSumFile(checksums, fileName); - console.log(`${pass} Created ${fileName} file.`) - await uploadShasumFile(filePath, fileName, release.id) + console.log(`${pass} Created ${fileName} file.`); + await uploadShasumFile(filePath, fileName, release.id); - console.log(`${pass} Successfully uploaded ${fileName} to GitHub.`) + console.log(`${pass} Successfully uploaded ${fileName} to GitHub.`); } async function uploadShasumFile (filePath, fileName, releaseId) { - const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}` + const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}`; return octokit.repos.uploadReleaseAsset({ url: uploadUrl, headers: { @@ -242,29 +242,29 @@ async function uploadShasumFile (filePath, fileName, releaseId) { file: fs.createReadStream(filePath), name: fileName }).catch(err => { - console.log(`${fail} Error uploading ${filePath} to GitHub:`, err) - process.exit(1) - }) + console.log(`${fail} Error uploading ${filePath} to GitHub:`, err); + process.exit(1); + }); } function saveShaSumFile (checksums, fileName) { return new Promise((resolve, reject) => { temp.open(fileName, (err, info) => { if (err) { - console.log(`${fail} Could not create ${fileName} file`) - process.exit(1) + console.log(`${fail} Could not create ${fileName} file`); + process.exit(1); } else { - fs.writeFileSync(info.fd, checksums) + fs.writeFileSync(info.fd, checksums); fs.close(info.fd, (err) => { if (err) { - console.log(`${fail} Could close ${fileName} file`) - process.exit(1) + console.log(`${fail} Could close ${fileName} file`); + process.exit(1); } - resolve(info.path) - }) + resolve(info.path); + }); } - }) - }) + }); + }); } async function publishRelease (release) { @@ -275,34 +275,34 @@ async function publishRelease (release) { tag_name: release.tag_name, draft: false }).catch(err => { - console.log(`${fail} Error publishing release:`, err) - process.exit(1) - }) + console.log(`${fail} Error publishing release:`, err); + process.exit(1); + }); } async function makeRelease (releaseToValidate) { if (releaseToValidate) { if (releaseToValidate === true) { - releaseToValidate = pkgVersion + releaseToValidate = pkgVersion; } else { - console.log('Release to validate !=== true') + console.log('Release to validate !=== true'); } - console.log(`Validating release ${releaseToValidate}`) - const release = await getDraftRelease(releaseToValidate) - await validateReleaseAssets(release, true) + console.log(`Validating release ${releaseToValidate}`); + const release = await getDraftRelease(releaseToValidate); + await validateReleaseAssets(release, true); } else { - let draftRelease = await getDraftRelease() - uploadNodeShasums() - uploadIndexJson() + let draftRelease = await getDraftRelease(); + uploadNodeShasums(); + uploadIndexJson(); - await createReleaseShasums(draftRelease) + await createReleaseShasums(draftRelease); // Fetch latest version of release before verifying - draftRelease = await getDraftRelease(pkgVersion, true) - await validateReleaseAssets(draftRelease) - await publishRelease(draftRelease) + draftRelease = await getDraftRelease(pkgVersion, true); + await validateReleaseAssets(draftRelease); + await publishRelease(draftRelease); console.log(`${pass} SUCCESS!!! Release has been published. Please run ` + - '"npm run publish-to-npm" to publish release to npm.') + '"npm run publish-to-npm" to publish release to npm.'); } } @@ -310,19 +310,19 @@ async function makeTempDir () { return new Promise((resolve, reject) => { temp.mkdir('electron-publish', (err, dirPath) => { if (err) { - reject(err) + reject(err); } else { - resolve(dirPath) + resolve(dirPath); } - }) - }) + }); + }); } async function verifyAssets (release) { - const downloadDir = await makeTempDir() + const downloadDir = await makeTempDir(); - console.log('Downloading files from GitHub to verify shasums') - const shaSumFile = 'SHASUMS256.txt' + console.log('Downloading files from GitHub to verify shasums'); + const shaSumFile = 'SHASUMS256.txt'; let filesToCheck = await Promise.all(release.assets.map(async asset => { const requestOptions = await octokit.repos.getReleaseAsset.endpoint({ @@ -332,26 +332,26 @@ async function verifyAssets (release) { headers: { Accept: 'application/octet-stream' } - }) + }); - const { url, headers } = requestOptions - headers.authorization = `token ${process.env.ELECTRON_GITHUB_TOKEN}` + const { url, headers } = requestOptions; + headers.authorization = `token ${process.env.ELECTRON_GITHUB_TOKEN}`; const response = await got(url, { followRedirect: false, method: 'HEAD', headers - }) + }); - await downloadFiles(response.headers.location, downloadDir, asset.name) - return asset.name + await downloadFiles(response.headers.location, downloadDir, asset.name); + return asset.name; })).catch(err => { - console.log(`${fail} Error downloading files from GitHub`, err) - process.exit(1) - }) + console.log(`${fail} Error downloading files from GitHub`, err); + process.exit(1); + }); - filesToCheck = filesToCheck.filter(fileName => fileName !== shaSumFile) - let checkerOpts + filesToCheck = filesToCheck.filter(fileName => fileName !== shaSumFile); + let checkerOpts; await validateChecksums({ algorithm: 'sha256', filesToCheck, @@ -359,71 +359,71 @@ async function verifyAssets (release) { shaSumFile, checkerOpts, fileSource: 'GitHub' - }) + }); } function downloadFiles (urls, directory, targetName) { return new Promise((resolve, reject) => { - const nuggetOpts = { dir: directory } - nuggetOpts.quiet = !args.verboseNugget - if (targetName) nuggetOpts.target = targetName + const nuggetOpts = { dir: directory }; + nuggetOpts.quiet = !args.verboseNugget; + if (targetName) nuggetOpts.target = targetName; nugget(urls, nuggetOpts, (err) => { if (err) { - reject(err) + reject(err); } else { - console.log(`${pass} all files downloaded successfully!`) - resolve() + console.log(`${pass} all files downloaded successfully!`); + resolve(); } - }) - }) + }); + }); } async function verifyShasums (urls, isS3) { - const fileSource = isS3 ? 'S3' : 'GitHub' - console.log(`Downloading files from ${fileSource} to verify shasums`) - const downloadDir = await makeTempDir() - let filesToCheck = [] + const fileSource = isS3 ? 'S3' : 'GitHub'; + console.log(`Downloading files from ${fileSource} to verify shasums`); + const downloadDir = await makeTempDir(); + let filesToCheck = []; try { if (!isS3) { - await downloadFiles(urls, downloadDir) + await downloadFiles(urls, downloadDir); filesToCheck = urls.map(url => { - const currentUrl = new URL(url) - return path.basename(currentUrl.pathname) - }).filter(file => file.indexOf('SHASUMS') === -1) + const currentUrl = new URL(url); + return path.basename(currentUrl.pathname); + }).filter(file => file.indexOf('SHASUMS') === -1); } else { - const s3VersionPath = `/atom-shell/dist/${pkgVersion}/` + const s3VersionPath = `/atom-shell/dist/${pkgVersion}/`; await Promise.all(urls.map(async (url) => { - const currentUrl = new URL(url) - const dirname = path.dirname(currentUrl.pathname) - const filename = path.basename(currentUrl.pathname) - const s3VersionPathIdx = dirname.indexOf(s3VersionPath) + const currentUrl = new URL(url); + const dirname = path.dirname(currentUrl.pathname); + const filename = path.basename(currentUrl.pathname); + const s3VersionPathIdx = dirname.indexOf(s3VersionPath); if (s3VersionPathIdx === -1 || dirname === s3VersionPath) { if (s3VersionPathIdx !== -1 && filename.indexof('SHASUMS') === -1) { - filesToCheck.push(filename) + filesToCheck.push(filename); } - await downloadFiles(url, downloadDir) + await downloadFiles(url, downloadDir); } else { - const subDirectory = dirname.substr(s3VersionPathIdx + s3VersionPath.length) - const fileDirectory = path.join(downloadDir, subDirectory) + const subDirectory = dirname.substr(s3VersionPathIdx + s3VersionPath.length); + const fileDirectory = path.join(downloadDir, subDirectory); try { - fs.statSync(fileDirectory) + fs.statSync(fileDirectory); } catch (err) { - fs.mkdirSync(fileDirectory) + fs.mkdirSync(fileDirectory); } - filesToCheck.push(path.join(subDirectory, filename)) - await downloadFiles(url, fileDirectory) + filesToCheck.push(path.join(subDirectory, filename)); + await downloadFiles(url, fileDirectory); } - })) + })); } } catch (err) { - console.log(`${fail} Error downloading files from ${fileSource}`, err) - process.exit(1) + console.log(`${fail} Error downloading files from ${fileSource}`, err); + process.exit(1); } - console.log(`${pass} Successfully downloaded the files from ${fileSource}.`) - let checkerOpts + console.log(`${pass} Successfully downloaded the files from ${fileSource}.`); + let checkerOpts; if (isS3) { - checkerOpts = { defaultTextEncoding: 'binary' } + checkerOpts = { defaultTextEncoding: 'binary' }; } await validateChecksums({ @@ -433,7 +433,7 @@ async function verifyShasums (urls, isS3) { shaSumFile: 'SHASUMS256.txt', checkerOpts, fileSource - }) + }); if (isS3) { await validateChecksums({ @@ -443,37 +443,37 @@ async function verifyShasums (urls, isS3) { shaSumFile: 'SHASUMS.txt', checkerOpts, fileSource - }) + }); } } async function validateChecksums (validationArgs) { console.log(`Validating checksums for files from ${validationArgs.fileSource} ` + - `against ${validationArgs.shaSumFile}.`) - const shaSumFilePath = path.join(validationArgs.fileDirectory, validationArgs.shaSumFile) + `against ${validationArgs.shaSumFile}.`); + const shaSumFilePath = path.join(validationArgs.fileDirectory, validationArgs.shaSumFile); const checker = new sumchecker.ChecksumValidator(validationArgs.algorithm, - shaSumFilePath, validationArgs.checkerOpts) + shaSumFilePath, validationArgs.checkerOpts); await checker.validate(validationArgs.fileDirectory, validationArgs.filesToCheck) .catch(err => { if (err instanceof sumchecker.ChecksumMismatchError) { console.error(`${fail} The checksum of ${err.filename} from ` + `${validationArgs.fileSource} did not match the shasum in ` + - `${validationArgs.shaSumFile}`) + `${validationArgs.shaSumFile}`); } else if (err instanceof sumchecker.ChecksumParseError) { console.error(`${fail} The checksum file ${validationArgs.shaSumFile} ` + - `from ${validationArgs.fileSource} could not be parsed.`, err) + `from ${validationArgs.fileSource} could not be parsed.`, err); } else if (err instanceof sumchecker.NoChecksumFoundError) { console.error(`${fail} The file ${err.filename} from ` + `${validationArgs.fileSource} was not in the shasum file ` + - `${validationArgs.shaSumFile}.`) + `${validationArgs.shaSumFile}.`); } else { console.error(`${fail} Error matching files from ` + - `${validationArgs.fileSource} shasums in ${validationArgs.shaSumFile}.`, err) + `${validationArgs.fileSource} shasums in ${validationArgs.shaSumFile}.`, err); } - process.exit(1) - }) + process.exit(1); + }); console.log(`${pass} All files from ${validationArgs.fileSource} match ` + - `shasums defined in ${validationArgs.shaSumFile}.`) + `shasums defined in ${validationArgs.shaSumFile}.`); } -makeRelease(args.validateRelease) +makeRelease(args.validateRelease); diff --git a/script/release/uploaders/upload-to-github.js b/script/release/uploaders/upload-to-github.js index fb7fa36fa5b8b..2ca28d911774e 100644 --- a/script/release/uploaders/upload-to-github.js +++ b/script/release/uploaders/upload-to-github.js @@ -1,40 +1,40 @@ -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); -const fs = require('fs') +const fs = require('fs'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) +}); if (process.argv.length < 6) { - console.log('Usage: upload-to-github filePath fileName releaseId') - process.exit(1) + console.log('Usage: upload-to-github filePath fileName releaseId'); + process.exit(1); } -const filePath = process.argv[2] -const fileName = process.argv[3] -const releaseId = process.argv[4] -const releaseVersion = process.argv[5] +const filePath = process.argv[2]; +const fileName = process.argv[3]; +const releaseId = process.argv[4]; +const releaseVersion = process.argv[5]; const getHeaders = (filePath, fileName) => { - const extension = fileName.split('.').pop() - const size = fs.statSync(filePath).size + const extension = fileName.split('.').pop(); + const size = fs.statSync(filePath).size; const options = { json: 'text/json', zip: 'application/zip', txt: 'text/plain', ts: 'application/typescript' - } + }; return { 'content-type': options[extension], 'content-length': size - } -} + }; +}; -const targetRepo = releaseVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron' -const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}` -let retry = 0 +const targetRepo = releaseVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron'; +const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}`; +let retry = 0; function uploadToGitHub () { octokit.repos.uploadReleaseAsset({ @@ -43,12 +43,12 @@ function uploadToGitHub () { file: fs.createReadStream(filePath), name: fileName }).then(() => { - console.log(`Successfully uploaded ${fileName} to GitHub.`) - process.exit() + console.log(`Successfully uploaded ${fileName} to GitHub.`); + process.exit(); }).catch((err) => { if (retry < 4) { - console.log(`Error uploading ${fileName} to GitHub, will retry. Error was:`, err) - retry++ + console.log(`Error uploading ${fileName} to GitHub, will retry. Error was:`, err); + retry++; octokit.repos.listAssetsForRelease({ owner: 'electron', @@ -56,31 +56,31 @@ function uploadToGitHub () { release_id: releaseId, per_page: 100 }).then(assets => { - console.log('Got list of assets for existing release:') - console.log(JSON.stringify(assets.data, null, ' ')) - const existingAssets = assets.data.filter(asset => asset.name === fileName) + console.log('Got list of assets for existing release:'); + console.log(JSON.stringify(assets.data, null, ' ')); + const existingAssets = assets.data.filter(asset => asset.name === fileName); if (existingAssets.length > 0) { - console.log(`${fileName} already exists; will delete before retrying upload.`) + console.log(`${fileName} already exists; will delete before retrying upload.`); octokit.repos.deleteReleaseAsset({ owner: 'electron', repo: targetRepo, asset_id: existingAssets[0].id }).catch((deleteErr) => { - console.log(`Failed to delete existing asset ${fileName}. Error was:`, deleteErr) - }).then(uploadToGitHub) + console.log(`Failed to delete existing asset ${fileName}. Error was:`, deleteErr); + }).then(uploadToGitHub); } else { - console.log(`Current asset ${fileName} not found in existing assets; retrying upload.`) - uploadToGitHub() + console.log(`Current asset ${fileName} not found in existing assets; retrying upload.`); + uploadToGitHub(); } }).catch((getReleaseErr) => { - console.log('Fatal: Unable to get current release assets via getRelease! Error was:', getReleaseErr) - }) + console.log('Fatal: Unable to get current release assets via getRelease! Error was:', getReleaseErr); + }); } else { - console.log(`Error retrying uploading ${fileName} to GitHub:`, err) - process.exitCode = 1 + console.log(`Error retrying uploading ${fileName} to GitHub:`, err); + process.exitCode = 1; } - }) + }); } -uploadToGitHub() +uploadToGitHub(); diff --git a/script/release/version-bumper.js b/script/release/version-bumper.js index 1efa1997cbb1c..34290da1a18fb 100644 --- a/script/release/version-bumper.js +++ b/script/release/version-bumper.js @@ -1,26 +1,26 @@ #!/usr/bin/env node -const { GitProcess } = require('dugite') -const fs = require('fs') -const semver = require('semver') -const path = require('path') -const { promisify } = require('util') -const minimist = require('minimist') +const { GitProcess } = require('dugite'); +const fs = require('fs'); +const semver = require('semver'); +const path = require('path'); +const { promisify } = require('util'); +const minimist = require('minimist'); -const { ELECTRON_DIR } = require('../lib/utils') -const versionUtils = require('./version-utils') +const { ELECTRON_DIR } = require('../lib/utils'); +const versionUtils = require('./version-utils'); -const writeFile = promisify(fs.writeFile) -const readFile = promisify(fs.readFile) +const writeFile = promisify(fs.writeFile); +const readFile = promisify(fs.readFile); function parseCommandLine () { - let help + let help; const opts = minimist(process.argv.slice(2), { string: ['bump', 'version'], boolean: ['dryRun', 'help'], alias: { version: ['v'] }, - unknown: arg => { help = true } - }) + unknown: arg => { help = true; } + }); if (help || opts.help || !opts.bump) { console.log(` Bump release version number. Possible arguments:\n @@ -28,30 +28,30 @@ function parseCommandLine () { --version={version} to set version number directly\n --dryRun to print the next version without updating files Note that you can use both --bump and --stable simultaneously. - `) - process.exit(0) + `); + process.exit(0); } - return opts + return opts; } // run the script async function main () { - const opts = parseCommandLine() - const currentVersion = await versionUtils.getElectronVersion() - const version = await nextVersion(opts.bump, currentVersion) + const opts = parseCommandLine(); + const currentVersion = await versionUtils.getElectronVersion(); + const version = await nextVersion(opts.bump, currentVersion); - const parsed = semver.parse(version) + const parsed = semver.parse(version); const components = { major: parsed.major, minor: parsed.minor, patch: parsed.patch, pre: parsed.prerelease - } + }; // print would-be new version and exit early if (opts.dryRun) { - console.log(`new version number would be: ${version}\n`) - return 0 + console.log(`new version number would be: ${version}\n`); + return 0; } // update all version-related files @@ -59,12 +59,12 @@ async function main () { updateVersion(version), updatePackageJSON(version), updateWinRC(components) - ]) + ]); // commit all updated version-related files - await commitVersionBump(version) + await commitVersionBump(version); - console.log(`Bumped to version: ${version}`) + console.log(`Bumped to version: ${version}`); } // get next version for release based on [nightly, beta, stable] @@ -72,81 +72,81 @@ async function nextVersion (bumpType, version) { if (versionUtils.isNightly(version) || versionUtils.isBeta(version)) { switch (bumpType) { case 'nightly': - version = await versionUtils.nextNightly(version) - break + version = await versionUtils.nextNightly(version); + break; case 'beta': - version = await versionUtils.nextBeta(version) - break + version = await versionUtils.nextBeta(version); + break; case 'stable': - version = semver.valid(semver.coerce(version)) - break + version = semver.valid(semver.coerce(version)); + break; default: - throw new Error('Invalid bump type.') + throw new Error('Invalid bump type.'); } } else if (versionUtils.isStable(version)) { switch (bumpType) { case 'nightly': - version = versionUtils.nextNightly(version) - break + version = versionUtils.nextNightly(version); + break; case 'beta': - throw new Error('Cannot bump to beta from stable.') + throw new Error('Cannot bump to beta from stable.'); case 'minor': - version = semver.inc(version, 'minor') - break + version = semver.inc(version, 'minor'); + break; case 'stable': - version = semver.inc(version, 'patch') - break + version = semver.inc(version, 'patch'); + break; default: - throw new Error('Invalid bump type.') + throw new Error('Invalid bump type.'); } } else { - throw new Error(`Invalid current version: ${version}`) + throw new Error(`Invalid current version: ${version}`); } - return version + return version; } // update VERSION file with latest release info async function updateVersion (version) { - const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION') - await writeFile(versionPath, version, 'utf8') + const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION'); + await writeFile(versionPath, version, 'utf8'); } // update package metadata files with new version async function updatePackageJSON (version) { - const filePath = path.resolve(ELECTRON_DIR, 'package.json') - const file = require(filePath) - file.version = version - await writeFile(filePath, JSON.stringify(file, null, 2)) + const filePath = path.resolve(ELECTRON_DIR, 'package.json'); + const file = require(filePath); + file.version = version; + await writeFile(filePath, JSON.stringify(file, null, 2)); } // push bump commit to release branch async function commitVersionBump (version) { - const gitArgs = ['commit', '-a', '-m', `Bump v${version}`, '-n'] - await GitProcess.exec(gitArgs, ELECTRON_DIR) + const gitArgs = ['commit', '-a', '-m', `Bump v${version}`, '-n']; + await GitProcess.exec(gitArgs, ELECTRON_DIR); } // updates atom.rc file with new semver values async function updateWinRC (components) { - const filePath = path.resolve(ELECTRON_DIR, 'shell', 'browser', 'resources', 'win', 'atom.rc') - const data = await readFile(filePath, 'utf8') - const arr = data.split('\n') + const filePath = path.resolve(ELECTRON_DIR, 'shell', 'browser', 'resources', 'win', 'atom.rc'); + const data = await readFile(filePath, 'utf8'); + const arr = data.split('\n'); arr.forEach((line, idx) => { if (line.includes('FILEVERSION')) { - arr[idx] = ` FILEVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}` - arr[idx + 1] = ` PRODUCTVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}` + arr[idx] = ` FILEVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}`; + arr[idx + 1] = ` PRODUCTVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}`; } else if (line.includes('FileVersion')) { - arr[idx] = ` VALUE "FileVersion", "${versionUtils.makeVersion(components, '.')}"` - arr[idx + 5] = ` VALUE "ProductVersion", "${versionUtils.makeVersion(components, '.')}"` + arr[idx] = ` VALUE "FileVersion", "${versionUtils.makeVersion(components, '.')}"`; + arr[idx + 5] = ` VALUE "ProductVersion", "${versionUtils.makeVersion(components, '.')}"`; } - }) - await writeFile(filePath, arr.join('\n')) + }); + await writeFile(filePath, arr.join('\n')); } if (process.mainModule === module) { main().catch((error) => { - console.error(error) - process.exit(1) - }) + console.error(error); + process.exit(1); + }); } -module.exports = { nextVersion } +module.exports = { nextVersion }; diff --git a/script/release/version-utils.js b/script/release/version-utils.js index 6d4edeb757a5d..6402237eab546 100644 --- a/script/release/version-utils.js +++ b/script/release/version-utils.js @@ -1,90 +1,90 @@ -const path = require('path') -const fs = require('fs') -const semver = require('semver') -const { GitProcess } = require('dugite') -const { promisify } = require('util') +const path = require('path'); +const fs = require('fs'); +const semver = require('semver'); +const { GitProcess } = require('dugite'); +const { promisify } = require('util'); -const { ELECTRON_DIR } = require('../lib/utils') +const { ELECTRON_DIR } = require('../lib/utils'); -const readFile = promisify(fs.readFile) +const readFile = promisify(fs.readFile); const preType = { NONE: 'none', PARTIAL: 'partial', FULL: 'full' -} +}; const getCurrentDate = () => { - const d = new Date() - const dd = `${d.getDate()}`.padStart(2, '0') - const mm = `${d.getMonth() + 1}`.padStart(2, '0') - const yyyy = d.getFullYear() - return `${yyyy}${mm}${dd}` -} - -const isNightly = v => v.includes('nightly') -const isBeta = v => v.includes('beta') + const d = new Date(); + const dd = `${d.getDate()}`.padStart(2, '0'); + const mm = `${d.getMonth() + 1}`.padStart(2, '0'); + const yyyy = d.getFullYear(); + return `${yyyy}${mm}${dd}`; +}; + +const isNightly = v => v.includes('nightly'); +const isBeta = v => v.includes('beta'); const isStable = v => { - const parsed = semver.parse(v) - return !!(parsed && parsed.prerelease.length === 0) -} + const parsed = semver.parse(v); + return !!(parsed && parsed.prerelease.length === 0); +}; const makeVersion = (components, delim, pre = preType.NONE) => { - let version = [components.major, components.minor, components.patch].join(delim) + let version = [components.major, components.minor, components.patch].join(delim); if (pre === preType.PARTIAL) { - version += `${delim}${components.pre[1] || 0}` + version += `${delim}${components.pre[1] || 0}`; } else if (pre === preType.FULL) { - version += `-${components.pre[0]}${delim}${components.pre[1]}` + version += `-${components.pre[0]}${delim}${components.pre[1]}`; } - return version -} + return version; +}; async function nextBeta (v) { - const next = semver.coerce(semver.clean(v)) + const next = semver.coerce(semver.clean(v)); - const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-beta.*`], ELECTRON_DIR) - const tags = tagBlob.stdout.split('\n').filter(e => e !== '') - tags.sort((t1, t2) => semver.gt(t1, t2)) + const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-beta.*`], ELECTRON_DIR); + const tags = tagBlob.stdout.split('\n').filter(e => e !== ''); + tags.sort((t1, t2) => semver.gt(t1, t2)); // increment the latest existing beta tag or start at beta.1 if it's a new beta line - return tags.length === 0 ? `${next}-beta.1` : semver.inc(tags.pop(), 'prerelease') + return tags.length === 0 ? `${next}-beta.1` : semver.inc(tags.pop(), 'prerelease'); } async function getElectronVersion () { - const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION') - const version = await readFile(versionPath, 'utf8') - return version.trim() + const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION'); + const version = await readFile(versionPath, 'utf8'); + return version.trim(); } async function nextNightly (v) { - let next = semver.valid(semver.coerce(v)) - const pre = `nightly.${getCurrentDate()}` + let next = semver.valid(semver.coerce(v)); + const pre = `nightly.${getCurrentDate()}`; - const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim() + const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim(); if (branch === 'master') { - next = semver.inc(await getLastMajorForMaster(), 'major') + next = semver.inc(await getLastMajorForMaster(), 'major'); } else if (isStable(v)) { - next = semver.inc(next, 'patch') + next = semver.inc(next, 'patch'); } - return `${next}-${pre}` + return `${next}-${pre}`; } async function getLastMajorForMaster () { - let branchNames - const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], ELECTRON_DIR) + let branchNames; + const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], ELECTRON_DIR); if (result.exitCode === 0) { - branchNames = result.stdout.trim().split('\n') - const filtered = branchNames.map(b => b.replace('origin/', '')) - return getNextReleaseBranch(filtered) + branchNames = result.stdout.trim().split('\n'); + const filtered = branchNames.map(b => b.replace('origin/', '')); + return getNextReleaseBranch(filtered); } else { - throw new Error('Release branches could not be fetched.') + throw new Error('Release branches could not be fetched.'); } } function getNextReleaseBranch (branches) { - const converted = branches.map(b => b.replace(/-/g, '.').replace('x', '0').replace('y', '0')) - return converted.reduce((v1, v2) => semver.gt(v1, v2) ? v1 : v2) + const converted = branches.map(b => b.replace(/-/g, '.').replace('x', '0').replace('y', '0')); + return converted.reduce((v1, v2) => semver.gt(v1, v2) ? v1 : v2); } module.exports = { @@ -96,4 +96,4 @@ module.exports = { getElectronVersion, nextNightly, preType -} +}; diff --git a/script/spec-runner.js b/script/spec-runner.js index 0a8cff3ce85a7..4d9915b81698e 100755 --- a/script/spec-runner.js +++ b/script/spec-runner.js @@ -1,76 +1,76 @@ #!/usr/bin/env node -const childProcess = require('child_process') -const crypto = require('crypto') -const fs = require('fs') -const { hashElement } = require('folder-hash') -const path = require('path') -const unknownFlags = [] +const childProcess = require('child_process'); +const crypto = require('crypto'); +const fs = require('fs'); +const { hashElement } = require('folder-hash'); +const path = require('path'); +const unknownFlags = []; -require('colors') -const pass = '✓'.green -const fail = '✗'.red +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; const args = require('minimist')(process.argv, { string: ['runners', 'target'], boolean: ['buildNativeTests'], unknown: arg => unknownFlags.push(arg) -}) +}); -const unknownArgs = [] +const unknownArgs = []; for (const flag of unknownFlags) { - unknownArgs.push(flag) - const onlyFlag = flag.replace(/^-+/, '') + unknownArgs.push(flag); + const onlyFlag = flag.replace(/^-+/, ''); if (args[onlyFlag]) { - unknownArgs.push(args[onlyFlag]) + unknownArgs.push(args[onlyFlag]); } } -const utils = require('./lib/utils') -const { YARN_VERSION } = require('./yarn') +const utils = require('./lib/utils'); +const { YARN_VERSION } = require('./yarn'); -const BASE = path.resolve(__dirname, '../..') -const NPM_CMD = process.platform === 'win32' ? 'npm.cmd' : 'npm' -const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' +const BASE = path.resolve(__dirname, '../..'); +const NPM_CMD = process.platform === 'win32' ? 'npm.cmd' : 'npm'; +const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; const runners = new Map([ ['main', { description: 'Main process specs', run: runMainProcessElectronTests }], ['remote', { description: 'Remote based specs', run: runRemoteBasedElectronTests }], ['native', { description: 'Native specs', run: runNativeElectronTests }] -]) +]); -const specHashPath = path.resolve(__dirname, '../spec/.hash') +const specHashPath = path.resolve(__dirname, '../spec/.hash'); -let runnersToRun = null +let runnersToRun = null; if (args.runners) { - runnersToRun = args.runners.split(',') + runnersToRun = args.runners.split(','); if (!runnersToRun.every(r => [...runners.keys()].includes(r))) { - console.log(`${fail} ${runnersToRun} must be a subset of [${[...runners.keys()].join(' | ')}]`) - process.exit(1) + console.log(`${fail} ${runnersToRun} must be a subset of [${[...runners.keys()].join(' | ')}]`); + process.exit(1); } - console.log('Only running:', runnersToRun) + console.log('Only running:', runnersToRun); } else { - console.log(`Triggering runners: ${[...runners.keys()].join(', ')}`) + console.log(`Triggering runners: ${[...runners.keys()].join(', ')}`); } async function main () { - const [lastSpecHash, lastSpecInstallHash] = loadLastSpecHash() - const [currentSpecHash, currentSpecInstallHash] = await getSpecHash() + const [lastSpecHash, lastSpecInstallHash] = loadLastSpecHash(); + const [currentSpecHash, currentSpecInstallHash] = await getSpecHash(); const somethingChanged = (currentSpecHash !== lastSpecHash) || - (lastSpecInstallHash !== currentSpecInstallHash) + (lastSpecInstallHash !== currentSpecInstallHash); if (somethingChanged) { - await installSpecModules(path.resolve(__dirname, '..', 'spec')) - await installSpecModules(path.resolve(__dirname, '..', 'spec-main')) - await getSpecHash().then(saveSpecHash) + await installSpecModules(path.resolve(__dirname, '..', 'spec')); + await installSpecModules(path.resolve(__dirname, '..', 'spec-main')); + await getSpecHash().then(saveSpecHash); } if (!fs.existsSync(path.resolve(__dirname, '../electron.d.ts'))) { - console.log('Generating electron.d.ts as it is missing') - generateTypeDefinitions() + console.log('Generating electron.d.ts as it is missing'); + generateTypeDefinitions(); } - await runElectronTests() + await runElectronTests(); } function generateTypeDefinitions () { @@ -78,80 +78,80 @@ function generateTypeDefinitions () { cwd: path.resolve(__dirname, '..'), stdio: 'inherit', shell: true - }) + }); if (status !== 0) { - throw new Error(`Electron typescript definition generation failed with exit code: ${status}.`) + throw new Error(`Electron typescript definition generation failed with exit code: ${status}.`); } } function loadLastSpecHash () { return fs.existsSync(specHashPath) ? fs.readFileSync(specHashPath, 'utf8').split('\n') - : [null, null] + : [null, null]; } function saveSpecHash ([newSpecHash, newSpecInstallHash]) { - fs.writeFileSync(specHashPath, `${newSpecHash}\n${newSpecInstallHash}`) + fs.writeFileSync(specHashPath, `${newSpecHash}\n${newSpecInstallHash}`); } async function runElectronTests () { - const errors = [] + const errors = []; - const testResultsDir = process.env.ELECTRON_TEST_RESULTS_DIR + const testResultsDir = process.env.ELECTRON_TEST_RESULTS_DIR; for (const [runnerId, { description, run }] of runners) { if (runnersToRun && !runnersToRun.includes(runnerId)) { - console.info('\nSkipping:', description) - continue + console.info('\nSkipping:', description); + continue; } try { - console.info('\nRunning:', description) + console.info('\nRunning:', description); if (testResultsDir) { - process.env.MOCHA_FILE = path.join(testResultsDir, `test-results-${runnerId}.xml`) + process.env.MOCHA_FILE = path.join(testResultsDir, `test-results-${runnerId}.xml`); } - await run() + await run(); } catch (err) { - errors.push([runnerId, err]) + errors.push([runnerId, err]); } } if (errors.length !== 0) { for (const err of errors) { - console.error('\n\nRunner Failed:', err[0]) - console.error(err[1]) + console.error('\n\nRunner Failed:', err[0]); + console.error(err[1]); } - console.log(`${fail} Electron test runners have failed`) - process.exit(1) + console.log(`${fail} Electron test runners have failed`); + process.exit(1); } } async function runRemoteBasedElectronTests () { - let exe = path.resolve(BASE, utils.getElectronExec()) - const runnerArgs = ['electron/spec', ...unknownArgs.slice(2)] + let exe = path.resolve(BASE, utils.getElectronExec()); + const runnerArgs = ['electron/spec', ...unknownArgs.slice(2)]; if (process.platform === 'linux') { - runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe) - exe = 'python' + runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe); + exe = 'python'; } const { status } = childProcess.spawnSync(exe, runnerArgs, { cwd: path.resolve(__dirname, '../..'), stdio: 'inherit' - }) + }); if (status !== 0) { - const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString() - console.log(`${fail} Electron tests failed with code ${textStatus}.`) - process.exit(1) + const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString(); + console.log(`${fail} Electron tests failed with code ${textStatus}.`); + process.exit(1); } - console.log(`${pass} Electron remote process tests passed.`) + console.log(`${pass} Electron remote process tests passed.`); } async function runNativeElectronTests () { - let testTargets = require('./native-test-targets.json') - const outDir = `out/${utils.getOutDir()}` + let testTargets = require('./native-test-targets.json'); + const outDir = `out/${utils.getOutDir()}`; // If native tests are being run, only one arg would be relevant if (args.target && !testTargets.includes(args.target)) { - console.log(`${fail} ${args.target} must be a subset of [${[testTargets].join(', ')}]`) - process.exit(1) + console.log(`${fail} ${args.target} must be a subset of [${[testTargets].join(', ')}]`); + process.exit(1); } // Optionally build all native test targets @@ -160,108 +160,108 @@ async function runNativeElectronTests () { const build = childProcess.spawnSync('ninja', ['-C', outDir, target], { cwd: path.resolve(__dirname, '../..'), stdio: 'inherit' - }) + }); // Exit if test target failed to build if (build.status !== 0) { - console.log(`${fail} ${target} failed to build.`) - process.exit(1) + console.log(`${fail} ${target} failed to build.`); + process.exit(1); } } } // If a specific target was passed, only build and run that target - if (args.target) testTargets = [args.target] + if (args.target) testTargets = [args.target]; // Run test targets - const failures = [] + const failures = []; for (const target of testTargets) { - console.info('\nRunning native test for target:', target) + console.info('\nRunning native test for target:', target); const testRun = childProcess.spawnSync(`./${outDir}/${target}`, { cwd: path.resolve(__dirname, '../..'), stdio: 'inherit' - }) + }); // Collect failures and log at end - if (testRun.status !== 0) failures.push({ target }) + if (testRun.status !== 0) failures.push({ target }); } // Exit if any failures if (failures.length > 0) { - console.log(`${fail} Electron native tests failed for the following targets: `, failures) - process.exit(1) + console.log(`${fail} Electron native tests failed for the following targets: `, failures); + process.exit(1); } - console.log(`${pass} Electron native tests passed.`) + console.log(`${pass} Electron native tests passed.`); } async function runMainProcessElectronTests () { - let exe = path.resolve(BASE, utils.getElectronExec()) - const runnerArgs = ['electron/spec-main', ...unknownArgs.slice(2)] + let exe = path.resolve(BASE, utils.getElectronExec()); + const runnerArgs = ['electron/spec-main', ...unknownArgs.slice(2)]; if (process.platform === 'linux') { - runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe) - exe = 'python' + runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe); + exe = 'python'; } const { status, signal } = childProcess.spawnSync(exe, runnerArgs, { cwd: path.resolve(__dirname, '../..'), stdio: 'inherit' - }) + }); if (status !== 0) { if (status) { - const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString() - console.log(`${fail} Electron tests failed with code ${textStatus}.`) + const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString(); + console.log(`${fail} Electron tests failed with code ${textStatus}.`); } else { - console.log(`${fail} Electron tests failed with kill signal ${signal}.`) + console.log(`${fail} Electron tests failed with kill signal ${signal}.`); } - process.exit(1) + process.exit(1); } - console.log(`${pass} Electron main process tests passed.`) + console.log(`${pass} Electron main process tests passed.`); } async function installSpecModules (dir) { - const nodeDir = path.resolve(BASE, `out/${utils.getOutDir({ shouldLog: true })}/gen/node_headers`) + const nodeDir = path.resolve(BASE, `out/${utils.getOutDir({ shouldLog: true })}/gen/node_headers`); const env = Object.assign({}, process.env, { npm_config_nodedir: nodeDir, npm_config_msvs_version: '2019' - }) + }); const { status } = childProcess.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install', '--frozen-lockfile'], { env, cwd: dir, stdio: 'inherit' - }) + }); if (status !== 0 && !process.env.IGNORE_YARN_INSTALL_ERROR) { - console.log(`${fail} Failed to yarn install in '${dir}'`) - process.exit(1) + console.log(`${fail} Failed to yarn install in '${dir}'`); + process.exit(1); } } function getSpecHash () { return Promise.all([ (async () => { - const hasher = crypto.createHash('SHA256') - hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json'))) - hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/package.json'))) - hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/yarn.lock'))) - hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/yarn.lock'))) - return hasher.digest('hex') + const hasher = crypto.createHash('SHA256'); + hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json'))); + hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/package.json'))); + hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/yarn.lock'))); + hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/yarn.lock'))); + return hasher.digest('hex'); })(), (async () => { - const specNodeModulesPath = path.resolve(__dirname, '../spec/node_modules') + const specNodeModulesPath = path.resolve(__dirname, '../spec/node_modules'); if (!fs.existsSync(specNodeModulesPath)) { - return null + return null; } const { hash } = await hashElement(specNodeModulesPath, { folders: { exclude: ['.bin'] } - }) - return hash + }); + return hash; })() - ]) + ]); } main().catch((error) => { - console.error('An error occurred inside the spec runner:', error) - process.exit(1) -}) + console.error('An error occurred inside the spec runner:', error); + process.exit(1); +}); diff --git a/script/start.js b/script/start.js index cadba0030c43d..0ba6e56a1fcb1 100644 --- a/script/start.js +++ b/script/start.js @@ -1,16 +1,16 @@ -const cp = require('child_process') -const utils = require('./lib/utils') -const electronPath = utils.getAbsoluteElectronExec() +const cp = require('child_process'); +const utils = require('./lib/utils'); +const electronPath = utils.getAbsoluteElectronExec(); -const child = cp.spawn(electronPath, process.argv.slice(2), { stdio: 'inherit' }) -child.on('close', (code) => process.exit(code)) +const child = cp.spawn(electronPath, process.argv.slice(2), { stdio: 'inherit' }); +child.on('close', (code) => process.exit(code)); const handleTerminationSignal = (signal) => process.on(signal, () => { if (!child.killed) { - child.kill(signal) + child.kill(signal); } - }) + }); -handleTerminationSignal('SIGINT') -handleTerminationSignal('SIGTERM') +handleTerminationSignal('SIGINT'); +handleTerminationSignal('SIGTERM'); diff --git a/script/yarn.js b/script/yarn.js index f8a5e94eb164e..7217501d3f241 100644 --- a/script/yarn.js +++ b/script/yarn.js @@ -1,18 +1,18 @@ -const cp = require('child_process') -const fs = require('fs') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); -const YARN_VERSION = /'yarn_version': '(.+?)'/.exec(fs.readFileSync(path.resolve(__dirname, '../DEPS'), 'utf8'))[1] +const YARN_VERSION = /'yarn_version': '(.+?)'/.exec(fs.readFileSync(path.resolve(__dirname, '../DEPS'), 'utf8'))[1]; -exports.YARN_VERSION = YARN_VERSION +exports.YARN_VERSION = YARN_VERSION; // If we are running "node script/yarn" run as the yarn CLI if (process.mainModule === module) { - const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' + const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; const child = cp.spawn(NPX_CMD, [`yarn@${YARN_VERSION}`, ...process.argv.slice(2)], { stdio: 'inherit' - }) + }); - child.on('exit', code => process.exit(code)) + child.on('exit', code => process.exit(code)); } diff --git a/spec-main/ambient.d.ts b/spec-main/ambient.d.ts index 779c9262653d1..a3eded2479cdf 100644 --- a/spec-main/ambient.d.ts +++ b/spec-main/ambient.d.ts @@ -1,4 +1,4 @@ -declare let standardScheme: string +declare let standardScheme: string; declare namespace Electron { interface Menu { diff --git a/spec-main/api-app-spec.ts b/spec-main/api-app-spec.ts index 797720c8a8dad..a23881f652756 100644 --- a/spec-main/api-app-spec.ts +++ b/spec-main/api-app-spec.ts @@ -1,38 +1,38 @@ -import { expect } from 'chai' -import * as cp from 'child_process' -import * as https from 'https' -import * as http from 'http' -import * as net from 'net' -import * as fs from 'fs' -import * as path from 'path' -import { app, BrowserWindow, Menu, session } from 'electron' -import { emittedOnce } from './events-helpers' -import { closeWindow, closeAllWindows } from './window-helpers' -import { ifdescribe } from './spec-helpers' +import { expect } from 'chai'; +import * as cp from 'child_process'; +import * as https from 'https'; +import * as http from 'http'; +import * as net from 'net'; +import * as fs from 'fs'; +import * as path from 'path'; +import { app, BrowserWindow, Menu, session } from 'electron'; +import { emittedOnce } from './events-helpers'; +import { closeWindow, closeAllWindows } from './window-helpers'; +import { ifdescribe } from './spec-helpers'; import split = require('split') -const features = process.electronBinding('features') +const features = process.electronBinding('features'); -const fixturesPath = path.resolve(__dirname, '../spec/fixtures') +const fixturesPath = path.resolve(__dirname, '../spec/fixtures'); describe('electron module', () => { it('does not expose internal modules to require', () => { expect(() => { - require('clipboard') - }).to.throw(/Cannot find module 'clipboard'/) - }) + require('clipboard'); + }).to.throw(/Cannot find module 'clipboard'/); + }); describe('require("electron")', () => { it('always returns the internal electron module', () => { - require('electron') - }) - }) -}) + require('electron'); + }); + }); +}); describe('app module', () => { - let server: https.Server - let secureUrl: string - const certPath = path.join(fixturesPath, 'certificates') + let server: https.Server; + let secureUrl: string; + const certPath = path.join(fixturesPath, 'certificates'); before((done) => { const options = { @@ -44,281 +44,281 @@ describe('app module', () => { ], requestCert: true, rejectUnauthorized: false - } + }; server = https.createServer(options, (req, res) => { if ((req as any).client.authorized) { - res.writeHead(200) - res.end('authorized') + res.writeHead(200); + res.end('authorized'); } else { - res.writeHead(401) - res.end('denied') + res.writeHead(401); + res.end('denied'); } - }) + }); server.listen(0, '127.0.0.1', () => { - const port = (server.address() as net.AddressInfo).port - secureUrl = `https://127.0.0.1:${port}` - done() - }) - }) + const port = (server.address() as net.AddressInfo).port; + secureUrl = `https://127.0.0.1:${port}`; + done(); + }); + }); after(done => { - server.close(() => done()) - }) + server.close(() => done()); + }); describe('app.getVersion()', () => { it('returns the version field of package.json', () => { - expect(app.getVersion()).to.equal('0.1.0') - }) - }) + expect(app.getVersion()).to.equal('0.1.0'); + }); + }); describe('app.setVersion(version)', () => { it('overrides the version', () => { - expect(app.getVersion()).to.equal('0.1.0') - app.setVersion('test-version') + expect(app.getVersion()).to.equal('0.1.0'); + app.setVersion('test-version'); - expect(app.getVersion()).to.equal('test-version') - app.setVersion('0.1.0') - }) - }) + expect(app.getVersion()).to.equal('test-version'); + app.setVersion('0.1.0'); + }); + }); describe('app name APIs', () => { it('with properties', () => { it('returns the name field of package.json', () => { - expect(app.name).to.equal('Electron Test Main') - }) + expect(app.name).to.equal('Electron Test Main'); + }); it('overrides the name', () => { - expect(app.name).to.equal('Electron Test Main') - app.name = 'test-name' + expect(app.name).to.equal('Electron Test Main'); + app.name = 'test-name'; - expect(app.name).to.equal('test-name') - app.name = 'Electron Test Main' - }) - }) + expect(app.name).to.equal('test-name'); + app.name = 'Electron Test Main'; + }); + }); it('with functions', () => { it('returns the name field of package.json', () => { - expect(app.getName()).to.equal('Electron Test Main') - }) + expect(app.getName()).to.equal('Electron Test Main'); + }); it('overrides the name', () => { - expect(app.getName()).to.equal('Electron Test Main') - app.setName('test-name') + expect(app.getName()).to.equal('Electron Test Main'); + app.setName('test-name'); - expect(app.getName()).to.equal('test-name') - app.setName('Electron Test Main') - }) - }) - }) + expect(app.getName()).to.equal('test-name'); + app.setName('Electron Test Main'); + }); + }); + }); describe('app.getLocale()', () => { it('should not be empty', () => { - expect(app.getLocale()).to.not.equal('') - }) - }) + expect(app.getLocale()).to.not.equal(''); + }); + }); describe('app.getLocaleCountryCode()', () => { it('should be empty or have length of two', () => { - let expectedLength = 2 + let expectedLength = 2; if (process.platform === 'linux') { // Linux CI machines have no locale. - expectedLength = 0 + expectedLength = 0; } - expect(app.getLocaleCountryCode()).to.be.a('string').and.have.lengthOf(expectedLength) - }) - }) + expect(app.getLocaleCountryCode()).to.be.a('string').and.have.lengthOf(expectedLength); + }); + }); describe('app.isPackaged', () => { it('should be false durings tests', () => { - expect(app.isPackaged).to.equal(false) - }) - }) + expect(app.isPackaged).to.equal(false); + }); + }); ifdescribe(process.platform === 'darwin')('app.isInApplicationsFolder()', () => { it('should be false during tests', () => { - expect(app.isInApplicationsFolder()).to.equal(false) - }) - }) + expect(app.isInApplicationsFolder()).to.equal(false); + }); + }); describe('app.exit(exitCode)', () => { - let appProcess: cp.ChildProcess | null = null + let appProcess: cp.ChildProcess | null = null; afterEach(() => { - if (appProcess) appProcess.kill() - }) + if (appProcess) appProcess.kill(); + }); it('emits a process exit event with the code', async () => { - const appPath = path.join(fixturesPath, 'api', 'quit-app') - const electronPath = process.execPath - let output = '' + const appPath = path.join(fixturesPath, 'api', 'quit-app'); + const electronPath = process.execPath; + let output = ''; - appProcess = cp.spawn(electronPath, [appPath]) + appProcess = cp.spawn(electronPath, [appPath]); if (appProcess && appProcess.stdout) { - appProcess.stdout.on('data', data => { output += data }) + appProcess.stdout.on('data', data => { output += data; }); } - const [code] = await emittedOnce(appProcess, 'exit') + const [code] = await emittedOnce(appProcess, 'exit'); if (process.platform !== 'win32') { - expect(output).to.include('Exit event with code: 123') + expect(output).to.include('Exit event with code: 123'); } - expect(code).to.equal(123) - }) + expect(code).to.equal(123); + }); it('closes all windows', async function () { - const appPath = path.join(fixturesPath, 'api', 'exit-closes-all-windows-app') - const electronPath = process.execPath + const appPath = path.join(fixturesPath, 'api', 'exit-closes-all-windows-app'); + const electronPath = process.execPath; - appProcess = cp.spawn(electronPath, [appPath]) - const [code, signal] = await emittedOnce(appProcess, 'exit') + appProcess = cp.spawn(electronPath, [appPath]); + const [code, signal] = await emittedOnce(appProcess, 'exit'); - expect(signal).to.equal(null, 'exit signal should be null, if you see this please tag @MarshallOfSound') - expect(code).to.equal(123, 'exit code should be 123, if you see this please tag @MarshallOfSound') - }) + expect(signal).to.equal(null, 'exit signal should be null, if you see this please tag @MarshallOfSound'); + expect(code).to.equal(123, 'exit code should be 123, if you see this please tag @MarshallOfSound'); + }); it('exits gracefully', async function () { if (!['darwin', 'linux'].includes(process.platform)) { - this.skip() - return + this.skip(); + return; } - const electronPath = process.execPath - const appPath = path.join(fixturesPath, 'api', 'singleton') - appProcess = cp.spawn(electronPath, [appPath]) + const electronPath = process.execPath; + const appPath = path.join(fixturesPath, 'api', 'singleton'); + appProcess = cp.spawn(electronPath, [appPath]); // Singleton will send us greeting data to let us know it's running. // After that, ask it to exit gracefully and confirm that it does. if (appProcess && appProcess.stdout) { - appProcess.stdout.on('data', () => appProcess!.kill()) + appProcess.stdout.on('data', () => appProcess!.kill()); } - const [code, signal] = await emittedOnce(appProcess, 'exit') + const [code, signal] = await emittedOnce(appProcess, 'exit'); - const message = `code:\n${code}\nsignal:\n${signal}` - expect(code).to.equal(0, message) - expect(signal).to.equal(null, message) - }) - }) + const message = `code:\n${code}\nsignal:\n${signal}`; + expect(code).to.equal(0, message); + expect(signal).to.equal(null, message); + }); + }); ifdescribe(process.platform === 'darwin')('app.setActivationPolicy', () => { it('throws an error on invalid application policies', () => { expect(() => { - app.setActivationPolicy('terrible' as any) - }).to.throw(/Invalid activation policy: must be one of 'regular', 'accessory', or 'prohibited'/) - }) - }) + app.setActivationPolicy('terrible' as any); + }).to.throw(/Invalid activation policy: must be one of 'regular', 'accessory', or 'prohibited'/); + }); + }); describe('app.requestSingleInstanceLock', () => { it('prevents the second launch of app', function (done) { - this.timeout(120000) - const appPath = path.join(fixturesPath, 'api', 'singleton') - const first = cp.spawn(process.execPath, [appPath]) + this.timeout(120000); + const appPath = path.join(fixturesPath, 'api', 'singleton'); + const first = cp.spawn(process.execPath, [appPath]); first.once('exit', code => { - expect(code).to.equal(0) - }) + expect(code).to.equal(0); + }); // Start second app when received output. first.stdout.once('data', () => { - const second = cp.spawn(process.execPath, [appPath]) + const second = cp.spawn(process.execPath, [appPath]); second.once('exit', code => { - expect(code).to.equal(1) - done() - }) - }) - }) + expect(code).to.equal(1); + done(); + }); + }); + }); it('passes arguments to the second-instance event', async () => { - const appPath = path.join(fixturesPath, 'api', 'singleton') - const first = cp.spawn(process.execPath, [appPath]) - const firstExited = emittedOnce(first, 'exit') + const appPath = path.join(fixturesPath, 'api', 'singleton'); + const first = cp.spawn(process.execPath, [appPath]); + const firstExited = emittedOnce(first, 'exit'); // Wait for the first app to boot. - const firstStdoutLines = first.stdout.pipe(split()) + const firstStdoutLines = first.stdout.pipe(split()); while ((await emittedOnce(firstStdoutLines, 'data')).toString() !== 'started') { // wait. } - const data2Promise = emittedOnce(firstStdoutLines, 'data') - - const secondInstanceArgs = [process.execPath, appPath, '--some-switch', 'some-arg'] - const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1)) - const [code2] = await emittedOnce(second, 'exit') - expect(code2).to.equal(1) - const [code1] = await firstExited - expect(code1).to.equal(0) - const data2 = (await data2Promise)[0].toString('ascii') - const secondInstanceArgsReceived: string[] = JSON.parse(data2.toString('ascii')) + const data2Promise = emittedOnce(firstStdoutLines, 'data'); + + const secondInstanceArgs = [process.execPath, appPath, '--some-switch', 'some-arg']; + const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1)); + const [code2] = await emittedOnce(second, 'exit'); + expect(code2).to.equal(1); + const [code1] = await firstExited; + expect(code1).to.equal(0); + const data2 = (await data2Promise)[0].toString('ascii'); + const secondInstanceArgsReceived: string[] = JSON.parse(data2.toString('ascii')); const expected = process.platform === 'win32' ? [process.execPath, '--some-switch', '--allow-file-access-from-files', appPath, 'some-arg'] - : secondInstanceArgs + : secondInstanceArgs; expect(secondInstanceArgsReceived).to.eql(expected, - `expected ${JSON.stringify(expected)} but got ${data2.toString('ascii')}`) - }) - }) + `expected ${JSON.stringify(expected)} but got ${data2.toString('ascii')}`); + }); + }); describe('app.relaunch', () => { - let server: net.Server | null = null - const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch' + let server: net.Server | null = null; + const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'; beforeEach(done => { fs.unlink(socketPath, () => { - server = net.createServer() - server.listen(socketPath) - done() - }) - }) + server = net.createServer(); + server.listen(socketPath); + done(); + }); + }); afterEach((done) => { server!.close(() => { if (process.platform === 'win32') { - done() + done(); } else { - fs.unlink(socketPath, () => done()) + fs.unlink(socketPath, () => done()); } - }) - }) + }); + }); it('relaunches the app', function (done) { - this.timeout(120000) + this.timeout(120000); - let state = 'none' - server!.once('error', error => done(error)) + let state = 'none'; + server!.once('error', error => done(error)); server!.on('connection', client => { client.once('data', data => { if (String(data) === 'false' && state === 'none') { - state = 'first-launch' + state = 'first-launch'; } else if (String(data) === 'true' && state === 'first-launch') { - done() + done(); } else { - done(`Unexpected state: ${state}`) + done(`Unexpected state: ${state}`); } - }) - }) + }); + }); - const appPath = path.join(fixturesPath, 'api', 'relaunch') - cp.spawn(process.execPath, [appPath]) - }) - }) + const appPath = path.join(fixturesPath, 'api', 'relaunch'); + cp.spawn(process.execPath, [appPath]); + }); + }); describe('app.setUserActivity(type, userInfo)', () => { before(function () { if (process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('sets the current activity', () => { - app.setUserActivity('com.electron.testActivity', { testData: '123' }) - expect(app.getCurrentActivityType()).to.equal('com.electron.testActivity') - }) - }) + app.setUserActivity('com.electron.testActivity', { testData: '123' }); + expect(app.getCurrentActivityType()).to.equal('com.electron.testActivity'); + }); + }); describe('certificate-error event', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('is emitted when visiting a server with a self-signed cert', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL(secureUrl) - await emittedOnce(app, 'certificate-error') - }) - }) + const w = new BrowserWindow({ show: false }); + w.loadURL(secureUrl); + await emittedOnce(app, 'certificate-error'); + }); + }); // xdescribe('app.importCertificate', () => { // let w = null @@ -372,65 +372,65 @@ describe('app module', () => { // }) describe('BrowserWindow events', () => { - let w: BrowserWindow = null as any + let w: BrowserWindow = null as any; - afterEach(() => closeWindow(w).then(() => { w = null as any })) + afterEach(() => closeWindow(w).then(() => { w = null as any; })); it('should emit browser-window-focus event when window is focused', (done) => { app.once('browser-window-focus', (e, window) => { - expect(w.id).to.equal(window.id) - done() - }) - w = new BrowserWindow({ show: false }) - w.emit('focus') - }) + expect(w.id).to.equal(window.id); + done(); + }); + w = new BrowserWindow({ show: false }); + w.emit('focus'); + }); it('should emit browser-window-blur event when window is blured', (done) => { app.once('browser-window-blur', (e, window) => { - expect(w.id).to.equal(window.id) - done() - }) - w = new BrowserWindow({ show: false }) - w.emit('blur') - }) + expect(w.id).to.equal(window.id); + done(); + }); + w = new BrowserWindow({ show: false }); + w.emit('blur'); + }); it('should emit browser-window-created event when window is created', (done) => { app.once('browser-window-created', (e, window) => { setImmediate(() => { - expect(w.id).to.equal(window.id) - done() - }) - }) - w = new BrowserWindow({ show: false }) - }) + expect(w.id).to.equal(window.id); + done(); + }); + }); + w = new BrowserWindow({ show: false }); + }); it('should emit web-contents-created event when a webContents is created', (done) => { app.once('web-contents-created', (e, webContents) => { setImmediate(() => { - expect(w.webContents.id).to.equal(webContents.id) - done() - }) - }) - w = new BrowserWindow({ show: false }) - }) + expect(w.webContents.id).to.equal(webContents.id); + done(); + }); + }); + w = new BrowserWindow({ show: false }); + }); it('should emit renderer-process-crashed event when renderer crashes', async function () { // FIXME: re-enable this test on win32. - if (process.platform === 'win32') { return this.skip() } + if (process.platform === 'win32') { return this.skip(); } w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } - }) - await w.loadURL('about:blank') + }); + await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'renderer-process-crashed') - w.webContents.executeJavaScript('process.crash()') + const promise = emittedOnce(app, 'renderer-process-crashed'); + w.webContents.executeJavaScript('process.crash()'); - const [, webContents] = await promise - expect(webContents).to.equal(w.webContents) - }) + const [, webContents] = await promise; + expect(webContents).to.equal(w.webContents); + }); ifdescribe(features.isDesktopCapturerEnabled())('desktopCapturer module filtering', () => { it('should emit desktop-capturer-get-sources event when desktopCapturer.getSources() is invoked', async () => { @@ -439,16 +439,16 @@ describe('app module', () => { webPreferences: { nodeIntegration: true } - }) - await w.loadURL('about:blank') + }); + await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'desktop-capturer-get-sources') - w.webContents.executeJavaScript('require(\'electron\').desktopCapturer.getSources({ types: [\'screen\'] })') + const promise = emittedOnce(app, 'desktop-capturer-get-sources'); + w.webContents.executeJavaScript('require(\'electron\').desktopCapturer.getSources({ types: [\'screen\'] })'); - const [, webContents] = await promise - expect(webContents).to.equal(w.webContents) - }) - }) + const [, webContents] = await promise; + expect(webContents).to.equal(w.webContents); + }); + }); ifdescribe(features.isRemoteModuleEnabled())('remote module filtering', () => { it('should emit remote-require event when remote.require() is invoked', async () => { @@ -458,16 +458,16 @@ describe('app module', () => { nodeIntegration: true, enableRemoteModule: true } - }) - await w.loadURL('about:blank') + }); + await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'remote-require') - w.webContents.executeJavaScript('require(\'electron\').remote.require(\'test\')') + const promise = emittedOnce(app, 'remote-require'); + w.webContents.executeJavaScript('require(\'electron\').remote.require(\'test\')'); - const [, webContents, moduleName] = await promise - expect(webContents).to.equal(w.webContents) - expect(moduleName).to.equal('test') - }) + const [, webContents, moduleName] = await promise; + expect(webContents).to.equal(w.webContents); + expect(moduleName).to.equal('test'); + }); it('should emit remote-get-global event when remote.getGlobal() is invoked', async () => { w = new BrowserWindow({ @@ -476,16 +476,16 @@ describe('app module', () => { nodeIntegration: true, enableRemoteModule: true } - }) - await w.loadURL('about:blank') + }); + await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'remote-get-global') - w.webContents.executeJavaScript('require(\'electron\').remote.getGlobal(\'test\')') + const promise = emittedOnce(app, 'remote-get-global'); + w.webContents.executeJavaScript('require(\'electron\').remote.getGlobal(\'test\')'); - const [, webContents, globalName] = await promise - expect(webContents).to.equal(w.webContents) - expect(globalName).to.equal('test') - }) + const [, webContents, globalName] = await promise; + expect(webContents).to.equal(w.webContents); + expect(globalName).to.equal('test'); + }); it('should emit remote-get-builtin event when remote.getBuiltin() is invoked', async () => { w = new BrowserWindow({ @@ -494,16 +494,16 @@ describe('app module', () => { nodeIntegration: true, enableRemoteModule: true } - }) - await w.loadURL('about:blank') + }); + await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'remote-get-builtin') - w.webContents.executeJavaScript('require(\'electron\').remote.app') + const promise = emittedOnce(app, 'remote-get-builtin'); + w.webContents.executeJavaScript('require(\'electron\').remote.app'); - const [, webContents, moduleName] = await promise - expect(webContents).to.equal(w.webContents) - expect(moduleName).to.equal('app') - }) + const [, webContents, moduleName] = await promise; + expect(webContents).to.equal(w.webContents); + expect(moduleName).to.equal('app'); + }); it('should emit remote-get-current-window event when remote.getCurrentWindow() is invoked', async () => { w = new BrowserWindow({ @@ -512,15 +512,15 @@ describe('app module', () => { nodeIntegration: true, enableRemoteModule: true } - }) - await w.loadURL('about:blank') + }); + await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'remote-get-current-window') - w.webContents.executeJavaScript('{ require(\'electron\').remote.getCurrentWindow() }') + const promise = emittedOnce(app, 'remote-get-current-window'); + w.webContents.executeJavaScript('{ require(\'electron\').remote.getCurrentWindow() }'); - const [, webContents] = await promise - expect(webContents).to.equal(w.webContents) - }) + const [, webContents] = await promise; + expect(webContents).to.equal(w.webContents); + }); it('should emit remote-get-current-web-contents event when remote.getCurrentWebContents() is invoked', async () => { w = new BrowserWindow({ @@ -529,193 +529,193 @@ describe('app module', () => { nodeIntegration: true, enableRemoteModule: true } - }) - await w.loadURL('about:blank') + }); + await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'remote-get-current-web-contents') - w.webContents.executeJavaScript('{ require(\'electron\').remote.getCurrentWebContents() }') + const promise = emittedOnce(app, 'remote-get-current-web-contents'); + w.webContents.executeJavaScript('{ require(\'electron\').remote.getCurrentWebContents() }'); - const [, webContents] = await promise - expect(webContents).to.equal(w.webContents) - }) - }) - }) + const [, webContents] = await promise; + expect(webContents).to.equal(w.webContents); + }); + }); + }); describe('app.badgeCount', () => { const platformIsNotSupported = (process.platform === 'win32') || - (process.platform === 'linux' && !app.isUnityRunning()) - const platformIsSupported = !platformIsNotSupported + (process.platform === 'linux' && !app.isUnityRunning()); + const platformIsSupported = !platformIsNotSupported; - const expectedBadgeCount = 42 + const expectedBadgeCount = 42; - after(() => { app.badgeCount = 0 }) + after(() => { app.badgeCount = 0; }); describe('on supported platform', () => { it('with properties', () => { it('sets a badge count', function () { - if (platformIsNotSupported) return this.skip() + if (platformIsNotSupported) return this.skip(); - app.badgeCount = expectedBadgeCount - expect(app.badgeCount).to.equal(expectedBadgeCount) - }) - }) + app.badgeCount = expectedBadgeCount; + expect(app.badgeCount).to.equal(expectedBadgeCount); + }); + }); it('with functions', () => { it('sets a badge count', function () { - if (platformIsNotSupported) return this.skip() + if (platformIsNotSupported) return this.skip(); - app.setBadgeCount(expectedBadgeCount) - expect(app.getBadgeCount()).to.equal(expectedBadgeCount) - }) - }) - }) + app.setBadgeCount(expectedBadgeCount); + expect(app.getBadgeCount()).to.equal(expectedBadgeCount); + }); + }); + }); describe('on unsupported platform', () => { it('with properties', () => { it('does not set a badge count', function () { - if (platformIsSupported) return this.skip() + if (platformIsSupported) return this.skip(); - app.badgeCount = 9999 - expect(app.badgeCount).to.equal(0) - }) - }) + app.badgeCount = 9999; + expect(app.badgeCount).to.equal(0); + }); + }); it('with functions', () => { it('does not set a badge count)', function () { - if (platformIsSupported) return this.skip() + if (platformIsSupported) return this.skip(); - app.setBadgeCount(9999) - expect(app.getBadgeCount()).to.equal(0) - }) - }) - }) - }) + app.setBadgeCount(9999); + expect(app.getBadgeCount()).to.equal(0); + }); + }); + }); + }); describe('app.get/setLoginItemSettings API', function () { - const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe') + const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe'); const processStartArgs = [ '--processStart', `"${path.basename(process.execPath)}"`, '--process-start-args', '"--hidden"' - ] + ]; before(function () { - if (process.platform === 'linux' || process.mas) this.skip() - }) + if (process.platform === 'linux' || process.mas) this.skip(); + }); beforeEach(() => { - app.setLoginItemSettings({ openAtLogin: false }) - app.setLoginItemSettings({ openAtLogin: false, path: updateExe, args: processStartArgs }) - }) + app.setLoginItemSettings({ openAtLogin: false }); + app.setLoginItemSettings({ openAtLogin: false, path: updateExe, args: processStartArgs }); + }); afterEach(() => { - app.setLoginItemSettings({ openAtLogin: false }) - app.setLoginItemSettings({ openAtLogin: false, path: updateExe, args: processStartArgs }) - }) + app.setLoginItemSettings({ openAtLogin: false }); + app.setLoginItemSettings({ openAtLogin: false, path: updateExe, args: processStartArgs }); + }); it('sets and returns the app as a login item', done => { - app.setLoginItemSettings({ openAtLogin: true }) + app.setLoginItemSettings({ openAtLogin: true }); expect(app.getLoginItemSettings()).to.deep.equal({ openAtLogin: true, openAsHidden: false, wasOpenedAtLogin: false, wasOpenedAsHidden: false, restoreState: false - }) - done() - }) + }); + done(); + }); it('adds a login item that loads in hidden mode', done => { - app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true }) + app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true }); expect(app.getLoginItemSettings()).to.deep.equal({ openAtLogin: true, openAsHidden: process.platform === 'darwin' && !process.mas, // Only available on macOS wasOpenedAtLogin: false, wasOpenedAsHidden: false, restoreState: false - }) - done() - }) + }); + done(); + }); it('correctly sets and unsets the LoginItem', function () { - expect(app.getLoginItemSettings().openAtLogin).to.equal(false) + expect(app.getLoginItemSettings().openAtLogin).to.equal(false); - app.setLoginItemSettings({ openAtLogin: true }) - expect(app.getLoginItemSettings().openAtLogin).to.equal(true) + app.setLoginItemSettings({ openAtLogin: true }); + expect(app.getLoginItemSettings().openAtLogin).to.equal(true); - app.setLoginItemSettings({ openAtLogin: false }) - expect(app.getLoginItemSettings().openAtLogin).to.equal(false) - }) + app.setLoginItemSettings({ openAtLogin: false }); + expect(app.getLoginItemSettings().openAtLogin).to.equal(false); + }); it('correctly sets and unsets the LoginItem as hidden', function () { - if (process.platform !== 'darwin') this.skip() + if (process.platform !== 'darwin') this.skip(); - expect(app.getLoginItemSettings().openAtLogin).to.equal(false) - expect(app.getLoginItemSettings().openAsHidden).to.equal(false) + expect(app.getLoginItemSettings().openAtLogin).to.equal(false); + expect(app.getLoginItemSettings().openAsHidden).to.equal(false); - app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true }) - expect(app.getLoginItemSettings().openAtLogin).to.equal(true) - expect(app.getLoginItemSettings().openAsHidden).to.equal(true) + app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true }); + expect(app.getLoginItemSettings().openAtLogin).to.equal(true); + expect(app.getLoginItemSettings().openAsHidden).to.equal(true); - app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false }) - expect(app.getLoginItemSettings().openAtLogin).to.equal(true) - expect(app.getLoginItemSettings().openAsHidden).to.equal(false) - }) + app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false }); + expect(app.getLoginItemSettings().openAtLogin).to.equal(true); + expect(app.getLoginItemSettings().openAsHidden).to.equal(false); + }); it('allows you to pass a custom executable and arguments', function () { - if (process.platform !== 'win32') this.skip() + if (process.platform !== 'win32') this.skip(); - app.setLoginItemSettings({ openAtLogin: true, path: updateExe, args: processStartArgs }) + app.setLoginItemSettings({ openAtLogin: true, path: updateExe, args: processStartArgs }); - expect(app.getLoginItemSettings().openAtLogin).to.equal(false) + expect(app.getLoginItemSettings().openAtLogin).to.equal(false); expect(app.getLoginItemSettings({ path: updateExe, args: processStartArgs - }).openAtLogin).to.equal(true) - }) - }) + }).openAtLogin).to.equal(true); + }); + }); ifdescribe(process.platform !== 'linux')('accessibilitySupportEnabled property', () => { it('with properties', () => { it('can set accessibility support enabled', () => { - expect(app.accessibilitySupportEnabled).to.eql(false) + expect(app.accessibilitySupportEnabled).to.eql(false); - app.accessibilitySupportEnabled = true - expect(app.accessibilitySupportEnabled).to.eql(true) - }) - }) + app.accessibilitySupportEnabled = true; + expect(app.accessibilitySupportEnabled).to.eql(true); + }); + }); it('with functions', () => { it('can set accessibility support enabled', () => { - expect(app.isAccessibilitySupportEnabled()).to.eql(false) + expect(app.isAccessibilitySupportEnabled()).to.eql(false); - app.setAccessibilitySupportEnabled(true) - expect(app.isAccessibilitySupportEnabled()).to.eql(true) - }) - }) - }) + app.setAccessibilitySupportEnabled(true); + expect(app.isAccessibilitySupportEnabled()).to.eql(true); + }); + }); + }); describe('getAppPath', () => { it('works for directories with package.json', async () => { - const { appPath } = await runTestApp('app-path') - expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path')) - }) + const { appPath } = await runTestApp('app-path'); + expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path')); + }); it('works for directories with index.js', async () => { - const { appPath } = await runTestApp('app-path/lib') - expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path/lib')) - }) + const { appPath } = await runTestApp('app-path/lib'); + expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path/lib')); + }); it('works for files without extension', async () => { - const { appPath } = await runTestApp('app-path/lib/index') - expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path/lib')) - }) + const { appPath } = await runTestApp('app-path/lib/index'); + expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path/lib')); + }); it('works for files', async () => { - const { appPath } = await runTestApp('app-path/lib/index.js') - expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path/lib')) - }) - }) + const { appPath } = await runTestApp('app-path/lib/index.js'); + expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path/lib')); + }); + }); describe('getPath(name)', () => { it('returns paths that exist', () => { @@ -723,61 +723,61 @@ describe('app module', () => { fs.existsSync(app.getPath('exe')), fs.existsSync(app.getPath('home')), fs.existsSync(app.getPath('temp')) - ] - expect(paths).to.deep.equal([true, true, true]) - }) + ]; + expect(paths).to.deep.equal([true, true, true]); + }); it('throws an error when the name is invalid', () => { expect(() => { - app.getPath('does-not-exist' as any) - }).to.throw(/Failed to get 'does-not-exist' path/) - }) + app.getPath('does-not-exist' as any); + }).to.throw(/Failed to get 'does-not-exist' path/); + }); it('returns the overridden path', () => { - app.setPath('music', __dirname) - expect(app.getPath('music')).to.equal(__dirname) - }) - }) + app.setPath('music', __dirname); + expect(app.getPath('music')).to.equal(__dirname); + }); + }); describe('setPath(name, path)', () => { it('throws when a relative path is passed', () => { - const badPath = 'hey/hi/hello' + const badPath = 'hey/hi/hello'; expect(() => { - app.setPath('music', badPath) - }).to.throw(/Path must be absolute/) - }) + app.setPath('music', badPath); + }).to.throw(/Path must be absolute/); + }); it('does not create a new directory by default', () => { - const badPath = path.join(__dirname, 'music') + const badPath = path.join(__dirname, 'music'); - expect(fs.existsSync(badPath)).to.be.false() - app.setPath('music', badPath) - expect(fs.existsSync(badPath)).to.be.false() + expect(fs.existsSync(badPath)).to.be.false(); + app.setPath('music', badPath); + expect(fs.existsSync(badPath)).to.be.false(); - expect(() => { app.getPath(badPath as any) }).to.throw() - }) - }) + expect(() => { app.getPath(badPath as any); }).to.throw(); + }); + }); describe('setAppLogsPath(path)', () => { it('throws when a relative path is passed', () => { - const badPath = 'hey/hi/hello' + const badPath = 'hey/hi/hello'; expect(() => { - app.setAppLogsPath(badPath) - }).to.throw(/Path must be absolute/) - }) - }) + app.setAppLogsPath(badPath); + }).to.throw(/Path must be absolute/); + }); + }); describe('select-client-certificate event', () => { - let w: BrowserWindow + let w: BrowserWindow; before(function () { if (process.platform === 'linux') { - this.skip() + this.skip(); } - session.fromPartition('empty-certificate').setCertificateVerifyProc((req, cb) => { cb(0) }) - }) + session.fromPartition('empty-certificate').setCertificateVerifyProc((req, cb) => { cb(0); }); + }); beforeEach(() => { w = new BrowserWindow({ @@ -786,144 +786,144 @@ describe('app module', () => { nodeIntegration: true, partition: 'empty-certificate' } - }) - }) + }); + }); - afterEach(() => closeWindow(w).then(() => { w = null as any })) + afterEach(() => closeWindow(w).then(() => { w = null as any; })); - after(() => session.fromPartition('empty-certificate').setCertificateVerifyProc(null)) + after(() => session.fromPartition('empty-certificate').setCertificateVerifyProc(null)); it('can respond with empty certificate list', async () => { app.once('select-client-certificate', function (event, webContents, url, list, callback) { - console.log('select-client-certificate emitted') - event.preventDefault() - callback() - }) - await w.webContents.loadURL(secureUrl) - expect(w.webContents.getTitle()).to.equal('denied') - }) - }) + console.log('select-client-certificate emitted'); + event.preventDefault(); + callback(); + }); + await w.webContents.loadURL(secureUrl); + expect(w.webContents.getTitle()).to.equal('denied'); + }); + }); describe('setAsDefaultProtocolClient(protocol, path, args)', () => { - const protocol = 'electron-test' - const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe') + const protocol = 'electron-test'; + const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe'); const processStartArgs = [ '--processStart', `"${path.basename(process.execPath)}"`, '--process-start-args', '"--hidden"' - ] + ]; - let Winreg: any - let classesKey: any + let Winreg: any; + let classesKey: any; before(function () { if (process.platform !== 'win32') { - this.skip() + this.skip(); } else { - Winreg = require('winreg') + Winreg = require('winreg'); classesKey = new Winreg({ hive: Winreg.HKCU, key: '\\Software\\Classes\\' - }) + }); } - }) + }); after(function (done) { if (process.platform !== 'win32') { - done() + done(); } else { const protocolKey = new Winreg({ hive: Winreg.HKCU, key: `\\Software\\Classes\\${protocol}` - }) + }); // The last test leaves the registry dirty, // delete the protocol key for those of us who test at home - protocolKey.destroy(() => done()) + protocolKey.destroy(() => done()); } - }) + }); beforeEach(() => { - app.removeAsDefaultProtocolClient(protocol) - app.removeAsDefaultProtocolClient(protocol, updateExe, processStartArgs) - }) + app.removeAsDefaultProtocolClient(protocol); + app.removeAsDefaultProtocolClient(protocol, updateExe, processStartArgs); + }); afterEach(() => { - app.removeAsDefaultProtocolClient(protocol) - expect(app.isDefaultProtocolClient(protocol)).to.equal(false) + app.removeAsDefaultProtocolClient(protocol); + expect(app.isDefaultProtocolClient(protocol)).to.equal(false); - app.removeAsDefaultProtocolClient(protocol, updateExe, processStartArgs) - expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(false) - }) + app.removeAsDefaultProtocolClient(protocol, updateExe, processStartArgs); + expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(false); + }); it('sets the app as the default protocol client', () => { - expect(app.isDefaultProtocolClient(protocol)).to.equal(false) - app.setAsDefaultProtocolClient(protocol) - expect(app.isDefaultProtocolClient(protocol)).to.equal(true) - }) + expect(app.isDefaultProtocolClient(protocol)).to.equal(false); + app.setAsDefaultProtocolClient(protocol); + expect(app.isDefaultProtocolClient(protocol)).to.equal(true); + }); it('allows a custom path and args to be specified', () => { - expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(false) - app.setAsDefaultProtocolClient(protocol, updateExe, processStartArgs) + expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(false); + app.setAsDefaultProtocolClient(protocol, updateExe, processStartArgs); - expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(true) - expect(app.isDefaultProtocolClient(protocol)).to.equal(false) - }) + expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(true); + expect(app.isDefaultProtocolClient(protocol)).to.equal(false); + }); it('creates a registry entry for the protocol class', (done) => { - app.setAsDefaultProtocolClient(protocol) + app.setAsDefaultProtocolClient(protocol); classesKey.keys((error: Error, keys: any[]) => { - if (error) throw error + if (error) throw error; - const exists = !!keys.find(key => key.key.includes(protocol)) - expect(exists).to.equal(true) + const exists = !!keys.find(key => key.key.includes(protocol)); + expect(exists).to.equal(true); - done() - }) - }) + done(); + }); + }); it('completely removes a registry entry for the protocol class', (done) => { - app.setAsDefaultProtocolClient(protocol) - app.removeAsDefaultProtocolClient(protocol) + app.setAsDefaultProtocolClient(protocol); + app.removeAsDefaultProtocolClient(protocol); classesKey.keys((error: Error, keys: any[]) => { - if (error) throw error + if (error) throw error; - const exists = !!keys.find(key => key.key.includes(protocol)) - expect(exists).to.equal(false) + const exists = !!keys.find(key => key.key.includes(protocol)); + expect(exists).to.equal(false); - done() - }) - }) + done(); + }); + }); it('only unsets a class registry key if it contains other data', (done) => { - app.setAsDefaultProtocolClient(protocol) + app.setAsDefaultProtocolClient(protocol); const protocolKey = new Winreg({ hive: Winreg.HKCU, key: `\\Software\\Classes\\${protocol}` - }) + }); protocolKey.set('test-value', 'REG_BINARY', '123', () => { - app.removeAsDefaultProtocolClient(protocol) + app.removeAsDefaultProtocolClient(protocol); classesKey.keys((error: Error, keys: any[]) => { - if (error) throw error + if (error) throw error; - const exists = !!keys.find(key => key.key.includes(protocol)) - expect(exists).to.equal(true) + const exists = !!keys.find(key => key.key.includes(protocol)); + expect(exists).to.equal(true); - done() - }) - }) - }) + done(); + }); + }); + }); it('sets the default client such that getApplicationNameForProtocol returns Electron', () => { - app.setAsDefaultProtocolClient(protocol) - expect(app.getApplicationNameForProtocol(`${protocol}://`)).to.equal('Electron') - }) - }) + app.setAsDefaultProtocolClient(protocol); + expect(app.getApplicationNameForProtocol(`${protocol}://`)).to.equal('Electron'); + }); + }); describe('getApplicationNameForProtocol()', () => { it('returns application names for common protocols', function () { @@ -931,236 +931,236 @@ describe('app module', () => { // at least have _something_ registered. Except on our Linux CI // environment apparently. if (process.platform === 'linux') { - this.skip() + this.skip(); } const protocols = [ 'http://', 'https://' - ] + ]; protocols.forEach((protocol) => { - expect(app.getApplicationNameForProtocol(protocol)).to.not.equal('') - }) - }) + expect(app.getApplicationNameForProtocol(protocol)).to.not.equal(''); + }); + }); it('returns an empty string for a bogus protocol', () => { - expect(app.getApplicationNameForProtocol('bogus-protocol://')).to.equal('') - }) - }) + expect(app.getApplicationNameForProtocol('bogus-protocol://')).to.equal(''); + }); + }); describe('isDefaultProtocolClient()', () => { it('returns false for a bogus protocol', () => { - expect(app.isDefaultProtocolClient('bogus-protocol://')).to.equal(false) - }) - }) + expect(app.isDefaultProtocolClient('bogus-protocol://')).to.equal(false); + }); + }); describe('app launch through uri', () => { before(function () { if (process.platform !== 'win32') { - this.skip() + this.skip(); } - }) + }); it('does not launch for argument following a URL', done => { - const appPath = path.join(fixturesPath, 'api', 'quit-app') + const appPath = path.join(fixturesPath, 'api', 'quit-app'); // App should exit with non 123 code. - const first = cp.spawn(process.execPath, [appPath, 'electron-test:?', 'abc']) + const first = cp.spawn(process.execPath, [appPath, 'electron-test:?', 'abc']); first.once('exit', code => { - expect(code).to.not.equal(123) - done() - }) - }) + expect(code).to.not.equal(123); + done(); + }); + }); it('launches successfully for argument following a file path', done => { - const appPath = path.join(fixturesPath, 'api', 'quit-app') + const appPath = path.join(fixturesPath, 'api', 'quit-app'); // App should exit with code 123. - const first = cp.spawn(process.execPath, [appPath, 'e:\\abc', 'abc']) + const first = cp.spawn(process.execPath, [appPath, 'e:\\abc', 'abc']); first.once('exit', code => { - expect(code).to.equal(123) - done() - }) - }) + expect(code).to.equal(123); + done(); + }); + }); it('launches successfully for multiple URIs following --', done => { - const appPath = path.join(fixturesPath, 'api', 'quit-app') + const appPath = path.join(fixturesPath, 'api', 'quit-app'); // App should exit with code 123. - const first = cp.spawn(process.execPath, [appPath, '--', 'http://electronjs.org', 'electron-test://testdata']) + const first = cp.spawn(process.execPath, [appPath, '--', 'http://electronjs.org', 'electron-test://testdata']); first.once('exit', code => { - expect(code).to.equal(123) - done() - }) - }) - }) + expect(code).to.equal(123); + done(); + }); + }); + }); // FIXME Get these specs running on Linux CI ifdescribe(process.platform !== 'linux')('getFileIcon() API', () => { - const iconPath = path.join(__dirname, 'fixtures/assets/icon.ico') + const iconPath = path.join(__dirname, 'fixtures/assets/icon.ico'); const sizes = { small: 16, normal: 32, large: process.platform === 'win32' ? 32 : 48 - } + }; it('fetches a non-empty icon', async () => { - const icon = await app.getFileIcon(iconPath) - expect(icon.isEmpty()).to.equal(false) - }) + const icon = await app.getFileIcon(iconPath); + expect(icon.isEmpty()).to.equal(false); + }); it('fetches normal icon size by default', async () => { - const icon = await app.getFileIcon(iconPath) - const size = icon.getSize() + const icon = await app.getFileIcon(iconPath); + const size = icon.getSize(); - expect(size.height).to.equal(sizes.normal) - expect(size.width).to.equal(sizes.normal) - }) + expect(size.height).to.equal(sizes.normal); + expect(size.width).to.equal(sizes.normal); + }); describe('size option', () => { it('fetches a small icon', async () => { - const icon = await app.getFileIcon(iconPath, { size: 'small' }) - const size = icon.getSize() + const icon = await app.getFileIcon(iconPath, { size: 'small' }); + const size = icon.getSize(); - expect(size.height).to.equal(sizes.small) - expect(size.width).to.equal(sizes.small) - }) + expect(size.height).to.equal(sizes.small); + expect(size.width).to.equal(sizes.small); + }); it('fetches a normal icon', async () => { - const icon = await app.getFileIcon(iconPath, { size: 'normal' }) - const size = icon.getSize() + const icon = await app.getFileIcon(iconPath, { size: 'normal' }); + const size = icon.getSize(); - expect(size.height).to.equal(sizes.normal) - expect(size.width).to.equal(sizes.normal) - }) + expect(size.height).to.equal(sizes.normal); + expect(size.width).to.equal(sizes.normal); + }); it('fetches a large icon', async () => { // macOS does not support large icons - if (process.platform === 'darwin') return + if (process.platform === 'darwin') return; - const icon = await app.getFileIcon(iconPath, { size: 'large' }) - const size = icon.getSize() + const icon = await app.getFileIcon(iconPath, { size: 'large' }); + const size = icon.getSize(); - expect(size.height).to.equal(sizes.large) - expect(size.width).to.equal(sizes.large) - }) - }) - }) + expect(size.height).to.equal(sizes.large); + expect(size.width).to.equal(sizes.large); + }); + }); + }); describe('getAppMetrics() API', () => { it('returns memory and cpu stats of all running electron processes', () => { - const appMetrics = app.getAppMetrics() - expect(appMetrics).to.be.an('array').and.have.lengthOf.at.least(1, 'App memory info object is not > 0') + const appMetrics = app.getAppMetrics(); + expect(appMetrics).to.be.an('array').and.have.lengthOf.at.least(1, 'App memory info object is not > 0'); - const types = [] + const types = []; for (const entry of appMetrics) { - expect(entry.pid).to.be.above(0, 'pid is not > 0') - expect(entry.type).to.be.a('string').that.does.not.equal('') - expect(entry.creationTime).to.be.a('number').that.is.greaterThan(0) + expect(entry.pid).to.be.above(0, 'pid is not > 0'); + expect(entry.type).to.be.a('string').that.does.not.equal(''); + expect(entry.creationTime).to.be.a('number').that.is.greaterThan(0); - types.push(entry.type) - expect(entry.cpu).to.have.ownProperty('percentCPUUsage').that.is.a('number') - expect(entry.cpu).to.have.ownProperty('idleWakeupsPerSecond').that.is.a('number') + types.push(entry.type); + expect(entry.cpu).to.have.ownProperty('percentCPUUsage').that.is.a('number'); + expect(entry.cpu).to.have.ownProperty('idleWakeupsPerSecond').that.is.a('number'); - expect(entry.memory).to.have.property('workingSetSize').that.is.greaterThan(0) - expect(entry.memory).to.have.property('peakWorkingSetSize').that.is.greaterThan(0) + expect(entry.memory).to.have.property('workingSetSize').that.is.greaterThan(0); + expect(entry.memory).to.have.property('peakWorkingSetSize').that.is.greaterThan(0); if (process.platform === 'win32') { - expect(entry.memory).to.have.property('privateBytes').that.is.greaterThan(0) + expect(entry.memory).to.have.property('privateBytes').that.is.greaterThan(0); } if (process.platform !== 'linux') { - expect(entry.sandboxed).to.be.a('boolean') + expect(entry.sandboxed).to.be.a('boolean'); } if (process.platform === 'win32') { - expect(entry.integrityLevel).to.be.a('string') + expect(entry.integrityLevel).to.be.a('string'); } } if (process.platform === 'darwin') { - expect(types).to.include('GPU') + expect(types).to.include('GPU'); } - expect(types).to.include('Browser') - }) - }) + expect(types).to.include('Browser'); + }); + }); describe('getGPUFeatureStatus() API', () => { it('returns the graphic features statuses', () => { - const features = app.getGPUFeatureStatus() - expect(features).to.have.ownProperty('webgl').that.is.a('string') - expect(features).to.have.ownProperty('gpu_compositing').that.is.a('string') - }) - }) + const features = app.getGPUFeatureStatus(); + expect(features).to.have.ownProperty('webgl').that.is.a('string'); + expect(features).to.have.ownProperty('gpu_compositing').that.is.a('string'); + }); + }); describe('getGPUInfo() API', () => { - const appPath = path.join(fixturesPath, 'api', 'gpu-info.js') + const appPath = path.join(fixturesPath, 'api', 'gpu-info.js'); const getGPUInfo = async (type: string) => { - const appProcess = cp.spawn(process.execPath, [appPath, type]) - let gpuInfoData = '' - let errorData = '' + const appProcess = cp.spawn(process.execPath, [appPath, type]); + let gpuInfoData = ''; + let errorData = ''; appProcess.stdout.on('data', (data) => { - gpuInfoData += data - }) + gpuInfoData += data; + }); appProcess.stderr.on('data', (data) => { - errorData += data - }) - const [exitCode] = await emittedOnce(appProcess, 'exit') + errorData += data; + }); + const [exitCode] = await emittedOnce(appProcess, 'exit'); if (exitCode === 0) { // return info data on successful exit - return JSON.parse(gpuInfoData) + return JSON.parse(gpuInfoData); } else { // return error if not clean exit - return Promise.reject(new Error(errorData)) + return Promise.reject(new Error(errorData)); } - } + }; const verifyBasicGPUInfo = async (gpuInfo: any) => { // Devices information is always present in the available info. expect(gpuInfo).to.have.ownProperty('gpuDevice') .that.is.an('array') - .and.does.not.equal([]) + .and.does.not.equal([]); - const device = gpuInfo.gpuDevice[0] + const device = gpuInfo.gpuDevice[0]; expect(device).to.be.an('object') .and.to.have.property('deviceId') .that.is.a('number') - .not.lessThan(0) - } + .not.lessThan(0); + }; it('succeeds with basic GPUInfo', async () => { - const gpuInfo = await getGPUInfo('basic') - await verifyBasicGPUInfo(gpuInfo) - }) + const gpuInfo = await getGPUInfo('basic'); + await verifyBasicGPUInfo(gpuInfo); + }); it('succeeds with complete GPUInfo', async () => { - const completeInfo = await getGPUInfo('complete') + const completeInfo = await getGPUInfo('complete'); if (process.platform === 'linux') { // For linux and macOS complete info is same as basic info - await verifyBasicGPUInfo(completeInfo) - const basicInfo = await getGPUInfo('basic') - expect(completeInfo).to.deep.equal(basicInfo) + await verifyBasicGPUInfo(completeInfo); + const basicInfo = await getGPUInfo('basic'); + expect(completeInfo).to.deep.equal(basicInfo); } else { // Gl version is present in the complete info. expect(completeInfo).to.have.ownProperty('auxAttributes') - .that.is.an('object') + .that.is.an('object'); if (completeInfo.gpuDevice.active) { expect(completeInfo.auxAttributes).to.have.ownProperty('glVersion') .that.is.a('string') - .and.does.not.equal([]) + .and.does.not.equal([]); } } - }) + }); it('fails for invalid info_type', () => { - const invalidType = 'invalid' - const expectedErrorMessage = "Invalid info type. Use 'basic' or 'complete'" - return expect(app.getGPUInfo(invalidType as any)).to.eventually.be.rejectedWith(expectedErrorMessage) - }) - }) + const invalidType = 'invalid'; + const expectedErrorMessage = "Invalid info type. Use 'basic' or 'complete'"; + return expect(app.getGPUInfo(invalidType as any)).to.eventually.be.rejectedWith(expectedErrorMessage); + }); + }); describe('sandbox options', () => { - let appProcess: cp.ChildProcess = null as any - let server: net.Server = null as any - const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox' + let appProcess: cp.ChildProcess = null as any; + let server: net.Server = null as any; + const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox'; beforeEach(function (done) { if (process.platform === 'linux' && (process.arch === 'arm64' || process.arch === 'arm')) { @@ -1176,357 +1176,357 @@ describe('app module', () => { // Adding `--cap-add SYS_ADMIN` or `--security-opt seccomp=unconfined` // to the Docker invocation allows the syscalls that Chrome needs, but // are probably more permissive than we'd like. - this.skip() + this.skip(); } fs.unlink(socketPath, () => { - server = net.createServer() - server.listen(socketPath) - done() - }) - }) + server = net.createServer(); + server.listen(socketPath); + done(); + }); + }); afterEach(done => { - if (appProcess != null) appProcess.kill() + if (appProcess != null) appProcess.kill(); server.close(() => { if (process.platform === 'win32') { - done() + done(); } else { - fs.unlink(socketPath, () => done()) + fs.unlink(socketPath, () => done()); } - }) - }) + }); + }); describe('when app.enableSandbox() is called', () => { it('adds --enable-sandbox to all renderer processes', done => { - const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app') - appProcess = cp.spawn(process.execPath, [appPath, '--app-enable-sandbox']) + const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app'); + appProcess = cp.spawn(process.execPath, [appPath, '--app-enable-sandbox']); - server.once('error', error => { done(error) }) + server.once('error', error => { done(error); }); server.on('connection', client => { client.once('data', (data) => { - const argv = JSON.parse(data.toString()) - expect(argv.sandbox).to.include('--enable-sandbox') - expect(argv.sandbox).to.not.include('--no-sandbox') + const argv = JSON.parse(data.toString()); + expect(argv.sandbox).to.include('--enable-sandbox'); + expect(argv.sandbox).to.not.include('--no-sandbox'); - expect(argv.noSandbox).to.include('--enable-sandbox') - expect(argv.noSandbox).to.not.include('--no-sandbox') + expect(argv.noSandbox).to.include('--enable-sandbox'); + expect(argv.noSandbox).to.not.include('--no-sandbox'); - expect(argv.noSandboxDevtools).to.equal(true) - expect(argv.sandboxDevtools).to.equal(true) + expect(argv.noSandboxDevtools).to.equal(true); + expect(argv.sandboxDevtools).to.equal(true); - done() - }) - }) - }) - }) + done(); + }); + }); + }); + }); describe('when the app is launched with --enable-sandbox', () => { it('adds --enable-sandbox to all renderer processes', done => { - const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app') - appProcess = cp.spawn(process.execPath, [appPath, '--enable-sandbox']) + const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app'); + appProcess = cp.spawn(process.execPath, [appPath, '--enable-sandbox']); - server.once('error', error => { done(error) }) + server.once('error', error => { done(error); }); server.on('connection', client => { client.once('data', data => { - const argv = JSON.parse(data.toString()) - expect(argv.sandbox).to.include('--enable-sandbox') - expect(argv.sandbox).to.not.include('--no-sandbox') + const argv = JSON.parse(data.toString()); + expect(argv.sandbox).to.include('--enable-sandbox'); + expect(argv.sandbox).to.not.include('--no-sandbox'); - expect(argv.noSandbox).to.include('--enable-sandbox') - expect(argv.noSandbox).to.not.include('--no-sandbox') + expect(argv.noSandbox).to.include('--enable-sandbox'); + expect(argv.noSandbox).to.not.include('--no-sandbox'); - expect(argv.noSandboxDevtools).to.equal(true) - expect(argv.sandboxDevtools).to.equal(true) + expect(argv.noSandboxDevtools).to.equal(true); + expect(argv.sandboxDevtools).to.equal(true); - done() - }) - }) - }) - }) - }) + done(); + }); + }); + }); + }); + }); describe('disableDomainBlockingFor3DAPIs() API', () => { it('throws when called after app is ready', () => { expect(() => { - app.disableDomainBlockingFor3DAPIs() - }).to.throw(/before app is ready/) - }) - }) + app.disableDomainBlockingFor3DAPIs(); + }).to.throw(/before app is ready/); + }); + }); - const dockDescribe = process.platform === 'darwin' ? describe : describe.skip + const dockDescribe = process.platform === 'darwin' ? describe : describe.skip; dockDescribe('dock APIs', () => { after(async () => { - await app.dock.show() - }) + await app.dock.show(); + }); describe('dock.setMenu', () => { it('can be retrieved via dock.getMenu', () => { - expect(app.dock.getMenu()).to.equal(null) - const menu = new Menu() - app.dock.setMenu(menu) - expect(app.dock.getMenu()).to.equal(menu) - }) + expect(app.dock.getMenu()).to.equal(null); + const menu = new Menu(); + app.dock.setMenu(menu); + expect(app.dock.getMenu()).to.equal(menu); + }); it('keeps references to the menu', () => { - app.dock.setMenu(new Menu()) - const v8Util = process.electronBinding('v8_util') - v8Util.requestGarbageCollectionForTesting() - }) - }) + app.dock.setMenu(new Menu()); + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + }); + }); describe('dock.bounce', () => { it('should return -1 for unknown bounce type', () => { - expect(app.dock.bounce('bad type' as any)).to.equal(-1) - }) + expect(app.dock.bounce('bad type' as any)).to.equal(-1); + }); it('should return a positive number for informational type', () => { - const appHasFocus = !!BrowserWindow.getFocusedWindow() + const appHasFocus = !!BrowserWindow.getFocusedWindow(); if (!appHasFocus) { - expect(app.dock.bounce('informational')).to.be.at.least(0) + expect(app.dock.bounce('informational')).to.be.at.least(0); } - }) + }); it('should return a positive number for critical type', () => { - const appHasFocus = !!BrowserWindow.getFocusedWindow() + const appHasFocus = !!BrowserWindow.getFocusedWindow(); if (!appHasFocus) { - expect(app.dock.bounce('critical')).to.be.at.least(0) + expect(app.dock.bounce('critical')).to.be.at.least(0); } - }) - }) + }); + }); describe('dock.cancelBounce', () => { it('should not throw', () => { - app.dock.cancelBounce(app.dock.bounce('critical')) - }) - }) + app.dock.cancelBounce(app.dock.bounce('critical')); + }); + }); describe('dock.setBadge', () => { after(() => { - app.dock.setBadge('') - }) + app.dock.setBadge(''); + }); it('should not throw', () => { - app.dock.setBadge('1') - }) + app.dock.setBadge('1'); + }); it('should be retrievable via getBadge', () => { - app.dock.setBadge('test') - expect(app.dock.getBadge()).to.equal('test') - }) - }) + app.dock.setBadge('test'); + expect(app.dock.getBadge()).to.equal('test'); + }); + }); describe('dock.show', () => { it('should not throw', () => { return app.dock.show().then(() => { - expect(app.dock.isVisible()).to.equal(true) - }) - }) + expect(app.dock.isVisible()).to.equal(true); + }); + }); it('returns a Promise', () => { - expect(app.dock.show()).to.be.a('promise') - }) + expect(app.dock.show()).to.be.a('promise'); + }); it('eventually fulfills', async () => { - await expect(app.dock.show()).to.eventually.be.fulfilled.equal(undefined) - }) - }) + await expect(app.dock.show()).to.eventually.be.fulfilled.equal(undefined); + }); + }); describe('dock.hide', () => { it('should not throw', () => { - app.dock.hide() - expect(app.dock.isVisible()).to.equal(false) - }) - }) - }) + app.dock.hide(); + expect(app.dock.isVisible()).to.equal(false); + }); + }); + }); describe('whenReady', () => { it('returns a Promise', () => { - expect(app.whenReady()).to.be.a('promise') - }) + expect(app.whenReady()).to.be.a('promise'); + }); it('becomes fulfilled if the app is already ready', async () => { - expect(app.isReady()).to.equal(true) - await expect(app.whenReady()).to.be.eventually.fulfilled.equal(undefined) - }) - }) + expect(app.isReady()).to.equal(true); + await expect(app.whenReady()).to.be.eventually.fulfilled.equal(undefined); + }); + }); describe('app.applicationMenu', () => { it('has the applicationMenu property', () => { - expect(app).to.have.property('applicationMenu') - }) - }) + expect(app).to.have.property('applicationMenu'); + }); + }); describe('commandLine.hasSwitch', () => { it('returns true when present', () => { - app.commandLine.appendSwitch('foobar1') - expect(app.commandLine.hasSwitch('foobar1')).to.equal(true) - }) + app.commandLine.appendSwitch('foobar1'); + expect(app.commandLine.hasSwitch('foobar1')).to.equal(true); + }); it('returns false when not present', () => { - expect(app.commandLine.hasSwitch('foobar2')).to.equal(false) - }) - }) + expect(app.commandLine.hasSwitch('foobar2')).to.equal(false); + }); + }); describe('commandLine.hasSwitch (existing argv)', () => { it('returns true when present', async () => { - const { hasSwitch } = await runTestApp('command-line', '--foobar') - expect(hasSwitch).to.equal(true) - }) + const { hasSwitch } = await runTestApp('command-line', '--foobar'); + expect(hasSwitch).to.equal(true); + }); it('returns false when not present', async () => { - const { hasSwitch } = await runTestApp('command-line') - expect(hasSwitch).to.equal(false) - }) - }) + const { hasSwitch } = await runTestApp('command-line'); + expect(hasSwitch).to.equal(false); + }); + }); describe('commandLine.getSwitchValue', () => { it('returns the value when present', () => { - app.commandLine.appendSwitch('foobar', 'æøåü') - expect(app.commandLine.getSwitchValue('foobar')).to.equal('æøåü') - }) + app.commandLine.appendSwitch('foobar', 'æøåü'); + expect(app.commandLine.getSwitchValue('foobar')).to.equal('æøåü'); + }); it('returns an empty string when present without value', () => { - app.commandLine.appendSwitch('foobar1') - expect(app.commandLine.getSwitchValue('foobar1')).to.equal('') - }) + app.commandLine.appendSwitch('foobar1'); + expect(app.commandLine.getSwitchValue('foobar1')).to.equal(''); + }); it('returns an empty string when not present', () => { - expect(app.commandLine.getSwitchValue('foobar2')).to.equal('') - }) - }) + expect(app.commandLine.getSwitchValue('foobar2')).to.equal(''); + }); + }); describe('commandLine.getSwitchValue (existing argv)', () => { it('returns the value when present', async () => { - const { getSwitchValue } = await runTestApp('command-line', '--foobar=test') - expect(getSwitchValue).to.equal('test') - }) + const { getSwitchValue } = await runTestApp('command-line', '--foobar=test'); + expect(getSwitchValue).to.equal('test'); + }); it('returns an empty string when present without value', async () => { - const { getSwitchValue } = await runTestApp('command-line', '--foobar') - expect(getSwitchValue).to.equal('') - }) + const { getSwitchValue } = await runTestApp('command-line', '--foobar'); + expect(getSwitchValue).to.equal(''); + }); it('returns an empty string when not present', async () => { - const { getSwitchValue } = await runTestApp('command-line') - expect(getSwitchValue).to.equal('') - }) - }) -}) + const { getSwitchValue } = await runTestApp('command-line'); + expect(getSwitchValue).to.equal(''); + }); + }); +}); describe('default behavior', () => { describe('application menu', () => { it('creates the default menu if the app does not set it', async () => { - const result = await runTestApp('default-menu') - expect(result).to.equal(false) - }) + const result = await runTestApp('default-menu'); + expect(result).to.equal(false); + }); it('does not create the default menu if the app sets a custom menu', async () => { - const result = await runTestApp('default-menu', '--custom-menu') - expect(result).to.equal(true) - }) + const result = await runTestApp('default-menu', '--custom-menu'); + expect(result).to.equal(true); + }); it('does not create the default menu if the app sets a null menu', async () => { - const result = await runTestApp('default-menu', '--null-menu') - expect(result).to.equal(true) - }) - }) + const result = await runTestApp('default-menu', '--null-menu'); + expect(result).to.equal(true); + }); + }); describe('window-all-closed', () => { it('quits when the app does not handle the event', async () => { - const result = await runTestApp('window-all-closed') - expect(result).to.equal(false) - }) + const result = await runTestApp('window-all-closed'); + expect(result).to.equal(false); + }); it('does not quit when the app handles the event', async () => { - const result = await runTestApp('window-all-closed', '--handle-event') - expect(result).to.equal(true) - }) - }) + const result = await runTestApp('window-all-closed', '--handle-event'); + expect(result).to.equal(true); + }); + }); describe('user agent fallback', () => { - let initialValue: string + let initialValue: string; before(() => { - initialValue = app.userAgentFallback! - }) + initialValue = app.userAgentFallback!; + }); it('should have a reasonable default', () => { - expect(initialValue).to.include(`Electron/${process.versions.electron}`) - expect(initialValue).to.include(`Chrome/${process.versions.chrome}`) - }) + expect(initialValue).to.include(`Electron/${process.versions.electron}`); + expect(initialValue).to.include(`Chrome/${process.versions.chrome}`); + }); it('should be overridable', () => { - app.userAgentFallback = 'test-agent/123' - expect(app.userAgentFallback).to.equal('test-agent/123') - }) + app.userAgentFallback = 'test-agent/123'; + expect(app.userAgentFallback).to.equal('test-agent/123'); + }); it('should be restorable', () => { - app.userAgentFallback = 'test-agent/123' - app.userAgentFallback = '' - expect(app.userAgentFallback).to.equal(initialValue) - }) - }) + app.userAgentFallback = 'test-agent/123'; + app.userAgentFallback = ''; + expect(app.userAgentFallback).to.equal(initialValue); + }); + }); describe('app.allowRendererProcessReuse', () => { it('should default to true', () => { - expect(app.allowRendererProcessReuse).to.equal(true) - }) + expect(app.allowRendererProcessReuse).to.equal(true); + }); it('should cause renderer processes to get new PIDs when false', async () => { - const output = await runTestApp('site-instance-overrides', 'false') - expect(output[0]).to.be.a('number').that.is.greaterThan(0) - expect(output[1]).to.be.a('number').that.is.greaterThan(0) - expect(output[0]).to.not.equal(output[1]) - }) + const output = await runTestApp('site-instance-overrides', 'false'); + expect(output[0]).to.be.a('number').that.is.greaterThan(0); + expect(output[1]).to.be.a('number').that.is.greaterThan(0); + expect(output[0]).to.not.equal(output[1]); + }); it('should cause renderer processes to keep the same PID when true', async () => { - const output = await runTestApp('site-instance-overrides', 'true') - expect(output[0]).to.be.a('number').that.is.greaterThan(0) - expect(output[1]).to.be.a('number').that.is.greaterThan(0) - expect(output[0]).to.equal(output[1]) - }) - }) + const output = await runTestApp('site-instance-overrides', 'true'); + expect(output[0]).to.be.a('number').that.is.greaterThan(0); + expect(output[1]).to.be.a('number').that.is.greaterThan(0); + expect(output[0]).to.equal(output[1]); + }); + }); describe('login event', () => { - afterEach(closeAllWindows) - let server: http.Server - let serverUrl: string + afterEach(closeAllWindows); + let server: http.Server; + let serverUrl: string; before((done) => { server = http.createServer((request, response) => { if (request.headers.authorization) { - return response.end('ok') + return response.end('ok'); } response .writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }) - .end() + .end(); }).listen(0, '127.0.0.1', () => { - serverUrl = 'http://127.0.0.1:' + (server.address() as net.AddressInfo).port - done() - }) - }) + serverUrl = 'http://127.0.0.1:' + (server.address() as net.AddressInfo).port; + done(); + }); + }); it('should emit a login event on app when a WebContents hits a 401', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL(serverUrl) - const [, webContents] = await emittedOnce(app, 'login') - expect(webContents).to.equal(w.webContents) - }) - }) -}) + const w = new BrowserWindow({ show: false }); + w.loadURL(serverUrl); + const [, webContents] = await emittedOnce(app, 'login'); + expect(webContents).to.equal(w.webContents); + }); + }); +}); async function runTestApp (name: string, ...args: any[]) { - const appPath = path.join(fixturesPath, 'api', name) - const electronPath = process.execPath - const appProcess = cp.spawn(electronPath, [appPath, ...args]) + const appPath = path.join(fixturesPath, 'api', name); + const electronPath = process.execPath; + const appProcess = cp.spawn(electronPath, [appPath, ...args]); - let output = '' - appProcess.stdout.on('data', (data) => { output += data }) + let output = ''; + appProcess.stdout.on('data', (data) => { output += data; }); - await emittedOnce(appProcess.stdout, 'end') + await emittedOnce(appProcess.stdout, 'end'); - return JSON.parse(output) + return JSON.parse(output); } diff --git a/spec-main/api-auto-updater-spec.ts b/spec-main/api-auto-updater-spec.ts index 919c9205ddf95..bb6d4916501b6 100644 --- a/spec-main/api-auto-updater-spec.ts +++ b/spec-main/api-auto-updater-spec.ts @@ -1,92 +1,92 @@ -import { autoUpdater } from 'electron' -import { expect } from 'chai' -import { ifit, ifdescribe } from './spec-helpers' +import { autoUpdater } from 'electron'; +import { expect } from 'chai'; +import { ifit, ifdescribe } from './spec-helpers'; ifdescribe(!process.mas)('autoUpdater module', function () { describe('checkForUpdates', function () { ifit(process.platform === 'win32')('emits an error on Windows if the feed URL is not set', function (done) { autoUpdater.once('error', function (error) { - expect(error.message).to.equal('Update URL is not set') - done() - }) - autoUpdater.setFeedURL({ url: '' }) - autoUpdater.checkForUpdates() - }) - }) + expect(error.message).to.equal('Update URL is not set'); + done(); + }); + autoUpdater.setFeedURL({ url: '' }); + autoUpdater.checkForUpdates(); + }); + }); describe('getFeedURL', () => { it('returns an empty string by default', () => { - expect(autoUpdater.getFeedURL()).to.equal('') - }) + expect(autoUpdater.getFeedURL()).to.equal(''); + }); ifit(process.platform === 'win32')('correctly fetches the previously set FeedURL', function (done) { - const updateURL = 'https://fake-update.electron.io' - autoUpdater.setFeedURL({ url: updateURL }) - expect(autoUpdater.getFeedURL()).to.equal(updateURL) - done() - }) - }) + const updateURL = 'https://fake-update.electron.io'; + autoUpdater.setFeedURL({ url: updateURL }); + expect(autoUpdater.getFeedURL()).to.equal(updateURL); + done(); + }); + }); describe('setFeedURL', function () { ifdescribe(process.platform === 'win32' || process.platform === 'darwin')('on Mac or Windows', () => { it('sets url successfully using old (url, headers) syntax', () => { - const url = 'http://electronjs.org' + const url = 'http://electronjs.org'; try { - (autoUpdater.setFeedURL as any)(url, { header: 'val' }) + (autoUpdater.setFeedURL as any)(url, { header: 'val' }); } catch (err) { /* ignore */ } - expect(autoUpdater.getFeedURL()).to.equal(url) - }) + expect(autoUpdater.getFeedURL()).to.equal(url); + }); it('throws if no url is provided when using the old style', () => { - expect(() => (autoUpdater.setFeedURL as any)()).to.throw('Expected an options object with a \'url\' property to be provided') - }) + expect(() => (autoUpdater.setFeedURL as any)()).to.throw('Expected an options object with a \'url\' property to be provided'); + }); it('sets url successfully using new ({ url }) syntax', () => { - const url = 'http://mymagicurl.local' + const url = 'http://mymagicurl.local'; try { - autoUpdater.setFeedURL({ url }) + autoUpdater.setFeedURL({ url }); } catch (err) { /* ignore */ } - expect(autoUpdater.getFeedURL()).to.equal(url) - }) + expect(autoUpdater.getFeedURL()).to.equal(url); + }); it('throws if no url is provided when using the new style', () => { expect(() => autoUpdater.setFeedURL({ noUrl: 'lol' } as any) - ).to.throw('Expected options object to contain a \'url\' string property in setFeedUrl call') - }) - }) + ).to.throw('Expected options object to contain a \'url\' string property in setFeedUrl call'); + }); + }); ifdescribe(process.platform === 'darwin')('on Mac', function () { it('emits an error when the application is unsigned', done => { autoUpdater.once('error', function (error) { - expect(error.message).equal('Could not get code signature for running application') - done() - }) - autoUpdater.setFeedURL({ url: '' }) - }) + expect(error.message).equal('Could not get code signature for running application'); + done(); + }); + autoUpdater.setFeedURL({ url: '' }); + }); it('does not throw if default is the serverType', () => { // "Could not get code signature..." means the function got far enough to validate that serverType was OK. - expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'default' })).to.throw('Could not get code signature for running application') - }) + expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'default' })).to.throw('Could not get code signature for running application'); + }); it('does not throw if json is the serverType', () => { // "Could not get code signature..." means the function got far enough to validate that serverType was OK. - expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'json' })).to.throw('Could not get code signature for running application') - }) + expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'json' })).to.throw('Could not get code signature for running application'); + }); it('does throw if an unknown string is the serverType', () => { - expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'weow' })).to.throw('Expected serverType to be \'default\' or \'json\'') - }) - }) - }) + expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'weow' })).to.throw('Expected serverType to be \'default\' or \'json\''); + }); + }); + }); describe('quitAndInstall', () => { ifit(process.platform === 'win32')('emits an error on Windows when no update is available', function (done) { autoUpdater.once('error', function (error) { - expect(error.message).to.equal('No update available, can\'t quit and install') - done() - }) - autoUpdater.quitAndInstall() - }) - }) -}) + expect(error.message).to.equal('No update available, can\'t quit and install'); + done(); + }); + autoUpdater.quitAndInstall(); + }); + }); +}); diff --git a/spec-main/api-autoupdater-darwin-spec.ts b/spec-main/api-autoupdater-darwin-spec.ts index 81c40d9db1ad6..0dc92134e4e8e 100644 --- a/spec-main/api-autoupdater-darwin-spec.ts +++ b/spec-main/api-autoupdater-darwin-spec.ts @@ -1,50 +1,50 @@ -import { expect } from 'chai' -import * as cp from 'child_process' -import * as http from 'http' -import * as express from 'express' -import * as fs from 'fs-extra' -import * as os from 'os' -import * as path from 'path' -import { AddressInfo } from 'net' +import { expect } from 'chai'; +import * as cp from 'child_process'; +import * as http from 'http'; +import * as express from 'express'; +import * as fs from 'fs-extra'; +import * as os from 'os'; +import * as path from 'path'; +import { AddressInfo } from 'net'; -const features = process.electronBinding('features') +const features = process.electronBinding('features'); -const fixturesPath = path.resolve(__dirname, 'fixtures') +const fixturesPath = path.resolve(__dirname, 'fixtures'); // We can only test the auto updater on darwin non-component builds -const describeFn = (process.platform === 'darwin' && !process.mas && !features.isComponentBuild() ? describe : describe.skip) +const describeFn = (process.platform === 'darwin' && !process.mas && !features.isComponentBuild() ? describe : describe.skip); describeFn('autoUpdater behavior', function () { - this.timeout(120000) + this.timeout(120000); - let identity = '' + let identity = ''; beforeEach(function () { - const result = cp.spawnSync(path.resolve(__dirname, '../script/codesign/get-trusted-identity.sh')) + const result = cp.spawnSync(path.resolve(__dirname, '../script/codesign/get-trusted-identity.sh')); if (result.status !== 0 || result.stdout.toString().trim().length === 0) { // Per https://circleci.com/docs/2.0/env-vars: // CIRCLE_PR_NUMBER is only present on forked PRs if (process.env.CI && !process.env.CIRCLE_PR_NUMBER) { - throw new Error('No valid signing identity available to run autoUpdater specs') + throw new Error('No valid signing identity available to run autoUpdater specs'); } - this.skip() + this.skip(); } else { - identity = result.stdout.toString().trim() + identity = result.stdout.toString().trim(); } - }) + }); it('should have a valid code signing identity', () => { - expect(identity).to.be.a('string').with.lengthOf.at.least(1) - }) + expect(identity).to.be.a('string').with.lengthOf.at.least(1); + }); const copyApp = async (newDir: string, fixture = 'initial') => { - const appBundlePath = path.resolve(process.execPath, '../../..') - const newPath = path.resolve(newDir, 'Electron.app') - cp.spawnSync('cp', ['-R', appBundlePath, path.dirname(newPath)]) - const appDir = path.resolve(newPath, 'Contents/Resources/app') - await fs.mkdirp(appDir) - await fs.copy(path.resolve(fixturesPath, 'auto-update', fixture), appDir) - const plistPath = path.resolve(newPath, 'Contents', 'Info.plist') + const appBundlePath = path.resolve(process.execPath, '../../..'); + const newPath = path.resolve(newDir, 'Electron.app'); + cp.spawnSync('cp', ['-R', appBundlePath, path.dirname(newPath)]); + const appDir = path.resolve(newPath, 'Contents/Resources/app'); + await fs.mkdirp(appDir); + await fs.copy(path.resolve(fixturesPath, 'auto-update', fixture), appDir); + const plistPath = path.resolve(newPath, 'Contents', 'Info.plist'); await fs.writeFile( plistPath, (await fs.readFile(plistPath, 'utf8')).replace('BuildMachineOSBuild', `NSAppTransportSecurity @@ -62,202 +62,202 @@ describeFn('autoUpdater behavior', function () { BuildMachineOSBuild`) - ) - return newPath - } + ); + return newPath; + }; const spawn = (cmd: string, args: string[], opts: any = {}) => { - let out = '' - const child = cp.spawn(cmd, args, opts) + let out = ''; + const child = cp.spawn(cmd, args, opts); child.stdout.on('data', (chunk: Buffer) => { - out += chunk.toString() - }) + out += chunk.toString(); + }); child.stderr.on('data', (chunk: Buffer) => { - out += chunk.toString() - }) + out += chunk.toString(); + }); return new Promise<{ code: number, out: string }>((resolve) => { child.on('exit', (code, signal) => { - expect(signal).to.equal(null) + expect(signal).to.equal(null); resolve({ code: code!, out - }) - }) - }) - } + }); + }); + }); + }; const signApp = (appPath: string) => { - return spawn('codesign', ['-s', identity, '--deep', '--force', appPath]) - } + return spawn('codesign', ['-s', identity, '--deep', '--force', appPath]); + }; const launchApp = (appPath: string, args: string[] = []) => { - return spawn(path.resolve(appPath, 'Contents/MacOS/Electron'), args) - } + return spawn(path.resolve(appPath, 'Contents/MacOS/Electron'), args); + }; const withTempDirectory = async (fn: (dir: string) => Promise) => { - const dir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-update-spec-')) + const dir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-update-spec-')); try { - await fn(dir) + await fn(dir); } finally { - cp.spawnSync('rm', ['-r', dir]) + cp.spawnSync('rm', ['-r', dir]); } - } + }; const logOnError = (what: any, fn: () => void) => { try { - fn() + fn(); } catch (err) { - console.error(what) - throw err + console.error(what); + throw err; } - } + }; it('should fail to set the feed URL when the app is not signed', async () => { await withTempDirectory(async (dir) => { - const appPath = await copyApp(dir) - const launchResult = await launchApp(appPath, ['http://myupdate']) - expect(launchResult.code).to.equal(1) - expect(launchResult.out).to.include('Could not get code signature for running application') - }) - }) + const appPath = await copyApp(dir); + const launchResult = await launchApp(appPath, ['http://myupdate']); + expect(launchResult.code).to.equal(1); + expect(launchResult.out).to.include('Could not get code signature for running application'); + }); + }); it('should cleanly set the feed URL when the app is signed', async () => { await withTempDirectory(async (dir) => { - const appPath = await copyApp(dir) - await signApp(appPath) - const launchResult = await launchApp(appPath, ['http://myupdate']) - expect(launchResult.code).to.equal(0) - expect(launchResult.out).to.include('Feed URL Set: http://myupdate') - }) - }) + const appPath = await copyApp(dir); + await signApp(appPath); + const launchResult = await launchApp(appPath, ['http://myupdate']); + expect(launchResult.code).to.equal(0); + expect(launchResult.out).to.include('Feed URL Set: http://myupdate'); + }); + }); describe('with update server', () => { - let port = 0 - let server: express.Application = null as any - let httpServer: http.Server = null as any - let requests: express.Request[] = [] + let port = 0; + let server: express.Application = null as any; + let httpServer: http.Server = null as any; + let requests: express.Request[] = []; beforeEach((done) => { - requests = [] - server = express() + requests = []; + server = express(); server.use((req, res, next) => { - requests.push(req) - next() - }) + requests.push(req); + next(); + }); httpServer = server.listen(0, '127.0.0.1', () => { - port = (httpServer.address() as AddressInfo).port - done() - }) - }) + port = (httpServer.address() as AddressInfo).port; + done(); + }); + }); afterEach((done) => { if (httpServer) { httpServer.close(() => { - httpServer = null as any - server = null as any - done() - }) + httpServer = null as any; + server = null as any; + done(); + }); } - }) + }); it('should hit the update endpoint when checkForUpdates is called', async () => { await withTempDirectory(async (dir) => { - const appPath = await copyApp(dir, 'check') - await signApp(appPath) + const appPath = await copyApp(dir, 'check'); + await signApp(appPath); server.get('/update-check', (req, res) => { - res.status(204).send() - }) - const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]) + res.status(204).send(); + }); + const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]); logOnError(launchResult, () => { - expect(launchResult.code).to.equal(0) - expect(requests).to.have.lengthOf(1) - expect(requests[0]).to.have.property('url', '/update-check') - expect(requests[0].header('user-agent')).to.include('Electron/') - }) - }) - }) + expect(launchResult.code).to.equal(0); + expect(requests).to.have.lengthOf(1); + expect(requests[0]).to.have.property('url', '/update-check'); + expect(requests[0].header('user-agent')).to.include('Electron/'); + }); + }); + }); it('should hit the download endpoint when an update is available and error if the file is bad', async () => { await withTempDirectory(async (dir) => { - const appPath = await copyApp(dir, 'update') - await signApp(appPath) + const appPath = await copyApp(dir, 'update'); + await signApp(appPath); server.get('/update-file', (req, res) => { - res.status(500).send('This is not a file') - }) + res.status(500).send('This is not a file'); + }); server.get('/update-check', (req, res) => { res.json({ url: `http://localhost:${port}/update-file`, name: 'My Release Name', notes: 'Theses are some release notes innit', pub_date: (new Date()).toString() - }) - }) - const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]) + }); + }); + const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]); logOnError(launchResult, () => { - expect(launchResult).to.have.property('code', 1) - expect(launchResult.out).to.include('Update download failed. The server sent an invalid response.') - expect(requests).to.have.lengthOf(2) - expect(requests[0]).to.have.property('url', '/update-check') - expect(requests[1]).to.have.property('url', '/update-file') - expect(requests[0].header('user-agent')).to.include('Electron/') - expect(requests[1].header('user-agent')).to.include('Electron/') - }) - }) - }) + expect(launchResult).to.have.property('code', 1); + expect(launchResult.out).to.include('Update download failed. The server sent an invalid response.'); + expect(requests).to.have.lengthOf(2); + expect(requests[0]).to.have.property('url', '/update-check'); + expect(requests[1]).to.have.property('url', '/update-file'); + expect(requests[0].header('user-agent')).to.include('Electron/'); + expect(requests[1].header('user-agent')).to.include('Electron/'); + }); + }); + }); it('should hit the download endpoint when an update is available and update successfully when the zip is provided', async () => { await withTempDirectory(async (dir) => { - const appPath = await copyApp(dir, 'update') - await signApp(appPath) + const appPath = await copyApp(dir, 'update'); + await signApp(appPath); // Prepare update await withTempDirectory(async (dir2) => { - const secondAppPath = await copyApp(dir2, 'update') - const appPJPath = path.resolve(secondAppPath, 'Contents', 'Resources', 'app', 'package.json') + const secondAppPath = await copyApp(dir2, 'update'); + const appPJPath = path.resolve(secondAppPath, 'Contents', 'Resources', 'app', 'package.json'); await fs.writeFile( appPJPath, (await fs.readFile(appPJPath, 'utf8')).replace('1.0.0', '2.0.0') - ) - await signApp(secondAppPath) - const updateZipPath = path.resolve(dir2, 'update.zip') + ); + await signApp(secondAppPath); + const updateZipPath = path.resolve(dir2, 'update.zip'); await spawn('zip', ['-r', '--symlinks', updateZipPath, './'], { cwd: dir2 - }) + }); server.get('/update-file', (req, res) => { - res.download(updateZipPath) - }) + res.download(updateZipPath); + }); server.get('/update-check', (req, res) => { res.json({ url: `http://localhost:${port}/update-file`, name: 'My Release Name', notes: 'Theses are some release notes innit', pub_date: (new Date()).toString() - }) - }) + }); + }); const relaunchPromise = new Promise((resolve) => { server.get('/update-check/updated/:version', (req, res) => { - res.status(204).send() - resolve() - }) - }) - const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]) + res.status(204).send(); + resolve(); + }); + }); + const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]); logOnError(launchResult, () => { - expect(launchResult).to.have.property('code', 0) - expect(launchResult.out).to.include('Update Downloaded') - expect(requests).to.have.lengthOf(2) - expect(requests[0]).to.have.property('url', '/update-check') - expect(requests[1]).to.have.property('url', '/update-file') - expect(requests[0].header('user-agent')).to.include('Electron/') - expect(requests[1].header('user-agent')).to.include('Electron/') - }) + expect(launchResult).to.have.property('code', 0); + expect(launchResult.out).to.include('Update Downloaded'); + expect(requests).to.have.lengthOf(2); + expect(requests[0]).to.have.property('url', '/update-check'); + expect(requests[1]).to.have.property('url', '/update-file'); + expect(requests[0].header('user-agent')).to.include('Electron/'); + expect(requests[1].header('user-agent')).to.include('Electron/'); + }); - await relaunchPromise - expect(requests).to.have.lengthOf(3) - expect(requests[2]).to.have.property('url', '/update-check/updated/2.0.0') - expect(requests[2].header('user-agent')).to.include('Electron/') - }) - }) - }) - }) -}) + await relaunchPromise; + expect(requests).to.have.lengthOf(3); + expect(requests[2]).to.have.property('url', '/update-check/updated/2.0.0'); + expect(requests[2].header('user-agent')).to.include('Electron/'); + }); + }); + }); + }); +}); diff --git a/spec-main/api-browser-view-spec.ts b/spec-main/api-browser-view-spec.ts index 3c36a1c9ebb51..85c3c78079d15 100644 --- a/spec-main/api-browser-view-spec.ts +++ b/spec-main/api-browser-view-spec.ts @@ -1,15 +1,15 @@ -import { expect } from 'chai' -import * as ChildProcess from 'child_process' -import * as path from 'path' -import { emittedOnce } from './events-helpers' -import { BrowserView, BrowserWindow } from 'electron' -import { closeWindow } from './window-helpers' +import { expect } from 'chai'; +import * as ChildProcess from 'child_process'; +import * as path from 'path'; +import { emittedOnce } from './events-helpers'; +import { BrowserView, BrowserWindow } from 'electron'; +import { closeWindow } from './window-helpers'; describe('BrowserView module', () => { - const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures') + const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures'); - let w: BrowserWindow - let view: BrowserView + let w: BrowserWindow; + let view: BrowserView; beforeEach(() => { w = new BrowserWindow({ @@ -19,231 +19,231 @@ describe('BrowserView module', () => { webPreferences: { backgroundThrottling: false } - }) - }) + }); + }); afterEach(async () => { - await closeWindow(w) + await closeWindow(w); if (view) { - view.destroy() + view.destroy(); } - }) + }); describe('BrowserView.destroy()', () => { it('does not throw', () => { - view = new BrowserView() - view.destroy() - }) - }) + view = new BrowserView(); + view.destroy(); + }); + }); describe('BrowserView.isDestroyed()', () => { it('returns correct value', () => { - view = new BrowserView() - expect(view.isDestroyed()).to.be.false('view is destroyed') - view.destroy() - expect(view.isDestroyed()).to.be.true('view is destroyed') - }) - }) + view = new BrowserView(); + expect(view.isDestroyed()).to.be.false('view is destroyed'); + view.destroy(); + expect(view.isDestroyed()).to.be.true('view is destroyed'); + }); + }); describe('BrowserView.setBackgroundColor()', () => { it('does not throw for valid args', () => { - view = new BrowserView() - view.setBackgroundColor('#000') - }) + view = new BrowserView(); + view.setBackgroundColor('#000'); + }); it('throws for invalid args', () => { - view = new BrowserView() + view = new BrowserView(); expect(() => { - view.setBackgroundColor(null as any) - }).to.throw(/conversion failure/) - }) - }) + view.setBackgroundColor(null as any); + }).to.throw(/conversion failure/); + }); + }); describe('BrowserView.setAutoResize()', () => { it('does not throw for valid args', () => { - view = new BrowserView() - view.setAutoResize({}) - view.setAutoResize({ width: true, height: false }) - }) + view = new BrowserView(); + view.setAutoResize({}); + view.setAutoResize({ width: true, height: false }); + }); it('throws for invalid args', () => { - view = new BrowserView() + view = new BrowserView(); expect(() => { - view.setAutoResize(null as any) - }).to.throw(/conversion failure/) - }) - }) + view.setAutoResize(null as any); + }).to.throw(/conversion failure/); + }); + }); describe('BrowserView.setBounds()', () => { it('does not throw for valid args', () => { - view = new BrowserView() - view.setBounds({ x: 0, y: 0, width: 1, height: 1 }) - }) + view = new BrowserView(); + view.setBounds({ x: 0, y: 0, width: 1, height: 1 }); + }); it('throws for invalid args', () => { - view = new BrowserView() + view = new BrowserView(); expect(() => { - view.setBounds(null as any) - }).to.throw(/conversion failure/) + view.setBounds(null as any); + }).to.throw(/conversion failure/); expect(() => { - view.setBounds({} as any) - }).to.throw(/conversion failure/) - }) - }) + view.setBounds({} as any); + }).to.throw(/conversion failure/); + }); + }); describe('BrowserView.getBounds()', () => { it('returns the current bounds', () => { - view = new BrowserView() - const bounds = { x: 10, y: 20, width: 30, height: 40 } - view.setBounds(bounds) - expect(view.getBounds()).to.deep.equal(bounds) - }) - }) + view = new BrowserView(); + const bounds = { x: 10, y: 20, width: 30, height: 40 }; + view.setBounds(bounds); + expect(view.getBounds()).to.deep.equal(bounds); + }); + }); describe('BrowserWindow.setBrowserView()', () => { it('does not throw for valid args', () => { - view = new BrowserView() - w.setBrowserView(view) - }) + view = new BrowserView(); + w.setBrowserView(view); + }); it('does not throw if called multiple times with same view', () => { - view = new BrowserView() - w.setBrowserView(view) - w.setBrowserView(view) - w.setBrowserView(view) - }) - }) + view = new BrowserView(); + w.setBrowserView(view); + w.setBrowserView(view); + w.setBrowserView(view); + }); + }); describe('BrowserWindow.getBrowserView()', () => { it('returns the set view', () => { - view = new BrowserView() - w.setBrowserView(view) - expect(view.id).to.not.be.null('view id') + view = new BrowserView(); + w.setBrowserView(view); + expect(view.id).to.not.be.null('view id'); - const view2 = w.getBrowserView() - expect(view2!.webContents.id).to.equal(view.webContents.id) - }) + const view2 = w.getBrowserView(); + expect(view2!.webContents.id).to.equal(view.webContents.id); + }); it('returns null if none is set', () => { - const view = w.getBrowserView() - expect(view).to.be.null('view') - }) - }) + const view = w.getBrowserView(); + expect(view).to.be.null('view'); + }); + }); describe('BrowserWindow.addBrowserView()', () => { it('does not throw for valid args', () => { - const view1 = new BrowserView() - w.addBrowserView(view1) - const view2 = new BrowserView() - w.addBrowserView(view2) - view1.destroy() - view2.destroy() - }) + const view1 = new BrowserView(); + w.addBrowserView(view1); + const view2 = new BrowserView(); + w.addBrowserView(view2); + view1.destroy(); + view2.destroy(); + }); it('does not throw if called multiple times with same view', () => { - view = new BrowserView() - w.addBrowserView(view) - w.addBrowserView(view) - w.addBrowserView(view) - }) - }) + view = new BrowserView(); + w.addBrowserView(view); + w.addBrowserView(view); + w.addBrowserView(view); + }); + }); describe('BrowserWindow.removeBrowserView()', () => { it('does not throw if called multiple times with same view', () => { - view = new BrowserView() - w.addBrowserView(view) - w.removeBrowserView(view) - w.removeBrowserView(view) - }) - }) + view = new BrowserView(); + w.addBrowserView(view); + w.removeBrowserView(view); + w.removeBrowserView(view); + }); + }); describe('BrowserWindow.getBrowserViews()', () => { it('returns same views as was added', () => { - const view1 = new BrowserView() - w.addBrowserView(view1) - const view2 = new BrowserView() - w.addBrowserView(view2) - - expect(view1.id).to.be.not.null('view id') - const views = w.getBrowserViews() - expect(views).to.have.lengthOf(2) - expect(views[0].webContents.id).to.equal(view1.webContents.id) - expect(views[1].webContents.id).to.equal(view2.webContents.id) - - view1.destroy() - view2.destroy() - }) - }) + const view1 = new BrowserView(); + w.addBrowserView(view1); + const view2 = new BrowserView(); + w.addBrowserView(view2); + + expect(view1.id).to.be.not.null('view id'); + const views = w.getBrowserViews(); + expect(views).to.have.lengthOf(2); + expect(views[0].webContents.id).to.equal(view1.webContents.id); + expect(views[1].webContents.id).to.equal(view2.webContents.id); + + view1.destroy(); + view2.destroy(); + }); + }); describe('BrowserView.webContents.getOwnerBrowserWindow()', () => { it('points to owning window', () => { - view = new BrowserView() - expect(view.webContents.getOwnerBrowserWindow()).to.be.null('owner browser window') + view = new BrowserView(); + expect(view.webContents.getOwnerBrowserWindow()).to.be.null('owner browser window'); - w.setBrowserView(view) - expect(view.webContents.getOwnerBrowserWindow()).to.equal(w) + w.setBrowserView(view); + expect(view.webContents.getOwnerBrowserWindow()).to.equal(w); - w.setBrowserView(null) - expect(view.webContents.getOwnerBrowserWindow()).to.be.null('owner browser window') - }) - }) + w.setBrowserView(null); + expect(view.webContents.getOwnerBrowserWindow()).to.be.null('owner browser window'); + }); + }); describe('BrowserView.fromId()', () => { it('returns the view with given id', () => { - view = new BrowserView() - w.setBrowserView(view) - expect(view.id).to.not.be.null('view id') + view = new BrowserView(); + w.setBrowserView(view); + expect(view.id).to.not.be.null('view id'); - const view2 = BrowserView.fromId(view.id) - expect(view2.webContents.id).to.equal(view.webContents.id) - }) - }) + const view2 = BrowserView.fromId(view.id); + expect(view2.webContents.id).to.equal(view.webContents.id); + }); + }); describe('BrowserView.fromWebContents()', () => { it('returns the view with given id', () => { - view = new BrowserView() - w.setBrowserView(view) - expect(view.id).to.not.be.null('view id') + view = new BrowserView(); + w.setBrowserView(view); + expect(view.id).to.not.be.null('view id'); - const view2 = BrowserView.fromWebContents(view.webContents) - expect(view2!.webContents.id).to.equal(view.webContents.id) - }) - }) + const view2 = BrowserView.fromWebContents(view.webContents); + expect(view2!.webContents.id).to.equal(view.webContents.id); + }); + }); describe('BrowserView.getAllViews()', () => { it('returns all views', () => { - view = new BrowserView() - w.setBrowserView(view) - expect(view.id).to.not.be.null('view id') + view = new BrowserView(); + w.setBrowserView(view); + expect(view.id).to.not.be.null('view id'); - const views = BrowserView.getAllViews() - expect(views).to.be.an('array').that.has.lengthOf(1) - expect(views[0].webContents.id).to.equal(view.webContents.id) - }) - }) + const views = BrowserView.getAllViews(); + expect(views).to.be.an('array').that.has.lengthOf(1); + expect(views[0].webContents.id).to.equal(view.webContents.id); + }); + }); describe('new BrowserView()', () => { it('does not crash on exit', async () => { - const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-browserview.js') - const electronPath = process.execPath - const appProcess = ChildProcess.spawn(electronPath, [appPath]) - const [code] = await emittedOnce(appProcess, 'exit') - expect(code).to.equal(0) - }) - }) + const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-browserview.js'); + const electronPath = process.execPath; + const appProcess = ChildProcess.spawn(electronPath, [appPath]); + const [code] = await emittedOnce(appProcess, 'exit'); + expect(code).to.equal(0); + }); + }); describe('window.open()', () => { it('works in BrowserView', (done) => { - view = new BrowserView() - w.setBrowserView(view) + view = new BrowserView(); + w.setBrowserView(view); view.webContents.once('new-window', (e, url, frameName, disposition, options, additionalFeatures) => { - e.preventDefault() - expect(url).to.equal('http://host/') - expect(frameName).to.equal('host') - expect(additionalFeatures[0]).to.equal('this-is-not-a-standard-feature') - done() - }) - view.webContents.loadFile(path.join(fixtures, 'pages', 'window-open.html')) - }) - }) -}) + e.preventDefault(); + expect(url).to.equal('http://host/'); + expect(frameName).to.equal('host'); + expect(additionalFeatures[0]).to.equal('this-is-not-a-standard-feature'); + done(); + }); + view.webContents.loadFile(path.join(fixtures, 'pages', 'window-open.html')); + }); + }); +}); diff --git a/spec-main/api-browser-window-affinity-spec.ts b/spec-main/api-browser-window-affinity-spec.ts index ff57f36668a62..18db25af778ea 100644 --- a/spec-main/api-browser-window-affinity-spec.ts +++ b/spec-main/api-browser-window-affinity-spec.ts @@ -1,22 +1,22 @@ -import { expect } from 'chai' -import * as path from 'path' +import { expect } from 'chai'; +import * as path from 'path'; -import { ipcMain, BrowserWindow, WebPreferences, app } from 'electron' -import { closeWindow } from './window-helpers' +import { ipcMain, BrowserWindow, WebPreferences, app } from 'electron'; +import { closeWindow } from './window-helpers'; describe('BrowserWindow with affinity module', () => { - const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures') - const myAffinityName = 'myAffinity' - const myAffinityNameUpper = 'MYAFFINITY' - const anotherAffinityName = 'anotherAffinity' + const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures'); + const myAffinityName = 'myAffinity'; + const myAffinityNameUpper = 'MYAFFINITY'; + const anotherAffinityName = 'anotherAffinity'; before(() => { - app.allowRendererProcessReuse = false - }) + app.allowRendererProcessReuse = false; + }); after(() => { - app.allowRendererProcessReuse = true - }) + app.allowRendererProcessReuse = true; + }); async function createWindowWithWebPrefs (webPrefs: WebPreferences) { const w = new BrowserWindow({ @@ -24,83 +24,83 @@ describe('BrowserWindow with affinity module', () => { width: 400, height: 400, webPreferences: webPrefs || {} - }) - await w.loadFile(path.join(fixtures, 'api', 'blank.html')) - return w + }); + await w.loadFile(path.join(fixtures, 'api', 'blank.html')); + return w; } function testAffinityProcessIds (name: string, webPreferences: WebPreferences = {}) { describe(name, () => { - let mAffinityWindow: BrowserWindow + let mAffinityWindow: BrowserWindow; before(async () => { - mAffinityWindow = await createWindowWithWebPrefs({ affinity: myAffinityName, ...webPreferences }) - }) + mAffinityWindow = await createWindowWithWebPrefs({ affinity: myAffinityName, ...webPreferences }); + }); after(async () => { - await closeWindow(mAffinityWindow, { assertNotWindows: false }) - mAffinityWindow = null as unknown as BrowserWindow - }) + await closeWindow(mAffinityWindow, { assertNotWindows: false }); + mAffinityWindow = null as unknown as BrowserWindow; + }); it('should have a different process id than a default window', async () => { - const w = await createWindowWithWebPrefs({ ...webPreferences }) - const affinityID = mAffinityWindow.webContents.getOSProcessId() - const wcID = w.webContents.getOSProcessId() + const w = await createWindowWithWebPrefs({ ...webPreferences }); + const affinityID = mAffinityWindow.webContents.getOSProcessId(); + const wcID = w.webContents.getOSProcessId(); - expect(affinityID).to.not.equal(wcID, 'Should have different OS process IDs') - await closeWindow(w, { assertNotWindows: false }) - }) + expect(affinityID).to.not.equal(wcID, 'Should have different OS process IDs'); + await closeWindow(w, { assertNotWindows: false }); + }); it(`should have a different process id than a window with a different affinity '${anotherAffinityName}'`, async () => { - const w = await createWindowWithWebPrefs({ affinity: anotherAffinityName, ...webPreferences }) - const affinityID = mAffinityWindow.webContents.getOSProcessId() - const wcID = w.webContents.getOSProcessId() + const w = await createWindowWithWebPrefs({ affinity: anotherAffinityName, ...webPreferences }); + const affinityID = mAffinityWindow.webContents.getOSProcessId(); + const wcID = w.webContents.getOSProcessId(); - expect(affinityID).to.not.equal(wcID, 'Should have different OS process IDs') - await closeWindow(w, { assertNotWindows: false }) - }) + expect(affinityID).to.not.equal(wcID, 'Should have different OS process IDs'); + await closeWindow(w, { assertNotWindows: false }); + }); it(`should have the same OS process id than a window with the same affinity '${myAffinityName}'`, async () => { - const w = await createWindowWithWebPrefs({ affinity: myAffinityName, ...webPreferences }) - const affinityID = mAffinityWindow.webContents.getOSProcessId() - const wcID = w.webContents.getOSProcessId() + const w = await createWindowWithWebPrefs({ affinity: myAffinityName, ...webPreferences }); + const affinityID = mAffinityWindow.webContents.getOSProcessId(); + const wcID = w.webContents.getOSProcessId(); - expect(affinityID).to.equal(wcID, 'Should have the same OS process ID') - await closeWindow(w, { assertNotWindows: false }) - }) + expect(affinityID).to.equal(wcID, 'Should have the same OS process ID'); + await closeWindow(w, { assertNotWindows: false }); + }); it(`should have the same OS process id than a window with an equivalent affinity '${myAffinityNameUpper}' (case insensitive)`, async () => { - const w = await createWindowWithWebPrefs({ affinity: myAffinityNameUpper, ...webPreferences }) - const affinityID = mAffinityWindow.webContents.getOSProcessId() - const wcID = w.webContents.getOSProcessId() - - expect(affinityID).to.equal(wcID, 'Should have the same OS process ID') - await closeWindow(w, { assertNotWindows: false }) - }) - }) + const w = await createWindowWithWebPrefs({ affinity: myAffinityNameUpper, ...webPreferences }); + const affinityID = mAffinityWindow.webContents.getOSProcessId(); + const wcID = w.webContents.getOSProcessId(); + + expect(affinityID).to.equal(wcID, 'Should have the same OS process ID'); + await closeWindow(w, { assertNotWindows: false }); + }); + }); } - testAffinityProcessIds(`BrowserWindow with an affinity '${myAffinityName}'`) - testAffinityProcessIds(`BrowserWindow with an affinity '${myAffinityName}' and sandbox enabled`, { sandbox: true }) - testAffinityProcessIds(`BrowserWindow with an affinity '${myAffinityName}' and nativeWindowOpen enabled`, { nativeWindowOpen: true }) + testAffinityProcessIds(`BrowserWindow with an affinity '${myAffinityName}'`); + testAffinityProcessIds(`BrowserWindow with an affinity '${myAffinityName}' and sandbox enabled`, { sandbox: true }); + testAffinityProcessIds(`BrowserWindow with an affinity '${myAffinityName}' and nativeWindowOpen enabled`, { nativeWindowOpen: true }); describe('BrowserWindow with an affinity : nodeIntegration=false', () => { - const preload = path.join(fixtures, 'module', 'send-later.js') - const affinityWithNodeTrue = 'affinityWithNodeTrue' - const affinityWithNodeFalse = 'affinityWithNodeFalse' + const preload = path.join(fixtures, 'module', 'send-later.js'); + const affinityWithNodeTrue = 'affinityWithNodeTrue'; + const affinityWithNodeFalse = 'affinityWithNodeFalse'; function testNodeIntegration (present: boolean) { return new Promise((resolve) => { ipcMain.once('answer', (event, typeofProcess, typeofBuffer) => { if (present) { - expect(typeofProcess).to.not.equal('undefined') - expect(typeofBuffer).to.not.equal('undefined') + expect(typeofProcess).to.not.equal('undefined'); + expect(typeofBuffer).to.not.equal('undefined'); } else { - expect(typeofProcess).to.equal('undefined') - expect(typeofBuffer).to.equal('undefined') + expect(typeofProcess).to.equal('undefined'); + expect(typeofBuffer).to.equal('undefined'); } - resolve() - }) - }) + resolve(); + }); + }); } it('disables node integration when specified to false', async () => { @@ -111,9 +111,9 @@ describe('BrowserWindow with affinity module', () => { preload, nodeIntegration: false }) - ]) - await closeWindow(w, { assertNotWindows: false }) - }) + ]); + await closeWindow(w, { assertNotWindows: false }); + }); it('disables node integration when first window is false', async () => { const [, w1] = await Promise.all([ testNodeIntegration(false), @@ -122,7 +122,7 @@ describe('BrowserWindow with affinity module', () => { preload, nodeIntegration: false }) - ]) + ]); const [, w2] = await Promise.all([ testNodeIntegration(false), createWindowWithWebPrefs({ @@ -130,12 +130,12 @@ describe('BrowserWindow with affinity module', () => { preload, nodeIntegration: true }) - ]) + ]); await Promise.all([ closeWindow(w1, { assertNotWindows: false }), closeWindow(w2, { assertNotWindows: false }) - ]) - }) + ]); + }); it('enables node integration when specified to true', async () => { const [, w] = await Promise.all([ @@ -145,9 +145,9 @@ describe('BrowserWindow with affinity module', () => { preload, nodeIntegration: true }) - ]) - await closeWindow(w, { assertNotWindows: false }) - }) + ]); + await closeWindow(w, { assertNotWindows: false }); + }); it('enables node integration when first window is true', async () => { const [, w1] = await Promise.all([ @@ -157,7 +157,7 @@ describe('BrowserWindow with affinity module', () => { preload, nodeIntegration: true }) - ]) + ]); const [, w2] = await Promise.all([ testNodeIntegration(true), createWindowWithWebPrefs({ @@ -165,11 +165,11 @@ describe('BrowserWindow with affinity module', () => { preload, nodeIntegration: false }) - ]) + ]); await Promise.all([ closeWindow(w1, { assertNotWindows: false }), closeWindow(w2, { assertNotWindows: false }) - ]) - }) - }) -}) + ]); + }); + }); +}); diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 80c9bdacb039d..e7e163744db4c 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -1,41 +1,41 @@ -import { expect } from 'chai' -import * as path from 'path' -import * as fs from 'fs' -import * as os from 'os' -import * as qs from 'querystring' -import * as http from 'http' -import { AddressInfo } from 'net' -import { app, BrowserWindow, BrowserView, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron' +import { expect } from 'chai'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as qs from 'querystring'; +import * as http from 'http'; +import { AddressInfo } from 'net'; +import { app, BrowserWindow, BrowserView, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron'; -import { emittedOnce } from './events-helpers' -import { ifit, ifdescribe } from './spec-helpers' -import { closeWindow, closeAllWindows } from './window-helpers' +import { emittedOnce } from './events-helpers'; +import { ifit, ifdescribe } from './spec-helpers'; +import { closeWindow, closeAllWindows } from './window-helpers'; -const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures') +const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures'); // Is the display's scale factor possibly causing rounding of pixel coordinate // values? const isScaleFactorRounding = () => { - const { scaleFactor } = screen.getPrimaryDisplay() + const { scaleFactor } = screen.getPrimaryDisplay(); // Return true if scale factor is non-integer value - if (Math.round(scaleFactor) !== scaleFactor) return true + if (Math.round(scaleFactor) !== scaleFactor) return true; // Return true if scale factor is odd number above 2 - return scaleFactor > 2 && scaleFactor % 2 === 1 -} + return scaleFactor > 2 && scaleFactor % 2 === 1; +}; const expectBoundsEqual = (actual: any, expected: any) => { if (!isScaleFactorRounding()) { - expect(expected).to.deep.equal(actual) + expect(expected).to.deep.equal(actual); } else if (Array.isArray(actual)) { - expect(actual[0]).to.be.closeTo(expected[0], 1) - expect(actual[1]).to.be.closeTo(expected[1], 1) + expect(actual[0]).to.be.closeTo(expected[0], 1); + expect(actual[1]).to.be.closeTo(expected[1], 1); } else { - expect(actual.x).to.be.closeTo(expected.x, 1) - expect(actual.y).to.be.closeTo(expected.y, 1) - expect(actual.width).to.be.closeTo(expected.width, 1) - expect(actual.height).to.be.closeTo(expected.height, 1) + expect(actual.x).to.be.closeTo(expected.x, 1); + expect(actual.y).to.be.closeTo(expected.y, 1); + expect(actual.width).to.be.closeTo(expected.width, 1); + expect(actual.height).to.be.closeTo(expected.height, 1); } -} +}; describe('BrowserWindow module', () => { describe('BrowserWindow constructor', () => { @@ -46,101 +46,101 @@ describe('BrowserWindow module', () => { // apparently void 0 had different behaviour from undefined in the // issue that this test is supposed to catch. webContents: void 0 // eslint-disable-line no-void - } as any) - w.destroy() - }).not.to.throw() - }) - }) + } as any); + w.destroy(); + }).not.to.throw(); + }); + }); describe('garbage collection', () => { - const v8Util = process.electronBinding('v8_util') - afterEach(closeAllWindows) + const v8Util = process.electronBinding('v8_util'); + afterEach(closeAllWindows); it('window does not get garbage collected when opened', (done) => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); // Keep a weak reference to the window. - const map = v8Util.createIDWeakMap() - map.set(0, w) + const map = v8Util.createIDWeakMap(); + map.set(0, w); setTimeout(() => { // Do garbage collection, since |w| is not referenced in this closure // it would be gone after next call if there is no other reference. - v8Util.requestGarbageCollectionForTesting() + v8Util.requestGarbageCollectionForTesting(); setTimeout(() => { - expect(map.has(0)).to.equal(true) - done() - }) - }) - }) - }) + expect(map.has(0)).to.equal(true); + done(); + }); + }); + }); + }); describe('BrowserWindow.close()', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); it('should emit unload handler', async () => { - await w.loadFile(path.join(fixtures, 'api', 'unload.html')) - const closed = emittedOnce(w, 'closed') - w.close() - await closed - const test = path.join(fixtures, 'api', 'unload') - const content = fs.readFileSync(test) - fs.unlinkSync(test) - expect(String(content)).to.equal('unload') - }) + await w.loadFile(path.join(fixtures, 'api', 'unload.html')); + const closed = emittedOnce(w, 'closed'); + w.close(); + await closed; + const test = path.join(fixtures, 'api', 'unload'); + const content = fs.readFileSync(test); + fs.unlinkSync(test); + expect(String(content)).to.equal('unload'); + }); it('should emit beforeunload handler', async () => { - await w.loadFile(path.join(fixtures, 'api', 'beforeunload-false.html')) + await w.loadFile(path.join(fixtures, 'api', 'beforeunload-false.html')); const beforeunload = new Promise(resolve => { ipcMain.once('onbeforeunload', (e) => { - e.returnValue = null - resolve() - }) - }) - w.close() - await beforeunload - }) + e.returnValue = null; + resolve(); + }); + }); + w.close(); + await beforeunload; + }); describe('when invoked synchronously inside navigation observer', () => { - let server: http.Server = null as unknown as http.Server - let url: string = null as unknown as string + let server: http.Server = null as unknown as http.Server; + let url: string = null as unknown as string; before((done) => { server = http.createServer((request, response) => { switch (request.url) { case '/net-error': - response.destroy() - break + response.destroy(); + break; case '/301': - response.statusCode = 301 - response.setHeader('Location', '/200') - response.end() - break + response.statusCode = 301; + response.setHeader('Location', '/200'); + response.end(); + break; case '/200': - response.statusCode = 200 - response.end('hello') - break + response.statusCode = 200; + response.end('hello'); + break; case '/title': - response.statusCode = 200 - response.end('Hello') - break + response.statusCode = 200; + response.end('Hello'); + break; default: - throw new Error(`unsupported endpoint: ${request.url}`) + throw new Error(`unsupported endpoint: ${request.url}`); } }).listen(0, '127.0.0.1', () => { - url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port - done() - }) - }) + url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port; + done(); + }); + }); after(() => { - server.close() - }) + server.close(); + }); const events = [ { name: 'did-start-loading', path: '/200' }, @@ -150,69 +150,69 @@ describe('BrowserWindow module', () => { { name: 'did-finish-load', path: '/200' }, { name: 'did-frame-finish-load', path: '/200' }, { name: 'did-fail-load', path: '/net-error' } - ] + ]; for (const { name, path } of events) { it(`should not crash when closed during ${name}`, async () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); w.webContents.once((name as any), () => { - w.close() - }) - const destroyed = emittedOnce(w.webContents, 'destroyed') - w.webContents.loadURL(url + path) - await destroyed - }) + w.close(); + }); + const destroyed = emittedOnce(w.webContents, 'destroyed'); + w.webContents.loadURL(url + path); + await destroyed; + }); } - }) - }) + }); + }); describe('window.close()', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); it('should emit unload event', async () => { - w.loadFile(path.join(fixtures, 'api', 'close.html')) - await emittedOnce(w, 'closed') - const test = path.join(fixtures, 'api', 'close') - const content = fs.readFileSync(test).toString() - fs.unlinkSync(test) - expect(content).to.equal('close') - }) + w.loadFile(path.join(fixtures, 'api', 'close.html')); + await emittedOnce(w, 'closed'); + const test = path.join(fixtures, 'api', 'close'); + const content = fs.readFileSync(test).toString(); + fs.unlinkSync(test); + expect(content).to.equal('close'); + }); it('should emit beforeunload event', async () => { - w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-false.html')) - const [e] = await emittedOnce(ipcMain, 'onbeforeunload') - e.returnValue = null - }) - }) + w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-false.html')); + const [e] = await emittedOnce(ipcMain, 'onbeforeunload'); + e.returnValue = null; + }); + }); describe('BrowserWindow.destroy()', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); it('prevents users to access methods of webContents', async () => { - const contents = w.webContents - w.destroy() - await new Promise(setImmediate) + const contents = w.webContents; + w.destroy(); + await new Promise(setImmediate); expect(() => { - contents.getProcessId() - }).to.throw('Object has been destroyed') - }) + contents.getProcessId(); + }).to.throw('Object has been destroyed'); + }); it('should not crash when destroying windows with pending events', () => { - const focusListener = () => { } - app.on('browser-window-focus', focusListener) - const windowCount = 3 + const focusListener = () => { }; + app.on('browser-window-focus', focusListener); + const windowCount = 3; const windowOptions = { show: false, width: 400, @@ -220,42 +220,42 @@ describe('BrowserWindow module', () => { webPreferences: { backgroundThrottling: false } - } - const windows = Array.from(Array(windowCount)).map(() => new BrowserWindow(windowOptions)) - windows.forEach(win => win.show()) - windows.forEach(win => win.focus()) - windows.forEach(win => win.destroy()) - app.removeListener('browser-window-focus', focusListener) - }) - }) + }; + const windows = Array.from(Array(windowCount)).map(() => new BrowserWindow(windowOptions)); + windows.forEach(win => win.show()); + windows.forEach(win => win.focus()); + windows.forEach(win => win.destroy()); + app.removeListener('browser-window-focus', focusListener); + }); + }); describe('BrowserWindow.loadURL(url)', () => { - let w = null as unknown as BrowserWindow - const scheme = 'other' - const srcPath = path.join(fixtures, 'api', 'loaded-from-dataurl.js') + let w = null as unknown as BrowserWindow; + const scheme = 'other'; + const srcPath = path.join(fixtures, 'api', 'loaded-from-dataurl.js'); before((done) => { protocol.registerFileProtocol(scheme, (request, callback) => { - callback(srcPath) - }, (error) => done(error)) - }) + callback(srcPath); + }, (error) => done(error)); + }); after(() => { - protocol.unregisterProtocol(scheme) - }) + protocol.unregisterProtocol(scheme); + }); beforeEach(() => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) - let server = null as unknown as http.Server - let url = null as unknown as string - let postData = null as any + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); + let server = null as unknown as http.Server; + let url = null as unknown as string; + let postData = null as any; before((done) => { - const filePath = path.join(fixtures, 'pages', 'a.html') - const fileStats = fs.statSync(filePath) + const filePath = path.join(fixtures, 'pages', 'a.html'); + const fileStats = fs.statSync(filePath); postData = [ { type: 'rawData', @@ -268,147 +268,147 @@ describe('BrowserWindow module', () => { length: fileStats.size, modificationTime: fileStats.mtime.getTime() / 1000 } - ] + ]; server = http.createServer((req, res) => { function respond () { if (req.method === 'POST') { - let body = '' + let body = ''; req.on('data', (data) => { - if (data) body += data - }) + if (data) body += data; + }); req.on('end', () => { - const parsedData = qs.parse(body) + const parsedData = qs.parse(body); fs.readFile(filePath, (err, data) => { - if (err) return + if (err) return; if (parsedData.username === 'test' && parsedData.file === data.toString()) { - res.end() + res.end(); } - }) - }) + }); + }); } else if (req.url === '/302') { - res.setHeader('Location', '/200') - res.statusCode = 302 - res.end() + res.setHeader('Location', '/200'); + res.statusCode = 302; + res.end(); } else { - res.end() + res.end(); } } - setTimeout(respond, req.url && req.url.includes('slow') ? 200 : 0) - }) + setTimeout(respond, req.url && req.url.includes('slow') ? 200 : 0); + }); server.listen(0, '127.0.0.1', () => { - url = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - done() - }) - }) + url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + done(); + }); + }); after(() => { - server.close() - }) + server.close(); + }); it('should emit did-start-loading event', (done) => { - w.webContents.on('did-start-loading', () => { done() }) - w.loadURL('about:blank') - }) + w.webContents.on('did-start-loading', () => { done(); }); + w.loadURL('about:blank'); + }); it('should emit ready-to-show event', (done) => { - w.on('ready-to-show', () => { done() }) - w.loadURL('about:blank') - }) + w.on('ready-to-show', () => { done(); }); + w.loadURL('about:blank'); + }); // TODO(deepak1556): The error code now seems to be `ERR_FAILED`, verify what // changed and adjust the test. it.skip('should emit did-fail-load event for files that do not exist', (done) => { w.webContents.on('did-fail-load', (event, code, desc, url, isMainFrame) => { - expect(code).to.equal(-6) - expect(desc).to.equal('ERR_FILE_NOT_FOUND') - expect(isMainFrame).to.equal(true) - done() - }) - w.loadURL('file://a.txt') - }) + expect(code).to.equal(-6); + expect(desc).to.equal('ERR_FILE_NOT_FOUND'); + expect(isMainFrame).to.equal(true); + done(); + }); + w.loadURL('file://a.txt'); + }); it('should emit did-fail-load event for invalid URL', (done) => { w.webContents.on('did-fail-load', (event, code, desc, url, isMainFrame) => { - expect(desc).to.equal('ERR_INVALID_URL') - expect(code).to.equal(-300) - expect(isMainFrame).to.equal(true) - done() - }) - w.loadURL('http://example:port') - }) + expect(desc).to.equal('ERR_INVALID_URL'); + expect(code).to.equal(-300); + expect(isMainFrame).to.equal(true); + done(); + }); + w.loadURL('http://example:port'); + }); it('should set `mainFrame = false` on did-fail-load events in iframes', (done) => { w.webContents.on('did-fail-load', (event, code, desc, url, isMainFrame) => { - expect(isMainFrame).to.equal(false) - done() - }) - w.loadFile(path.join(fixtures, 'api', 'did-fail-load-iframe.html')) - }) + expect(isMainFrame).to.equal(false); + done(); + }); + w.loadFile(path.join(fixtures, 'api', 'did-fail-load-iframe.html')); + }); it('does not crash in did-fail-provisional-load handler', (done) => { w.webContents.once('did-fail-provisional-load', () => { - w.loadURL('http://127.0.0.1:11111') - done() - }) - w.loadURL('http://127.0.0.1:11111') - }) + w.loadURL('http://127.0.0.1:11111'); + done(); + }); + w.loadURL('http://127.0.0.1:11111'); + }); it('should emit did-fail-load event for URL exceeding character limit', (done) => { w.webContents.on('did-fail-load', (event, code, desc, url, isMainFrame) => { - expect(desc).to.equal('ERR_INVALID_URL') - expect(code).to.equal(-300) - expect(isMainFrame).to.equal(true) - done() - }) - const data = Buffer.alloc(2 * 1024 * 1024).toString('base64') - w.loadURL(`data:image/png;base64,${data}`) - }) + expect(desc).to.equal('ERR_INVALID_URL'); + expect(code).to.equal(-300); + expect(isMainFrame).to.equal(true); + done(); + }); + const data = Buffer.alloc(2 * 1024 * 1024).toString('base64'); + w.loadURL(`data:image/png;base64,${data}`); + }); it('should return a promise', () => { - const p = w.loadURL('about:blank') - expect(p).to.have.property('then') - }) + const p = w.loadURL('about:blank'); + expect(p).to.have.property('then'); + }); it('should return a promise that resolves', async () => { - await expect(w.loadURL('about:blank')).to.eventually.be.fulfilled() - }) + await expect(w.loadURL('about:blank')).to.eventually.be.fulfilled(); + }); it('should return a promise that rejects on a load failure', async () => { - const data = Buffer.alloc(2 * 1024 * 1024).toString('base64') - const p = w.loadURL(`data:image/png;base64,${data}`) - await expect(p).to.eventually.be.rejected - }) + const data = Buffer.alloc(2 * 1024 * 1024).toString('base64'); + const p = w.loadURL(`data:image/png;base64,${data}`); + await expect(p).to.eventually.be.rejected; + }); it('should return a promise that resolves even if pushState occurs during navigation', async () => { - const p = w.loadURL('data:text/html,') - await expect(p).to.eventually.be.fulfilled - }) + const p = w.loadURL('data:text/html,'); + await expect(p).to.eventually.be.fulfilled; + }); // FIXME(robo/nornagon): re-enable these once service workers work describe.skip('POST navigations', () => { - afterEach(() => { w.webContents.session.webRequest.onBeforeSendHeaders(null) }) + afterEach(() => { w.webContents.session.webRequest.onBeforeSendHeaders(null); }); it('supports specifying POST data', async () => { - await w.loadURL(url, { postData }) - }) + await w.loadURL(url, { postData }); + }); it('sets the content type header on URL encoded forms', async () => { - await w.loadURL(url) + await w.loadURL(url); const requestDetails: Promise = new Promise(resolve => { w.webContents.session.webRequest.onBeforeSendHeaders((details) => { - resolve(details) - }) - }) + resolve(details); + }); + }); w.webContents.executeJavaScript(` form = document.createElement('form') document.body.appendChild(form) form.method = 'POST' form.submit() - `) - const details = await requestDetails - expect(details.requestHeaders['Content-Type']).to.equal('application/x-www-form-urlencoded') - }) + `); + const details = await requestDetails; + expect(details.requestHeaders['Content-Type']).to.equal('application/x-www-form-urlencoded'); + }); it('sets the content type header on multi part forms', async () => { - await w.loadURL(url) + await w.loadURL(url); const requestDetails: Promise = new Promise(resolve => { w.webContents.session.webRequest.onBeforeSendHeaders((details) => { - resolve(details) - }) - }) + resolve(details); + }); + }); w.webContents.executeJavaScript(` form = document.createElement('form') document.body.appendChild(form) @@ -419,930 +419,930 @@ describe('BrowserWindow module', () => { file.name = 'file' form.appendChild(file) form.submit() - `) - const details = await requestDetails - expect(details.requestHeaders['Content-Type'].startsWith('multipart/form-data; boundary=----WebKitFormBoundary')).to.equal(true) - }) - }) + `); + const details = await requestDetails; + expect(details.requestHeaders['Content-Type'].startsWith('multipart/form-data; boundary=----WebKitFormBoundary')).to.equal(true); + }); + }); it('should support support base url for data urls', (done) => { ipcMain.once('answer', (event, test) => { - expect(test).to.equal('test') - done() - }) - w.loadURL('data:text/html,', { baseURLForDataURL: `other://${path.join(fixtures, 'api')}${path.sep}` }) - }) - }) + expect(test).to.equal('test'); + done(); + }); + w.loadURL('data:text/html,', { baseURLForDataURL: `other://${path.join(fixtures, 'api')}${path.sep}` }); + }); + }); for (const sandbox of [false, true]) { describe(`navigation events${sandbox ? ' with sandbox' : ''}`, () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: false, sandbox } }) - }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: false, sandbox } }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); describe('will-navigate event', () => { - let server = null as unknown as http.Server - let url = null as unknown as string + let server = null as unknown as http.Server; + let url = null as unknown as string; before((done) => { - server = http.createServer((req, res) => { res.end('') }) + server = http.createServer((req, res) => { res.end(''); }); server.listen(0, '127.0.0.1', () => { - url = `http://127.0.0.1:${(server.address() as AddressInfo).port}/` - done() - }) - }) + url = `http://127.0.0.1:${(server.address() as AddressInfo).port}/`; + done(); + }); + }); after(() => { - server.close() - }) + server.close(); + }); it('allows the window to be closed from the event listener', (done) => { w.webContents.once('will-navigate', () => { - w.close() - done() - }) - w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html')) - }) + w.close(); + done(); + }); + w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html')); + }); it('can be prevented', (done) => { - let willNavigate = false + let willNavigate = false; w.webContents.once('will-navigate', (e) => { - willNavigate = true - e.preventDefault() - }) + willNavigate = true; + e.preventDefault(); + }); w.webContents.on('did-stop-loading', () => { if (willNavigate) { // i.e. it shouldn't have had '?navigated' appended to it. - expect(w.webContents.getURL().endsWith('will-navigate.html')).to.be.true() - done() + expect(w.webContents.getURL().endsWith('will-navigate.html')).to.be.true(); + done(); } - }) - w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html')) - }) + }); + w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html')); + }); it('is triggered when navigating from file: to http:', async () => { - await w.loadFile(path.join(fixtures, 'api', 'blank.html')) - w.webContents.executeJavaScript(`location.href = ${JSON.stringify(url)}`) + await w.loadFile(path.join(fixtures, 'api', 'blank.html')); + w.webContents.executeJavaScript(`location.href = ${JSON.stringify(url)}`); const navigatedTo = await new Promise(resolve => { w.webContents.once('will-navigate', (e, url) => { - e.preventDefault() - resolve(url) - }) - }) - expect(navigatedTo).to.equal(url) - expect(w.webContents.getURL()).to.match(/^file:/) - }) + e.preventDefault(); + resolve(url); + }); + }); + expect(navigatedTo).to.equal(url); + expect(w.webContents.getURL()).to.match(/^file:/); + }); it('is triggered when navigating from about:blank to http:', async () => { - await w.loadURL('about:blank') - w.webContents.executeJavaScript(`location.href = ${JSON.stringify(url)}`) + await w.loadURL('about:blank'); + w.webContents.executeJavaScript(`location.href = ${JSON.stringify(url)}`); const navigatedTo = await new Promise(resolve => { w.webContents.once('will-navigate', (e, url) => { - e.preventDefault() - resolve(url) - }) - }) - expect(navigatedTo).to.equal(url) - expect(w.webContents.getURL()).to.equal('about:blank') - }) - }) + e.preventDefault(); + resolve(url); + }); + }); + expect(navigatedTo).to.equal(url); + expect(w.webContents.getURL()).to.equal('about:blank'); + }); + }); describe('will-redirect event', () => { - let server = null as unknown as http.Server - let url = null as unknown as string + let server = null as unknown as http.Server; + let url = null as unknown as string; before((done) => { server = http.createServer((req, res) => { if (req.url === '/302') { - res.setHeader('Location', '/200') - res.statusCode = 302 - res.end() + res.setHeader('Location', '/200'); + res.statusCode = 302; + res.end(); } else if (req.url === '/navigate-302') { - res.end(``) + res.end(``); } else { - res.end() + res.end(); } - }) + }); server.listen(0, '127.0.0.1', () => { - url = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - done() - }) - }) + url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + done(); + }); + }); after(() => { - server.close() - }) + server.close(); + }); it('is emitted on redirects', (done) => { w.webContents.on('will-redirect', () => { - done() - }) - w.loadURL(`${url}/302`) - }) + done(); + }); + w.loadURL(`${url}/302`); + }); it('is emitted after will-navigate on redirects', (done) => { - let navigateCalled = false + let navigateCalled = false; w.webContents.on('will-navigate', () => { - navigateCalled = true - }) + navigateCalled = true; + }); w.webContents.on('will-redirect', () => { - expect(navigateCalled).to.equal(true, 'should have called will-navigate first') - done() - }) - w.loadURL(`${url}/navigate-302`) - }) + expect(navigateCalled).to.equal(true, 'should have called will-navigate first'); + done(); + }); + w.loadURL(`${url}/navigate-302`); + }); it('is emitted before did-stop-loading on redirects', (done) => { - let stopCalled = false + let stopCalled = false; w.webContents.on('did-stop-loading', () => { - stopCalled = true - }) + stopCalled = true; + }); w.webContents.on('will-redirect', () => { - expect(stopCalled).to.equal(false, 'should not have called did-stop-loading first') - done() - }) - w.loadURL(`${url}/302`) - }) + expect(stopCalled).to.equal(false, 'should not have called did-stop-loading first'); + done(); + }); + w.loadURL(`${url}/302`); + }); it('allows the window to be closed from the event listener', (done) => { w.webContents.once('will-redirect', () => { - w.close() - done() - }) - w.loadURL(`${url}/302`) - }) + w.close(); + done(); + }); + w.loadURL(`${url}/302`); + }); it('can be prevented', (done) => { w.webContents.once('will-redirect', (event) => { - event.preventDefault() - }) + event.preventDefault(); + }); w.webContents.on('will-navigate', (e, u) => { - expect(u).to.equal(`${url}/302`) - }) + expect(u).to.equal(`${url}/302`); + }); w.webContents.on('did-stop-loading', () => { expect(w.webContents.getURL()).to.equal( `${url}/navigate-302`, 'url should not have changed after navigation event' - ) - done() - }) + ); + done(); + }); w.webContents.on('will-redirect', (e, u) => { - expect(u).to.equal(`${url}/200`) - }) - w.loadURL(`${url}/navigate-302`) - }) - }) - }) + expect(u).to.equal(`${url}/200`); + }); + w.loadURL(`${url}/navigate-302`); + }); + }); + }); } describe('focus and visibility', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false }) - }) + w = new BrowserWindow({ show: false }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); describe('BrowserWindow.show()', () => { it('should focus on window', () => { - w.show() - expect(w.isFocused()).to.equal(true) - }) + w.show(); + expect(w.isFocused()).to.equal(true); + }); it('should make the window visible', () => { - w.show() - expect(w.isVisible()).to.equal(true) - }) + w.show(); + expect(w.isVisible()).to.equal(true); + }); it('emits when window is shown', (done) => { w.once('show', () => { - expect(w.isVisible()).to.equal(true) - done() - }) - w.show() - }) - }) + expect(w.isVisible()).to.equal(true); + done(); + }); + w.show(); + }); + }); describe('BrowserWindow.hide()', () => { it('should defocus on window', () => { - w.hide() - expect(w.isFocused()).to.equal(false) - }) + w.hide(); + expect(w.isFocused()).to.equal(false); + }); it('should make the window not visible', () => { - w.show() - w.hide() - expect(w.isVisible()).to.equal(false) - }) + w.show(); + w.hide(); + expect(w.isVisible()).to.equal(false); + }); it('emits when window is hidden', async () => { - const shown = emittedOnce(w, 'show') - w.show() - await shown - const hidden = emittedOnce(w, 'hide') - w.hide() - await hidden - expect(w.isVisible()).to.equal(false) - }) - }) + const shown = emittedOnce(w, 'show'); + w.show(); + await shown; + const hidden = emittedOnce(w, 'hide'); + w.hide(); + await hidden; + expect(w.isVisible()).to.equal(false); + }); + }); describe('BrowserWindow.showInactive()', () => { it('should not focus on window', () => { - w.showInactive() - expect(w.isFocused()).to.equal(false) - }) - }) + w.showInactive(); + expect(w.isFocused()).to.equal(false); + }); + }); describe('BrowserWindow.focus()', () => { it('does not make the window become visible', () => { - expect(w.isVisible()).to.equal(false) - w.focus() - expect(w.isVisible()).to.equal(false) - }) - }) + expect(w.isVisible()).to.equal(false); + w.focus(); + expect(w.isVisible()).to.equal(false); + }); + }); describe('BrowserWindow.blur()', () => { it('removes focus from window', () => { - w.blur() - expect(w.isFocused()).to.equal(false) - }) - }) + w.blur(); + expect(w.isFocused()).to.equal(false); + }); + }); describe('BrowserWindow.getFocusedWindow()', () => { it('returns the opener window when dev tools window is focused', async () => { - w.show() - w.webContents.openDevTools({ mode: 'undocked' }) - await emittedOnce(w.webContents, 'devtools-focused') - expect(BrowserWindow.getFocusedWindow()).to.equal(w) - }) - }) + w.show(); + w.webContents.openDevTools({ mode: 'undocked' }); + await emittedOnce(w.webContents, 'devtools-focused'); + expect(BrowserWindow.getFocusedWindow()).to.equal(w); + }); + }); describe('BrowserWindow.moveTop()', () => { it('should not steal focus', async () => { - const posDelta = 50 - const wShownInactive = emittedOnce(w, 'show') - w.showInactive() - await wShownInactive - expect(w.isFocused()).to.equal(false) - - const otherWindow = new BrowserWindow({ show: false, title: 'otherWindow' }) - const otherWindowShown = emittedOnce(otherWindow, 'show') - const otherWindowFocused = emittedOnce(otherWindow, 'focus') - otherWindow.show() - await otherWindowShown - await otherWindowFocused - expect(otherWindow.isFocused()).to.equal(true) - - w.moveTop() - const wPos = w.getPosition() - const wMoving = emittedOnce(w, 'move') - w.setPosition(wPos[0] + posDelta, wPos[1] + posDelta) - await wMoving - expect(w.isFocused()).to.equal(false) - expect(otherWindow.isFocused()).to.equal(true) - - const wFocused = emittedOnce(w, 'focus') - w.focus() - await wFocused - expect(w.isFocused()).to.equal(true) - - otherWindow.moveTop() - const otherWindowPos = otherWindow.getPosition() - const otherWindowMoving = emittedOnce(otherWindow, 'move') - otherWindow.setPosition(otherWindowPos[0] + posDelta, otherWindowPos[1] + posDelta) - await otherWindowMoving - expect(otherWindow.isFocused()).to.equal(false) - expect(w.isFocused()).to.equal(true) - - await closeWindow(otherWindow, { assertNotWindows: false }) - expect(BrowserWindow.getAllWindows()).to.have.lengthOf(1) - }) - }) + const posDelta = 50; + const wShownInactive = emittedOnce(w, 'show'); + w.showInactive(); + await wShownInactive; + expect(w.isFocused()).to.equal(false); + + const otherWindow = new BrowserWindow({ show: false, title: 'otherWindow' }); + const otherWindowShown = emittedOnce(otherWindow, 'show'); + const otherWindowFocused = emittedOnce(otherWindow, 'focus'); + otherWindow.show(); + await otherWindowShown; + await otherWindowFocused; + expect(otherWindow.isFocused()).to.equal(true); + + w.moveTop(); + const wPos = w.getPosition(); + const wMoving = emittedOnce(w, 'move'); + w.setPosition(wPos[0] + posDelta, wPos[1] + posDelta); + await wMoving; + expect(w.isFocused()).to.equal(false); + expect(otherWindow.isFocused()).to.equal(true); + + const wFocused = emittedOnce(w, 'focus'); + w.focus(); + await wFocused; + expect(w.isFocused()).to.equal(true); + + otherWindow.moveTop(); + const otherWindowPos = otherWindow.getPosition(); + const otherWindowMoving = emittedOnce(otherWindow, 'move'); + otherWindow.setPosition(otherWindowPos[0] + posDelta, otherWindowPos[1] + posDelta); + await otherWindowMoving; + expect(otherWindow.isFocused()).to.equal(false); + expect(w.isFocused()).to.equal(true); + + await closeWindow(otherWindow, { assertNotWindows: false }); + expect(BrowserWindow.getAllWindows()).to.have.lengthOf(1); + }); + }); describe('BrowserWindow.moveAbove(mediaSourceId)', () => { it('should throw an exception if wrong formatting', async () => { const fakeSourceIds = [ 'none', 'screen:0', 'window:fake', 'window:1234', 'foobar:1:2' - ] + ]; fakeSourceIds.forEach((sourceId) => { expect(() => { - w.moveAbove(sourceId) - }).to.throw(/Invalid media source id/) - }) - }) + w.moveAbove(sourceId); + }).to.throw(/Invalid media source id/); + }); + }); it('should throw an exception if wrong type', async () => { - const fakeSourceIds = [null as any, 123 as any] + const fakeSourceIds = [null as any, 123 as any]; fakeSourceIds.forEach((sourceId) => { expect(() => { - w.moveAbove(sourceId) - }).to.throw(/Error processing argument at index 0 */) - }) - }) + w.moveAbove(sourceId); + }).to.throw(/Error processing argument at index 0 */); + }); + }); it('should throw an exception if invalid window', async () => { // It is very unlikely that these window id exist. const fakeSourceIds = ['window:99999999:0', 'window:123456:1', - 'window:123456:9'] + 'window:123456:9']; fakeSourceIds.forEach((sourceId) => { expect(() => { - w.moveAbove(sourceId) - }).to.throw(/Invalid media source id/) - }) - }) + w.moveAbove(sourceId); + }).to.throw(/Invalid media source id/); + }); + }); it('should not throw an exception', async () => { - const w2 = new BrowserWindow({ show: false, title: 'window2' }) - const w2Shown = emittedOnce(w2, 'show') - w2.show() - await w2Shown + const w2 = new BrowserWindow({ show: false, title: 'window2' }); + const w2Shown = emittedOnce(w2, 'show'); + w2.show(); + await w2Shown; expect(() => { - w.moveAbove(w2.getMediaSourceId()) - }).to.not.throw() + w.moveAbove(w2.getMediaSourceId()); + }).to.not.throw(); - await closeWindow(w2, { assertNotWindows: false }) - }) - }) + await closeWindow(w2, { assertNotWindows: false }); + }); + }); describe('BrowserWindow.setFocusable()', () => { it('can set unfocusable window to focusable', async () => { - const w2 = new BrowserWindow({ focusable: false }) - const w2Focused = emittedOnce(w2, 'focus') - w2.setFocusable(true) - w2.focus() - await w2Focused - await closeWindow(w2, { assertNotWindows: false }) - }) - }) - }) + const w2 = new BrowserWindow({ focusable: false }); + const w2Focused = emittedOnce(w2, 'focus'); + w2.setFocusable(true); + w2.focus(); + await w2Focused; + await closeWindow(w2, { assertNotWindows: false }); + }); + }); + }); describe('sizing', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false, width: 400, height: 400 }) - }) + w = new BrowserWindow({ show: false, width: 400, height: 400 }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); describe('BrowserWindow.setBounds(bounds[, animate])', () => { it('sets the window bounds with full bounds', () => { - const fullBounds = { x: 440, y: 225, width: 500, height: 400 } - w.setBounds(fullBounds) + const fullBounds = { x: 440, y: 225, width: 500, height: 400 }; + w.setBounds(fullBounds); - expectBoundsEqual(w.getBounds(), fullBounds) - }) + expectBoundsEqual(w.getBounds(), fullBounds); + }); it('sets the window bounds with partial bounds', () => { - const fullBounds = { x: 440, y: 225, width: 500, height: 400 } - w.setBounds(fullBounds) + const fullBounds = { x: 440, y: 225, width: 500, height: 400 }; + w.setBounds(fullBounds); - const boundsUpdate = { width: 200 } - w.setBounds(boundsUpdate as any) + const boundsUpdate = { width: 200 }; + w.setBounds(boundsUpdate as any); - const expectedBounds = Object.assign(fullBounds, boundsUpdate) - expectBoundsEqual(w.getBounds(), expectedBounds) - }) - }) + const expectedBounds = Object.assign(fullBounds, boundsUpdate); + expectBoundsEqual(w.getBounds(), expectedBounds); + }); + }); describe('BrowserWindow.setSize(width, height)', () => { it('sets the window size', async () => { - const size = [300, 400] + const size = [300, 400]; - const resized = emittedOnce(w, 'resize') - w.setSize(size[0], size[1]) - await resized + const resized = emittedOnce(w, 'resize'); + w.setSize(size[0], size[1]); + await resized; - expectBoundsEqual(w.getSize(), size) - }) - }) + expectBoundsEqual(w.getSize(), size); + }); + }); describe('BrowserWindow.setMinimum/MaximumSize(width, height)', () => { it('sets the maximum and minimum size of the window', () => { - expect(w.getMinimumSize()).to.deep.equal([0, 0]) - expect(w.getMaximumSize()).to.deep.equal([0, 0]) + expect(w.getMinimumSize()).to.deep.equal([0, 0]); + expect(w.getMaximumSize()).to.deep.equal([0, 0]); - w.setMinimumSize(100, 100) - expectBoundsEqual(w.getMinimumSize(), [100, 100]) - expectBoundsEqual(w.getMaximumSize(), [0, 0]) + w.setMinimumSize(100, 100); + expectBoundsEqual(w.getMinimumSize(), [100, 100]); + expectBoundsEqual(w.getMaximumSize(), [0, 0]); - w.setMaximumSize(900, 600) - expectBoundsEqual(w.getMinimumSize(), [100, 100]) - expectBoundsEqual(w.getMaximumSize(), [900, 600]) - }) - }) + w.setMaximumSize(900, 600); + expectBoundsEqual(w.getMinimumSize(), [100, 100]); + expectBoundsEqual(w.getMaximumSize(), [900, 600]); + }); + }); describe('BrowserWindow.setAspectRatio(ratio)', () => { it('resets the behaviour when passing in 0', (done) => { - const size = [300, 400] - w.setAspectRatio(1 / 2) - w.setAspectRatio(0) + const size = [300, 400]; + w.setAspectRatio(1 / 2); + w.setAspectRatio(0); w.once('resize', () => { - expectBoundsEqual(w.getSize(), size) - done() - }) - w.setSize(size[0], size[1]) - }) - }) + expectBoundsEqual(w.getSize(), size); + done(); + }); + w.setSize(size[0], size[1]); + }); + }); describe('BrowserWindow.setPosition(x, y)', () => { it('sets the window position', (done) => { - const pos = [10, 10] + const pos = [10, 10]; w.once('move', () => { - const newPos = w.getPosition() - expect(newPos).to.deep.equal(pos) - done() - }) - w.setPosition(pos[0], pos[1]) - }) - }) + const newPos = w.getPosition(); + expect(newPos).to.deep.equal(pos); + done(); + }); + w.setPosition(pos[0], pos[1]); + }); + }); describe('BrowserWindow.setContentSize(width, height)', () => { it('sets the content size', (done) => { // NB. The CI server has a very small screen. Attempting to size the window // larger than the screen will limit the window's size to the screen and // cause the test to fail. - const size = [456, 567] - w.setContentSize(size[0], size[1]) + const size = [456, 567]; + w.setContentSize(size[0], size[1]); setImmediate(() => { - const after = w.getContentSize() - expect(after).to.deep.equal(size) - done() - }) - }) + const after = w.getContentSize(); + expect(after).to.deep.equal(size); + done(); + }); + }); it('works for a frameless window', (done) => { - w.destroy() + w.destroy(); w = new BrowserWindow({ show: false, frame: false, width: 400, height: 400 - }) - const size = [456, 567] - w.setContentSize(size[0], size[1]) + }); + const size = [456, 567]; + w.setContentSize(size[0], size[1]); setImmediate(() => { - const after = w.getContentSize() - expect(after).to.deep.equal(size) - done() - }) - }) - }) + const after = w.getContentSize(); + expect(after).to.deep.equal(size); + done(); + }); + }); + }); describe('BrowserWindow.setContentBounds(bounds)', () => { it('sets the content size and position', (done) => { - const bounds = { x: 10, y: 10, width: 250, height: 250 } + const bounds = { x: 10, y: 10, width: 250, height: 250 }; w.once('resize', () => { setTimeout(() => { - expectBoundsEqual(w.getContentBounds(), bounds) - done() - }) - }) - w.setContentBounds(bounds) - }) + expectBoundsEqual(w.getContentBounds(), bounds); + done(); + }); + }); + w.setContentBounds(bounds); + }); it('works for a frameless window', (done) => { - w.destroy() + w.destroy(); w = new BrowserWindow({ show: false, frame: false, width: 300, height: 300 - }) - const bounds = { x: 10, y: 10, width: 250, height: 250 } + }); + const bounds = { x: 10, y: 10, width: 250, height: 250 }; w.once('resize', () => { setTimeout(() => { - expect(w.getContentBounds()).to.deep.equal(bounds) - done() - }) - }) - w.setContentBounds(bounds) - }) - }) + expect(w.getContentBounds()).to.deep.equal(bounds); + done(); + }); + }); + w.setContentBounds(bounds); + }); + }); describe('BrowserWindow.getBackgroundColor()', () => { it('returns default value if no backgroundColor is set', () => { - w.destroy() - w = new BrowserWindow({}) - expect(w.getBackgroundColor()).to.equal('#FFFFFF') - }) + w.destroy(); + w = new BrowserWindow({}); + expect(w.getBackgroundColor()).to.equal('#FFFFFF'); + }); it('returns correct value if backgroundColor is set', () => { - const backgroundColor = '#BBAAFF' - w.destroy() + const backgroundColor = '#BBAAFF'; + w.destroy(); w = new BrowserWindow({ backgroundColor: backgroundColor - }) - expect(w.getBackgroundColor()).to.equal(backgroundColor) - }) + }); + expect(w.getBackgroundColor()).to.equal(backgroundColor); + }); it('returns correct value from setBackgroundColor()', () => { - const backgroundColor = '#AABBFF' - w.destroy() - w = new BrowserWindow({}) - w.setBackgroundColor(backgroundColor) - expect(w.getBackgroundColor()).to.equal(backgroundColor) - }) - }) + const backgroundColor = '#AABBFF'; + w.destroy(); + w = new BrowserWindow({}); + w.setBackgroundColor(backgroundColor); + expect(w.getBackgroundColor()).to.equal(backgroundColor); + }); + }); describe('BrowserWindow.getNormalBounds()', () => { describe('Normal state', () => { it('checks normal bounds after resize', (done) => { - const size = [300, 400] + const size = [300, 400]; w.once('resize', () => { - expectBoundsEqual(w.getNormalBounds(), w.getBounds()) - done() - }) - w.setSize(size[0], size[1]) - }) + expectBoundsEqual(w.getNormalBounds(), w.getBounds()); + done(); + }); + w.setSize(size[0], size[1]); + }); it('checks normal bounds after move', (done) => { - const pos = [10, 10] + const pos = [10, 10]; w.once('move', () => { - expectBoundsEqual(w.getNormalBounds(), w.getBounds()) - done() - }) - w.setPosition(pos[0], pos[1]) - }) - }) + expectBoundsEqual(w.getNormalBounds(), w.getBounds()); + done(); + }); + w.setPosition(pos[0], pos[1]); + }); + }); ifdescribe(process.platform !== 'linux')('Maximized state', () => { it('checks normal bounds when maximized', (done) => { - const bounds = w.getBounds() + const bounds = w.getBounds(); w.once('maximize', () => { - expectBoundsEqual(w.getNormalBounds(), bounds) - done() - }) - w.show() - w.maximize() - }) + expectBoundsEqual(w.getNormalBounds(), bounds); + done(); + }); + w.show(); + w.maximize(); + }); it('checks normal bounds when unmaximized', (done) => { - const bounds = w.getBounds() + const bounds = w.getBounds(); w.once('maximize', () => { - w.unmaximize() - }) + w.unmaximize(); + }); w.once('unmaximize', () => { - expectBoundsEqual(w.getNormalBounds(), bounds) - done() - }) - w.show() - w.maximize() - }) - }) + expectBoundsEqual(w.getNormalBounds(), bounds); + done(); + }); + w.show(); + w.maximize(); + }); + }); ifdescribe(process.platform !== 'linux')('Minimized state', () => { it('checks normal bounds when minimized', (done) => { - const bounds = w.getBounds() + const bounds = w.getBounds(); w.once('minimize', () => { - expectBoundsEqual(w.getNormalBounds(), bounds) - done() - }) - w.show() - w.minimize() - }) + expectBoundsEqual(w.getNormalBounds(), bounds); + done(); + }); + w.show(); + w.minimize(); + }); it('checks normal bounds when restored', (done) => { - const bounds = w.getBounds() + const bounds = w.getBounds(); w.once('minimize', () => { - w.restore() - }) + w.restore(); + }); w.once('restore', () => { - expectBoundsEqual(w.getNormalBounds(), bounds) - done() - }) - w.show() - w.minimize() - }) - }) + expectBoundsEqual(w.getNormalBounds(), bounds); + done(); + }); + w.show(); + w.minimize(); + }); + }); ifdescribe(process.platform === 'win32')('Fullscreen state', () => { it('checks normal bounds when fullscreen\'ed', (done) => { - const bounds = w.getBounds() + const bounds = w.getBounds(); w.once('enter-full-screen', () => { - expectBoundsEqual(w.getNormalBounds(), bounds) - done() - }) - w.show() - w.setFullScreen(true) - }) + expectBoundsEqual(w.getNormalBounds(), bounds); + done(); + }); + w.show(); + w.setFullScreen(true); + }); it('checks normal bounds when unfullscreen\'ed', (done) => { - const bounds = w.getBounds() + const bounds = w.getBounds(); w.once('enter-full-screen', () => { - w.setFullScreen(false) - }) + w.setFullScreen(false); + }); w.once('leave-full-screen', () => { - expectBoundsEqual(w.getNormalBounds(), bounds) - done() - }) - w.show() - w.setFullScreen(true) - }) - }) - }) - }) + expectBoundsEqual(w.getNormalBounds(), bounds); + done(); + }); + w.show(); + w.setFullScreen(true); + }); + }); + }); + }); ifdescribe(process.platform === 'darwin')('tabbed windows', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false }) - }) + w = new BrowserWindow({ show: false }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); describe('BrowserWindow.selectPreviousTab()', () => { it('does not throw', () => { expect(() => { - w.selectPreviousTab() - }).to.not.throw() - }) - }) + w.selectPreviousTab(); + }).to.not.throw(); + }); + }); describe('BrowserWindow.selectNextTab()', () => { it('does not throw', () => { expect(() => { - w.selectNextTab() - }).to.not.throw() - }) - }) + w.selectNextTab(); + }).to.not.throw(); + }); + }); describe('BrowserWindow.mergeAllWindows()', () => { it('does not throw', () => { expect(() => { - w.mergeAllWindows() - }).to.not.throw() - }) - }) + w.mergeAllWindows(); + }).to.not.throw(); + }); + }); describe('BrowserWindow.moveTabToNewWindow()', () => { it('does not throw', () => { expect(() => { - w.moveTabToNewWindow() - }).to.not.throw() - }) - }) + w.moveTabToNewWindow(); + }).to.not.throw(); + }); + }); describe('BrowserWindow.toggleTabBar()', () => { it('does not throw', () => { expect(() => { - w.toggleTabBar() - }).to.not.throw() - }) - }) + w.toggleTabBar(); + }).to.not.throw(); + }); + }); describe('BrowserWindow.addTabbedWindow()', () => { it('does not throw', async () => { - const tabbedWindow = new BrowserWindow({}) + const tabbedWindow = new BrowserWindow({}); expect(() => { - w.addTabbedWindow(tabbedWindow) - }).to.not.throw() + w.addTabbedWindow(tabbedWindow); + }).to.not.throw(); - expect(BrowserWindow.getAllWindows()).to.have.lengthOf(2) // w + tabbedWindow + expect(BrowserWindow.getAllWindows()).to.have.lengthOf(2); // w + tabbedWindow - await closeWindow(tabbedWindow, { assertNotWindows: false }) - expect(BrowserWindow.getAllWindows()).to.have.lengthOf(1) // w - }) + await closeWindow(tabbedWindow, { assertNotWindows: false }); + expect(BrowserWindow.getAllWindows()).to.have.lengthOf(1); // w + }); it('throws when called on itself', () => { expect(() => { - w.addTabbedWindow(w) - }).to.throw('AddTabbedWindow cannot be called by a window on itself.') - }) - }) - }) + w.addTabbedWindow(w); + }).to.throw('AddTabbedWindow cannot be called by a window on itself.'); + }); + }); + }); describe('autoHideMenuBar state', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('for properties', () => { it('can be set with autoHideMenuBar constructor option', () => { - const w = new BrowserWindow({ show: false, autoHideMenuBar: true }) - expect(w.autoHideMenuBar).to.be.true('autoHideMenuBar') - }) + const w = new BrowserWindow({ show: false, autoHideMenuBar: true }); + expect(w.autoHideMenuBar).to.be.true('autoHideMenuBar'); + }); it('can be changed', () => { - const w = new BrowserWindow({ show: false }) - expect(w.autoHideMenuBar).to.be.false('autoHideMenuBar') - w.autoHideMenuBar = true - expect(w.autoHideMenuBar).to.be.true('autoHideMenuBar') - w.autoHideMenuBar = false - expect(w.autoHideMenuBar).to.be.false('autoHideMenuBar') - }) - }) + const w = new BrowserWindow({ show: false }); + expect(w.autoHideMenuBar).to.be.false('autoHideMenuBar'); + w.autoHideMenuBar = true; + expect(w.autoHideMenuBar).to.be.true('autoHideMenuBar'); + w.autoHideMenuBar = false; + expect(w.autoHideMenuBar).to.be.false('autoHideMenuBar'); + }); + }); it('for functions', () => { it('can be set with autoHideMenuBar constructor option', () => { - const w = new BrowserWindow({ show: false, autoHideMenuBar: true }) - expect(w.isMenuBarAutoHide()).to.be.true('autoHideMenuBar') - }) + const w = new BrowserWindow({ show: false, autoHideMenuBar: true }); + expect(w.isMenuBarAutoHide()).to.be.true('autoHideMenuBar'); + }); it('can be changed', () => { - const w = new BrowserWindow({ show: false }) - expect(w.isMenuBarAutoHide()).to.be.false('autoHideMenuBar') - w.setAutoHideMenuBar(true) - expect(w.isMenuBarAutoHide()).to.be.true('autoHideMenuBar') - w.setAutoHideMenuBar(false) - expect(w.isMenuBarAutoHide()).to.be.false('autoHideMenuBar') - }) - }) - }) + const w = new BrowserWindow({ show: false }); + expect(w.isMenuBarAutoHide()).to.be.false('autoHideMenuBar'); + w.setAutoHideMenuBar(true); + expect(w.isMenuBarAutoHide()).to.be.true('autoHideMenuBar'); + w.setAutoHideMenuBar(false); + expect(w.isMenuBarAutoHide()).to.be.false('autoHideMenuBar'); + }); + }); + }); describe('BrowserWindow.capturePage(rect)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('returns a Promise with a Buffer', async () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); const image = await w.capturePage({ x: 0, y: 0, width: 100, height: 100 - }) + }); - expect(image.isEmpty()).to.equal(true) - }) + expect(image.isEmpty()).to.equal(true); + }); it('preserves transparency', async () => { - const w = new BrowserWindow({ show: false, transparent: true }) - w.loadURL('about:blank') - await emittedOnce(w, 'ready-to-show') - w.show() + const w = new BrowserWindow({ show: false, transparent: true }); + w.loadURL('about:blank'); + await emittedOnce(w, 'ready-to-show'); + w.show(); - const image = await w.capturePage() - const imgBuffer = image.toPNG() + const image = await w.capturePage(); + const imgBuffer = image.toPNG(); // Check the 25th byte in the PNG. // Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha - expect(imgBuffer[25]).to.equal(6) - }) - }) + expect(imgBuffer[25]).to.equal(6); + }); + }); describe('BrowserWindow.setProgressBar(progress)', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; before(() => { - w = new BrowserWindow({ show: false }) - }) + w = new BrowserWindow({ show: false }); + }); after(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); it('sets the progress', () => { expect(() => { if (process.platform === 'darwin') { - app.dock.setIcon(path.join(fixtures, 'assets', 'logo.png')) + app.dock.setIcon(path.join(fixtures, 'assets', 'logo.png')); } - w.setProgressBar(0.5) + w.setProgressBar(0.5); if (process.platform === 'darwin') { - app.dock.setIcon(null as any) + app.dock.setIcon(null as any); } - w.setProgressBar(-1) - }).to.not.throw() - }) + w.setProgressBar(-1); + }).to.not.throw(); + }); it('sets the progress using "paused" mode', () => { expect(() => { - w.setProgressBar(0.5, { mode: 'paused' }) - }).to.not.throw() - }) + w.setProgressBar(0.5, { mode: 'paused' }); + }).to.not.throw(); + }); it('sets the progress using "error" mode', () => { expect(() => { - w.setProgressBar(0.5, { mode: 'error' }) - }).to.not.throw() - }) + w.setProgressBar(0.5, { mode: 'error' }); + }).to.not.throw(); + }); it('sets the progress using "normal" mode', () => { expect(() => { - w.setProgressBar(0.5, { mode: 'normal' }) - }).to.not.throw() - }) - }) + w.setProgressBar(0.5, { mode: 'normal' }); + }).to.not.throw(); + }); + }); describe('BrowserWindow.setAlwaysOnTop(flag, level)', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false }) - }) + w = new BrowserWindow({ show: false }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); it('sets the window as always on top', () => { - expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop') - w.setAlwaysOnTop(true, 'screen-saver') - expect(w.isAlwaysOnTop()).to.be.true('is not alwaysOnTop') - w.setAlwaysOnTop(false) - expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop') - w.setAlwaysOnTop(true) - expect(w.isAlwaysOnTop()).to.be.true('is not alwaysOnTop') - }) + expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop'); + w.setAlwaysOnTop(true, 'screen-saver'); + expect(w.isAlwaysOnTop()).to.be.true('is not alwaysOnTop'); + w.setAlwaysOnTop(false); + expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop'); + w.setAlwaysOnTop(true); + expect(w.isAlwaysOnTop()).to.be.true('is not alwaysOnTop'); + }); ifit(process.platform === 'darwin')('resets the windows level on minimize', () => { - expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop') - w.setAlwaysOnTop(true, 'screen-saver') - expect(w.isAlwaysOnTop()).to.be.true('is not alwaysOnTop') - w.minimize() - expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop') - w.restore() - expect(w.isAlwaysOnTop()).to.be.true('is not alwaysOnTop') - }) + expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop'); + w.setAlwaysOnTop(true, 'screen-saver'); + expect(w.isAlwaysOnTop()).to.be.true('is not alwaysOnTop'); + w.minimize(); + expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop'); + w.restore(); + expect(w.isAlwaysOnTop()).to.be.true('is not alwaysOnTop'); + }); it('causes the right value to be emitted on `always-on-top-changed`', (done) => { w.on('always-on-top-changed', (e, alwaysOnTop) => { - expect(alwaysOnTop).to.be.true('is not alwaysOnTop') - done() - }) + expect(alwaysOnTop).to.be.true('is not alwaysOnTop'); + done(); + }); - expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop') - w.setAlwaysOnTop(true) - }) - }) + expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop'); + w.setAlwaysOnTop(true); + }); + }); describe('preconnect feature', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; - let server = null as unknown as http.Server - let url = null as unknown as string - let connections = 0 + let server = null as unknown as http.Server; + let url = null as unknown as string; + let connections = 0; beforeEach(async () => { - connections = 0 + connections = 0; server = http.createServer((req, res) => { if (req.url === '/link') { - res.setHeader('Content-type', 'text/html') - res.end('foo') - return + res.setHeader('Content-type', 'text/html'); + res.end('foo'); + return; } - res.end() - }) - server.on('connection', () => { connections++ }) + res.end(); + }); + server.on('connection', () => { connections++; }); - await new Promise(resolve => server.listen(0, '127.0.0.1', () => resolve())) - url = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - }) + await new Promise(resolve => server.listen(0, '127.0.0.1', () => resolve())); + url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + }); afterEach(async () => { - server.close() - await closeWindow(w) - w = null as unknown as BrowserWindow - server = null as unknown as http.Server - }) + server.close(); + await closeWindow(w); + w = null as unknown as BrowserWindow; + server = null as unknown as http.Server; + }); it('calling preconnect() connects to the server', (done) => { - w = new BrowserWindow({ show: false }) + w = new BrowserWindow({ show: false }); w.webContents.on('did-start-navigation', (event, url) => { - w.webContents.session.preconnect({ url, numSockets: 4 }) - }) + w.webContents.session.preconnect({ url, numSockets: 4 }); + }); w.webContents.on('did-finish-load', () => { - expect(connections).to.equal(4) - done() - }) - w.loadURL(url) - }) + expect(connections).to.equal(4); + done(); + }); + w.loadURL(url); + }); it('does not preconnect unless requested', async () => { - w = new BrowserWindow({ show: false }) - await w.loadURL(url) - expect(connections).to.equal(1) - }) + w = new BrowserWindow({ show: false }); + await w.loadURL(url); + expect(connections).to.equal(1); + }); it('parses ', async () => { - w = new BrowserWindow({ show: true }) - const p = emittedOnce(w.webContents.session, 'preconnect') - w.loadURL(url + '/link') - const [, preconnectUrl, allowCredentials] = await p - expect(preconnectUrl).to.equal('http://example.com/') - expect(allowCredentials).to.be.true('allowCredentials') - }) - }) + w = new BrowserWindow({ show: true }); + const p = emittedOnce(w.webContents.session, 'preconnect'); + w.loadURL(url + '/link'); + const [, preconnectUrl, allowCredentials] = await p; + expect(preconnectUrl).to.equal('http://example.com/'); + expect(allowCredentials).to.be.true('allowCredentials'); + }); + }); describe('BrowserWindow.setAutoHideCursor(autoHide)', () => { - let w = null as unknown as BrowserWindow + let w = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false }) - }) + w = new BrowserWindow({ show: false }); + }); afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + await closeWindow(w); + w = null as unknown as BrowserWindow; + }); ifit(process.platform === 'darwin')('on macOS', () => { it('allows changing cursor auto-hiding', () => { expect(() => { - w.setAutoHideCursor(false) - w.setAutoHideCursor(true) - }).to.not.throw() - }) - }) + w.setAutoHideCursor(false); + w.setAutoHideCursor(true); + }).to.not.throw(); + }); + }); ifit(process.platform !== 'darwin')('on non-macOS platforms', () => { it('is not available', () => { - expect(w.setAutoHideCursor).to.be.undefined('setAutoHideCursor function') - }) - }) - }) + expect(w.setAutoHideCursor).to.be.undefined('setAutoHideCursor function'); + }); + }); + }); ifdescribe(process.platform === 'darwin')('BrowserWindow.setWindowButtonVisibility()', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('does not throw', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); expect(() => { - w.setWindowButtonVisibility(true) - w.setWindowButtonVisibility(false) - }).to.not.throw() - }) + w.setWindowButtonVisibility(true); + w.setWindowButtonVisibility(false); + }).to.not.throw(); + }); it('throws with custom title bar buttons', () => { expect(() => { @@ -1350,211 +1350,211 @@ describe('BrowserWindow module', () => { show: false, titleBarStyle: 'customButtonsOnHover', frame: false - }) - w.setWindowButtonVisibility(true) - }).to.throw('Not supported for this window') - }) - }) + }); + w.setWindowButtonVisibility(true); + }).to.throw('Not supported for this window'); + }); + }); ifdescribe(process.platform === 'darwin')('BrowserWindow.setVibrancy(type)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('allows setting, changing, and removing the vibrancy', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); expect(() => { - w.setVibrancy('light') - w.setVibrancy('dark') - w.setVibrancy(null) - w.setVibrancy('ultra-dark') - w.setVibrancy('' as any) - }).to.not.throw() - }) - }) + w.setVibrancy('light'); + w.setVibrancy('dark'); + w.setVibrancy(null); + w.setVibrancy('ultra-dark'); + w.setVibrancy('' as any); + }).to.not.throw(); + }); + }); ifdescribe(process.platform === 'darwin')('BrowserWindow.getTrafficLightPosition(pos)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('gets the set traffic light position property', () => { - const pos = { x: 10, y: 10 } - const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos }) - const currentPosition = w.getTrafficLightPosition() + const pos = { x: 10, y: 10 }; + const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos }); + const currentPosition = w.getTrafficLightPosition(); - expect(currentPosition).to.deep.equal(pos) - }) - }) + expect(currentPosition).to.deep.equal(pos); + }); + }); ifdescribe(process.platform === 'darwin')('BrowserWindow.setTrafficLightPosition(pos)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can set the traffic light position property', () => { - const pos = { x: 10, y: 10 } - const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos }) - w.setTrafficLightPosition(pos) - const currentPosition = w.getTrafficLightPosition() + const pos = { x: 10, y: 10 }; + const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos }); + w.setTrafficLightPosition(pos); + const currentPosition = w.getTrafficLightPosition(); - expect(currentPosition).to.deep.equal(pos) - }) - }) + expect(currentPosition).to.deep.equal(pos); + }); + }); ifdescribe(process.platform === 'win32')('BrowserWindow.setAppDetails(options)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('supports setting the app details', () => { - const w = new BrowserWindow({ show: false }) - const iconPath = path.join(fixtures, 'assets', 'icon.ico') + const w = new BrowserWindow({ show: false }); + const iconPath = path.join(fixtures, 'assets', 'icon.ico'); expect(() => { - w.setAppDetails({ appId: 'my.app.id' }) - w.setAppDetails({ appIconPath: iconPath, appIconIndex: 0 }) - w.setAppDetails({ appIconPath: iconPath }) - w.setAppDetails({ relaunchCommand: 'my-app.exe arg1 arg2', relaunchDisplayName: 'My app name' }) - w.setAppDetails({ relaunchCommand: 'my-app.exe arg1 arg2' }) - w.setAppDetails({ relaunchDisplayName: 'My app name' }) + w.setAppDetails({ appId: 'my.app.id' }); + w.setAppDetails({ appIconPath: iconPath, appIconIndex: 0 }); + w.setAppDetails({ appIconPath: iconPath }); + w.setAppDetails({ relaunchCommand: 'my-app.exe arg1 arg2', relaunchDisplayName: 'My app name' }); + w.setAppDetails({ relaunchCommand: 'my-app.exe arg1 arg2' }); + w.setAppDetails({ relaunchDisplayName: 'My app name' }); w.setAppDetails({ appId: 'my.app.id', appIconPath: iconPath, appIconIndex: 0, relaunchCommand: 'my-app.exe arg1 arg2', relaunchDisplayName: 'My app name' - }) - w.setAppDetails({}) - }).to.not.throw() + }); + w.setAppDetails({}); + }).to.not.throw(); expect(() => { - (w.setAppDetails as any)() - }).to.throw('Insufficient number of arguments.') - }) - }) + (w.setAppDetails as any)(); + }).to.throw('Insufficient number of arguments.'); + }); + }); describe('BrowserWindow.fromId(id)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('returns the window with id', () => { - const w = new BrowserWindow({ show: false }) - expect(BrowserWindow.fromId(w.id).id).to.equal(w.id) - }) - }) + const w = new BrowserWindow({ show: false }); + expect(BrowserWindow.fromId(w.id).id).to.equal(w.id); + }); + }); describe('BrowserWindow.fromWebContents(webContents)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('returns the window with the webContents', () => { - const w = new BrowserWindow({ show: false }) - const found = BrowserWindow.fromWebContents(w.webContents) - expect(found!.id).to.equal(w.id) - }) + const w = new BrowserWindow({ show: false }); + const found = BrowserWindow.fromWebContents(w.webContents); + expect(found!.id).to.equal(w.id); + }); it('returns null for webContents without a BrowserWindow', () => { - const contents = (webContents as any).create({}) + const contents = (webContents as any).create({}); try { - expect(BrowserWindow.fromWebContents(contents)).to.be.null('BrowserWindow.fromWebContents(contents)') + expect(BrowserWindow.fromWebContents(contents)).to.be.null('BrowserWindow.fromWebContents(contents)'); } finally { - contents.destroy() + contents.destroy(); } - }) - }) + }); + }); describe('BrowserWindow.openDevTools()', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('does not crash for frameless window', () => { - const w = new BrowserWindow({ show: false, frame: false }) - w.webContents.openDevTools() - }) - }) + const w = new BrowserWindow({ show: false, frame: false }); + w.webContents.openDevTools(); + }); + }); describe('BrowserWindow.fromBrowserView(browserView)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('returns the window with the browserView', () => { - const w = new BrowserWindow({ show: false }) - const bv = new BrowserView() - w.setBrowserView(bv) - expect(BrowserWindow.fromBrowserView(bv)!.id).to.equal(w.id) + const w = new BrowserWindow({ show: false }); + const bv = new BrowserView(); + w.setBrowserView(bv); + expect(BrowserWindow.fromBrowserView(bv)!.id).to.equal(w.id); // if BrowserView isn't explicitly destroyed, it will crash in GC later - bv.destroy() - }) + bv.destroy(); + }); it('returns undefined if not attached', () => { - const bv = new BrowserView() - expect(BrowserWindow.fromBrowserView(bv)).to.be.null('BrowserWindow associated with bv') + const bv = new BrowserView(); + expect(BrowserWindow.fromBrowserView(bv)).to.be.null('BrowserWindow associated with bv'); // if BrowserView isn't explicitly destroyed, it will crash in GC later - bv.destroy() - }) - }) + bv.destroy(); + }); + }); describe('BrowserWindow.setOpacity(opacity)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); ifdescribe(process.platform !== 'linux')(('Windows and Mac'), () => { it('make window with initial opacity', () => { - const w = new BrowserWindow({ show: false, opacity: 0.5 }) - expect(w.getOpacity()).to.equal(0.5) - }) + const w = new BrowserWindow({ show: false, opacity: 0.5 }); + expect(w.getOpacity()).to.equal(0.5); + }); it('allows setting the opacity', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); expect(() => { - w.setOpacity(0.0) - expect(w.getOpacity()).to.equal(0.0) - w.setOpacity(0.5) - expect(w.getOpacity()).to.equal(0.5) - w.setOpacity(1.0) - expect(w.getOpacity()).to.equal(1.0) - }).to.not.throw() - }) + w.setOpacity(0.0); + expect(w.getOpacity()).to.equal(0.0); + w.setOpacity(0.5); + expect(w.getOpacity()).to.equal(0.5); + w.setOpacity(1.0); + expect(w.getOpacity()).to.equal(1.0); + }).to.not.throw(); + }); it('clamps opacity to [0.0...1.0]', () => { - const w = new BrowserWindow({ show: false, opacity: 0.5 }) - w.setOpacity(100) - expect(w.getOpacity()).to.equal(1.0) - w.setOpacity(-100) - expect(w.getOpacity()).to.equal(0.0) - }) - }) + const w = new BrowserWindow({ show: false, opacity: 0.5 }); + w.setOpacity(100); + expect(w.getOpacity()).to.equal(1.0); + w.setOpacity(-100); + expect(w.getOpacity()).to.equal(0.0); + }); + }); ifdescribe(process.platform === 'linux')(('Linux'), () => { it('sets 1 regardless of parameter', () => { - const w = new BrowserWindow({ show: false }) - w.setOpacity(0) - expect(w.getOpacity()).to.equal(1.0) - w.setOpacity(0.5) - expect(w.getOpacity()).to.equal(1.0) - }) - }) - }) + const w = new BrowserWindow({ show: false }); + w.setOpacity(0); + expect(w.getOpacity()).to.equal(1.0); + w.setOpacity(0.5); + expect(w.getOpacity()).to.equal(1.0); + }); + }); + }); describe('BrowserWindow.setShape(rects)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('allows setting shape', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); expect(() => { - w.setShape([]) - w.setShape([{ x: 0, y: 0, width: 100, height: 100 }]) - w.setShape([{ x: 0, y: 0, width: 100, height: 100 }, { x: 0, y: 200, width: 1000, height: 100 }]) - w.setShape([]) - }).to.not.throw() - }) - }) + w.setShape([]); + w.setShape([{ x: 0, y: 0, width: 100, height: 100 }]); + w.setShape([{ x: 0, y: 0, width: 100, height: 100 }, { x: 0, y: 200, width: 1000, height: 100 }]); + w.setShape([]); + }).to.not.throw(); + }); + }); describe('"useContentSize" option', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('make window created with content size when used', () => { const w = new BrowserWindow({ show: false, width: 400, height: 400, useContentSize: true - }) - const contentSize = w.getContentSize() - expect(contentSize).to.deep.equal([400, 400]) - }) + }); + const contentSize = w.getContentSize(); + expect(contentSize).to.deep.equal([400, 400]); + }); it('make window created with window size when not used', () => { const w = new BrowserWindow({ show: false, width: 400, height: 400 - }) - const size = w.getSize() - expect(size).to.deep.equal([400, 400]) - }) + }); + const size = w.getSize(); + expect(size).to.deep.equal([400, 400]); + }); it('works for a frameless window', () => { const w = new BrowserWindow({ show: false, @@ -1562,104 +1562,104 @@ describe('BrowserWindow module', () => { width: 400, height: 400, useContentSize: true - }) - const contentSize = w.getContentSize() - expect(contentSize).to.deep.equal([400, 400]) - const size = w.getSize() - expect(size).to.deep.equal([400, 400]) - }) - }) + }); + const contentSize = w.getContentSize(); + expect(contentSize).to.deep.equal([400, 400]); + const size = w.getSize(); + expect(size).to.deep.equal([400, 400]); + }); + }); ifdescribe(process.platform === 'darwin' && parseInt(os.release().split('.')[0]) >= 14)('"titleBarStyle" option', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('creates browser window with hidden title bar', () => { const w = new BrowserWindow({ show: false, width: 400, height: 400, titleBarStyle: 'hidden' - }) - const contentSize = w.getContentSize() - expect(contentSize).to.deep.equal([400, 400]) - }) + }); + const contentSize = w.getContentSize(); + expect(contentSize).to.deep.equal([400, 400]); + }); it('creates browser window with hidden inset title bar', () => { const w = new BrowserWindow({ show: false, width: 400, height: 400, titleBarStyle: 'hiddenInset' - }) - const contentSize = w.getContentSize() - expect(contentSize).to.deep.equal([400, 400]) - }) - }) + }); + const contentSize = w.getContentSize(); + expect(contentSize).to.deep.equal([400, 400]); + }); + }); ifdescribe(process.platform === 'darwin')('"enableLargerThanScreen" option', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can move the window out of screen', () => { - const w = new BrowserWindow({ show: true, enableLargerThanScreen: true }) - w.setPosition(-10, -10) - const after = w.getPosition() - expect(after).to.deep.equal([-10, -10]) - }) + const w = new BrowserWindow({ show: true, enableLargerThanScreen: true }); + w.setPosition(-10, -10); + const after = w.getPosition(); + expect(after).to.deep.equal([-10, -10]); + }); it('without it, cannot move the window out of screen', () => { - const w = new BrowserWindow({ show: true, enableLargerThanScreen: false }) - w.setPosition(-10, -10) - const after = w.getPosition() - expect(after[1]).to.be.at.least(0) - }) + const w = new BrowserWindow({ show: true, enableLargerThanScreen: false }); + w.setPosition(-10, -10); + const after = w.getPosition(); + expect(after[1]).to.be.at.least(0); + }); it('can set the window larger than screen', () => { - const w = new BrowserWindow({ show: true, enableLargerThanScreen: true }) - const size = screen.getPrimaryDisplay().size - size.width += 100 - size.height += 100 - w.setSize(size.width, size.height) - expectBoundsEqual(w.getSize(), [size.width, size.height]) - }) + const w = new BrowserWindow({ show: true, enableLargerThanScreen: true }); + const size = screen.getPrimaryDisplay().size; + size.width += 100; + size.height += 100; + w.setSize(size.width, size.height); + expectBoundsEqual(w.getSize(), [size.width, size.height]); + }); it('without it, cannot set the window larger than screen', () => { - const w = new BrowserWindow({ show: true, enableLargerThanScreen: false }) - const size = screen.getPrimaryDisplay().size - size.width += 100 - size.height += 100 - w.setSize(size.width, size.height) - expect(w.getSize()[1]).to.at.most(screen.getPrimaryDisplay().size.height) - }) - }) + const w = new BrowserWindow({ show: true, enableLargerThanScreen: false }); + const size = screen.getPrimaryDisplay().size; + size.width += 100; + size.height += 100; + w.setSize(size.width, size.height); + expect(w.getSize()[1]).to.at.most(screen.getPrimaryDisplay().size.height); + }); + }); ifdescribe(process.platform === 'darwin')('"zoomToPageWidth" option', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('sets the window width to the page width when used', () => { const w = new BrowserWindow({ show: false, width: 500, height: 400, zoomToPageWidth: true - }) - w.maximize() - expect(w.getSize()[0]).to.equal(500) - }) - }) + }); + w.maximize(); + expect(w.getSize()[0]).to.equal(500); + }); + }); describe('"tabbingIdentifier" option', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can be set on a window', () => { expect(() => { /* eslint-disable no-new */ new BrowserWindow({ tabbingIdentifier: 'group1' - }) + }); new BrowserWindow({ tabbingIdentifier: 'group2', frame: false - }) + }); /* eslint-enable no-new */ - }).not.to.throw() - }) - }) + }).not.to.throw(); + }); + }); describe('"webPreferences" option', () => { - afterEach(() => { ipcMain.removeAllListeners('answer') }) - afterEach(closeAllWindows) + afterEach(() => { ipcMain.removeAllListeners('answer'); }); + afterEach(closeAllWindows); describe('"preload" option', () => { const doesNotLeakSpec = (name: string, webPrefs: { nodeIntegration: boolean, sandbox: boolean, contextIsolation: boolean }) => { @@ -1670,36 +1670,36 @@ describe('BrowserWindow module', () => { preload: path.resolve(fixtures, 'module', 'empty.js') }, show: false - }) - w.loadFile(path.join(fixtures, 'api', 'no-leak.html')) - const [, result] = await emittedOnce(ipcMain, 'leak-result') - expect(result).to.have.property('require', 'undefined') - expect(result).to.have.property('exports', 'undefined') - expect(result).to.have.property('windowExports', 'undefined') - expect(result).to.have.property('windowPreload', 'undefined') - expect(result).to.have.property('windowRequire', 'undefined') - }) - } + }); + w.loadFile(path.join(fixtures, 'api', 'no-leak.html')); + const [, result] = await emittedOnce(ipcMain, 'leak-result'); + expect(result).to.have.property('require', 'undefined'); + expect(result).to.have.property('exports', 'undefined'); + expect(result).to.have.property('windowExports', 'undefined'); + expect(result).to.have.property('windowPreload', 'undefined'); + expect(result).to.have.property('windowRequire', 'undefined'); + }); + }; doesNotLeakSpec('does not leak require', { nodeIntegration: false, sandbox: false, contextIsolation: false - }) + }); doesNotLeakSpec('does not leak require when sandbox is enabled', { nodeIntegration: false, sandbox: true, contextIsolation: false - }) + }); doesNotLeakSpec('does not leak require when context isolation is enabled', { nodeIntegration: false, sandbox: false, contextIsolation: true - }) + }); doesNotLeakSpec('does not leak require when context isolation and sandbox are enabled', { nodeIntegration: false, sandbox: true, contextIsolation: true - }) + }); it('does not leak any node globals on the window object with nodeIntegration is disabled', async () => { let w = new BrowserWindow({ webPreferences: { @@ -1708,12 +1708,12 @@ describe('BrowserWindow module', () => { preload: path.resolve(fixtures, 'module', 'empty.js') }, show: false - }) - w.loadFile(path.join(fixtures, 'api', 'globals.html')) - const [, notIsolated] = await emittedOnce(ipcMain, 'leak-result') - expect(notIsolated).to.have.property('globals') + }); + w.loadFile(path.join(fixtures, 'api', 'globals.html')); + const [, notIsolated] = await emittedOnce(ipcMain, 'leak-result'); + expect(notIsolated).to.have.property('globals'); - w.destroy() + w.destroy(); w = new BrowserWindow({ webPreferences: { contextIsolation: true, @@ -1721,32 +1721,32 @@ describe('BrowserWindow module', () => { preload: path.resolve(fixtures, 'module', 'empty.js') }, show: false - }) - w.loadFile(path.join(fixtures, 'api', 'globals.html')) - const [, isolated] = await emittedOnce(ipcMain, 'leak-result') - expect(isolated).to.have.property('globals') - const notIsolatedGlobals = new Set(notIsolated.globals) + }); + w.loadFile(path.join(fixtures, 'api', 'globals.html')); + const [, isolated] = await emittedOnce(ipcMain, 'leak-result'); + expect(isolated).to.have.property('globals'); + const notIsolatedGlobals = new Set(notIsolated.globals); for (const isolatedGlobal of isolated.globals) { - notIsolatedGlobals.delete(isolatedGlobal) + notIsolatedGlobals.delete(isolatedGlobal); } - expect([...notIsolatedGlobals]).to.deep.equal([], 'non-isoalted renderer should have no additional globals') - }) + expect([...notIsolatedGlobals]).to.deep.equal([], 'non-isoalted renderer should have no additional globals'); + }); it('loads the script before other scripts in window', async () => { - const preload = path.join(fixtures, 'module', 'set-global.js') + const preload = path.join(fixtures, 'module', 'set-global.js'); const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, preload } - }) - w.loadFile(path.join(fixtures, 'api', 'preload.html')) - const [, test] = await emittedOnce(ipcMain, 'answer') - expect(test).to.eql('preload') - }) + }); + w.loadFile(path.join(fixtures, 'api', 'preload.html')); + const [, test] = await emittedOnce(ipcMain, 'answer'); + expect(test).to.eql('preload'); + }); it('can successfully delete the Buffer global', async () => { - const preload = path.join(__dirname, 'fixtures', 'module', 'delete-buffer.js') + const preload = path.join(__dirname, 'fixtures', 'module', 'delete-buffer.js'); const w = new BrowserWindow({ show: false, webPreferences: { @@ -1754,48 +1754,48 @@ describe('BrowserWindow module', () => { enableRemoteModule: true, preload } - }) - w.loadFile(path.join(fixtures, 'api', 'preload.html')) - const [, test] = await emittedOnce(ipcMain, 'answer') - expect(test).to.eql(Buffer.from('buffer')) - }) + }); + w.loadFile(path.join(fixtures, 'api', 'preload.html')); + const [, test] = await emittedOnce(ipcMain, 'answer'); + expect(test).to.eql(Buffer.from('buffer')); + }); it('has synchronous access to all eventual window APIs', async () => { - const preload = path.join(fixtures, 'module', 'access-blink-apis.js') + const preload = path.join(fixtures, 'module', 'access-blink-apis.js'); const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, preload } - }) - w.loadFile(path.join(fixtures, 'api', 'preload.html')) - const [, test] = await emittedOnce(ipcMain, 'answer') - expect(test).to.be.an('object') - expect(test.atPreload).to.be.an('array') - expect(test.atLoad).to.be.an('array') - expect(test.atPreload).to.deep.equal(test.atLoad, 'should have access to the same window APIs') - }) - }) + }); + w.loadFile(path.join(fixtures, 'api', 'preload.html')); + const [, test] = await emittedOnce(ipcMain, 'answer'); + expect(test).to.be.an('object'); + expect(test.atPreload).to.be.an('array'); + expect(test.atLoad).to.be.an('array'); + expect(test.atPreload).to.deep.equal(test.atLoad, 'should have access to the same window APIs'); + }); + }); describe('session preload scripts', function () { const preloads = [ path.join(fixtures, 'module', 'set-global-preload-1.js'), path.join(fixtures, 'module', 'set-global-preload-2.js'), path.relative(process.cwd(), path.join(fixtures, 'module', 'set-global-preload-3.js')) - ] - const defaultSession = session.defaultSession + ]; + const defaultSession = session.defaultSession; beforeEach(() => { - expect(defaultSession.getPreloads()).to.deep.equal([]) - defaultSession.setPreloads(preloads) - }) + expect(defaultSession.getPreloads()).to.deep.equal([]); + defaultSession.setPreloads(preloads); + }); afterEach(() => { - defaultSession.setPreloads([]) - }) + defaultSession.setPreloads([]); + }); it('can set multiple session preload script', () => { - expect(defaultSession.getPreloads()).to.deep.equal(preloads) - }) + expect(defaultSession.getPreloads()).to.deep.equal(preloads); + }); const generateSpecs = (description: string, sandbox: boolean) => { describe(description, () => { @@ -1806,23 +1806,23 @@ describe('BrowserWindow module', () => { sandbox, preload: path.join(fixtures, 'module', 'get-global-preload.js') } - }) - w.loadURL('about:blank') - const [, preload1, preload2, preload3] = await emittedOnce(ipcMain, 'vars') - expect(preload1).to.equal('preload-1') - expect(preload2).to.equal('preload-1-2') - expect(preload3).to.be.undefined('preload 3') - }) - }) - } - - generateSpecs('without sandbox', false) - generateSpecs('with sandbox', true) - }) + }); + w.loadURL('about:blank'); + const [, preload1, preload2, preload3] = await emittedOnce(ipcMain, 'vars'); + expect(preload1).to.equal('preload-1'); + expect(preload2).to.equal('preload-1-2'); + expect(preload3).to.be.undefined('preload 3'); + }); + }); + }; + + generateSpecs('without sandbox', false); + generateSpecs('with sandbox', true); + }); describe('"additionalArguments" option', () => { it('adds extra args to process.argv in the renderer process', async () => { - const preload = path.join(fixtures, 'module', 'check-arguments.js') + const preload = path.join(fixtures, 'module', 'check-arguments.js'); const w = new BrowserWindow({ show: false, webPreferences: { @@ -1830,14 +1830,14 @@ describe('BrowserWindow module', () => { preload, additionalArguments: ['--my-magic-arg'] } - }) - w.loadFile(path.join(fixtures, 'api', 'blank.html')) - const [, argv] = await emittedOnce(ipcMain, 'answer') - expect(argv).to.include('--my-magic-arg') - }) + }); + w.loadFile(path.join(fixtures, 'api', 'blank.html')); + const [, argv] = await emittedOnce(ipcMain, 'answer'); + expect(argv).to.include('--my-magic-arg'); + }); it('adds extra value args to process.argv in the renderer process', async () => { - const preload = path.join(fixtures, 'module', 'check-arguments.js') + const preload = path.join(fixtures, 'module', 'check-arguments.js'); const w = new BrowserWindow({ show: false, webPreferences: { @@ -1845,33 +1845,33 @@ describe('BrowserWindow module', () => { preload, additionalArguments: ['--my-magic-arg=foo'] } - }) - w.loadFile(path.join(fixtures, 'api', 'blank.html')) - const [, argv] = await emittedOnce(ipcMain, 'answer') - expect(argv).to.include('--my-magic-arg=foo') - }) - }) + }); + w.loadFile(path.join(fixtures, 'api', 'blank.html')); + const [, argv] = await emittedOnce(ipcMain, 'answer'); + expect(argv).to.include('--my-magic-arg=foo'); + }); + }); describe('"node-integration" option', () => { it('disables node integration by default', async () => { - const preload = path.join(fixtures, 'module', 'send-later.js') + const preload = path.join(fixtures, 'module', 'send-later.js'); const w = new BrowserWindow({ show: false, webPreferences: { preload } - }) - w.loadFile(path.join(fixtures, 'api', 'blank.html')) - const [, typeofProcess, typeofBuffer] = await emittedOnce(ipcMain, 'answer') - expect(typeofProcess).to.equal('undefined') - expect(typeofBuffer).to.equal('undefined') - }) - }) + }); + w.loadFile(path.join(fixtures, 'api', 'blank.html')); + const [, typeofProcess, typeofBuffer] = await emittedOnce(ipcMain, 'answer'); + expect(typeofProcess).to.equal('undefined'); + expect(typeofBuffer).to.equal('undefined'); + }); + }); describe('"enableRemoteModule" option', () => { const generateSpecs = (description: string, sandbox: boolean) => { describe(description, () => { - const preload = path.join(__dirname, 'fixtures', 'module', 'preload-remote.js') + const preload = path.join(__dirname, 'fixtures', 'module', 'preload-remote.js'); it('disables the remote module by default', async () => { const w = new BrowserWindow({ @@ -1880,12 +1880,12 @@ describe('BrowserWindow module', () => { preload, sandbox } - }) - const p = emittedOnce(ipcMain, 'remote') - w.loadFile(path.join(fixtures, 'api', 'blank.html')) - const [, remote] = await p - expect(remote).to.equal('undefined') - }) + }); + const p = emittedOnce(ipcMain, 'remote'); + w.loadFile(path.join(fixtures, 'api', 'blank.html')); + const [, remote] = await p; + expect(remote).to.equal('undefined'); + }); it('disables the remote module when false', async () => { const w = new BrowserWindow({ @@ -1895,12 +1895,12 @@ describe('BrowserWindow module', () => { sandbox, enableRemoteModule: false } - }) - const p = emittedOnce(ipcMain, 'remote') - w.loadFile(path.join(fixtures, 'api', 'blank.html')) - const [, remote] = await p - expect(remote).to.equal('undefined') - }) + }); + const p = emittedOnce(ipcMain, 'remote'); + w.loadFile(path.join(fixtures, 'api', 'blank.html')); + const [, remote] = await p; + expect(remote).to.equal('undefined'); + }); it('enables the remote module when true', async () => { const w = new BrowserWindow({ @@ -1910,52 +1910,52 @@ describe('BrowserWindow module', () => { sandbox, enableRemoteModule: true } - }) - const p = emittedOnce(ipcMain, 'remote') - w.loadFile(path.join(fixtures, 'api', 'blank.html')) - const [, remote] = await p - expect(remote).to.equal('object') - }) - }) - } - - generateSpecs('without sandbox', false) - generateSpecs('with sandbox', true) - }) + }); + const p = emittedOnce(ipcMain, 'remote'); + w.loadFile(path.join(fixtures, 'api', 'blank.html')); + const [, remote] = await p; + expect(remote).to.equal('object'); + }); + }); + }; + + generateSpecs('without sandbox', false); + generateSpecs('with sandbox', true); + }); describe('"sandbox" option', () => { function waitForEvents (emitter: { once: Function }, events: string[], callback: () => void) { - let count = events.length + let count = events.length; for (const event of events) { emitter.once(event, () => { - if (!--count) callback() - }) + if (!--count) callback(); + }); } } - const preload = path.join(fixtures, 'module', 'preload-sandbox.js') + const preload = path.join(fixtures, 'module', 'preload-sandbox.js'); - let server: http.Server = null as unknown as http.Server - let serverUrl: string = null as unknown as string + let server: http.Server = null as unknown as http.Server; + let serverUrl: string = null as unknown as string; before((done) => { server = http.createServer((request, response) => { switch (request.url) { case '/cross-site': - response.end(`

${request.url}

`) - break + response.end(`

${request.url}

`); + break; default: - throw new Error(`unsupported endpoint: ${request.url}`) + throw new Error(`unsupported endpoint: ${request.url}`); } }).listen(0, '127.0.0.1', () => { - serverUrl = 'http://127.0.0.1:' + (server.address() as AddressInfo).port - done() - }) - }) + serverUrl = 'http://127.0.0.1:' + (server.address() as AddressInfo).port; + done(); + }); + }); after(() => { - server.close() - }) + server.close(); + }); it('exposes ipcRenderer to preload script', async () => { const w = new BrowserWindow({ @@ -1964,25 +1964,25 @@ describe('BrowserWindow module', () => { sandbox: true, preload } - }) - w.loadFile(path.join(fixtures, 'api', 'preload.html')) - const [, test] = await emittedOnce(ipcMain, 'answer') - expect(test).to.equal('preload') - }) + }); + w.loadFile(path.join(fixtures, 'api', 'preload.html')); + const [, test] = await emittedOnce(ipcMain, 'answer'); + expect(test).to.equal('preload'); + }); it('exposes ipcRenderer to preload script (path has special chars)', async () => { - const preloadSpecialChars = path.join(fixtures, 'module', 'preload-sandboxæø åü.js') + const preloadSpecialChars = path.join(fixtures, 'module', 'preload-sandboxæø åü.js'); const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true, preload: preloadSpecialChars } - }) - w.loadFile(path.join(fixtures, 'api', 'preload.html')) - const [, test] = await emittedOnce(ipcMain, 'answer') - expect(test).to.equal('preload') - }) + }); + w.loadFile(path.join(fixtures, 'api', 'preload.html')); + const [, test] = await emittedOnce(ipcMain, 'answer'); + expect(test).to.equal('preload'); + }); it('exposes "loaded" event to preload script', async () => { const w = new BrowserWindow({ @@ -1991,10 +1991,10 @@ describe('BrowserWindow module', () => { sandbox: true, preload } - }) - w.loadURL('about:blank') - await emittedOnce(ipcMain, 'process-loaded') - }) + }); + w.loadURL('about:blank'); + await emittedOnce(ipcMain, 'process-loaded'); + }); it('exposes "exit" event to preload script', async () => { const w = new BrowserWindow({ @@ -2003,16 +2003,16 @@ describe('BrowserWindow module', () => { sandbox: true, preload } - }) - const htmlPath = path.join(__dirname, 'fixtures', 'api', 'sandbox.html?exit-event') - const pageUrl = 'file://' + htmlPath - w.loadURL(pageUrl) - const [, url] = await emittedOnce(ipcMain, 'answer') + }); + const htmlPath = path.join(__dirname, 'fixtures', 'api', 'sandbox.html?exit-event'); + const pageUrl = 'file://' + htmlPath; + w.loadURL(pageUrl); + const [, url] = await emittedOnce(ipcMain, 'answer'); const expectedUrl = process.platform === 'win32' ? 'file:///' + htmlPath.replace(/\\/g, '/') - : pageUrl - expect(url).to.equal(expectedUrl) - }) + : pageUrl; + expect(url).to.equal(expectedUrl); + }); it('should open windows in same domain with cross-scripting enabled', async () => { const w = new BrowserWindow({ @@ -2021,25 +2021,25 @@ describe('BrowserWindow module', () => { sandbox: true, preload } - }) + }); w.webContents.once('new-window', (event, url, frameName, disposition, options) => { - options.webPreferences!.preload = preload - }) - const htmlPath = path.join(__dirname, 'fixtures', 'api', 'sandbox.html?window-open') - const pageUrl = 'file://' + htmlPath - const answer = emittedOnce(ipcMain, 'answer') - w.loadURL(pageUrl) - const [, url, frameName, , options] = await emittedOnce(w.webContents, 'new-window') + options.webPreferences!.preload = preload; + }); + const htmlPath = path.join(__dirname, 'fixtures', 'api', 'sandbox.html?window-open'); + const pageUrl = 'file://' + htmlPath; + const answer = emittedOnce(ipcMain, 'answer'); + w.loadURL(pageUrl); + const [, url, frameName, , options] = await emittedOnce(w.webContents, 'new-window'); const expectedUrl = process.platform === 'win32' ? 'file:///' + htmlPath.replace(/\\/g, '/') - : pageUrl - expect(url).to.equal(expectedUrl) - expect(frameName).to.equal('popup!') - expect(options.width).to.equal(500) - expect(options.height).to.equal(600) - const [, html] = await answer - expect(html).to.equal('

scripting from opener

') - }) + : pageUrl; + expect(url).to.equal(expectedUrl); + expect(frameName).to.equal('popup!'); + expect(options.width).to.equal(500); + expect(options.height).to.equal(600); + const [, html] = await answer; + expect(html).to.equal('

scripting from opener

'); + }); it('should open windows in another domain with cross-scripting disabled', async () => { const w = new BrowserWindow({ @@ -2048,56 +2048,56 @@ describe('BrowserWindow module', () => { sandbox: true, preload } - }) + }); w.webContents.once('new-window', (event, url, frameName, disposition, options) => { - options.webPreferences!.preload = preload - }) + options.webPreferences!.preload = preload; + }); w.loadFile( path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'window-open-external' } - ) + ); // Wait for a message from the main window saying that it's ready. - await emittedOnce(ipcMain, 'opener-loaded') + await emittedOnce(ipcMain, 'opener-loaded'); // Ask the opener to open a popup with window.opener. - const expectedPopupUrl = `${serverUrl}/cross-site` // Set in "sandbox.html". + const expectedPopupUrl = `${serverUrl}/cross-site`; // Set in "sandbox.html". - w.webContents.send('open-the-popup', expectedPopupUrl) + w.webContents.send('open-the-popup', expectedPopupUrl); // The page is going to open a popup that it won't be able to close. // We have to close it from here later. - const [, popupWindow] = await emittedOnce(app, 'browser-window-created') + const [, popupWindow] = await emittedOnce(app, 'browser-window-created'); // Ask the popup window for details. - const detailsAnswer = emittedOnce(ipcMain, 'child-loaded') - popupWindow.webContents.send('provide-details') - const [, openerIsNull, , locationHref] = await detailsAnswer - expect(openerIsNull).to.be.false('window.opener is null') - expect(locationHref).to.equal(expectedPopupUrl) + const detailsAnswer = emittedOnce(ipcMain, 'child-loaded'); + popupWindow.webContents.send('provide-details'); + const [, openerIsNull, , locationHref] = await detailsAnswer; + expect(openerIsNull).to.be.false('window.opener is null'); + expect(locationHref).to.equal(expectedPopupUrl); // Ask the page to access the popup. - const touchPopupResult = emittedOnce(ipcMain, 'answer') - w.webContents.send('touch-the-popup') - const [, popupAccessMessage] = await touchPopupResult + const touchPopupResult = emittedOnce(ipcMain, 'answer'); + w.webContents.send('touch-the-popup'); + const [, popupAccessMessage] = await touchPopupResult; // Ask the popup to access the opener. - const touchOpenerResult = emittedOnce(ipcMain, 'answer') - popupWindow.webContents.send('touch-the-opener') - const [, openerAccessMessage] = await touchOpenerResult + const touchOpenerResult = emittedOnce(ipcMain, 'answer'); + popupWindow.webContents.send('touch-the-opener'); + const [, openerAccessMessage] = await touchOpenerResult; // We don't need the popup anymore, and its parent page can't close it, // so let's close it from here before we run any checks. - await closeWindow(popupWindow, { assertNotWindows: false }) + await closeWindow(popupWindow, { assertNotWindows: false }); expect(popupAccessMessage).to.be.a('string', - 'child\'s .document is accessible from its parent window') - expect(popupAccessMessage).to.match(/^Blocked a frame with origin/) + 'child\'s .document is accessible from its parent window'); + expect(popupAccessMessage).to.match(/^Blocked a frame with origin/); expect(openerAccessMessage).to.be.a('string', - 'opener .document is accessible from a popup window') - expect(openerAccessMessage).to.match(/^Blocked a frame with origin/) - }) + 'opener .document is accessible from a popup window'); + expect(openerAccessMessage).to.match(/^Blocked a frame with origin/); + }); it('should inherit the sandbox setting in opened windows', async () => { const w = new BrowserWindow({ @@ -2105,16 +2105,16 @@ describe('BrowserWindow module', () => { webPreferences: { sandbox: true } - }) + }); - const preloadPath = path.join(fixtures, 'api', 'new-window-preload.js') + const preloadPath = path.join(fixtures, 'api', 'new-window-preload.js'); w.webContents.once('new-window', (event, url, frameName, disposition, options) => { - options.webPreferences!.preload = preloadPath - }) - w.loadFile(path.join(fixtures, 'api', 'new-window.html')) - const [, args] = await emittedOnce(ipcMain, 'answer') - expect(args).to.include('--enable-sandbox') - }) + options.webPreferences!.preload = preloadPath; + }); + w.loadFile(path.join(fixtures, 'api', 'new-window.html')); + const [, args] = await emittedOnce(ipcMain, 'answer'); + expect(args).to.include('--enable-sandbox'); + }); it('should open windows with the options configured via new-window event listeners', async () => { const w = new BrowserWindow({ @@ -2122,22 +2122,22 @@ describe('BrowserWindow module', () => { webPreferences: { sandbox: true } - }) + }); - const preloadPath = path.join(fixtures, 'api', 'new-window-preload.js') + const preloadPath = path.join(fixtures, 'api', 'new-window-preload.js'); w.webContents.once('new-window', (event, url, frameName, disposition, options) => { - options.webPreferences!.preload = preloadPath - const prefs = options.webPreferences as any - prefs.foo = 'bar' - }) - w.loadFile(path.join(fixtures, 'api', 'new-window.html')) + options.webPreferences!.preload = preloadPath; + const prefs = options.webPreferences as any; + prefs.foo = 'bar'; + }); + w.loadFile(path.join(fixtures, 'api', 'new-window.html')); const [[, childWebContents]] = await Promise.all([ emittedOnce(app, 'web-contents-created'), emittedOnce(ipcMain, 'answer') - ]) - const webPreferences = (childWebContents as any).getLastWebPreferences() - expect(webPreferences.foo).to.equal('bar') - }) + ]); + const webPreferences = (childWebContents as any).getLastWebPreferences(); + expect(webPreferences.foo).to.equal('bar'); + }); it('should set ipc event sender correctly', (done) => { const w = new BrowserWindow({ @@ -2146,49 +2146,49 @@ describe('BrowserWindow module', () => { sandbox: true, preload } - }) - let childWc: WebContents | null = null + }); + let childWc: WebContents | null = null; w.webContents.on('new-window', (event, url, frameName, disposition, options) => { - options.webPreferences!.preload = preload - childWc = (options as any).webContents - expect(w.webContents).to.not.equal(childWc) - }) + options.webPreferences!.preload = preload; + childWc = (options as any).webContents; + expect(w.webContents).to.not.equal(childWc); + }); ipcMain.once('parent-ready', function (event) { - expect(event.sender).to.equal(w.webContents, 'sender should be the parent') - event.sender.send('verified') - }) + expect(event.sender).to.equal(w.webContents, 'sender should be the parent'); + event.sender.send('verified'); + }); ipcMain.once('child-ready', function (event) { - expect(childWc).to.not.be.null('child webcontents should be available') - expect(event.sender).to.equal(childWc, 'sender should be the child') - event.sender.send('verified') - }) + expect(childWc).to.not.be.null('child webcontents should be available'); + expect(event.sender).to.equal(childWc, 'sender should be the child'); + event.sender.send('verified'); + }); waitForEvents(ipcMain, [ 'parent-answer', 'child-answer' - ], done) - w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'verify-ipc-sender' }) - }) + ], done); + w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'verify-ipc-sender' }); + }); describe('event handling', () => { - let w: BrowserWindow = null as unknown as BrowserWindow + let w: BrowserWindow = null as unknown as BrowserWindow; beforeEach(() => { - w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }) - }) + w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); + }); it('works for window events', (done) => { waitForEvents(w, [ 'page-title-updated' - ], done) - w.loadURL('data:text/html,') - }) + ], done); + w.loadURL('data:text/html,'); + }); it('works for stop events', (done) => { waitForEvents(w.webContents, [ 'did-navigate', 'did-fail-load', 'did-stop-loading' - ], done) - w.loadURL('data:text/html,') - }) + ], done); + w.loadURL('data:text/html,'); + }); it('works for web contents events', (done) => { waitForEvents(w.webContents, [ @@ -2200,10 +2200,10 @@ describe('BrowserWindow module', () => { 'did-stop-loading', 'did-frame-finish-load', 'dom-ready' - ], done) - w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'webcontents-events' }) - }) - }) + ], done); + w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'webcontents-events' }); + }); + }); it('supports calling preventDefault on new-window events', (done) => { const w = new BrowserWindow({ @@ -2211,19 +2211,19 @@ describe('BrowserWindow module', () => { webPreferences: { sandbox: true } - }) - const initialWebContents = webContents.getAllWebContents().map((i) => i.id) + }); + const initialWebContents = webContents.getAllWebContents().map((i) => i.id); w.webContents.once('new-window', (e) => { - e.preventDefault() + e.preventDefault(); // We need to give it some time so the windows get properly disposed (at least on OSX). setTimeout(() => { - const currentWebContents = webContents.getAllWebContents().map((i) => i.id) - expect(currentWebContents).to.deep.equal(initialWebContents) - done() - }, 100) - }) - w.loadFile(path.join(fixtures, 'pages', 'window-open.html')) - }) + const currentWebContents = webContents.getAllWebContents().map((i) => i.id); + expect(currentWebContents).to.deep.equal(initialWebContents); + done(); + }, 100); + }); + w.loadFile(path.join(fixtures, 'pages', 'window-open.html')); + }); // see #9387 it('properly manages remote object references after page reload', (done) => { @@ -2234,30 +2234,30 @@ describe('BrowserWindow module', () => { sandbox: true, enableRemoteModule: true } - }) - w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote' }) + }); + w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote' }); ipcMain.on('get-remote-module-path', (event) => { - event.returnValue = path.join(fixtures, 'module', 'hello.js') - }) + event.returnValue = path.join(fixtures, 'module', 'hello.js'); + }); - let reload = false + let reload = false; ipcMain.on('reloaded', (event) => { - event.returnValue = reload - reload = !reload - }) + event.returnValue = reload; + reload = !reload; + }); ipcMain.once('reload', (event) => { - event.sender.reload() - }) + event.sender.reload(); + }); ipcMain.once('answer', (event, arg) => { - ipcMain.removeAllListeners('reloaded') - ipcMain.removeAllListeners('get-remote-module-path') - expect(arg).to.equal('hi') - done() - }) - }) + ipcMain.removeAllListeners('reloaded'); + ipcMain.removeAllListeners('get-remote-module-path'); + expect(arg).to.equal('hi'); + done(); + }); + }); it('properly manages remote object references after page reload in child window', (done) => { const w = new BrowserWindow({ @@ -2267,34 +2267,34 @@ describe('BrowserWindow module', () => { sandbox: true, enableRemoteModule: true } - }) + }); w.webContents.once('new-window', (event, url, frameName, disposition, options) => { - options.webPreferences!.preload = preload - }) + options.webPreferences!.preload = preload; + }); - w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote-child' }) + w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote-child' }); ipcMain.on('get-remote-module-path', (event) => { - event.returnValue = path.join(fixtures, 'module', 'hello-child.js') - }) + event.returnValue = path.join(fixtures, 'module', 'hello-child.js'); + }); - let reload = false + let reload = false; ipcMain.on('reloaded', (event) => { - event.returnValue = reload - reload = !reload - }) + event.returnValue = reload; + reload = !reload; + }); ipcMain.once('reload', (event) => { - event.sender.reload() - }) + event.sender.reload(); + }); ipcMain.once('answer', (event, arg) => { - ipcMain.removeAllListeners('reloaded') - ipcMain.removeAllListeners('get-remote-module-path') - expect(arg).to.equal('hi child window') - done() - }) - }) + ipcMain.removeAllListeners('reloaded'); + ipcMain.removeAllListeners('get-remote-module-path'); + expect(arg).to.equal('hi child window'); + done(); + }); + }); it('validates process APIs access in sandboxed renderer', async () => { const w = new BrowserWindow({ @@ -2303,38 +2303,38 @@ describe('BrowserWindow module', () => { sandbox: true, preload } - }) + }); w.webContents.once('preload-error', (event, preloadPath, error) => { - throw error - }) - process.env.sandboxmain = 'foo' - w.loadFile(path.join(fixtures, 'api', 'preload.html')) - const [, test] = await emittedOnce(ipcMain, 'answer') - expect(test.hasCrash).to.be.true('has crash') - expect(test.hasHang).to.be.true('has hang') - expect(test.heapStatistics).to.be.an('object') - expect(test.blinkMemoryInfo).to.be.an('object') - expect(test.processMemoryInfo).to.be.an('object') - expect(test.systemVersion).to.be.a('string') - expect(test.cpuUsage).to.be.an('object') - expect(test.ioCounters).to.be.an('object') - expect(test.arch).to.equal(process.arch) - expect(test.platform).to.equal(process.platform) - expect(test.env).to.deep.equal(process.env) - expect(test.execPath).to.equal(process.helperExecPath) - expect(test.sandboxed).to.be.true('sandboxed') - expect(test.type).to.equal('renderer') - expect(test.version).to.equal(process.version) - expect(test.versions).to.deep.equal(process.versions) + throw error; + }); + process.env.sandboxmain = 'foo'; + w.loadFile(path.join(fixtures, 'api', 'preload.html')); + const [, test] = await emittedOnce(ipcMain, 'answer'); + expect(test.hasCrash).to.be.true('has crash'); + expect(test.hasHang).to.be.true('has hang'); + expect(test.heapStatistics).to.be.an('object'); + expect(test.blinkMemoryInfo).to.be.an('object'); + expect(test.processMemoryInfo).to.be.an('object'); + expect(test.systemVersion).to.be.a('string'); + expect(test.cpuUsage).to.be.an('object'); + expect(test.ioCounters).to.be.an('object'); + expect(test.arch).to.equal(process.arch); + expect(test.platform).to.equal(process.platform); + expect(test.env).to.deep.equal(process.env); + expect(test.execPath).to.equal(process.helperExecPath); + expect(test.sandboxed).to.be.true('sandboxed'); + expect(test.type).to.equal('renderer'); + expect(test.version).to.equal(process.version); + expect(test.versions).to.deep.equal(process.versions); if (process.platform === 'linux' && test.osSandbox) { - expect(test.creationTime).to.be.null('creation time') - expect(test.systemMemoryInfo).to.be.null('system memory info') + expect(test.creationTime).to.be.null('creation time'); + expect(test.systemMemoryInfo).to.be.null('system memory info'); } else { - expect(test.creationTime).to.be.a('number') - expect(test.systemMemoryInfo).to.be.an('object') + expect(test.creationTime).to.be.a('number'); + expect(test.systemMemoryInfo).to.be.an('object'); } - }) + }); it('webview in sandbox renderer', async () => { const w = new BrowserWindow({ @@ -2344,19 +2344,19 @@ describe('BrowserWindow module', () => { preload, webviewTag: true } - }) - const didAttachWebview = emittedOnce(w.webContents, 'did-attach-webview') - const webviewDomReady = emittedOnce(ipcMain, 'webview-dom-ready') - w.loadFile(path.join(fixtures, 'pages', 'webview-did-attach-event.html')) + }); + const didAttachWebview = emittedOnce(w.webContents, 'did-attach-webview'); + const webviewDomReady = emittedOnce(ipcMain, 'webview-dom-ready'); + w.loadFile(path.join(fixtures, 'pages', 'webview-did-attach-event.html')); - const [, webContents] = await didAttachWebview - const [, id] = await webviewDomReady - expect(webContents.id).to.equal(id) - }) - }) + const [, webContents] = await didAttachWebview; + const [, id] = await webviewDomReady; + expect(webContents.id).to.equal(id); + }); + }); describe('nativeWindowOpen option', () => { - let w: BrowserWindow = null as unknown as BrowserWindow + let w: BrowserWindow = null as unknown as BrowserWindow; beforeEach(() => { w = new BrowserWindow({ @@ -2367,51 +2367,51 @@ describe('BrowserWindow module', () => { // tests relies on preloads in opened windows nodeIntegrationInSubFrames: true } - }) - }) + }); + }); it('opens window of about:blank with cross-scripting enabled', (done) => { ipcMain.once('answer', (event, content) => { - expect(content).to.equal('Hello') - done() - }) - w.loadFile(path.join(fixtures, 'api', 'native-window-open-blank.html')) - }) + expect(content).to.equal('Hello'); + done(); + }); + w.loadFile(path.join(fixtures, 'api', 'native-window-open-blank.html')); + }); it('opens window of same domain with cross-scripting enabled', (done) => { ipcMain.once('answer', (event, content) => { - expect(content).to.equal('Hello') - done() - }) - w.loadFile(path.join(fixtures, 'api', 'native-window-open-file.html')) - }) + expect(content).to.equal('Hello'); + done(); + }); + w.loadFile(path.join(fixtures, 'api', 'native-window-open-file.html')); + }); it('blocks accessing cross-origin frames', (done) => { ipcMain.once('answer', (event, content) => { - expect(content).to.equal('Blocked a frame with origin "file://" from accessing a cross-origin frame.') - done() - }) - w.loadFile(path.join(fixtures, 'api', 'native-window-open-cross-origin.html')) - }) + expect(content).to.equal('Blocked a frame with origin "file://" from accessing a cross-origin frame.'); + done(); + }); + w.loadFile(path.join(fixtures, 'api', 'native-window-open-cross-origin.html')); + }); it('opens window from ') - resp = res + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.write(''); + resp = res; // don't end the response yet - }) - await new Promise(resolve => s.listen(0, '127.0.0.1', resolve)) - const { port } = s.address() as AddressInfo + }); + await new Promise(resolve => s.listen(0, '127.0.0.1', resolve)); + const { port } = s.address() as AddressInfo; const p = new Promise(resolve => { w.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL, isMainFrame) => { if (!isMainFrame) { - resolve() + resolve(); } - }) - }) - const main = w.loadURL(`http://127.0.0.1:${port}`) - await p - resp.end() - await main - s.close() - }) + }); + }); + const main = w.loadURL(`http://127.0.0.1:${port}`); + await p; + resp.end(); + await main; + s.close(); + }); it("doesn't resolve when a subframe loads", async () => { - let resp = null as unknown as http.ServerResponse + let resp = null as unknown as http.ServerResponse; const s = http.createServer((req, res) => { - res.writeHead(200, { 'Content-Type': 'text/html' }) - res.write('') - resp = res + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.write(''); + resp = res; // don't end the response yet - }) - await new Promise(resolve => s.listen(0, '127.0.0.1', resolve)) - const { port } = s.address() as AddressInfo + }); + await new Promise(resolve => s.listen(0, '127.0.0.1', resolve)); + const { port } = s.address() as AddressInfo; const p = new Promise(resolve => { w.webContents.on('did-frame-finish-load', (event, isMainFrame) => { if (!isMainFrame) { - resolve() + resolve(); } - }) - }) - const main = w.loadURL(`http://127.0.0.1:${port}`) - await p - resp.destroy() // cause the main request to fail + }); + }); + const main = w.loadURL(`http://127.0.0.1:${port}`); + await p; + resp.destroy(); // cause the main request to fail await expect(main).to.eventually.be.rejected() - .and.have.property('errno', -355) // ERR_INCOMPLETE_CHUNKED_ENCODING - s.close() - }) - }) + .and.have.property('errno', -355); // ERR_INCOMPLETE_CHUNKED_ENCODING + s.close(); + }); + }); describe('getFocusedWebContents() API', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); - const testFn = (process.platform === 'win32' && process.arch === 'arm64' ? it.skip : it) + const testFn = (process.platform === 'win32' && process.arch === 'arm64' ? it.skip : it); testFn('returns the focused web contents', async () => { - const w = new BrowserWindow({ show: true }) - await w.loadURL('about:blank') - expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.id) - - const devToolsOpened = emittedOnce(w.webContents, 'devtools-opened') - w.webContents.openDevTools() - await devToolsOpened - expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.devToolsWebContents.id) - const devToolsClosed = emittedOnce(w.webContents, 'devtools-closed') - w.webContents.closeDevTools() - await devToolsClosed - expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.id) - }) + const w = new BrowserWindow({ show: true }); + await w.loadURL('about:blank'); + expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.id); + + const devToolsOpened = emittedOnce(w.webContents, 'devtools-opened'); + w.webContents.openDevTools(); + await devToolsOpened; + expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.devToolsWebContents.id); + const devToolsClosed = emittedOnce(w.webContents, 'devtools-closed'); + w.webContents.closeDevTools(); + await devToolsClosed; + expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.id); + }); it('does not crash when called on a detached dev tools window', async () => { - const w = new BrowserWindow({ show: true }) + const w = new BrowserWindow({ show: true }); - w.webContents.openDevTools({ mode: 'detach' }) - w.webContents.inspectElement(100, 100) + w.webContents.openDevTools({ mode: 'detach' }); + w.webContents.inspectElement(100, 100); // For some reason we have to wait for two focused events...? - await emittedOnce(w.webContents, 'devtools-focused') + await emittedOnce(w.webContents, 'devtools-focused'); - expect(() => { webContents.getFocusedWebContents() }).to.not.throw() + expect(() => { webContents.getFocusedWebContents(); }).to.not.throw(); // Work around https://github.com/electron/electron/issues/19985 - await new Promise(resolve => setTimeout(resolve, 0)) + await new Promise(resolve => setTimeout(resolve, 0)); - const devToolsClosed = emittedOnce(w.webContents, 'devtools-closed') - w.webContents.closeDevTools() - await devToolsClosed - expect(() => { webContents.getFocusedWebContents() }).to.not.throw() - }) - }) + const devToolsClosed = emittedOnce(w.webContents, 'devtools-closed'); + w.webContents.closeDevTools(); + await devToolsClosed; + expect(() => { webContents.getFocusedWebContents(); }).to.not.throw(); + }); + }); describe('setDevToolsWebContents() API', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('sets arbitrary webContents as devtools', async () => { - const w = new BrowserWindow({ show: false }) - const devtools = new BrowserWindow({ show: false }) - const promise = emittedOnce(devtools.webContents, 'dom-ready') - w.webContents.setDevToolsWebContents(devtools.webContents) - w.webContents.openDevTools() - await promise - expect(devtools.webContents.getURL().startsWith('devtools://devtools')).to.be.true() - const result = await devtools.webContents.executeJavaScript('InspectorFrontendHost.constructor.name') - expect(result).to.equal('InspectorFrontendHostImpl') - devtools.destroy() - }) - }) + const w = new BrowserWindow({ show: false }); + const devtools = new BrowserWindow({ show: false }); + const promise = emittedOnce(devtools.webContents, 'dom-ready'); + w.webContents.setDevToolsWebContents(devtools.webContents); + w.webContents.openDevTools(); + await promise; + expect(devtools.webContents.getURL().startsWith('devtools://devtools')).to.be.true(); + const result = await devtools.webContents.executeJavaScript('InspectorFrontendHost.constructor.name'); + expect(result).to.equal('InspectorFrontendHostImpl'); + devtools.destroy(); + }); + }); describe('isFocused() API', () => { it('returns false when the window is hidden', async () => { - const w = new BrowserWindow({ show: false }) - await w.loadURL('about:blank') - expect(w.isVisible()).to.be.false() - expect(w.webContents.isFocused()).to.be.false() - }) - }) + const w = new BrowserWindow({ show: false }); + await w.loadURL('about:blank'); + expect(w.isVisible()).to.be.false(); + expect(w.webContents.isFocused()).to.be.false(); + }); + }); describe('isCurrentlyAudible() API', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('returns whether audio is playing', async () => { - const w = new BrowserWindow({ show: false }) - await w.loadURL('about:blank') + const w = new BrowserWindow({ show: false }); + await w.loadURL('about:blank'); await w.webContents.executeJavaScript(` window.context = new AudioContext // Start in suspended state, because of the @@ -447,100 +447,100 @@ describe('webContents module', () => { window.oscillator = context.createOscillator() oscillator.connect(context.destination) oscillator.start() - `) - let p = emittedOnce(w.webContents, '-audio-state-changed') - w.webContents.executeJavaScript('context.resume()') - await p - expect(w.webContents.isCurrentlyAudible()).to.be.true() - p = emittedOnce(w.webContents, '-audio-state-changed') - w.webContents.executeJavaScript('oscillator.stop()') - await p - expect(w.webContents.isCurrentlyAudible()).to.be.false() - }) - }) + `); + let p = emittedOnce(w.webContents, '-audio-state-changed'); + w.webContents.executeJavaScript('context.resume()'); + await p; + expect(w.webContents.isCurrentlyAudible()).to.be.true(); + p = emittedOnce(w.webContents, '-audio-state-changed'); + w.webContents.executeJavaScript('oscillator.stop()'); + await p; + expect(w.webContents.isCurrentlyAudible()).to.be.false(); + }); + }); describe('getWebPreferences() API', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('should not crash when called for devTools webContents', (done) => { - const w = new BrowserWindow({ show: false }) - w.webContents.openDevTools() + const w = new BrowserWindow({ show: false }); + w.webContents.openDevTools(); w.webContents.once('devtools-opened', () => { - expect(w.webContents.devToolsWebContents.getWebPreferences()).to.be.null() - done() - }) - }) - }) + expect(w.webContents.devToolsWebContents.getWebPreferences()).to.be.null(); + done(); + }); + }); + }); describe('openDevTools() API', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can show window with activation', async () => { - const w = new BrowserWindow({ show: false }) - const focused = emittedOnce(w, 'focus') - w.show() - await focused - expect(w.isFocused()).to.be.true() - w.webContents.openDevTools({ mode: 'detach', activate: true }) + const w = new BrowserWindow({ show: false }); + const focused = emittedOnce(w, 'focus'); + w.show(); + await focused; + expect(w.isFocused()).to.be.true(); + w.webContents.openDevTools({ mode: 'detach', activate: true }); await Promise.all([ emittedOnce(w.webContents, 'devtools-opened'), emittedOnce(w.webContents, 'devtools-focused') - ]) - await new Promise(resolve => setTimeout(resolve, 0)) - expect(w.isFocused()).to.be.false() - }) + ]); + await new Promise(resolve => setTimeout(resolve, 0)); + expect(w.isFocused()).to.be.false(); + }); it('can show window without activation', async () => { - const w = new BrowserWindow({ show: false }) - const devtoolsOpened = emittedOnce(w.webContents, 'devtools-opened') - w.webContents.openDevTools({ mode: 'detach', activate: false }) - await devtoolsOpened - expect(w.webContents.isDevToolsOpened()).to.be.true() - }) - }) + const w = new BrowserWindow({ show: false }); + const devtoolsOpened = emittedOnce(w.webContents, 'devtools-opened'); + w.webContents.openDevTools({ mode: 'detach', activate: false }); + await devtoolsOpened; + expect(w.webContents.isDevToolsOpened()).to.be.true(); + }); + }); describe('before-input-event event', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can prevent document keyboard events', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - await w.loadFile(path.join(fixturesPath, 'pages', 'key-events.html')) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + await w.loadFile(path.join(fixturesPath, 'pages', 'key-events.html')); const keyDown = new Promise(resolve => { - ipcMain.once('keydown', (event, key) => resolve(key)) - }) + ipcMain.once('keydown', (event, key) => resolve(key)); + }); w.webContents.once('before-input-event', (event, input) => { - if (input.key === 'a') event.preventDefault() - }) - w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'a' }) - w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'b' }) - expect(await keyDown).to.equal('b') - }) + if (input.key === 'a') event.preventDefault(); + }); + w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'a' }); + w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'b' }); + expect(await keyDown).to.equal('b'); + }); it('has the correct properties', async () => { - const w = new BrowserWindow({ show: false }) - await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')) + const w = new BrowserWindow({ show: false }); + await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')); const testBeforeInput = async (opts: any) => { - const modifiers = [] - if (opts.shift) modifiers.push('shift') - if (opts.control) modifiers.push('control') - if (opts.alt) modifiers.push('alt') - if (opts.meta) modifiers.push('meta') - if (opts.isAutoRepeat) modifiers.push('isAutoRepeat') - - const p = emittedOnce(w.webContents, 'before-input-event') + const modifiers = []; + if (opts.shift) modifiers.push('shift'); + if (opts.control) modifiers.push('control'); + if (opts.alt) modifiers.push('alt'); + if (opts.meta) modifiers.push('meta'); + if (opts.isAutoRepeat) modifiers.push('isAutoRepeat'); + + const p = emittedOnce(w.webContents, 'before-input-event'); w.webContents.sendInputEvent({ type: opts.type, keyCode: opts.keyCode, modifiers: modifiers as any - }) - const [, input] = await p - - expect(input.type).to.equal(opts.type) - expect(input.key).to.equal(opts.key) - expect(input.code).to.equal(opts.code) - expect(input.isAutoRepeat).to.equal(opts.isAutoRepeat) - expect(input.shift).to.equal(opts.shift) - expect(input.control).to.equal(opts.control) - expect(input.alt).to.equal(opts.alt) - expect(input.meta).to.equal(opts.meta) - } + }); + const [, input] = await p; + + expect(input.type).to.equal(opts.type); + expect(input.key).to.equal(opts.key); + expect(input.code).to.equal(opts.code); + expect(input.isAutoRepeat).to.equal(opts.isAutoRepeat); + expect(input.shift).to.equal(opts.shift); + expect(input.control).to.equal(opts.control); + expect(input.alt).to.equal(opts.alt); + expect(input.meta).to.equal(opts.meta); + }; await testBeforeInput({ type: 'keyDown', key: 'A', @@ -551,7 +551,7 @@ describe('webContents module', () => { alt: true, meta: true, isAutoRepeat: true - }) + }); await testBeforeInput({ type: 'keyUp', key: '.', @@ -562,7 +562,7 @@ describe('webContents module', () => { alt: true, meta: false, isAutoRepeat: false - }) + }); await testBeforeInput({ type: 'keyUp', key: '!', @@ -573,7 +573,7 @@ describe('webContents module', () => { alt: false, meta: true, isAutoRepeat: false - }) + }); await testBeforeInput({ type: 'keyUp', key: 'Tab', @@ -584,16 +584,16 @@ describe('webContents module', () => { alt: false, meta: false, isAutoRepeat: true - }) - }) - }) + }); + }); + }); // On Mac, zooming isn't done with the mouse wheel. ifdescribe(process.platform !== 'darwin')('zoom-changed', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('is emitted with the correct zooming info', async () => { - const w = new BrowserWindow({ show: false }) - await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')) + const w = new BrowserWindow({ show: false }); + await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')); const testZoomChanged = async ({ zoomingIn }: { zoomingIn: boolean }) => { w.webContents.sendInputEvent({ @@ -605,227 +605,227 @@ describe('webContents module', () => { wheelTicksX: 0, wheelTicksY: zoomingIn ? 1 : -1, modifiers: ['control', 'meta'] - }) + }); - const [, zoomDirection] = await emittedOnce(w.webContents, 'zoom-changed') - expect(zoomDirection).to.equal(zoomingIn ? 'in' : 'out') - } + const [, zoomDirection] = await emittedOnce(w.webContents, 'zoom-changed'); + expect(zoomDirection).to.equal(zoomingIn ? 'in' : 'out'); + }; - await testZoomChanged({ zoomingIn: true }) - await testZoomChanged({ zoomingIn: false }) - }) - }) + await testZoomChanged({ zoomingIn: true }); + await testZoomChanged({ zoomingIn: false }); + }); + }); describe('sendInputEvent(event)', () => { - let w: BrowserWindow + let w: BrowserWindow; beforeEach(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - await w.loadFile(path.join(fixturesPath, 'pages', 'key-events.html')) - }) - afterEach(closeAllWindows) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + await w.loadFile(path.join(fixturesPath, 'pages', 'key-events.html')); + }); + afterEach(closeAllWindows); it('can send keydown events', (done) => { ipcMain.once('keydown', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => { - expect(key).to.equal('a') - expect(code).to.equal('KeyA') - expect(keyCode).to.equal(65) - expect(shiftKey).to.be.false() - expect(ctrlKey).to.be.false() - expect(altKey).to.be.false() - done() - }) - w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' }) - }) + expect(key).to.equal('a'); + expect(code).to.equal('KeyA'); + expect(keyCode).to.equal(65); + expect(shiftKey).to.be.false(); + expect(ctrlKey).to.be.false(); + expect(altKey).to.be.false(); + done(); + }); + w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' }); + }); it('can send keydown events with modifiers', (done) => { ipcMain.once('keydown', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => { - expect(key).to.equal('Z') - expect(code).to.equal('KeyZ') - expect(keyCode).to.equal(90) - expect(shiftKey).to.be.true() - expect(ctrlKey).to.be.true() - expect(altKey).to.be.false() - done() - }) - w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z', modifiers: ['shift', 'ctrl'] }) - }) + expect(key).to.equal('Z'); + expect(code).to.equal('KeyZ'); + expect(keyCode).to.equal(90); + expect(shiftKey).to.be.true(); + expect(ctrlKey).to.be.true(); + expect(altKey).to.be.false(); + done(); + }); + w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z', modifiers: ['shift', 'ctrl'] }); + }); it('can send keydown events with special keys', (done) => { ipcMain.once('keydown', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => { - expect(key).to.equal('Tab') - expect(code).to.equal('Tab') - expect(keyCode).to.equal(9) - expect(shiftKey).to.be.false() - expect(ctrlKey).to.be.false() - expect(altKey).to.be.true() - done() - }) - w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Tab', modifiers: ['alt'] }) - }) + expect(key).to.equal('Tab'); + expect(code).to.equal('Tab'); + expect(keyCode).to.equal(9); + expect(shiftKey).to.be.false(); + expect(ctrlKey).to.be.false(); + expect(altKey).to.be.true(); + done(); + }); + w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Tab', modifiers: ['alt'] }); + }); it('can send char events', (done) => { ipcMain.once('keypress', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => { - expect(key).to.equal('a') - expect(code).to.equal('KeyA') - expect(keyCode).to.equal(65) - expect(shiftKey).to.be.false() - expect(ctrlKey).to.be.false() - expect(altKey).to.be.false() - done() - }) - w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' }) - w.webContents.sendInputEvent({ type: 'char', keyCode: 'A' }) - }) + expect(key).to.equal('a'); + expect(code).to.equal('KeyA'); + expect(keyCode).to.equal(65); + expect(shiftKey).to.be.false(); + expect(ctrlKey).to.be.false(); + expect(altKey).to.be.false(); + done(); + }); + w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' }); + w.webContents.sendInputEvent({ type: 'char', keyCode: 'A' }); + }); it('can send char events with modifiers', (done) => { ipcMain.once('keypress', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => { - expect(key).to.equal('Z') - expect(code).to.equal('KeyZ') - expect(keyCode).to.equal(90) - expect(shiftKey).to.be.true() - expect(ctrlKey).to.be.true() - expect(altKey).to.be.false() - done() - }) - w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z' }) - w.webContents.sendInputEvent({ type: 'char', keyCode: 'Z', modifiers: ['shift', 'ctrl'] }) - }) - }) + expect(key).to.equal('Z'); + expect(code).to.equal('KeyZ'); + expect(keyCode).to.equal(90); + expect(shiftKey).to.be.true(); + expect(ctrlKey).to.be.true(); + expect(altKey).to.be.false(); + done(); + }); + w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z' }); + w.webContents.sendInputEvent({ type: 'char', keyCode: 'Z', modifiers: ['shift', 'ctrl'] }); + }); + }); describe('insertCSS', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('supports inserting CSS', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - await w.webContents.insertCSS('body { background-repeat: round; }') - const result = await w.webContents.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")') - expect(result).to.equal('round') - }) + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + await w.webContents.insertCSS('body { background-repeat: round; }'); + const result = await w.webContents.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")'); + expect(result).to.equal('round'); + }); it('supports removing inserted CSS', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - const key = await w.webContents.insertCSS('body { background-repeat: round; }') - await w.webContents.removeInsertedCSS(key) - const result = await w.webContents.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")') - expect(result).to.equal('repeat') - }) - }) + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + const key = await w.webContents.insertCSS('body { background-repeat: round; }'); + await w.webContents.removeInsertedCSS(key); + const result = await w.webContents.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")'); + expect(result).to.equal('repeat'); + }); + }); describe('inspectElement()', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('supports inspecting an element in the devtools', (done) => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - w.webContents.once('devtools-opened', () => { done() }) - w.webContents.inspectElement(10, 10) - }) - }) + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + w.webContents.once('devtools-opened', () => { done(); }); + w.webContents.inspectElement(10, 10); + }); + }); describe('startDrag({file, icon})', () => { it('throws errors for a missing file or a missing/empty icon', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); expect(() => { - w.webContents.startDrag({ icon: path.join(fixturesPath, 'assets', 'logo.png') } as any) - }).to.throw('Must specify either \'file\' or \'files\' option') + w.webContents.startDrag({ icon: path.join(fixturesPath, 'assets', 'logo.png') } as any); + }).to.throw('Must specify either \'file\' or \'files\' option'); expect(() => { - w.webContents.startDrag({ file: __filename } as any) - }).to.throw('Must specify non-empty \'icon\' option') + w.webContents.startDrag({ file: __filename } as any); + }).to.throw('Must specify non-empty \'icon\' option'); expect(() => { - w.webContents.startDrag({ file: __filename, icon: __filename }) - }).to.throw('Must specify non-empty \'icon\' option') - }) - }) + w.webContents.startDrag({ file: __filename, icon: __filename }); + }).to.throw('Must specify non-empty \'icon\' option'); + }); + }); describe('focus()', () => { describe('when the web contents is hidden', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('does not blur the focused window', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - w.show() - await w.loadURL('about:blank') - w.focus() - const child = new BrowserWindow({ show: false }) - child.loadURL('about:blank') - child.webContents.focus() - const currentFocused = w.isFocused() - const childFocused = child.isFocused() - child.close() - expect(currentFocused).to.be.true() - expect(childFocused).to.be.false() - }) - }) - }) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + w.show(); + await w.loadURL('about:blank'); + w.focus(); + const child = new BrowserWindow({ show: false }); + child.loadURL('about:blank'); + child.webContents.focus(); + const currentFocused = w.isFocused(); + const childFocused = child.isFocused(); + child.close(); + expect(currentFocused).to.be.true(); + expect(childFocused).to.be.false(); + }); + }); + }); describe('getOSProcessId()', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('returns a valid procress id', async () => { - const w = new BrowserWindow({ show: false }) - expect(w.webContents.getOSProcessId()).to.equal(0) + const w = new BrowserWindow({ show: false }); + expect(w.webContents.getOSProcessId()).to.equal(0); - await w.loadURL('about:blank') - expect(w.webContents.getOSProcessId()).to.be.above(0) - }) - }) + await w.loadURL('about:blank'); + expect(w.webContents.getOSProcessId()).to.be.above(0); + }); + }); describe('userAgent APIs', () => { it('can set the user agent (functions)', () => { - const w = new BrowserWindow({ show: false }) - const userAgent = w.webContents.getUserAgent() + const w = new BrowserWindow({ show: false }); + const userAgent = w.webContents.getUserAgent(); - w.webContents.setUserAgent('my-user-agent') - expect(w.webContents.getUserAgent()).to.equal('my-user-agent') + w.webContents.setUserAgent('my-user-agent'); + expect(w.webContents.getUserAgent()).to.equal('my-user-agent'); - w.webContents.setUserAgent(userAgent) - expect(w.webContents.getUserAgent()).to.equal(userAgent) - }) + w.webContents.setUserAgent(userAgent); + expect(w.webContents.getUserAgent()).to.equal(userAgent); + }); it('can set the user agent (properties)', () => { - const w = new BrowserWindow({ show: false }) - const userAgent = w.webContents.userAgent + const w = new BrowserWindow({ show: false }); + const userAgent = w.webContents.userAgent; - w.webContents.userAgent = 'my-user-agent' - expect(w.webContents.userAgent).to.equal('my-user-agent') + w.webContents.userAgent = 'my-user-agent'; + expect(w.webContents.userAgent).to.equal('my-user-agent'); - w.webContents.userAgent = userAgent - expect(w.webContents.userAgent).to.equal(userAgent) - }) - }) + w.webContents.userAgent = userAgent; + expect(w.webContents.userAgent).to.equal(userAgent); + }); + }); describe('audioMuted APIs', () => { it('can set the audio mute level (functions)', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); - w.webContents.setAudioMuted(true) - expect(w.webContents.isAudioMuted()).to.be.true() + w.webContents.setAudioMuted(true); + expect(w.webContents.isAudioMuted()).to.be.true(); - w.webContents.setAudioMuted(false) - expect(w.webContents.isAudioMuted()).to.be.false() - }) + w.webContents.setAudioMuted(false); + expect(w.webContents.isAudioMuted()).to.be.false(); + }); it('can set the audio mute level (functions)', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); - w.webContents.audioMuted = true - expect(w.webContents.audioMuted).to.be.true() + w.webContents.audioMuted = true; + expect(w.webContents.audioMuted).to.be.true(); - w.webContents.audioMuted = false - expect(w.webContents.audioMuted).to.be.false() - }) - }) + w.webContents.audioMuted = false; + expect(w.webContents.audioMuted).to.be.false(); + }); + }); describe('zoom api', () => { - const scheme = (global as any).standardScheme + const scheme = (global as any).standardScheme; const hostZoomMap: Record = { host1: 0.3, host2: 0.7, host3: 0.2 - } + }; before((done) => { - const protocol = session.defaultSession.protocol + const protocol = session.defaultSession.protocol; protocol.registerStringProtocol(scheme, (request, callback) => { const response = `` - callback({ data: response, mimeType: 'text/html' }) - }, (error) => done(error)) - }) + `; + callback({ data: response, mimeType: 'text/html' }); + }, (error) => done(error)); + }); after((done) => { - const protocol = session.defaultSession.protocol - protocol.unregisterProtocol(scheme, (error) => done(error)) - }) + const protocol = session.defaultSession.protocol; + protocol.unregisterProtocol(scheme, (error) => done(error)); + }); - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('throws on an invalid zoomFactor', async () => { - const w = new BrowserWindow({ show: false }) - await w.loadURL('about:blank') + const w = new BrowserWindow({ show: false }); + await w.loadURL('about:blank'); expect(() => { - w.webContents.setZoomFactor(0.0) - }).to.throw(/'zoomFactor' must be a double greater than 0.0/) + w.webContents.setZoomFactor(0.0); + }).to.throw(/'zoomFactor' must be a double greater than 0.0/); expect(() => { - w.webContents.setZoomFactor(-2.0) - }).to.throw(/'zoomFactor' must be a double greater than 0.0/) - }) + w.webContents.setZoomFactor(-2.0); + }).to.throw(/'zoomFactor' must be a double greater than 0.0/); + }); it('can set the correct zoom level (functions)', async () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); try { - await w.loadURL('about:blank') - const zoomLevel = w.webContents.getZoomLevel() - expect(zoomLevel).to.eql(0.0) - w.webContents.setZoomLevel(0.5) - const newZoomLevel = w.webContents.getZoomLevel() - expect(newZoomLevel).to.eql(0.5) + await w.loadURL('about:blank'); + const zoomLevel = w.webContents.getZoomLevel(); + expect(zoomLevel).to.eql(0.0); + w.webContents.setZoomLevel(0.5); + const newZoomLevel = w.webContents.getZoomLevel(); + expect(newZoomLevel).to.eql(0.5); } finally { - w.webContents.setZoomLevel(0) + w.webContents.setZoomLevel(0); } - }) + }); it('can set the correct zoom level (properties)', async () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); try { - await w.loadURL('about:blank') - const zoomLevel = w.webContents.zoomLevel - expect(zoomLevel).to.eql(0.0) - w.webContents.zoomLevel = 0.5 - const newZoomLevel = w.webContents.zoomLevel - expect(newZoomLevel).to.eql(0.5) + await w.loadURL('about:blank'); + const zoomLevel = w.webContents.zoomLevel; + expect(zoomLevel).to.eql(0.0); + w.webContents.zoomLevel = 0.5; + const newZoomLevel = w.webContents.zoomLevel; + expect(newZoomLevel).to.eql(0.5); } finally { - w.webContents.zoomLevel = 0 + w.webContents.zoomLevel = 0; } - }) + }); it('can set the correct zoom factor (functions)', async () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); try { - await w.loadURL('about:blank') - const zoomFactor = w.webContents.getZoomFactor() - expect(zoomFactor).to.eql(1.0) + await w.loadURL('about:blank'); + const zoomFactor = w.webContents.getZoomFactor(); + expect(zoomFactor).to.eql(1.0); - w.webContents.setZoomFactor(0.5) - const newZoomFactor = w.webContents.getZoomFactor() - expect(newZoomFactor).to.eql(0.5) + w.webContents.setZoomFactor(0.5); + const newZoomFactor = w.webContents.getZoomFactor(); + expect(newZoomFactor).to.eql(0.5); } finally { - w.webContents.setZoomFactor(1.0) + w.webContents.setZoomFactor(1.0); } - }) + }); it('can set the correct zoom factor (properties)', async () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); try { - await w.loadURL('about:blank') - const zoomFactor = w.webContents.zoomFactor - expect(zoomFactor).to.eql(1.0) + await w.loadURL('about:blank'); + const zoomFactor = w.webContents.zoomFactor; + expect(zoomFactor).to.eql(1.0); - w.webContents.zoomFactor = 0.5 - const newZoomFactor = w.webContents.zoomFactor - expect(newZoomFactor).to.eql(0.5) + w.webContents.zoomFactor = 0.5; + const newZoomFactor = w.webContents.zoomFactor; + expect(newZoomFactor).to.eql(0.5); } finally { - w.webContents.zoomFactor = 1.0 + w.webContents.zoomFactor = 1.0; } - }) + }); it('can persist zoom level across navigation', (done) => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, enableRemoteModule: true } }) - let finalNavigation = false + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, enableRemoteModule: true } }); + let finalNavigation = false; ipcMain.on('set-zoom', (e, host) => { - const zoomLevel = hostZoomMap[host] - if (!finalNavigation) w.webContents.zoomLevel = zoomLevel - e.sender.send(`${host}-zoom-set`) - }) + const zoomLevel = hostZoomMap[host]; + if (!finalNavigation) w.webContents.zoomLevel = zoomLevel; + e.sender.send(`${host}-zoom-set`); + }); ipcMain.on('host1-zoom-level', (e, zoomLevel) => { - const expectedZoomLevel = hostZoomMap.host1 - expect(zoomLevel).to.equal(expectedZoomLevel) + const expectedZoomLevel = hostZoomMap.host1; + expect(zoomLevel).to.equal(expectedZoomLevel); if (finalNavigation) { - done() + done(); } else { - w.loadURL(`${scheme}://host2`) + w.loadURL(`${scheme}://host2`); } - }) + }); ipcMain.once('host2-zoom-level', (e, zoomLevel) => { - const expectedZoomLevel = hostZoomMap.host2 - expect(zoomLevel).to.equal(expectedZoomLevel) - finalNavigation = true - w.webContents.goBack() - }) - w.loadURL(`${scheme}://host1`) - }) + const expectedZoomLevel = hostZoomMap.host2; + expect(zoomLevel).to.equal(expectedZoomLevel); + finalNavigation = true; + w.webContents.goBack(); + }); + w.loadURL(`${scheme}://host1`); + }); it('can propagate zoom level across same session', (done) => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, enableRemoteModule: true } }) - const w2 = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, enableRemoteModule: true } }); + const w2 = new BrowserWindow({ show: false }); w2.webContents.on('did-finish-load', () => { - const zoomLevel1 = w.webContents.zoomLevel - expect(zoomLevel1).to.equal(hostZoomMap.host3) - - const zoomLevel2 = w2.webContents.zoomLevel - expect(zoomLevel1).to.equal(zoomLevel2) - w2.setClosable(true) - w2.close() - done() - }) + const zoomLevel1 = w.webContents.zoomLevel; + expect(zoomLevel1).to.equal(hostZoomMap.host3); + + const zoomLevel2 = w2.webContents.zoomLevel; + expect(zoomLevel1).to.equal(zoomLevel2); + w2.setClosable(true); + w2.close(); + done(); + }); w.webContents.on('did-finish-load', () => { - w.webContents.zoomLevel = hostZoomMap.host3 - w2.loadURL(`${scheme}://host3`) - }) - w.loadURL(`${scheme}://host3`) - }) + w.webContents.zoomLevel = hostZoomMap.host3; + w2.loadURL(`${scheme}://host3`); + }); + w.loadURL(`${scheme}://host3`); + }); it('cannot propagate zoom level across different session', (done) => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); const w2 = new BrowserWindow({ show: false, webPreferences: { partition: 'temp' } - }) - const protocol = w2.webContents.session.protocol + }); + const protocol = w2.webContents.session.protocol; protocol.registerStringProtocol(scheme, (request, callback) => { - callback('hello') + callback('hello'); }, (error) => { - if (error) return done(error) + if (error) return done(error); w2.webContents.on('did-finish-load', () => { - const zoomLevel1 = w.webContents.zoomLevel - expect(zoomLevel1).to.equal(hostZoomMap.host3) + const zoomLevel1 = w.webContents.zoomLevel; + expect(zoomLevel1).to.equal(hostZoomMap.host3); - const zoomLevel2 = w2.webContents.zoomLevel - expect(zoomLevel2).to.equal(0) - expect(zoomLevel1).to.not.equal(zoomLevel2) + const zoomLevel2 = w2.webContents.zoomLevel; + expect(zoomLevel2).to.equal(0); + expect(zoomLevel1).to.not.equal(zoomLevel2); protocol.unregisterProtocol(scheme, (error) => { - if (error) return done(error) - w2.setClosable(true) - w2.close() - done() - }) - }) + if (error) return done(error); + w2.setClosable(true); + w2.close(); + done(); + }); + }); w.webContents.on('did-finish-load', () => { - w.webContents.zoomLevel = hostZoomMap.host3 - w2.loadURL(`${scheme}://host3`) - }) - w.loadURL(`${scheme}://host3`) - }) - }) + w.webContents.zoomLevel = hostZoomMap.host3; + w2.loadURL(`${scheme}://host3`); + }); + w.loadURL(`${scheme}://host3`); + }); + }); it('can persist when it contains iframe', (done) => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); const server = http.createServer((req, res) => { setTimeout(() => { - res.end() - }, 200) - }) + res.end(); + }, 200); + }); server.listen(0, '127.0.0.1', () => { - const url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port - const content = `` + const url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port; + const content = ``; w.webContents.on('did-frame-finish-load', (e, isMainFrame) => { if (!isMainFrame) { - const zoomLevel = w.webContents.zoomLevel - expect(zoomLevel).to.equal(2.0) + const zoomLevel = w.webContents.zoomLevel; + expect(zoomLevel).to.equal(2.0); - w.webContents.zoomLevel = 0 - server.close() - done() + w.webContents.zoomLevel = 0; + server.close(); + done(); } - }) + }); w.webContents.on('dom-ready', () => { - w.webContents.zoomLevel = 2.0 - }) - w.loadURL(`data:text/html,${content}`) - }) - }) + w.webContents.zoomLevel = 2.0; + }); + w.loadURL(`data:text/html,${content}`); + }); + }); it('cannot propagate when used with webframe', (done) => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - let finalZoomLevel = 0 + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + let finalZoomLevel = 0; const w2 = new BrowserWindow({ show: false - }) + }); w2.webContents.on('did-finish-load', () => { - const zoomLevel1 = w.webContents.zoomLevel - expect(zoomLevel1).to.equal(finalZoomLevel) + const zoomLevel1 = w.webContents.zoomLevel; + expect(zoomLevel1).to.equal(finalZoomLevel); - const zoomLevel2 = w2.webContents.zoomLevel - expect(zoomLevel2).to.equal(0) - expect(zoomLevel1).to.not.equal(zoomLevel2) + const zoomLevel2 = w2.webContents.zoomLevel; + expect(zoomLevel2).to.equal(0); + expect(zoomLevel1).to.not.equal(zoomLevel2); - w2.setClosable(true) - w2.close() - done() - }) + w2.setClosable(true); + w2.close(); + done(); + }); ipcMain.once('temporary-zoom-set', (e, zoomLevel) => { - w2.loadFile(path.join(fixturesPath, 'pages', 'c.html')) - finalZoomLevel = zoomLevel - }) - w.loadFile(path.join(fixturesPath, 'pages', 'webframe-zoom.html')) - }) + w2.loadFile(path.join(fixturesPath, 'pages', 'c.html')); + finalZoomLevel = zoomLevel; + }); + w.loadFile(path.join(fixturesPath, 'pages', 'webframe-zoom.html')); + }); describe('with unique domains', () => { - let server: http.Server - let serverUrl: string - let crossSiteUrl: string + let server: http.Server; + let serverUrl: string; + let crossSiteUrl: string; before((done) => { server = http.createServer((req, res) => { - setTimeout(() => res.end('hey'), 0) - }) + setTimeout(() => res.end('hey'), 0); + }); server.listen(0, '127.0.0.1', () => { - serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - crossSiteUrl = `http://localhost:${(server.address() as AddressInfo).port}` - done() - }) - }) + serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + crossSiteUrl = `http://localhost:${(server.address() as AddressInfo).port}`; + done(); + }); + }); after(() => { - server.close() - }) + server.close(); + }); it('cannot persist zoom level after navigation with webFrame', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); const source = ` const {ipcRenderer, webFrame} = require('electron') webFrame.setZoomLevel(0.6) ipcRenderer.send('zoom-level-set', webFrame.getZoomLevel()) - ` - const zoomLevelPromise = emittedOnce(ipcMain, 'zoom-level-set') - await w.loadURL(serverUrl) - await w.webContents.executeJavaScript(source) - let [, zoomLevel] = await zoomLevelPromise - expect(zoomLevel).to.equal(0.6) - const loadPromise = emittedOnce(w.webContents, 'did-finish-load') - await w.loadURL(crossSiteUrl) - await loadPromise - zoomLevel = w.webContents.zoomLevel - expect(zoomLevel).to.equal(0) - }) - }) - }) + `; + const zoomLevelPromise = emittedOnce(ipcMain, 'zoom-level-set'); + await w.loadURL(serverUrl); + await w.webContents.executeJavaScript(source); + let [, zoomLevel] = await zoomLevelPromise; + expect(zoomLevel).to.equal(0.6); + const loadPromise = emittedOnce(w.webContents, 'did-finish-load'); + await w.loadURL(crossSiteUrl); + await loadPromise; + zoomLevel = w.webContents.zoomLevel; + expect(zoomLevel).to.equal(0); + }); + }); + }); describe('webrtc ip policy api', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can set and get webrtc ip policies', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); const policies = [ 'default', 'default_public_interface_only', 'default_public_and_private_interfaces', 'disable_non_proxied_udp' - ] + ]; policies.forEach((policy) => { - w.webContents.setWebRTCIPHandlingPolicy(policy as any) - expect(w.webContents.getWebRTCIPHandlingPolicy()).to.equal(policy) - }) - }) - }) + w.webContents.setWebRTCIPHandlingPolicy(policy as any); + expect(w.webContents.getWebRTCIPHandlingPolicy()).to.equal(policy); + }); + }); + }); describe('render view deleted events', () => { - let server: http.Server - let serverUrl: string - let crossSiteUrl: string + let server: http.Server; + let serverUrl: string; + let crossSiteUrl: string; before((done) => { server = http.createServer((req, res) => { const respond = () => { if (req.url === '/redirect-cross-site') { - res.setHeader('Location', `${crossSiteUrl}/redirected`) - res.statusCode = 302 - res.end() + res.setHeader('Location', `${crossSiteUrl}/redirected`); + res.statusCode = 302; + res.end(); } else if (req.url === '/redirected') { - res.end('') + res.end(''); } else { - res.end() + res.end(); } - } - setTimeout(respond, 0) - }) + }; + setTimeout(respond, 0); + }); server.listen(0, '127.0.0.1', () => { - serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - crossSiteUrl = `http://localhost:${(server.address() as AddressInfo).port}` - done() - }) - }) + serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + crossSiteUrl = `http://localhost:${(server.address() as AddressInfo).port}`; + done(); + }); + }); after(() => { - server.close() - }) + server.close(); + }); - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('does not emit current-render-view-deleted when speculative RVHs are deleted', (done) => { - const w = new BrowserWindow({ show: false }) - let currentRenderViewDeletedEmitted = false + const w = new BrowserWindow({ show: false }); + let currentRenderViewDeletedEmitted = false; w.webContents.once('destroyed', () => { - expect(currentRenderViewDeletedEmitted).to.be.false('current-render-view-deleted was emitted') - done() - }) + expect(currentRenderViewDeletedEmitted).to.be.false('current-render-view-deleted was emitted'); + done(); + }); const renderViewDeletedHandler = () => { - currentRenderViewDeletedEmitted = true - } - w.webContents.on('current-render-view-deleted' as any, renderViewDeletedHandler) + currentRenderViewDeletedEmitted = true; + }; + w.webContents.on('current-render-view-deleted' as any, renderViewDeletedHandler); w.webContents.on('did-finish-load', () => { - w.webContents.removeListener('current-render-view-deleted' as any, renderViewDeletedHandler) - w.close() - }) - w.loadURL(`${serverUrl}/redirect-cross-site`) - }) + w.webContents.removeListener('current-render-view-deleted' as any, renderViewDeletedHandler); + w.close(); + }); + w.loadURL(`${serverUrl}/redirect-cross-site`); + }); it('emits current-render-view-deleted if the current RVHs are deleted', (done) => { - const w = new BrowserWindow({ show: false }) - let currentRenderViewDeletedEmitted = false + const w = new BrowserWindow({ show: false }); + let currentRenderViewDeletedEmitted = false; w.webContents.once('destroyed', () => { - expect(currentRenderViewDeletedEmitted).to.be.true('current-render-view-deleted wasn\'t emitted') - done() - }) + expect(currentRenderViewDeletedEmitted).to.be.true('current-render-view-deleted wasn\'t emitted'); + done(); + }); w.webContents.on('current-render-view-deleted' as any, () => { - currentRenderViewDeletedEmitted = true - }) + currentRenderViewDeletedEmitted = true; + }); w.webContents.on('did-finish-load', () => { - w.close() - }) - w.loadURL(`${serverUrl}/redirect-cross-site`) - }) + w.close(); + }); + w.loadURL(`${serverUrl}/redirect-cross-site`); + }); it('emits render-view-deleted if any RVHs are deleted', (done) => { - const w = new BrowserWindow({ show: false }) - let rvhDeletedCount = 0 + const w = new BrowserWindow({ show: false }); + let rvhDeletedCount = 0; w.webContents.once('destroyed', () => { - const expectedRenderViewDeletedEventCount = 1 - expect(rvhDeletedCount).to.equal(expectedRenderViewDeletedEventCount, 'render-view-deleted wasn\'t emitted the expected nr. of times') - done() - }) + const expectedRenderViewDeletedEventCount = 1; + expect(rvhDeletedCount).to.equal(expectedRenderViewDeletedEventCount, 'render-view-deleted wasn\'t emitted the expected nr. of times'); + done(); + }); w.webContents.on('render-view-deleted' as any, () => { - rvhDeletedCount++ - }) + rvhDeletedCount++; + }); w.webContents.on('did-finish-load', () => { - w.close() - }) - w.loadURL(`${serverUrl}/redirect-cross-site`) - }) - }) + w.close(); + }); + w.loadURL(`${serverUrl}/redirect-cross-site`); + }); + }); describe('setIgnoreMenuShortcuts(ignore)', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('does not throw', () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); expect(() => { - w.webContents.setIgnoreMenuShortcuts(true) - w.webContents.setIgnoreMenuShortcuts(false) - }).to.not.throw() - }) - }) + w.webContents.setIgnoreMenuShortcuts(true); + w.webContents.setIgnoreMenuShortcuts(false); + }).to.not.throw(); + }); + }); describe('create()', () => { it('does not crash on exit', async () => { - const appPath = path.join(fixturesPath, 'api', 'leak-exit-webcontents.js') - const electronPath = process.execPath - const appProcess = ChildProcess.spawn(electronPath, [appPath]) - const [code] = await emittedOnce(appProcess, 'close') - expect(code).to.equal(0) - }) - }) + const appPath = path.join(fixturesPath, 'api', 'leak-exit-webcontents.js'); + const electronPath = process.execPath; + const appProcess = ChildProcess.spawn(electronPath, [appPath]); + const [code] = await emittedOnce(appProcess, 'close'); + expect(code).to.equal(0); + }); + }); // Destroying webContents in its event listener is going to crash when // Electron is built in Debug mode. describe('destroy()', () => { - let server: http.Server - let serverUrl: string + let server: http.Server; + let serverUrl: string; before((done) => { server = http.createServer((request, response) => { switch (request.url) { case '/net-error': - response.destroy() - break + response.destroy(); + break; case '/200': - response.end() - break + response.end(); + break; default: - done('unsupported endpoint') + done('unsupported endpoint'); } }).listen(0, '127.0.0.1', () => { - serverUrl = 'http://127.0.0.1:' + (server.address() as AddressInfo).port - done() - }) - }) + serverUrl = 'http://127.0.0.1:' + (server.address() as AddressInfo).port; + done(); + }); + }); after(() => { - server.close() - }) + server.close(); + }); const events = [ { name: 'did-start-loading', url: '/200' }, @@ -1253,161 +1253,161 @@ describe('webContents module', () => { // violates this contract and crashes. { name: 'did-frame-finish-load', url: '/200' }, { name: 'did-fail-load', url: '/net-error' } - ] + ]; for (const e of events) { it(`should not crash when invoked synchronously inside ${e.name} handler`, async function () { // This test is flaky on Windows CI and we don't know why, but the // purpose of this test is to make sure Electron does not crash so it // is fine to retry this test for a few times. - this.retries(3) - - const contents = (webContents as any).create() as WebContents - const originalEmit = contents.emit.bind(contents) - contents.emit = (...args) => { return originalEmit(...args) } - contents.once(e.name as any, () => (contents as any).destroy()) - const destroyed = emittedOnce(contents, 'destroyed') - contents.loadURL(serverUrl + e.url) - await destroyed - }) + this.retries(3); + + const contents = (webContents as any).create() as WebContents; + const originalEmit = contents.emit.bind(contents); + contents.emit = (...args) => { return originalEmit(...args); }; + contents.once(e.name as any, () => (contents as any).destroy()); + const destroyed = emittedOnce(contents, 'destroyed'); + contents.loadURL(serverUrl + e.url); + await destroyed; + }); } - }) + }); describe('did-change-theme-color event', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('is triggered with correct theme color', (done) => { - const w = new BrowserWindow({ show: true }) - let count = 0 + const w = new BrowserWindow({ show: true }); + let count = 0; w.webContents.on('did-change-theme-color', (e, color) => { if (count === 0) { - count += 1 - expect(color).to.equal('#FFEEDD') - w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')) + count += 1; + expect(color).to.equal('#FFEEDD'); + w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')); } else if (count === 1) { - expect(color).to.be.null() - done() + expect(color).to.be.null(); + done(); } - }) - w.loadFile(path.join(fixturesPath, 'pages', 'theme-color.html')) - }) - }) + }); + w.loadFile(path.join(fixturesPath, 'pages', 'theme-color.html')); + }); + }); describe('console-message event', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('is triggered with correct log message', (done) => { - const w = new BrowserWindow({ show: true }) + const w = new BrowserWindow({ show: true }); w.webContents.on('console-message', (e, level, message) => { // Don't just assert as Chromium might emit other logs that we should ignore. if (message === 'a') { - done() + done(); } - }) - w.loadFile(path.join(fixturesPath, 'pages', 'a.html')) - }) - }) + }); + w.loadFile(path.join(fixturesPath, 'pages', 'a.html')); + }); + }); describe('ipc-message event', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('emits when the renderer process sends an asynchronous message', async () => { - const w = new BrowserWindow({ show: true, webPreferences: { nodeIntegration: true } }) - await w.webContents.loadURL('about:blank') + const w = new BrowserWindow({ show: true, webPreferences: { nodeIntegration: true } }); + await w.webContents.loadURL('about:blank'); w.webContents.executeJavaScript(` require('electron').ipcRenderer.send('message', 'Hello World!') - `) + `); - const [, channel, message] = await emittedOnce(w.webContents, 'ipc-message') - expect(channel).to.equal('message') - expect(message).to.equal('Hello World!') - }) - }) + const [, channel, message] = await emittedOnce(w.webContents, 'ipc-message'); + expect(channel).to.equal('message'); + expect(message).to.equal('Hello World!'); + }); + }); describe('ipc-message-sync event', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('emits when the renderer process sends a synchronous message', async () => { - const w = new BrowserWindow({ show: true, webPreferences: { nodeIntegration: true } }) - await w.webContents.loadURL('about:blank') + const w = new BrowserWindow({ show: true, webPreferences: { nodeIntegration: true } }); + await w.webContents.loadURL('about:blank'); const promise: Promise<[string, string]> = new Promise(resolve => { w.webContents.once('ipc-message-sync', (event, channel, arg) => { - event.returnValue = 'foobar' as any - resolve([channel, arg]) - }) - }) + event.returnValue = 'foobar' as any; + resolve([channel, arg]); + }); + }); const result = await w.webContents.executeJavaScript(` require('electron').ipcRenderer.sendSync('message', 'Hello World!') - `) + `); - const [channel, message] = await promise - expect(channel).to.equal('message') - expect(message).to.equal('Hello World!') - expect(result).to.equal('foobar') - }) - }) + const [channel, message] = await promise; + expect(channel).to.equal('message'); + expect(message).to.equal('Hello World!'); + expect(result).to.equal('foobar'); + }); + }); describe('referrer', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('propagates referrer information to new target=_blank windows', (done) => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); const server = http.createServer((req, res) => { if (req.url === '/should_have_referrer') { - expect(req.headers.referer).to.equal(`http://127.0.0.1:${(server.address() as AddressInfo).port}/`) - server.close() - return done() + expect(req.headers.referer).to.equal(`http://127.0.0.1:${(server.address() as AddressInfo).port}/`); + server.close(); + return done(); } - res.end('link') - }) + res.end('link'); + }); server.listen(0, '127.0.0.1', () => { - const url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port + '/' + const url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port + '/'; w.webContents.once('did-finish-load', () => { w.webContents.once('new-window', (event, newUrl, frameName, disposition, options, features, referrer) => { - expect(referrer.url).to.equal(url) - expect(referrer.policy).to.equal('no-referrer-when-downgrade') - }) - w.webContents.executeJavaScript('a.click()') - }) - w.loadURL(url) - }) - }) + expect(referrer.url).to.equal(url); + expect(referrer.policy).to.equal('no-referrer-when-downgrade'); + }); + w.webContents.executeJavaScript('a.click()'); + }); + w.loadURL(url); + }); + }); // TODO(jeremy): window.open() in a real browser passes the referrer, but // our hacked-up window.open() shim doesn't. It should. xit('propagates referrer information to windows opened with window.open', (done) => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); const server = http.createServer((req, res) => { if (req.url === '/should_have_referrer') { - expect(req.headers.referer).to.equal(`http://127.0.0.1:${(server.address() as AddressInfo).port}/`) - return done() + expect(req.headers.referer).to.equal(`http://127.0.0.1:${(server.address() as AddressInfo).port}/`); + return done(); } - res.end('') - }) + res.end(''); + }); server.listen(0, '127.0.0.1', () => { - const url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port + '/' + const url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port + '/'; w.webContents.once('did-finish-load', () => { w.webContents.once('new-window', (event, newUrl, frameName, disposition, options, features, referrer) => { - expect(referrer.url).to.equal(url) - expect(referrer.policy).to.equal('no-referrer-when-downgrade') - }) - w.webContents.executeJavaScript('window.open(location.href + "should_have_referrer")') - }) - w.loadURL(url) - }) - }) - }) + expect(referrer.url).to.equal(url); + expect(referrer.policy).to.equal('no-referrer-when-downgrade'); + }); + w.webContents.executeJavaScript('window.open(location.href + "should_have_referrer")'); + }); + w.loadURL(url); + }); + }); + }); describe('webframe messages in sandboxed contents', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('responds to executeJavaScript', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }) - await w.loadURL('about:blank') - const result = await w.webContents.executeJavaScript('37 + 5') - expect(result).to.equal(42) - }) - }) + const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); + await w.loadURL('about:blank'); + const result = await w.webContents.executeJavaScript('37 + 5'); + expect(result).to.equal(42); + }); + }); describe('preload-error event', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); const generateSpecs = (description: string, sandbox: boolean) => { describe(description, () => { it('is triggered when unhandled exception is thrown', async () => { - const preload = path.join(fixturesPath, 'module', 'preload-error-exception.js') + const preload = path.join(fixturesPath, 'module', 'preload-error-exception.js'); const w = new BrowserWindow({ show: false, @@ -1415,18 +1415,18 @@ describe('webContents module', () => { sandbox, preload } - }) + }); - const promise = emittedOnce(w.webContents, 'preload-error') - w.loadURL('about:blank') + const promise = emittedOnce(w.webContents, 'preload-error'); + w.loadURL('about:blank'); - const [, preloadPath, error] = await promise - expect(preloadPath).to.equal(preload) - expect(error.message).to.equal('Hello World!') - }) + const [, preloadPath, error] = await promise; + expect(preloadPath).to.equal(preload); + expect(error.message).to.equal('Hello World!'); + }); it('is triggered on syntax errors', async () => { - const preload = path.join(fixturesPath, 'module', 'preload-error-syntax.js') + const preload = path.join(fixturesPath, 'module', 'preload-error-syntax.js'); const w = new BrowserWindow({ show: false, @@ -1434,18 +1434,18 @@ describe('webContents module', () => { sandbox, preload } - }) + }); - const promise = emittedOnce(w.webContents, 'preload-error') - w.loadURL('about:blank') + const promise = emittedOnce(w.webContents, 'preload-error'); + w.loadURL('about:blank'); - const [, preloadPath, error] = await promise - expect(preloadPath).to.equal(preload) - expect(error.message).to.equal('foobar is not defined') - }) + const [, preloadPath, error] = await promise; + expect(preloadPath).to.equal(preload); + expect(error.message).to.equal('foobar is not defined'); + }); it('is triggered when preload script loading fails', async () => { - const preload = path.join(fixturesPath, 'module', 'preload-invalid.js') + const preload = path.join(fixturesPath, 'module', 'preload-invalid.js'); const w = new BrowserWindow({ show: false, @@ -1453,24 +1453,24 @@ describe('webContents module', () => { sandbox, preload } - }) + }); - const promise = emittedOnce(w.webContents, 'preload-error') - w.loadURL('about:blank') + const promise = emittedOnce(w.webContents, 'preload-error'); + w.loadURL('about:blank'); - const [, preloadPath, error] = await promise - expect(preloadPath).to.equal(preload) - expect(error.message).to.contain('preload-invalid.js') - }) - }) - } + const [, preloadPath, error] = await promise; + expect(preloadPath).to.equal(preload); + expect(error.message).to.contain('preload-invalid.js'); + }); + }); + }; - generateSpecs('without sandbox', false) - generateSpecs('with sandbox', true) - }) + generateSpecs('without sandbox', false); + generateSpecs('with sandbox', true); + }); describe('takeHeapSnapshot()', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('works with sandboxed renderers', async () => { const w = new BrowserWindow({ @@ -1478,28 +1478,28 @@ describe('webContents module', () => { webPreferences: { sandbox: true } - }) + }); - await w.loadURL('about:blank') + await w.loadURL('about:blank'); - const filePath = path.join(app.getPath('temp'), 'test.heapsnapshot') + const filePath = path.join(app.getPath('temp'), 'test.heapsnapshot'); const cleanup = () => { try { - fs.unlinkSync(filePath) + fs.unlinkSync(filePath); } catch (e) { // ignore error } - } + }; try { - await w.webContents.takeHeapSnapshot(filePath) - const stats = fs.statSync(filePath) - expect(stats.size).not.to.be.equal(0) + await w.webContents.takeHeapSnapshot(filePath); + const stats = fs.statSync(filePath); + expect(stats.size).not.to.be.equal(0); } finally { - cleanup() + cleanup(); } - }) + }); it('fails with invalid file path', async () => { const w = new BrowserWindow({ @@ -1507,54 +1507,54 @@ describe('webContents module', () => { webPreferences: { sandbox: true } - }) + }); - await w.loadURL('about:blank') + await w.loadURL('about:blank'); - const promise = w.webContents.takeHeapSnapshot('') - return expect(promise).to.be.eventually.rejectedWith(Error, 'takeHeapSnapshot failed') - }) - }) + const promise = w.webContents.takeHeapSnapshot(''); + return expect(promise).to.be.eventually.rejectedWith(Error, 'takeHeapSnapshot failed'); + }); + }); describe('setBackgroundThrottling()', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('does not crash when allowing', () => { - const w = new BrowserWindow({ show: false }) - w.webContents.setBackgroundThrottling(true) - }) + const w = new BrowserWindow({ show: false }); + w.webContents.setBackgroundThrottling(true); + }); it('does not crash when called via BrowserWindow', () => { const w = new BrowserWindow({ show: false }); - (w as any).setBackgroundThrottling(true) - }) + (w as any).setBackgroundThrottling(true); + }); it('does not crash when disallowing', () => { - const w = new BrowserWindow({ show: false, webPreferences: { backgroundThrottling: true } }) + const w = new BrowserWindow({ show: false, webPreferences: { backgroundThrottling: true } }); - w.webContents.setBackgroundThrottling(false) - }) - }) + w.webContents.setBackgroundThrottling(false); + }); + }); ifdescribe(features.isPrintingEnabled())('getPrinters()', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can get printer list', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }) - await w.loadURL('about:blank') - const printers = w.webContents.getPrinters() - expect(printers).to.be.an('array') - }) - }) + const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); + await w.loadURL('about:blank'); + const printers = w.webContents.getPrinters(); + expect(printers).to.be.an('array'); + }); + }); ifdescribe(features.isPrintingEnabled())('printToPDF()', () => { - let w: BrowserWindow + let w: BrowserWindow; beforeEach(async () => { - w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }) - await w.loadURL('data:text/html,

Hello, World!

') - }) + w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); + await w.loadURL('data:text/html,

Hello, World!

'); + }); - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('rejects on incorrectly typed parameters', async () => { const badTypes = { @@ -1566,23 +1566,23 @@ describe('webContents module', () => { printSelectionOnly: 1, printBackground: 2, pageSize: 'IAmAPageSize' - } + }; // These will hard crash in Chromium unless we type-check for (const [key, value] of Object.entries(badTypes)) { - const param = { [key]: value } - await expect(w.webContents.printToPDF(param)).to.eventually.be.rejected() + const param = { [key]: value }; + await expect(w.webContents.printToPDF(param)).to.eventually.be.rejected(); } - }) + }); it('can print to PDF', async () => { - const data = await w.webContents.printToPDF({}) - expect(data).to.be.an.instanceof(Buffer).that.is.not.empty() - }) + const data = await w.webContents.printToPDF({}); + expect(data).to.be.an.instanceof(Buffer).that.is.not.empty(); + }); it('respects custom settings', async () => { - w.loadFile(path.join(__dirname, 'fixtures', 'api', 'print-to-pdf.html')) - await emittedOnce(w.webContents, 'did-finish-load') + w.loadFile(path.join(__dirname, 'fixtures', 'api', 'print-to-pdf.html')); + await emittedOnce(w.webContents, 'did-finish-load'); const data = await w.webContents.printToPDF({ pageRanges: { @@ -1590,71 +1590,71 @@ describe('webContents module', () => { to: 2 }, landscape: true - }) + }); - const doc = await pdfjs.getDocument(data).promise + const doc = await pdfjs.getDocument(data).promise; // Check that correct # of pages are rendered. - expect(doc.numPages).to.equal(3) + expect(doc.numPages).to.equal(3); // Check that PDF is generated in landscape mode. - const firstPage = await doc.getPage(1) - const { width, height } = firstPage.getViewport({ scale: 100 }) - expect(width).to.be.greaterThan(height) - }) + const firstPage = await doc.getPage(1); + const { width, height } = firstPage.getViewport({ scale: 100 }); + expect(width).to.be.greaterThan(height); + }); it('does not crash when called multiple times', async () => { - const promises = [] + const promises = []; for (let i = 0; i < 2; i++) { - promises.push(w.webContents.printToPDF({})) + promises.push(w.webContents.printToPDF({})); } - const results = await Promise.all(promises) + const results = await Promise.all(promises); for (const data of results) { - expect(data).to.be.an.instanceof(Buffer).that.is.not.empty() + expect(data).to.be.an.instanceof(Buffer).that.is.not.empty(); } - }) - }) + }); + }); describe('PictureInPicture video', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('works as expected', (done) => { - const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }) + const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); w.webContents.once('did-finish-load', async () => { const result = await w.webContents.executeJavaScript( - `runTest(${features.isPictureInPictureEnabled()})`, true) - expect(result).to.be.true() - done() - }) - w.loadFile(path.join(fixturesPath, 'api', 'picture-in-picture.html')) - }) - }) + `runTest(${features.isPictureInPictureEnabled()})`, true); + expect(result).to.be.true(); + done(); + }); + w.loadFile(path.join(fixturesPath, 'api', 'picture-in-picture.html')); + }); + }); describe('devtools window', () => { - let hasRobotJS = false + let hasRobotJS = false; try { // We have other tests that check if native modules work, if we fail to require // robotjs let's skip this test to avoid false negatives - require('robotjs') - hasRobotJS = true + require('robotjs'); + hasRobotJS = true; } catch (err) { /* no-op */ } - afterEach(closeAllWindows) + afterEach(closeAllWindows); // NB. on macOS, this requires that you grant your terminal the ability to // control your computer. Open System Preferences > Security & Privacy > // Privacy > Accessibility and grant your terminal the permission to control // your computer. ifit(hasRobotJS)('can receive and handle menu events', async () => { - const w = new BrowserWindow({ show: true, webPreferences: { nodeIntegration: true } }) - w.loadFile(path.join(fixturesPath, 'pages', 'key-events.html')) + const w = new BrowserWindow({ show: true, webPreferences: { nodeIntegration: true } }); + w.loadFile(path.join(fixturesPath, 'pages', 'key-events.html')); // Ensure the devtools are loaded - w.webContents.closeDevTools() - const opened = emittedOnce(w.webContents, 'devtools-opened') - w.webContents.openDevTools() - await opened - await emittedOnce(w.webContents.devToolsWebContents, 'did-finish-load') - w.webContents.devToolsWebContents.focus() + w.webContents.closeDevTools(); + const opened = emittedOnce(w.webContents, 'devtools-opened'); + w.webContents.openDevTools(); + await opened; + await emittedOnce(w.webContents.devToolsWebContents, 'did-finish-load'); + w.webContents.devToolsWebContents.focus(); // Focus an input field await w.webContents.devToolsWebContents.executeJavaScript(` @@ -1662,170 +1662,170 @@ describe('webContents module', () => { document.body.innerHTML = '' document.body.appendChild(input) input.focus() - `) + `); // Write something to the clipboard - clipboard.writeText('test value') + clipboard.writeText('test value'); const pasted = w.webContents.devToolsWebContents.executeJavaScript(`new Promise(resolve => { document.querySelector('input').addEventListener('paste', (e) => { resolve(e.target.value) }) - })`) + })`); // Fake a paste request using robotjs to emulate a REAL keyboard paste event - require('robotjs').keyTap('v', process.platform === 'darwin' ? ['command'] : ['control']) + require('robotjs').keyTap('v', process.platform === 'darwin' ? ['command'] : ['control']); - const val = await pasted + const val = await pasted; // Once we're done expect the paste to have been successful - expect(val).to.equal('test value', 'value should eventually become the pasted value') - }) - }) + expect(val).to.equal('test value', 'value should eventually become the pasted value'); + }); + }); describe('Shared Workers', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can get multiple shared workers', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); - const ready = emittedOnce(ipcMain, 'ready') - w.loadFile(path.join(fixturesPath, 'api', 'shared-worker', 'shared-worker.html')) - await ready + const ready = emittedOnce(ipcMain, 'ready'); + w.loadFile(path.join(fixturesPath, 'api', 'shared-worker', 'shared-worker.html')); + await ready; - const sharedWorkers = w.webContents.getAllSharedWorkers() + const sharedWorkers = w.webContents.getAllSharedWorkers(); - expect(sharedWorkers).to.have.lengthOf(2) - expect(sharedWorkers[0].url).to.contain('shared-worker') - expect(sharedWorkers[1].url).to.contain('shared-worker') - }) + expect(sharedWorkers).to.have.lengthOf(2); + expect(sharedWorkers[0].url).to.contain('shared-worker'); + expect(sharedWorkers[1].url).to.contain('shared-worker'); + }); it('can inspect a specific shared worker', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); - const ready = emittedOnce(ipcMain, 'ready') - w.loadFile(path.join(fixturesPath, 'api', 'shared-worker', 'shared-worker.html')) - await ready + const ready = emittedOnce(ipcMain, 'ready'); + w.loadFile(path.join(fixturesPath, 'api', 'shared-worker', 'shared-worker.html')); + await ready; - const sharedWorkers = w.webContents.getAllSharedWorkers() + const sharedWorkers = w.webContents.getAllSharedWorkers(); - const devtoolsOpened = emittedOnce(w.webContents, 'devtools-opened') - w.webContents.inspectSharedWorkerById(sharedWorkers[0].id) - await devtoolsOpened + const devtoolsOpened = emittedOnce(w.webContents, 'devtools-opened'); + w.webContents.inspectSharedWorkerById(sharedWorkers[0].id); + await devtoolsOpened; - const devtoolsClosed = emittedOnce(w.webContents, 'devtools-closed') - w.webContents.closeDevTools() - await devtoolsClosed - }) - }) + const devtoolsClosed = emittedOnce(w.webContents, 'devtools-closed'); + w.webContents.closeDevTools(); + await devtoolsClosed; + }); + }); describe('login event', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); - let server: http.Server - let serverUrl: string - let serverPort: number - let proxyServer: http.Server - let proxyServerPort: number + let server: http.Server; + let serverUrl: string; + let serverPort: number; + let proxyServer: http.Server; + let proxyServerPort: number; before((done) => { server = http.createServer((request, response) => { if (request.url === '/no-auth') { - return response.end('ok') + return response.end('ok'); } if (request.headers.authorization) { - response.writeHead(200, { 'Content-type': 'text/plain' }) - return response.end(request.headers.authorization) + response.writeHead(200, { 'Content-type': 'text/plain' }); + return response.end(request.headers.authorization); } response .writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }) - .end('401') + .end('401'); }).listen(0, '127.0.0.1', () => { - serverPort = (server.address() as AddressInfo).port - serverUrl = `http://127.0.0.1:${serverPort}` - done() - }) - }) + serverPort = (server.address() as AddressInfo).port; + serverUrl = `http://127.0.0.1:${serverPort}`; + done(); + }); + }); before((done) => { proxyServer = http.createServer((request, response) => { if (request.headers['proxy-authorization']) { - response.writeHead(200, { 'Content-type': 'text/plain' }) - return response.end(request.headers['proxy-authorization']) + response.writeHead(200, { 'Content-type': 'text/plain' }); + return response.end(request.headers['proxy-authorization']); } response .writeHead(407, { 'Proxy-Authenticate': 'Basic realm="Foo"' }) - .end() + .end(); }).listen(0, '127.0.0.1', () => { - proxyServerPort = (proxyServer.address() as AddressInfo).port - done() - }) - }) + proxyServerPort = (proxyServer.address() as AddressInfo).port; + done(); + }); + }); afterEach(async () => { - await session.defaultSession.clearAuthCache() - }) + await session.defaultSession.clearAuthCache(); + }); after(() => { - server.close() - proxyServer.close() - }) + server.close(); + proxyServer.close(); + }); it('is emitted when navigating', async () => { - const [user, pass] = ['user', 'pass'] - const w = new BrowserWindow({ show: false }) - let eventRequest: any - let eventAuthInfo: any + const [user, pass] = ['user', 'pass']; + const w = new BrowserWindow({ show: false }); + let eventRequest: any; + let eventAuthInfo: any; w.webContents.on('login', (event, request, authInfo, cb) => { - eventRequest = request - eventAuthInfo = authInfo - event.preventDefault() - cb(user, pass) - }) - await w.loadURL(serverUrl) - const body = await w.webContents.executeJavaScript('document.documentElement.textContent') - expect(body).to.equal(`Basic ${Buffer.from(`${user}:${pass}`).toString('base64')}`) - expect(eventRequest.url).to.equal(serverUrl + '/') - expect(eventAuthInfo.isProxy).to.be.false() - expect(eventAuthInfo.scheme).to.equal('basic') - expect(eventAuthInfo.host).to.equal('127.0.0.1') - expect(eventAuthInfo.port).to.equal(serverPort) - expect(eventAuthInfo.realm).to.equal('Foo') - }) + eventRequest = request; + eventAuthInfo = authInfo; + event.preventDefault(); + cb(user, pass); + }); + await w.loadURL(serverUrl); + const body = await w.webContents.executeJavaScript('document.documentElement.textContent'); + expect(body).to.equal(`Basic ${Buffer.from(`${user}:${pass}`).toString('base64')}`); + expect(eventRequest.url).to.equal(serverUrl + '/'); + expect(eventAuthInfo.isProxy).to.be.false(); + expect(eventAuthInfo.scheme).to.equal('basic'); + expect(eventAuthInfo.host).to.equal('127.0.0.1'); + expect(eventAuthInfo.port).to.equal(serverPort); + expect(eventAuthInfo.realm).to.equal('Foo'); + }); it('is emitted when a proxy requests authorization', async () => { - const customSession = session.fromPartition(`${Math.random()}`) - await customSession.setProxy({ proxyRules: `127.0.0.1:${proxyServerPort}`, proxyBypassRules: '<-loopback>' }) - const [user, pass] = ['user', 'pass'] - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) - let eventRequest: any - let eventAuthInfo: any + const customSession = session.fromPartition(`${Math.random()}`); + await customSession.setProxy({ proxyRules: `127.0.0.1:${proxyServerPort}`, proxyBypassRules: '<-loopback>' }); + const [user, pass] = ['user', 'pass']; + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); + let eventRequest: any; + let eventAuthInfo: any; w.webContents.on('login', (event, request, authInfo, cb) => { - eventRequest = request - eventAuthInfo = authInfo - event.preventDefault() - cb(user, pass) - }) - await w.loadURL(`${serverUrl}/no-auth`) - const body = await w.webContents.executeJavaScript('document.documentElement.textContent') - expect(body).to.equal(`Basic ${Buffer.from(`${user}:${pass}`).toString('base64')}`) - expect(eventRequest.url).to.equal(`${serverUrl}/no-auth`) - expect(eventAuthInfo.isProxy).to.be.true() - expect(eventAuthInfo.scheme).to.equal('basic') - expect(eventAuthInfo.host).to.equal('127.0.0.1') - expect(eventAuthInfo.port).to.equal(proxyServerPort) - expect(eventAuthInfo.realm).to.equal('Foo') - }) + eventRequest = request; + eventAuthInfo = authInfo; + event.preventDefault(); + cb(user, pass); + }); + await w.loadURL(`${serverUrl}/no-auth`); + const body = await w.webContents.executeJavaScript('document.documentElement.textContent'); + expect(body).to.equal(`Basic ${Buffer.from(`${user}:${pass}`).toString('base64')}`); + expect(eventRequest.url).to.equal(`${serverUrl}/no-auth`); + expect(eventAuthInfo.isProxy).to.be.true(); + expect(eventAuthInfo.scheme).to.equal('basic'); + expect(eventAuthInfo.host).to.equal('127.0.0.1'); + expect(eventAuthInfo.port).to.equal(proxyServerPort); + expect(eventAuthInfo.realm).to.equal('Foo'); + }); it('cancels authentication when callback is called with no arguments', async () => { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); w.webContents.on('login', (event, request, authInfo, cb) => { - event.preventDefault() - cb() - }) - await w.loadURL(serverUrl) - const body = await w.webContents.executeJavaScript('document.documentElement.textContent') - expect(body).to.equal('401') - }) - }) -}) + event.preventDefault(); + cb(); + }); + await w.loadURL(serverUrl); + const body = await w.webContents.executeJavaScript('document.documentElement.textContent'); + expect(body).to.equal('401'); + }); + }); +}); diff --git a/spec-main/api-web-contents-view-spec.ts b/spec-main/api-web-contents-view-spec.ts index 9f3688896c6a5..b83acb45cbafe 100644 --- a/spec-main/api-web-contents-view-spec.ts +++ b/spec-main/api-web-contents-view-spec.ts @@ -1,58 +1,58 @@ -import { expect } from 'chai' -import * as ChildProcess from 'child_process' -import * as path from 'path' -import { emittedOnce } from './events-helpers' -import { closeWindow } from './window-helpers' +import { expect } from 'chai'; +import * as ChildProcess from 'child_process'; +import * as path from 'path'; +import { emittedOnce } from './events-helpers'; +import { closeWindow } from './window-helpers'; -import { webContents, TopLevelWindow, WebContentsView } from 'electron' +import { webContents, TopLevelWindow, WebContentsView } from 'electron'; describe('WebContentsView', () => { - let w: TopLevelWindow - afterEach(() => closeWindow(w as any).then(() => { w = null as unknown as TopLevelWindow })) + let w: TopLevelWindow; + afterEach(() => closeWindow(w as any).then(() => { w = null as unknown as TopLevelWindow; })); it('can be used as content view', () => { - const web = (webContents as any).create({}) - w = new TopLevelWindow({ show: false }) - w.setContentView(new WebContentsView(web)) - }) + const web = (webContents as any).create({}); + w = new TopLevelWindow({ show: false }); + w.setContentView(new WebContentsView(web)); + }); it('prevents adding same WebContents', () => { - const web = (webContents as any).create({}) - w = new TopLevelWindow({ show: false }) - w.setContentView(new WebContentsView(web)) + const web = (webContents as any).create({}); + w = new TopLevelWindow({ show: false }); + w.setContentView(new WebContentsView(web)); expect(() => { - w.setContentView(new WebContentsView(web)) - }).to.throw('The WebContents has already been added to a View') - }) + w.setContentView(new WebContentsView(web)); + }).to.throw('The WebContents has already been added to a View'); + }); describe('new WebContentsView()', () => { it('does not crash on exit', async () => { - const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-webcontentsview.js') - const electronPath = process.execPath - const appProcess = ChildProcess.spawn(electronPath, ['--enable-logging', appPath]) - let output = '' - appProcess.stdout.on('data', data => { output += data }) - appProcess.stderr.on('data', data => { output += data }) - const [code] = await emittedOnce(appProcess, 'exit') + const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-webcontentsview.js'); + const electronPath = process.execPath; + const appProcess = ChildProcess.spawn(electronPath, ['--enable-logging', appPath]); + let output = ''; + appProcess.stdout.on('data', data => { output += data; }); + appProcess.stderr.on('data', data => { output += data; }); + const [code] = await emittedOnce(appProcess, 'exit'); if (code !== 0) { - console.log(code, output) + console.log(code, output); } - expect(code).to.equal(0) - }) - }) + expect(code).to.equal(0); + }); + }); function triggerGCByAllocation () { - const arr = [] + const arr = []; for (let i = 0; i < 1000000; i++) { - arr.push([]) + arr.push([]); } - return arr + return arr; } it('doesn\'t crash when GCed during allocation', (done) => { - const web = (webContents as any).create({}) + const web = (webContents as any).create({}); // eslint-disable-next-line no-new - new WebContentsView(web) + new WebContentsView(web); setTimeout(() => { // NB. the crash we're testing for is the lack of a current `v8::Context` // when emitting an event in WebContents's destructor. V8 is inconsistent @@ -61,8 +61,8 @@ describe('WebContentsView', () => { // causes a GC in which there _is_ a current context, so the crash isn't // triggered. Thus, we force a GC by other means: namely, by allocating a // bunch of stuff. - triggerGCByAllocation() - done() - }) - }) -}) + triggerGCByAllocation(); + done(); + }); + }); +}); diff --git a/spec-main/api-web-frame-spec.ts b/spec-main/api-web-frame-spec.ts index 3f14556743dd1..53d3c2c6f616c 100644 --- a/spec-main/api-web-frame-spec.ts +++ b/spec-main/api-web-frame-spec.ts @@ -1,12 +1,12 @@ -import { expect } from 'chai' -import * as path from 'path' -import { BrowserWindow, ipcMain } from 'electron' -import { closeAllWindows } from './window-helpers' +import { expect } from 'chai'; +import * as path from 'path'; +import { BrowserWindow, ipcMain } from 'electron'; +import { closeAllWindows } from './window-helpers'; describe('webFrame module', () => { - const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures') + const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures'); - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('calls a spellcheck provider', async () => { const w = new BrowserWindow({ @@ -14,10 +14,10 @@ describe('webFrame module', () => { webPreferences: { nodeIntegration: true } - }) - await w.loadFile(path.join(fixtures, 'pages', 'webframe-spell-check.html')) - w.focus() - await w.webContents.executeJavaScript('document.querySelector("input").focus()', true) + }); + await w.loadFile(path.join(fixtures, 'pages', 'webframe-spell-check.html')); + w.focus(); + await w.webContents.executeJavaScript('document.querySelector("input").focus()', true); const spellCheckerFeedback = new Promise<[string[], boolean]>(resolve => { @@ -25,16 +25,16 @@ describe('webFrame module', () => { if (words.length === 5) { // The API calls the provider after every completed word. // The promise is resolved only after this event is received with all words. - resolve([words, callbackDefined]) + resolve([words, callbackDefined]); } - }) - }) - const inputText = 'spleling test you\'re ' + }); + }); + const inputText = 'spleling test you\'re '; for (const keyCode of inputText) { - w.webContents.sendInputEvent({ type: 'char', keyCode }) + w.webContents.sendInputEvent({ type: 'char', keyCode }); } - const [words, callbackDefined] = await spellCheckerFeedback - expect(words.sort()).to.deep.equal(['spleling', 'test', 'you\'re', 'you', 're'].sort()) - expect(callbackDefined).to.be.true() - }) -}) + const [words, callbackDefined] = await spellCheckerFeedback; + expect(words.sort()).to.deep.equal(['spleling', 'test', 'you\'re', 'you', 're'].sort()); + expect(callbackDefined).to.be.true(); + }); +}); diff --git a/spec-main/api-web-request-spec.ts b/spec-main/api-web-request-spec.ts index fe98ae4ebcc8a..14faf4ed1d08c 100644 --- a/spec-main/api-web-request-spec.ts +++ b/spec-main/api-web-request-spec.ts @@ -1,285 +1,285 @@ -import { expect } from 'chai' -import * as http from 'http' -import * as qs from 'querystring' -import * as path from 'path' -import * as WebSocket from 'ws' -import { ipcMain, protocol, session, WebContents, webContents } from 'electron' -import { AddressInfo } from 'net' -import { emittedOnce } from './events-helpers' +import { expect } from 'chai'; +import * as http from 'http'; +import * as qs from 'querystring'; +import * as path from 'path'; +import * as WebSocket from 'ws'; +import { ipcMain, protocol, session, WebContents, webContents } from 'electron'; +import { AddressInfo } from 'net'; +import { emittedOnce } from './events-helpers'; -const fixturesPath = path.resolve(__dirname, 'fixtures') +const fixturesPath = path.resolve(__dirname, 'fixtures'); describe('webRequest module', () => { - const ses = session.defaultSession + const ses = session.defaultSession; const server = http.createServer((req, res) => { if (req.url === '/serverRedirect') { - res.statusCode = 301 - res.setHeader('Location', 'http://' + req.rawHeaders[1]) - res.end() + res.statusCode = 301; + res.setHeader('Location', 'http://' + req.rawHeaders[1]); + res.end(); } else { - res.setHeader('Custom', ['Header']) - let content = req.url + res.setHeader('Custom', ['Header']); + let content = req.url; if (req.headers.accept === '*/*;test/header') { - content += 'header/received' + content += 'header/received'; } if (req.headers.origin === 'http://new-origin') { - content += 'new/origin' + content += 'new/origin'; } - res.end(content) + res.end(content); } - }) - let defaultURL: string + }); + let defaultURL: string; before((done) => { - protocol.registerStringProtocol('neworigin', (req, cb) => cb('')) + protocol.registerStringProtocol('neworigin', (req, cb) => cb('')); server.listen(0, '127.0.0.1', () => { - const port = (server.address() as AddressInfo).port - defaultURL = `http://127.0.0.1:${port}/` - done() - }) - }) + const port = (server.address() as AddressInfo).port; + defaultURL = `http://127.0.0.1:${port}/`; + done(); + }); + }); after(() => { - server.close() - protocol.unregisterProtocol('neworigin') - }) + server.close(); + protocol.unregisterProtocol('neworigin'); + }); - let contents: WebContents = null as unknown as WebContents + let contents: WebContents = null as unknown as WebContents; // NB. sandbox: true is used because it makes navigations much (~8x) faster. before(async () => { - contents = (webContents as any).create({ sandbox: true }) - await contents.loadFile(path.join(fixturesPath, 'pages', 'jquery.html')) - }) - after(() => (contents as any).destroy()) + contents = (webContents as any).create({ sandbox: true }); + await contents.loadFile(path.join(fixturesPath, 'pages', 'jquery.html')); + }); + after(() => (contents as any).destroy()); async function ajax (url: string, options = {}) { - return contents.executeJavaScript(`ajax("${url}", ${JSON.stringify(options)})`) + return contents.executeJavaScript(`ajax("${url}", ${JSON.stringify(options)})`); } describe('webRequest.onBeforeRequest', () => { afterEach(() => { - ses.webRequest.onBeforeRequest(null) - }) + ses.webRequest.onBeforeRequest(null); + }); it('can cancel the request', async () => { ses.webRequest.onBeforeRequest((details, callback) => { callback({ cancel: true - }) - }) - await expect(ajax(defaultURL)).to.eventually.be.rejectedWith('404') - }) + }); + }); + await expect(ajax(defaultURL)).to.eventually.be.rejectedWith('404'); + }); it('can filter URLs', async () => { - const filter = { urls: [defaultURL + 'filter/*'] } + const filter = { urls: [defaultURL + 'filter/*'] }; ses.webRequest.onBeforeRequest(filter, (details, callback) => { - callback({ cancel: true }) - }) - const { data } = await ajax(`${defaultURL}nofilter/test`) - expect(data).to.equal('/nofilter/test') - await expect(ajax(`${defaultURL}filter/test`)).to.eventually.be.rejectedWith('404') - }) + callback({ cancel: true }); + }); + const { data } = await ajax(`${defaultURL}nofilter/test`); + expect(data).to.equal('/nofilter/test'); + await expect(ajax(`${defaultURL}filter/test`)).to.eventually.be.rejectedWith('404'); + }); it('receives details object', async () => { ses.webRequest.onBeforeRequest((details, callback) => { - expect(details.id).to.be.a('number') - expect(details.timestamp).to.be.a('number') - expect(details.webContentsId).to.be.a('number') - expect(details.url).to.be.a('string').that.is.equal(defaultURL) - expect(details.method).to.be.a('string').that.is.equal('GET') - expect(details.resourceType).to.be.a('string').that.is.equal('xhr') - expect(details.uploadData).to.be.undefined() - callback({}) - }) - const { data } = await ajax(defaultURL) - expect(data).to.equal('/') - }) + expect(details.id).to.be.a('number'); + expect(details.timestamp).to.be.a('number'); + expect(details.webContentsId).to.be.a('number'); + expect(details.url).to.be.a('string').that.is.equal(defaultURL); + expect(details.method).to.be.a('string').that.is.equal('GET'); + expect(details.resourceType).to.be.a('string').that.is.equal('xhr'); + expect(details.uploadData).to.be.undefined(); + callback({}); + }); + const { data } = await ajax(defaultURL); + expect(data).to.equal('/'); + }); it('receives post data in details object', async () => { const postData = { name: 'post test', type: 'string' - } + }; ses.webRequest.onBeforeRequest((details, callback) => { - expect(details.url).to.equal(defaultURL) - expect(details.method).to.equal('POST') - expect(details.uploadData).to.have.lengthOf(1) - const data = qs.parse(details.uploadData[0].bytes.toString()) - expect(data).to.deep.equal(postData) - callback({ cancel: true }) - }) + expect(details.url).to.equal(defaultURL); + expect(details.method).to.equal('POST'); + expect(details.uploadData).to.have.lengthOf(1); + const data = qs.parse(details.uploadData[0].bytes.toString()); + expect(data).to.deep.equal(postData); + callback({ cancel: true }); + }); await expect(ajax(defaultURL, { type: 'POST', data: postData - })).to.eventually.be.rejectedWith('404') - }) + })).to.eventually.be.rejectedWith('404'); + }); it('can redirect the request', async () => { ses.webRequest.onBeforeRequest((details, callback) => { if (details.url === defaultURL) { - callback({ redirectURL: `${defaultURL}redirect` }) + callback({ redirectURL: `${defaultURL}redirect` }); } else { - callback({}) + callback({}); } - }) - const { data } = await ajax(defaultURL) - expect(data).to.equal('/redirect') - }) + }); + const { data } = await ajax(defaultURL); + expect(data).to.equal('/redirect'); + }); it('does not crash for redirects', async () => { ses.webRequest.onBeforeRequest((details, callback) => { - callback({ cancel: false }) - }) - await ajax(defaultURL + 'serverRedirect') - await ajax(defaultURL + 'serverRedirect') - }) - }) + callback({ cancel: false }); + }); + await ajax(defaultURL + 'serverRedirect'); + await ajax(defaultURL + 'serverRedirect'); + }); + }); describe('webRequest.onBeforeSendHeaders', () => { afterEach(() => { - ses.webRequest.onBeforeSendHeaders(null) - }) + ses.webRequest.onBeforeSendHeaders(null); + }); it('receives details object', async () => { ses.webRequest.onBeforeSendHeaders((details, callback) => { - expect(details.requestHeaders).to.be.an('object') - expect(details.requestHeaders['Foo.Bar']).to.equal('baz') - callback({}) - }) - const { data } = await ajax(defaultURL, { headers: { 'Foo.Bar': 'baz' } }) - expect(data).to.equal('/') - }) + expect(details.requestHeaders).to.be.an('object'); + expect(details.requestHeaders['Foo.Bar']).to.equal('baz'); + callback({}); + }); + const { data } = await ajax(defaultURL, { headers: { 'Foo.Bar': 'baz' } }); + expect(data).to.equal('/'); + }); it('can change the request headers', async () => { ses.webRequest.onBeforeSendHeaders((details, callback) => { - const requestHeaders = details.requestHeaders - requestHeaders.Accept = '*/*;test/header' - callback({ requestHeaders: requestHeaders }) - }) - const { data } = await ajax(defaultURL) - expect(data).to.equal('/header/received') - }) + const requestHeaders = details.requestHeaders; + requestHeaders.Accept = '*/*;test/header'; + callback({ requestHeaders: requestHeaders }); + }); + const { data } = await ajax(defaultURL); + expect(data).to.equal('/header/received'); + }); it('can change request origin', async () => { ses.webRequest.onBeforeSendHeaders((details, callback) => { - const requestHeaders = details.requestHeaders - requestHeaders.Origin = 'http://new-origin' - callback({ requestHeaders: requestHeaders }) - }) - const { data } = await ajax(defaultURL) - expect(data).to.equal('/new/origin') - }) + const requestHeaders = details.requestHeaders; + requestHeaders.Origin = 'http://new-origin'; + callback({ requestHeaders: requestHeaders }); + }); + const { data } = await ajax(defaultURL); + expect(data).to.equal('/new/origin'); + }); it('can capture CORS requests', async () => { - let called = false + let called = false; ses.webRequest.onBeforeSendHeaders((details, callback) => { - called = true - callback({ requestHeaders: details.requestHeaders }) - }) - await ajax('neworigin://host') - expect(called).to.be.true() - }) + called = true; + callback({ requestHeaders: details.requestHeaders }); + }); + await ajax('neworigin://host'); + expect(called).to.be.true(); + }); it('resets the whole headers', async () => { const requestHeaders = { Test: 'header' - } + }; ses.webRequest.onBeforeSendHeaders((details, callback) => { - callback({ requestHeaders: requestHeaders }) - }) + callback({ requestHeaders: requestHeaders }); + }); ses.webRequest.onSendHeaders((details) => { - expect(details.requestHeaders).to.deep.equal(requestHeaders) - }) - await ajax(defaultURL) - }) - }) + expect(details.requestHeaders).to.deep.equal(requestHeaders); + }); + await ajax(defaultURL); + }); + }); describe('webRequest.onSendHeaders', () => { afterEach(() => { - ses.webRequest.onSendHeaders(null) - }) + ses.webRequest.onSendHeaders(null); + }); it('receives details object', async () => { ses.webRequest.onSendHeaders((details) => { - expect(details.requestHeaders).to.be.an('object') - }) - const { data } = await ajax(defaultURL) - expect(data).to.equal('/') - }) - }) + expect(details.requestHeaders).to.be.an('object'); + }); + const { data } = await ajax(defaultURL); + expect(data).to.equal('/'); + }); + }); describe('webRequest.onHeadersReceived', () => { afterEach(() => { - ses.webRequest.onHeadersReceived(null) - }) + ses.webRequest.onHeadersReceived(null); + }); it('receives details object', async () => { ses.webRequest.onHeadersReceived((details, callback) => { - expect(details.statusLine).to.equal('HTTP/1.1 200 OK') - expect(details.statusCode).to.equal(200) - expect(details.responseHeaders!.Custom).to.deep.equal(['Header']) - callback({}) - }) - const { data } = await ajax(defaultURL) - expect(data).to.equal('/') - }) + expect(details.statusLine).to.equal('HTTP/1.1 200 OK'); + expect(details.statusCode).to.equal(200); + expect(details.responseHeaders!.Custom).to.deep.equal(['Header']); + callback({}); + }); + const { data } = await ajax(defaultURL); + expect(data).to.equal('/'); + }); it('can change the response header', async () => { ses.webRequest.onHeadersReceived((details, callback) => { - const responseHeaders = details.responseHeaders! - responseHeaders.Custom = ['Changed'] as any - callback({ responseHeaders: responseHeaders }) - }) - const { headers } = await ajax(defaultURL) - expect(headers).to.match(/^custom: Changed$/m) - }) + const responseHeaders = details.responseHeaders!; + responseHeaders.Custom = ['Changed'] as any; + callback({ responseHeaders: responseHeaders }); + }); + const { headers } = await ajax(defaultURL); + expect(headers).to.match(/^custom: Changed$/m); + }); it('can change response origin', async () => { ses.webRequest.onHeadersReceived((details, callback) => { - const responseHeaders = details.responseHeaders! - responseHeaders['access-control-allow-origin'] = ['http://new-origin'] as any - callback({ responseHeaders: responseHeaders }) - }) - const { headers } = await ajax(defaultURL) - expect(headers).to.match(/^access-control-allow-origin: http:\/\/new-origin$/m) - }) + const responseHeaders = details.responseHeaders!; + responseHeaders['access-control-allow-origin'] = ['http://new-origin'] as any; + callback({ responseHeaders: responseHeaders }); + }); + const { headers } = await ajax(defaultURL); + expect(headers).to.match(/^access-control-allow-origin: http:\/\/new-origin$/m); + }); it('can change headers of CORS responses', async () => { ses.webRequest.onHeadersReceived((details, callback) => { - const responseHeaders = details.responseHeaders! - responseHeaders.Custom = ['Changed'] as any - callback({ responseHeaders: responseHeaders }) - }) - const { headers } = await ajax('neworigin://host') - expect(headers).to.match(/^custom: Changed$/m) - }) + const responseHeaders = details.responseHeaders!; + responseHeaders.Custom = ['Changed'] as any; + callback({ responseHeaders: responseHeaders }); + }); + const { headers } = await ajax('neworigin://host'); + expect(headers).to.match(/^custom: Changed$/m); + }); it('does not change header by default', async () => { ses.webRequest.onHeadersReceived((details, callback) => { - callback({}) - }) - const { data, headers } = await ajax(defaultURL) - expect(headers).to.match(/^custom: Header$/m) - expect(data).to.equal('/') - }) + callback({}); + }); + const { data, headers } = await ajax(defaultURL); + expect(headers).to.match(/^custom: Header$/m); + expect(data).to.equal('/'); + }); it('follows server redirect', async () => { ses.webRequest.onHeadersReceived((details, callback) => { - const responseHeaders = details.responseHeaders - callback({ responseHeaders: responseHeaders }) - }) - const { headers } = await ajax(defaultURL + 'serverRedirect') - expect(headers).to.match(/^custom: Header$/m) - }) + const responseHeaders = details.responseHeaders; + callback({ responseHeaders: responseHeaders }); + }); + const { headers } = await ajax(defaultURL + 'serverRedirect'); + expect(headers).to.match(/^custom: Header$/m); + }); it('can change the header status', async () => { ses.webRequest.onHeadersReceived((details, callback) => { - const responseHeaders = details.responseHeaders + const responseHeaders = details.responseHeaders; callback({ responseHeaders: responseHeaders, statusLine: 'HTTP/1.1 404 Not Found' - }) - }) + }); + }); const { headers } = await contents.executeJavaScript(`new Promise((resolve, reject) => { const options = { ...${JSON.stringify({ url: defaultURL })}, @@ -291,181 +291,181 @@ describe('webRequest module', () => { } } $.ajax(options) - })`) - expect(headers).to.match(/^custom: Header$/m) - }) - }) + })`); + expect(headers).to.match(/^custom: Header$/m); + }); + }); describe('webRequest.onResponseStarted', () => { afterEach(() => { - ses.webRequest.onResponseStarted(null) - }) + ses.webRequest.onResponseStarted(null); + }); it('receives details object', async () => { ses.webRequest.onResponseStarted((details) => { - expect(details.fromCache).to.be.a('boolean') - expect(details.statusLine).to.equal('HTTP/1.1 200 OK') - expect(details.statusCode).to.equal(200) - expect(details.responseHeaders!.Custom).to.deep.equal(['Header']) - }) - const { data, headers } = await ajax(defaultURL) - expect(headers).to.match(/^custom: Header$/m) - expect(data).to.equal('/') - }) - }) + expect(details.fromCache).to.be.a('boolean'); + expect(details.statusLine).to.equal('HTTP/1.1 200 OK'); + expect(details.statusCode).to.equal(200); + expect(details.responseHeaders!.Custom).to.deep.equal(['Header']); + }); + const { data, headers } = await ajax(defaultURL); + expect(headers).to.match(/^custom: Header$/m); + expect(data).to.equal('/'); + }); + }); describe('webRequest.onBeforeRedirect', () => { afterEach(() => { - ses.webRequest.onBeforeRedirect(null) - ses.webRequest.onBeforeRequest(null) - }) + ses.webRequest.onBeforeRedirect(null); + ses.webRequest.onBeforeRequest(null); + }); it('receives details object', async () => { - const redirectURL = defaultURL + 'redirect' + const redirectURL = defaultURL + 'redirect'; ses.webRequest.onBeforeRequest((details, callback) => { if (details.url === defaultURL) { - callback({ redirectURL: redirectURL }) + callback({ redirectURL: redirectURL }); } else { - callback({}) + callback({}); } - }) + }); ses.webRequest.onBeforeRedirect((details) => { - expect(details.fromCache).to.be.a('boolean') - expect(details.statusLine).to.equal('HTTP/1.1 307 Internal Redirect') - expect(details.statusCode).to.equal(307) - expect(details.redirectURL).to.equal(redirectURL) - }) - const { data } = await ajax(defaultURL) - expect(data).to.equal('/redirect') - }) - }) + expect(details.fromCache).to.be.a('boolean'); + expect(details.statusLine).to.equal('HTTP/1.1 307 Internal Redirect'); + expect(details.statusCode).to.equal(307); + expect(details.redirectURL).to.equal(redirectURL); + }); + const { data } = await ajax(defaultURL); + expect(data).to.equal('/redirect'); + }); + }); describe('webRequest.onCompleted', () => { afterEach(() => { - ses.webRequest.onCompleted(null) - }) + ses.webRequest.onCompleted(null); + }); it('receives details object', async () => { ses.webRequest.onCompleted((details) => { - expect(details.fromCache).to.be.a('boolean') - expect(details.statusLine).to.equal('HTTP/1.1 200 OK') - expect(details.statusCode).to.equal(200) - }) - const { data } = await ajax(defaultURL) - expect(data).to.equal('/') - }) - }) + expect(details.fromCache).to.be.a('boolean'); + expect(details.statusLine).to.equal('HTTP/1.1 200 OK'); + expect(details.statusCode).to.equal(200); + }); + const { data } = await ajax(defaultURL); + expect(data).to.equal('/'); + }); + }); describe('webRequest.onErrorOccurred', () => { afterEach(() => { - ses.webRequest.onErrorOccurred(null) - ses.webRequest.onBeforeRequest(null) - }) + ses.webRequest.onErrorOccurred(null); + ses.webRequest.onBeforeRequest(null); + }); it('receives details object', async () => { ses.webRequest.onBeforeRequest((details, callback) => { - callback({ cancel: true }) - }) + callback({ cancel: true }); + }); ses.webRequest.onErrorOccurred((details) => { - expect(details.error).to.equal('net::ERR_BLOCKED_BY_CLIENT') - }) - await expect(ajax(defaultURL)).to.eventually.be.rejectedWith('404') - }) - }) + expect(details.error).to.equal('net::ERR_BLOCKED_BY_CLIENT'); + }); + await expect(ajax(defaultURL)).to.eventually.be.rejectedWith('404'); + }); + }); describe('WebSocket connections', () => { it('can be proxyed', async () => { // Setup server. - const reqHeaders : { [key: string] : any } = {} + const reqHeaders : { [key: string] : any } = {}; const server = http.createServer((req, res) => { - reqHeaders[req.url!] = req.headers - res.setHeader('foo1', 'bar1') - res.end('ok') - }) - const wss = new WebSocket.Server({ noServer: true }) + reqHeaders[req.url!] = req.headers; + res.setHeader('foo1', 'bar1'); + res.end('ok'); + }); + const wss = new WebSocket.Server({ noServer: true }); wss.on('connection', function connection (ws) { ws.on('message', function incoming (message) { if (message === 'foo') { - ws.send('bar') + ws.send('bar'); } - }) - }) + }); + }); server.on('upgrade', function upgrade (request, socket, head) { - const pathname = require('url').parse(request.url).pathname + const pathname = require('url').parse(request.url).pathname; if (pathname === '/websocket') { - reqHeaders[request.url] = request.headers + reqHeaders[request.url] = request.headers; wss.handleUpgrade(request, socket, head, function done (ws) { - wss.emit('connection', ws, request) - }) + wss.emit('connection', ws, request); + }); } - }) + }); // Start server. - await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) - const port = String((server.address() as AddressInfo).port) + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); + const port = String((server.address() as AddressInfo).port); // Use a separate session for testing. - const ses = session.fromPartition('WebRequestWebSocket') + const ses = session.fromPartition('WebRequestWebSocket'); // Setup listeners. - const receivedHeaders : { [key: string] : any } = {} + const receivedHeaders : { [key: string] : any } = {}; ses.webRequest.onBeforeSendHeaders((details, callback) => { - details.requestHeaders.foo = 'bar' - callback({ requestHeaders: details.requestHeaders }) - }) + details.requestHeaders.foo = 'bar'; + callback({ requestHeaders: details.requestHeaders }); + }); ses.webRequest.onHeadersReceived((details, callback) => { - const pathname = require('url').parse(details.url).pathname - receivedHeaders[pathname] = details.responseHeaders - callback({ cancel: false }) - }) + const pathname = require('url').parse(details.url).pathname; + receivedHeaders[pathname] = details.responseHeaders; + callback({ cancel: false }); + }); ses.webRequest.onResponseStarted((details) => { if (details.url.startsWith('ws://')) { - expect(details.responseHeaders!.Connection[0]).be.equal('Upgrade') + expect(details.responseHeaders!.Connection[0]).be.equal('Upgrade'); } else if (details.url.startsWith('http')) { - expect(details.responseHeaders!.foo1[0]).be.equal('bar1') + expect(details.responseHeaders!.foo1[0]).be.equal('bar1'); } - }) + }); ses.webRequest.onSendHeaders((details) => { if (details.url.startsWith('ws://')) { - expect(details.requestHeaders.foo).be.equal('bar') - expect(details.requestHeaders.Upgrade).be.equal('websocket') + expect(details.requestHeaders.foo).be.equal('bar'); + expect(details.requestHeaders.Upgrade).be.equal('websocket'); } else if (details.url.startsWith('http')) { - expect(details.requestHeaders.foo).be.equal('bar') + expect(details.requestHeaders.foo).be.equal('bar'); } - }) + }); ses.webRequest.onCompleted((details) => { if (details.url.startsWith('ws://')) { - expect(details.error).be.equal('net::ERR_WS_UPGRADE') + expect(details.error).be.equal('net::ERR_WS_UPGRADE'); } else if (details.url.startsWith('http')) { - expect(details.error).be.equal('net::OK') + expect(details.error).be.equal('net::OK'); } - }) + }); const contents = (webContents as any).create({ session: ses, nodeIntegration: true, webSecurity: false - }) + }); // Cleanup. after(() => { - contents.destroy() - server.close() - ses.webRequest.onBeforeRequest(null) - ses.webRequest.onBeforeSendHeaders(null) - ses.webRequest.onHeadersReceived(null) - ses.webRequest.onResponseStarted(null) - ses.webRequest.onSendHeaders(null) - ses.webRequest.onCompleted(null) - }) - - contents.loadFile(path.join(fixturesPath, 'api', 'webrequest.html'), { query: { port } }) - await emittedOnce(ipcMain, 'websocket-success') - - expect(receivedHeaders['/websocket'].Upgrade[0]).to.equal('websocket') - expect(receivedHeaders['/'].foo1[0]).to.equal('bar1') - expect(reqHeaders['/websocket'].foo).to.equal('bar') - expect(reqHeaders['/'].foo).to.equal('bar') - }) - }) -}) + contents.destroy(); + server.close(); + ses.webRequest.onBeforeRequest(null); + ses.webRequest.onBeforeSendHeaders(null); + ses.webRequest.onHeadersReceived(null); + ses.webRequest.onResponseStarted(null); + ses.webRequest.onSendHeaders(null); + ses.webRequest.onCompleted(null); + }); + + contents.loadFile(path.join(fixturesPath, 'api', 'webrequest.html'), { query: { port } }); + await emittedOnce(ipcMain, 'websocket-success'); + + expect(receivedHeaders['/websocket'].Upgrade[0]).to.equal('websocket'); + expect(receivedHeaders['/'].foo1[0]).to.equal('bar1'); + expect(reqHeaders['/websocket'].foo).to.equal('bar'); + expect(reqHeaders['/'].foo).to.equal('bar'); + }); + }); +}); diff --git a/spec-main/asar-spec.ts b/spec-main/asar-spec.ts index 9df18720fcd1e..7a1af715920db 100644 --- a/spec-main/asar-spec.ts +++ b/spec-main/asar-spec.ts @@ -1,19 +1,19 @@ -import { expect } from 'chai' -import * as path from 'path' -import { BrowserWindow, ipcMain } from 'electron' -import { closeAllWindows } from './window-helpers' +import { expect } from 'chai'; +import * as path from 'path'; +import { BrowserWindow, ipcMain } from 'electron'; +import { closeAllWindows } from './window-helpers'; describe('asar package', () => { - const fixtures = path.join(__dirname, '..', 'spec', 'fixtures') - const asarDir = path.join(fixtures, 'test.asar') + const fixtures = path.join(__dirname, '..', 'spec', 'fixtures'); + const asarDir = path.join(fixtures, 'test.asar'); - afterEach(closeAllWindows) + afterEach(closeAllWindows); describe('asar protocol', () => { it('sets __dirname correctly', function (done) { after(function () { - ipcMain.removeAllListeners('dirname') - }) + ipcMain.removeAllListeners('dirname'); + }); const w = new BrowserWindow({ show: false, @@ -22,19 +22,19 @@ describe('asar package', () => { webPreferences: { nodeIntegration: true } - }) - const p = path.resolve(asarDir, 'web.asar', 'index.html') + }); + const p = path.resolve(asarDir, 'web.asar', 'index.html'); ipcMain.once('dirname', function (event, dirname) { - expect(dirname).to.equal(path.dirname(p)) - done() - }) - w.loadFile(p) - }) + expect(dirname).to.equal(path.dirname(p)); + done(); + }); + w.loadFile(p); + }); it('loads script tag in html', function (done) { after(function () { - ipcMain.removeAllListeners('ping') - }) + ipcMain.removeAllListeners('ping'); + }); const w = new BrowserWindow({ show: false, @@ -43,21 +43,21 @@ describe('asar package', () => { webPreferences: { nodeIntegration: true } - }) - const p = path.resolve(asarDir, 'script.asar', 'index.html') - w.loadFile(p) + }); + const p = path.resolve(asarDir, 'script.asar', 'index.html'); + w.loadFile(p); ipcMain.once('ping', function (event, message) { - expect(message).to.equal('pong') - done() - }) - }) + expect(message).to.equal('pong'); + done(); + }); + }); it('loads video tag in html', function (done) { - this.timeout(60000) + this.timeout(60000); after(function () { - ipcMain.removeAllListeners('asar-video') - }) + ipcMain.removeAllListeners('asar-video'); + }); const w = new BrowserWindow({ show: false, @@ -66,17 +66,17 @@ describe('asar package', () => { webPreferences: { nodeIntegration: true } - }) - const p = path.resolve(asarDir, 'video.asar', 'index.html') - w.loadFile(p) + }); + const p = path.resolve(asarDir, 'video.asar', 'index.html'); + w.loadFile(p); ipcMain.on('asar-video', function (event, message, error) { if (message === 'ended') { - expect(error).to.be.null() - done() + expect(error).to.be.null(); + done(); } else if (message === 'error') { - done(error) + done(error); } - }) - }) - }) -}) + }); + }); + }); +}); diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index 28dd119496582..f3a61a9d1d8b7 100644 --- a/spec-main/chromium-spec.ts +++ b/spec-main/chromium-spec.ts @@ -1,32 +1,32 @@ -import { expect } from 'chai' -import { BrowserWindow, WebContents, session, ipcMain, app, protocol, webContents } from 'electron' -import { emittedOnce } from './events-helpers' -import { closeAllWindows } from './window-helpers' -import * as https from 'https' -import * as http from 'http' -import * as path from 'path' -import * as fs from 'fs' -import * as url from 'url' -import * as ChildProcess from 'child_process' -import { EventEmitter } from 'events' -import { promisify } from 'util' -import { ifit, ifdescribe } from './spec-helpers' -import { AddressInfo } from 'net' - -const features = process.electronBinding('features') - -const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures') +import { expect } from 'chai'; +import { BrowserWindow, WebContents, session, ipcMain, app, protocol, webContents } from 'electron'; +import { emittedOnce } from './events-helpers'; +import { closeAllWindows } from './window-helpers'; +import * as https from 'https'; +import * as http from 'http'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as url from 'url'; +import * as ChildProcess from 'child_process'; +import { EventEmitter } from 'events'; +import { promisify } from 'util'; +import { ifit, ifdescribe } from './spec-helpers'; +import { AddressInfo } from 'net'; + +const features = process.electronBinding('features'); + +const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures'); describe('reporting api', () => { it('sends a report for a deprecation', async () => { - const reports = new EventEmitter() + const reports = new EventEmitter(); // The Reporting API only works on https with valid certs. To dodge having // to set up a trusted certificate, hack the validator. session.defaultSession.setCertificateVerifyProc((req, cb) => { - cb(0) - }) - const certPath = path.join(fixturesPath, 'certificates') + cb(0); + }); + const certPath = path.join(fixturesPath, 'certificates'); const options = { key: fs.readFileSync(path.join(certPath, 'server.key')), cert: fs.readFileSync(path.join(certPath, 'server.pem')), @@ -36,65 +36,65 @@ describe('reporting api', () => { ], requestCert: true, rejectUnauthorized: false - } + }; const server = https.createServer(options, (req, res) => { if (req.url === '/report') { - let data = '' - req.on('data', (d) => { data += d.toString('utf-8') }) + let data = ''; + req.on('data', (d) => { data += d.toString('utf-8'); }); req.on('end', () => { - reports.emit('report', JSON.parse(data)) - }) + reports.emit('report', JSON.parse(data)); + }); } res.setHeader('Report-To', JSON.stringify({ group: 'default', max_age: 120, endpoints: [{ url: `https://localhost:${(server.address() as any).port}/report` }] - })) - res.setHeader('Content-Type', 'text/html') + })); + res.setHeader('Content-Type', 'text/html'); // using the deprecated `webkitRequestAnimationFrame` will trigger a // "deprecation" report. - res.end('') - }) - await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) + res.end(''); + }); + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const bw = new BrowserWindow({ show: false - }) + }); try { - const reportGenerated = emittedOnce(reports, 'report') - const url = `https://localhost:${(server.address() as any).port}/a` - await bw.loadURL(url) - const [report] = await reportGenerated - expect(report).to.be.an('array') - expect(report[0].type).to.equal('deprecation') - expect(report[0].url).to.equal(url) - expect(report[0].body.id).to.equal('PrefixedRequestAnimationFrame') + const reportGenerated = emittedOnce(reports, 'report'); + const url = `https://localhost:${(server.address() as any).port}/a`; + await bw.loadURL(url); + const [report] = await reportGenerated; + expect(report).to.be.an('array'); + expect(report[0].type).to.equal('deprecation'); + expect(report[0].url).to.equal(url); + expect(report[0].body.id).to.equal('PrefixedRequestAnimationFrame'); } finally { - bw.destroy() - server.close() + bw.destroy(); + server.close(); } - }) -}) + }); +}); describe('window.postMessage', () => { afterEach(async () => { - await closeAllWindows() - }) + await closeAllWindows(); + }); it('sets the source and origin correctly', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - w.loadURL(`file://${fixturesPath}/pages/window-open-postMessage-driver.html`) - const [, message] = await emittedOnce(ipcMain, 'complete') - expect(message.data).to.equal('testing') - expect(message.origin).to.equal('file://') - expect(message.sourceEqualsOpener).to.equal(true) - expect(message.eventOrigin).to.equal('file://') - }) -}) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + w.loadURL(`file://${fixturesPath}/pages/window-open-postMessage-driver.html`); + const [, message] = await emittedOnce(ipcMain, 'complete'); + expect(message.data).to.equal('testing'); + expect(message.origin).to.equal('file://'); + expect(message.sourceEqualsOpener).to.equal(true); + expect(message.eventOrigin).to.equal('file://'); + }); +}); describe('focus handling', () => { - let webviewContents: WebContents = null as unknown as WebContents - let w: BrowserWindow = null as unknown as BrowserWindow + let webviewContents: WebContents = null as unknown as WebContents; + let w: BrowserWindow = null as unknown as BrowserWindow; beforeEach(async () => { w = new BrowserWindow({ @@ -103,126 +103,126 @@ describe('focus handling', () => { nodeIntegration: true, webviewTag: true } - }) + }); - const webviewReady = emittedOnce(w.webContents, 'did-attach-webview') - await w.loadFile(path.join(fixturesPath, 'pages', 'tab-focus-loop-elements.html')) - const [, wvContents] = await webviewReady - webviewContents = wvContents - await emittedOnce(webviewContents, 'did-finish-load') - w.focus() - }) + const webviewReady = emittedOnce(w.webContents, 'did-attach-webview'); + await w.loadFile(path.join(fixturesPath, 'pages', 'tab-focus-loop-elements.html')); + const [, wvContents] = await webviewReady; + webviewContents = wvContents; + await emittedOnce(webviewContents, 'did-finish-load'); + w.focus(); + }); afterEach(() => { - webviewContents = null as unknown as WebContents - w.destroy() - w = null as unknown as BrowserWindow - }) + webviewContents = null as unknown as WebContents; + w.destroy(); + w = null as unknown as BrowserWindow; + }); const expectFocusChange = async () => { - const [, focusedElementId] = await emittedOnce(ipcMain, 'focus-changed') - return focusedElementId - } + const [, focusedElementId] = await emittedOnce(ipcMain, 'focus-changed'); + return focusedElementId; + }; describe('a TAB press', () => { const tabPressEvent: any = { type: 'keyDown', keyCode: 'Tab' - } + }; it('moves focus to the next focusable item', async () => { - let focusChange = expectFocusChange() - w.webContents.sendInputEvent(tabPressEvent) - let focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-element-1', `should start focused in element-1, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - w.webContents.sendInputEvent(tabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-element-2', `focus should've moved to element-2, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - w.webContents.sendInputEvent(tabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-wv-element-1', `focus should've moved to the webview's element-1, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - webviewContents.sendInputEvent(tabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-wv-element-2', `focus should've moved to the webview's element-2, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - webviewContents.sendInputEvent(tabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-element-3', `focus should've moved to element-3, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - w.webContents.sendInputEvent(tabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-element-1', `focus should've looped back to element-1, it's instead in ${focusedElementId}`) - }) - }) + let focusChange = expectFocusChange(); + w.webContents.sendInputEvent(tabPressEvent); + let focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-element-1', `should start focused in element-1, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + w.webContents.sendInputEvent(tabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-element-2', `focus should've moved to element-2, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + w.webContents.sendInputEvent(tabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-wv-element-1', `focus should've moved to the webview's element-1, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + webviewContents.sendInputEvent(tabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-wv-element-2', `focus should've moved to the webview's element-2, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + webviewContents.sendInputEvent(tabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-element-3', `focus should've moved to element-3, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + w.webContents.sendInputEvent(tabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-element-1', `focus should've looped back to element-1, it's instead in ${focusedElementId}`); + }); + }); describe('a SHIFT + TAB press', () => { const shiftTabPressEvent: any = { type: 'keyDown', modifiers: ['Shift'], keyCode: 'Tab' - } + }; it('moves focus to the previous focusable item', async () => { - let focusChange = expectFocusChange() - w.webContents.sendInputEvent(shiftTabPressEvent) - let focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-element-3', `should start focused in element-3, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - w.webContents.sendInputEvent(shiftTabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-wv-element-2', `focus should've moved to the webview's element-2, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - webviewContents.sendInputEvent(shiftTabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-wv-element-1', `focus should've moved to the webview's element-1, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - webviewContents.sendInputEvent(shiftTabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-element-2', `focus should've moved to element-2, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - w.webContents.sendInputEvent(shiftTabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-element-1', `focus should've moved to element-1, it's instead in ${focusedElementId}`) - - focusChange = expectFocusChange() - w.webContents.sendInputEvent(shiftTabPressEvent) - focusedElementId = await focusChange - expect(focusedElementId).to.equal('BUTTON-element-3', `focus should've looped back to element-3, it's instead in ${focusedElementId}`) - }) - }) -}) + let focusChange = expectFocusChange(); + w.webContents.sendInputEvent(shiftTabPressEvent); + let focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-element-3', `should start focused in element-3, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + w.webContents.sendInputEvent(shiftTabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-wv-element-2', `focus should've moved to the webview's element-2, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + webviewContents.sendInputEvent(shiftTabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-wv-element-1', `focus should've moved to the webview's element-1, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + webviewContents.sendInputEvent(shiftTabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-element-2', `focus should've moved to element-2, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + w.webContents.sendInputEvent(shiftTabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-element-1', `focus should've moved to element-1, it's instead in ${focusedElementId}`); + + focusChange = expectFocusChange(); + w.webContents.sendInputEvent(shiftTabPressEvent); + focusedElementId = await focusChange; + expect(focusedElementId).to.equal('BUTTON-element-3', `focus should've looped back to element-3, it's instead in ${focusedElementId}`); + }); + }); +}); describe('web security', () => { - afterEach(closeAllWindows) - let server: http.Server - let serverUrl: string + afterEach(closeAllWindows); + let server: http.Server; + let serverUrl: string; before(async () => { server = http.createServer((req, res) => { - res.setHeader('Content-Type', 'text/html') - res.end('') - }) - await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) - serverUrl = `http://localhost:${(server.address() as any).port}` - }) + res.setHeader('Content-Type', 'text/html'); + res.end(''); + }); + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); + serverUrl = `http://localhost:${(server.address() as any).port}`; + }); after(() => { - server.close() - }) + server.close(); + }); it('engages CORB when web security is not disabled', async () => { - const w = new BrowserWindow({ show: true, webPreferences: { webSecurity: true, nodeIntegration: true } }) - const p = emittedOnce(ipcMain, 'success') + const w = new BrowserWindow({ show: true, webPreferences: { webSecurity: true, nodeIntegration: true } }); + const p = emittedOnce(ipcMain, 'success'); await w.loadURL(`data:text/html,`) - await p - }) + `); + await p; + }); it('bypasses CORB when web security is disabled', async () => { - const w = new BrowserWindow({ show: true, webPreferences: { webSecurity: false, nodeIntegration: true } }) - const p = emittedOnce(ipcMain, 'success') + const w = new BrowserWindow({ show: true, webPreferences: { webSecurity: false, nodeIntegration: true } }); + const p = emittedOnce(ipcMain, 'success'); await w.loadURL(`data:text/html, - `) - await p - }) -}) + `); + await p; + }); +}); describe('command line switches', () => { describe('--lang switch', () => { - const currentLocale = app.getLocale() + const currentLocale = app.getLocale(); const testLocale = (locale: string, result: string, done: () => void) => { - const appPath = path.join(fixturesPath, 'api', 'locale-check') - const electronPath = process.execPath - let output = '' - const appProcess = ChildProcess.spawn(electronPath, [appPath, `--lang=${locale}`]) + const appPath = path.join(fixturesPath, 'api', 'locale-check'); + const electronPath = process.execPath; + let output = ''; + const appProcess = ChildProcess.spawn(electronPath, [appPath, `--lang=${locale}`]); - appProcess.stdout.on('data', (data) => { output += data }) + appProcess.stdout.on('data', (data) => { output += data; }); appProcess.stdout.on('end', () => { - output = output.replace(/(\r\n|\n|\r)/gm, '') - expect(output).to.equal(result) - done() - }) - } + output = output.replace(/(\r\n|\n|\r)/gm, ''); + expect(output).to.equal(result); + done(); + }); + }; - it('should set the locale', (done) => testLocale('fr', 'fr', done)) - it('should not set an invalid locale', (done) => testLocale('asdfkl', currentLocale, done)) - }) + it('should set the locale', (done) => testLocale('fr', 'fr', done)); + it('should not set an invalid locale', (done) => testLocale('asdfkl', currentLocale, done)); + }); describe('--remote-debugging-port switch', () => { it('should display the discovery page', (done) => { - const electronPath = process.execPath - let output = '' - const appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-port=']) + const electronPath = process.execPath; + let output = ''; + const appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-port=']); appProcess.stderr.on('data', (data) => { - output += data - const m = /DevTools listening on ws:\/\/127.0.0.1:(\d+)\//.exec(output) + output += data; + const m = /DevTools listening on ws:\/\/127.0.0.1:(\d+)\//.exec(output); if (m) { - appProcess.stderr.removeAllListeners('data') - const port = m[1] + appProcess.stderr.removeAllListeners('data'); + const port = m[1]; http.get(`http://127.0.0.1:${port}`, (res) => { - res.destroy() - appProcess.kill() - expect(res.statusCode).to.eql(200) - expect(parseInt(res.headers['content-length']!)).to.be.greaterThan(0) - done() - }) + res.destroy(); + appProcess.kill(); + expect(res.statusCode).to.eql(200); + expect(parseInt(res.headers['content-length']!)).to.be.greaterThan(0); + done(); + }); } - }) - }) - }) -}) + }); + }); + }); +}); describe('chromium features', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); describe('accessing key names also used as Node.js module names', () => { it('does not crash', (done) => { - const w = new BrowserWindow({ show: false }) - w.webContents.once('did-finish-load', () => { done() }) - w.webContents.once('crashed', () => done(new Error('WebContents crashed.'))) - w.loadFile(path.join(fixturesPath, 'pages', 'external-string.html')) - }) - }) + const w = new BrowserWindow({ show: false }); + w.webContents.once('did-finish-load', () => { done(); }); + w.webContents.once('crashed', () => done(new Error('WebContents crashed.'))); + w.loadFile(path.join(fixturesPath, 'pages', 'external-string.html')); + }); + }); describe('loading jquery', () => { it('does not crash', (done) => { - const w = new BrowserWindow({ show: false }) - w.webContents.once('did-finish-load', () => { done() }) - w.webContents.once('crashed', () => done(new Error('WebContents crashed.'))) - w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'jquery.html')) - }) - }) + const w = new BrowserWindow({ show: false }); + w.webContents.once('did-finish-load', () => { done(); }); + w.webContents.once('crashed', () => done(new Error('WebContents crashed.'))); + w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'jquery.html')); + }); + }); describe('navigator.languages', () => { it('should return the system locale only', async () => { - const appLocale = app.getLocale() - const w = new BrowserWindow({ show: false }) - await w.loadURL('about:blank') - const languages = await w.webContents.executeJavaScript('navigator.languages') - expect(languages).to.deep.equal([appLocale]) - }) - }) + const appLocale = app.getLocale(); + const w = new BrowserWindow({ show: false }); + await w.loadURL('about:blank'); + const languages = await w.webContents.executeJavaScript('navigator.languages'); + expect(languages).to.deep.equal([appLocale]); + }); + }); describe('navigator.serviceWorker', () => { it('should register for file scheme', (done) => { @@ -331,38 +331,38 @@ describe('chromium features', () => { nodeIntegration: true, partition: 'sw-file-scheme-spec' } - }) + }); w.webContents.on('ipc-message', (event, channel, message) => { if (channel === 'reload') { - w.webContents.reload() + w.webContents.reload(); } else if (channel === 'error') { - done(message) + done(message); } else if (channel === 'response') { - expect(message).to.equal('Hello from serviceWorker!') + expect(message).to.equal('Hello from serviceWorker!'); session.fromPartition('sw-file-scheme-spec').clearStorageData({ storages: ['serviceworkers'] - }).then(() => done()) + }).then(() => done()); } - }) - w.webContents.on('crashed', () => done(new Error('WebContents crashed.'))) - w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'index.html')) - }) + }); + w.webContents.on('crashed', () => done(new Error('WebContents crashed.'))); + w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'index.html')); + }); it('should register for intercepted file scheme', (done) => { - const customSession = session.fromPartition('intercept-file') + const customSession = session.fromPartition('intercept-file'); customSession.protocol.interceptBufferProtocol('file', (request, callback) => { - let file = url.parse(request.url).pathname! - if (file[0] === '/' && process.platform === 'win32') file = file.slice(1) + let file = url.parse(request.url).pathname!; + if (file[0] === '/' && process.platform === 'win32') file = file.slice(1); - const content = fs.readFileSync(path.normalize(file)) - const ext = path.extname(file) - let type = 'text/html' + const content = fs.readFileSync(path.normalize(file)); + const ext = path.extname(file); + let type = 'text/html'; - if (ext === '.js') type = 'application/javascript' - callback({ data: content, mimeType: type } as any) + if (ext === '.js') type = 'application/javascript'; + callback({ data: content, mimeType: type } as any); }, (error) => { - if (error) done(error) - }) + if (error) done(error); + }); const w = new BrowserWindow({ show: false, @@ -370,32 +370,32 @@ describe('chromium features', () => { nodeIntegration: true, session: customSession } - }) + }); w.webContents.on('ipc-message', (event, channel, message) => { if (channel === 'reload') { - w.webContents.reload() + w.webContents.reload(); } else if (channel === 'error') { - done(`unexpected error : ${message}`) + done(`unexpected error : ${message}`); } else if (channel === 'response') { - expect(message).to.equal('Hello from serviceWorker!') + expect(message).to.equal('Hello from serviceWorker!'); customSession.clearStorageData({ storages: ['serviceworkers'] }).then(() => { - customSession.protocol.uninterceptProtocol('file', error => done(error)) - }) + customSession.protocol.uninterceptProtocol('file', error => done(error)); + }); } - }) - w.webContents.on('crashed', () => done(new Error('WebContents crashed.'))) - w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'index.html')) - }) - }) + }); + w.webContents.on('crashed', () => done(new Error('WebContents crashed.'))); + w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'index.html')); + }); + }); describe('navigator.geolocation', () => { before(function () { if (!features.isFakeLocationProviderEnabled()) { - return this.skip() + return this.skip(); } - }) + }); it('returns error when permission is denied', (done) => { const w = new BrowserWindow({ @@ -404,46 +404,46 @@ describe('chromium features', () => { nodeIntegration: true, partition: 'geolocation-spec' } - }) + }); w.webContents.on('ipc-message', (event, channel) => { if (channel === 'success') { - done() + done(); } else { - done('unexpected response from geolocation api') + done('unexpected response from geolocation api'); } - }) + }); w.webContents.session.setPermissionRequestHandler((wc, permission, callback) => { if (permission === 'geolocation') { - callback(false) + callback(false); } else { - callback(true) + callback(true); } - }) - w.loadFile(path.join(fixturesPath, 'pages', 'geolocation', 'index.html')) - }) - }) + }); + w.loadFile(path.join(fixturesPath, 'pages', 'geolocation', 'index.html')); + }); + }); describe('form submit', () => { - let server: http.Server - let serverUrl: string + let server: http.Server; + let serverUrl: string; before(async () => { server = http.createServer((req, res) => { - let body = '' + let body = ''; req.on('data', (chunk) => { - body += chunk - }) - res.setHeader('Content-Type', 'application/json') + body += chunk; + }); + res.setHeader('Content-Type', 'application/json'); req.on('end', () => { - res.end(`body:${body}`) - }) - }) - await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) - serverUrl = `http://localhost:${(server.address() as any).port}` - }) + res.end(`body:${body}`); + }); + }); + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); + serverUrl = `http://localhost:${(server.address() as any).port}`; + }); after(async () => { - server.close() - await closeAllWindows() + server.close(); + await closeAllWindows(); }); [true, false].forEach((isSandboxEnabled) => @@ -454,23 +454,23 @@ describe('chromium features', () => { webPreferences: { sandbox: isSandboxEnabled } - }) + }); - await w.loadFile(path.join(fixturesPath, 'pages', 'form-with-data.html')) + await w.loadFile(path.join(fixturesPath, 'pages', 'form-with-data.html')); - const loadPromise = emittedOnce(w.webContents, 'did-finish-load') + const loadPromise = emittedOnce(w.webContents, 'did-finish-load'); w.webContents.executeJavaScript(` const form = document.querySelector('form') form.action = '${serverUrl}'; form.submit(); - `) + `); - await loadPromise + await loadPromise; - const res = await w.webContents.executeJavaScript('document.body.innerText') - expect(res).to.equal('body:greeting=hello') - }) + const res = await w.webContents.executeJavaScript('document.body.innerText'); + expect(res).to.equal('body:greeting=hello'); + }); it('posts data to a new window with target=_blank', async () => { const w = new BrowserWindow({ @@ -478,95 +478,95 @@ describe('chromium features', () => { webPreferences: { sandbox: isSandboxEnabled } - }) + }); - await w.loadFile(path.join(fixturesPath, 'pages', 'form-with-data.html')) + await w.loadFile(path.join(fixturesPath, 'pages', 'form-with-data.html')); - const windowCreatedPromise = emittedOnce(app, 'browser-window-created') + const windowCreatedPromise = emittedOnce(app, 'browser-window-created'); w.webContents.executeJavaScript(` const form = document.querySelector('form') form.action = '${serverUrl}'; form.target = '_blank'; form.submit(); - `) + `); - const [, newWin] = await windowCreatedPromise + const [, newWin] = await windowCreatedPromise; - const res = await newWin.webContents.executeJavaScript('document.body.innerText') - expect(res).to.equal('body:greeting=hello') - }) + const res = await newWin.webContents.executeJavaScript('document.body.innerText'); + expect(res).to.equal('body:greeting=hello'); + }); }) - ) - }) + ); + }); describe('window.open', () => { for (const show of [true, false]) { it(`inherits parent visibility over parent {show=${show}} option`, (done) => { - const w = new BrowserWindow({ show }) + const w = new BrowserWindow({ show }); // toggle visibility if (show) { - w.hide() + w.hide(); } else { - w.show() + w.show(); } w.webContents.once('new-window', (e, url, frameName, disposition, options) => { - expect(options.show).to.equal(w.isVisible()) - w.close() - done() - }) - w.loadFile(path.join(fixturesPath, 'pages', 'window-open.html')) - }) + expect(options.show).to.equal(w.isVisible()); + w.close(); + done(); + }); + w.loadFile(path.join(fixturesPath, 'pages', 'window-open.html')); + }); } it('disables node integration when it is disabled on the parent window for chrome devtools URLs', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - w.loadURL('about:blank') + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + w.loadURL('about:blank'); w.webContents.executeJavaScript(` b = window.open('devtools://devtools/bundled/inspector.html', '', 'nodeIntegration=no,show=no') - `) - const [, contents] = await emittedOnce(app, 'web-contents-created') - const typeofProcessGlobal = await contents.executeJavaScript('typeof process') - expect(typeofProcessGlobal).to.equal('undefined') - }) + `); + const [, contents] = await emittedOnce(app, 'web-contents-created'); + const typeofProcessGlobal = await contents.executeJavaScript('typeof process'); + expect(typeofProcessGlobal).to.equal('undefined'); + }); it('disables JavaScript when it is disabled on the parent window', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - w.webContents.loadURL('about:blank') + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + w.webContents.loadURL('about:blank'); const windowUrl = require('url').format({ pathname: `${fixturesPath}/pages/window-no-javascript.html`, protocol: 'file', slashes: true - }) + }); w.webContents.executeJavaScript(` b = window.open(${JSON.stringify(windowUrl)}, '', 'javascript=no,show=no') - `) - const [, contents] = await emittedOnce(app, 'web-contents-created') - await emittedOnce(contents, 'did-finish-load') + `); + const [, contents] = await emittedOnce(app, 'web-contents-created'); + await emittedOnce(contents, 'did-finish-load'); // Click link on page - contents.sendInputEvent({ type: 'mouseDown', clickCount: 1, x: 1, y: 1 }) - contents.sendInputEvent({ type: 'mouseUp', clickCount: 1, x: 1, y: 1 }) - const [, window] = await emittedOnce(app, 'browser-window-created') - const preferences = (window.webContents as any).getLastWebPreferences() - expect(preferences.javascript).to.be.false() - }) + contents.sendInputEvent({ type: 'mouseDown', clickCount: 1, x: 1, y: 1 }); + contents.sendInputEvent({ type: 'mouseUp', clickCount: 1, x: 1, y: 1 }); + const [, window] = await emittedOnce(app, 'browser-window-created'); + const preferences = (window.webContents as any).getLastWebPreferences(); + expect(preferences.javascript).to.be.false(); + }); it('handles cycles when merging the parent options into the child options', (done) => { - const foo = {} as any - foo.bar = foo + const foo = {} as any; + foo.bar = foo; foo.baz = { hello: { world: true } - } - foo.baz2 = foo.baz - const w = new BrowserWindow({ show: false, foo: foo } as any) + }; + foo.baz2 = foo.baz; + const w = new BrowserWindow({ show: false, foo: foo } as any); - w.loadFile(path.join(fixturesPath, 'pages', 'window-open.html')) + w.loadFile(path.join(fixturesPath, 'pages', 'window-open.html')); w.webContents.once('new-window', (event, url, frameName, disposition, options) => { - expect(options.show).to.be.false() + expect(options.show).to.be.false(); expect((options as any).foo).to.deep.equal({ bar: undefined, baz: { @@ -579,82 +579,82 @@ describe('chromium features', () => { world: true } } - }) - done() - }) - }) + }); + done(); + }); + }); it('defines a window.location getter', async () => { - let targetURL: string + let targetURL: string; if (process.platform === 'win32') { - targetURL = `file:///${fixturesPath.replace(/\\/g, '/')}/pages/base-page.html` + targetURL = `file:///${fixturesPath.replace(/\\/g, '/')}/pages/base-page.html`; } else { - targetURL = `file://${fixturesPath}/pages/base-page.html` + targetURL = `file://${fixturesPath}/pages/base-page.html`; } - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - w.webContents.executeJavaScript(`{ b = window.open(${JSON.stringify(targetURL)}); null }`) - const [, window] = await emittedOnce(app, 'browser-window-created') - await emittedOnce(window.webContents, 'did-finish-load') - expect(await w.webContents.executeJavaScript('b.location.href')).to.equal(targetURL) - }) + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + w.webContents.executeJavaScript(`{ b = window.open(${JSON.stringify(targetURL)}); null }`); + const [, window] = await emittedOnce(app, 'browser-window-created'); + await emittedOnce(window.webContents, 'did-finish-load'); + expect(await w.webContents.executeJavaScript('b.location.href')).to.equal(targetURL); + }); it('defines a window.location setter', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - w.webContents.executeJavaScript('{ b = window.open("about:blank"); null }') - const [, { webContents }] = await emittedOnce(app, 'browser-window-created') - await emittedOnce(webContents, 'did-finish-load') + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + w.webContents.executeJavaScript('{ b = window.open("about:blank"); null }'); + const [, { webContents }] = await emittedOnce(app, 'browser-window-created'); + await emittedOnce(webContents, 'did-finish-load'); // When it loads, redirect - w.webContents.executeJavaScript(`{ b.location = ${JSON.stringify(`file://${fixturesPath}/pages/base-page.html`)}; null }`) - await emittedOnce(webContents, 'did-finish-load') - }) + w.webContents.executeJavaScript(`{ b.location = ${JSON.stringify(`file://${fixturesPath}/pages/base-page.html`)}; null }`); + await emittedOnce(webContents, 'did-finish-load'); + }); it('defines a window.location.href setter', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - w.webContents.executeJavaScript('{ b = window.open("about:blank"); null }') - const [, { webContents }] = await emittedOnce(app, 'browser-window-created') - await emittedOnce(webContents, 'did-finish-load') + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + w.webContents.executeJavaScript('{ b = window.open("about:blank"); null }'); + const [, { webContents }] = await emittedOnce(app, 'browser-window-created'); + await emittedOnce(webContents, 'did-finish-load'); // When it loads, redirect - w.webContents.executeJavaScript(`{ b.location.href = ${JSON.stringify(`file://${fixturesPath}/pages/base-page.html`)}; null }`) - await emittedOnce(webContents, 'did-finish-load') - }) + w.webContents.executeJavaScript(`{ b.location.href = ${JSON.stringify(`file://${fixturesPath}/pages/base-page.html`)}; null }`); + await emittedOnce(webContents, 'did-finish-load'); + }); it('open a blank page when no URL is specified', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - w.webContents.executeJavaScript('{ b = window.open(); null }') - const [, { webContents }] = await emittedOnce(app, 'browser-window-created') - await emittedOnce(webContents, 'did-finish-load') - expect(await w.webContents.executeJavaScript('b.location.href')).to.equal('about:blank') - }) + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + w.webContents.executeJavaScript('{ b = window.open(); null }'); + const [, { webContents }] = await emittedOnce(app, 'browser-window-created'); + await emittedOnce(webContents, 'did-finish-load'); + expect(await w.webContents.executeJavaScript('b.location.href')).to.equal('about:blank'); + }); it('open a blank page when an empty URL is specified', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - w.webContents.executeJavaScript('{ b = window.open(\'\'); null }') - const [, { webContents }] = await emittedOnce(app, 'browser-window-created') - await emittedOnce(webContents, 'did-finish-load') - expect(await w.webContents.executeJavaScript('b.location.href')).to.equal('about:blank') - }) + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + w.webContents.executeJavaScript('{ b = window.open(\'\'); null }'); + const [, { webContents }] = await emittedOnce(app, 'browser-window-created'); + await emittedOnce(webContents, 'did-finish-load'); + expect(await w.webContents.executeJavaScript('b.location.href')).to.equal('about:blank'); + }); it('sets the window title to the specified frameName', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - w.webContents.executeJavaScript('{ b = window.open(\'\', \'hello\'); null }') - const [, window] = await emittedOnce(app, 'browser-window-created') - expect(window.getTitle()).to.equal('hello') - }) + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + w.webContents.executeJavaScript('{ b = window.open(\'\', \'hello\'); null }'); + const [, window] = await emittedOnce(app, 'browser-window-created'); + expect(window.getTitle()).to.equal('hello'); + }); it('does not throw an exception when the frameName is a built-in object property', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - w.webContents.executeJavaScript('{ b = window.open(\'\', \'__proto__\'); null }') - const [, window] = await emittedOnce(app, 'browser-window-created') - expect(window.getTitle()).to.equal('__proto__') - }) - }) + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + w.webContents.executeJavaScript('{ b = window.open(\'\', \'__proto__\'); null }'); + const [, window] = await emittedOnce(app, 'browser-window-created'); + expect(window.getTitle()).to.equal('__proto__'); + }); + }); describe('window.opener', () => { it('is null for main window', async () => { @@ -663,75 +663,75 @@ describe('chromium features', () => { webPreferences: { nodeIntegration: true } - }) - w.loadFile(path.join(fixturesPath, 'pages', 'window-opener.html')) - const [, channel, opener] = await emittedOnce(w.webContents, 'ipc-message') - expect(channel).to.equal('opener') - expect(opener).to.equal(null) - }) - }) + }); + w.loadFile(path.join(fixturesPath, 'pages', 'window-opener.html')); + const [, channel, opener] = await emittedOnce(w.webContents, 'ipc-message'); + expect(channel).to.equal('opener'); + expect(opener).to.equal(null); + }); + }); describe('navigator.mediaDevices', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); afterEach(() => { - session.defaultSession.setPermissionCheckHandler(null) - }) + session.defaultSession.setPermissionCheckHandler(null); + }); it('can return labels of enumerated devices', async () => { - const w = new BrowserWindow({ show: false }) - w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')) - const labels = await w.webContents.executeJavaScript('navigator.mediaDevices.enumerateDevices().then(ds => ds.map(d => d.label))') - expect(labels.some((l: any) => l)).to.be.true() - }) + const w = new BrowserWindow({ show: false }); + w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); + const labels = await w.webContents.executeJavaScript('navigator.mediaDevices.enumerateDevices().then(ds => ds.map(d => d.label))'); + expect(labels.some((l: any) => l)).to.be.true(); + }); it('does not return labels of enumerated devices when permission denied', async () => { - session.defaultSession.setPermissionCheckHandler(() => false) - const w = new BrowserWindow({ show: false }) - w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')) - const labels = await w.webContents.executeJavaScript('navigator.mediaDevices.enumerateDevices().then(ds => ds.map(d => d.label))') - expect(labels.some((l: any) => l)).to.be.false() - }) + session.defaultSession.setPermissionCheckHandler(() => false); + const w = new BrowserWindow({ show: false }); + w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); + const labels = await w.webContents.executeJavaScript('navigator.mediaDevices.enumerateDevices().then(ds => ds.map(d => d.label))'); + expect(labels.some((l: any) => l)).to.be.false(); + }); it('returns the same device ids across reloads', async () => { - const ses = session.fromPartition('persist:media-device-id') + const ses = session.fromPartition('persist:media-device-id'); const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, session: ses } - }) - w.loadFile(path.join(fixturesPath, 'pages', 'media-id-reset.html')) - const [, firstDeviceIds] = await emittedOnce(ipcMain, 'deviceIds') - const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds', () => w.webContents.reload()) - expect(firstDeviceIds).to.deep.equal(secondDeviceIds) - }) + }); + w.loadFile(path.join(fixturesPath, 'pages', 'media-id-reset.html')); + const [, firstDeviceIds] = await emittedOnce(ipcMain, 'deviceIds'); + const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds', () => w.webContents.reload()); + expect(firstDeviceIds).to.deep.equal(secondDeviceIds); + }); it('can return new device id when cookie storage is cleared', async () => { - const ses = session.fromPartition('persist:media-device-id') + const ses = session.fromPartition('persist:media-device-id'); const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, session: ses } - }) - w.loadFile(path.join(fixturesPath, 'pages', 'media-id-reset.html')) - const [, firstDeviceIds] = await emittedOnce(ipcMain, 'deviceIds') - await ses.clearStorageData({ storages: ['cookies'] }) - const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds', () => w.webContents.reload()) - expect(firstDeviceIds).to.not.deep.equal(secondDeviceIds) - }) - }) + }); + w.loadFile(path.join(fixturesPath, 'pages', 'media-id-reset.html')); + const [, firstDeviceIds] = await emittedOnce(ipcMain, 'deviceIds'); + await ses.clearStorageData({ storages: ['cookies'] }); + const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds', () => w.webContents.reload()); + expect(firstDeviceIds).to.not.deep.equal(secondDeviceIds); + }); + }); describe('window.opener access', () => { - const scheme = 'app' + const scheme = 'app'; - const fileUrl = `file://${fixturesPath}/pages/window-opener-location.html` - const httpUrl1 = `${scheme}://origin1` - const httpUrl2 = `${scheme}://origin2` - const fileBlank = `file://${fixturesPath}/pages/blank.html` - const httpBlank = `${scheme}://origin1/blank` + const fileUrl = `file://${fixturesPath}/pages/window-opener-location.html`; + const httpUrl1 = `${scheme}://origin1`; + const httpUrl2 = `${scheme}://origin2`; + const fileBlank = `file://${fixturesPath}/pages/blank.html`; + const httpBlank = `${scheme}://origin1/blank`; const table = [ { parent: fileBlank, child: httpUrl1, nodeIntegration: false, nativeWindowOpen: false, openerAccessible: false }, @@ -759,54 +759,54 @@ describe('chromium features', () => { { parent: httpBlank, child: httpUrl2, nodeIntegration: false, nativeWindowOpen: true, openerAccessible: false }, { parent: httpBlank, child: httpUrl2, nodeIntegration: true, nativeWindowOpen: false, openerAccessible: true }, { parent: httpBlank, child: httpUrl2, nodeIntegration: true, nativeWindowOpen: true, openerAccessible: false } - ] - const s = (url: string) => url.startsWith('file') ? 'file://...' : url + ]; + const s = (url: string) => url.startsWith('file') ? 'file://...' : url; before(async () => { await promisify(protocol.registerFileProtocol)(scheme, (request, callback) => { if (request.url.includes('blank')) { - callback(`${fixturesPath}/pages/blank.html`) + callback(`${fixturesPath}/pages/blank.html`); } else { - callback(`${fixturesPath}/pages/window-opener-location.html`) + callback(`${fixturesPath}/pages/window-opener-location.html`); } - }) - }) + }); + }); after(async () => { - await promisify(protocol.unregisterProtocol)(scheme) - }) - afterEach(closeAllWindows) + await promisify(protocol.unregisterProtocol)(scheme); + }); + afterEach(closeAllWindows); describe('when opened from main window', () => { for (const { parent, child, nodeIntegration, nativeWindowOpen, openerAccessible } of table) { for (const sandboxPopup of [false, true]) { - const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen} sandboxPopup=${sandboxPopup}, child should ${openerAccessible ? '' : 'not '}be able to access opener` + const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen} sandboxPopup=${sandboxPopup}, child should ${openerAccessible ? '' : 'not '}be able to access opener`; it(description, async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen } }) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen } }); w.webContents.once('new-window', (e, url, frameName, disposition, options) => { - options!.webPreferences!.sandbox = sandboxPopup - }) - await w.loadURL(parent) + options!.webPreferences!.sandbox = sandboxPopup; + }); + await w.loadURL(parent); const childOpenerLocation = await w.webContents.executeJavaScript(`new Promise(resolve => { window.addEventListener('message', function f(e) { resolve(e.data) }) window.open(${JSON.stringify(child)}, "", "show=no,nodeIntegration=${nodeIntegration ? 'yes' : 'no'}") - })`) + })`); if (openerAccessible) { - expect(childOpenerLocation).to.be.a('string') + expect(childOpenerLocation).to.be.a('string'); } else { - expect(childOpenerLocation).to.be.null() + expect(childOpenerLocation).to.be.null(); } - }) + }); } } - }) + }); describe('when opened from ', () => { for (const { parent, child, nodeIntegration, nativeWindowOpen, openerAccessible } of table) { - const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen}, child should ${openerAccessible ? '' : 'not '}be able to access opener` + const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen}, child should ${openerAccessible ? '' : 'not '}be able to access opener`; // WebView erroneously allows access to the parent window when nativeWindowOpen is false. - const skip = !nativeWindowOpen && !openerAccessible + const skip = !nativeWindowOpen && !openerAccessible; ifit(!skip)(description, async () => { // This test involves three contexts: // 1. The root BrowserWindow in which the test is run, @@ -815,8 +815,8 @@ describe('chromium features', () => { // We are testing whether context (3) can access context (2) under various conditions. // This is context (1), the base window for the test. - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }) - await w.loadURL('about:blank') + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }); + await w.loadURL('about:blank'); const parentCode = `new Promise((resolve) => { // This is context (3), a child window of the WebView. @@ -824,7 +824,7 @@ describe('chromium features', () => { window.addEventListener("message", e => { resolve(e.data) }) - })` + })`; const childOpenerLocation = await w.webContents.executeJavaScript(`new Promise((resolve, reject) => { // This is context (2), a WebView which will call window.open() const webview = new WebView() @@ -836,218 +836,218 @@ describe('chromium features', () => { webview.executeJavaScript(${JSON.stringify(parentCode)}).then(resolve, reject) }) document.body.appendChild(webview) - })`) + })`); if (openerAccessible) { - expect(childOpenerLocation).to.be.a('string') + expect(childOpenerLocation).to.be.a('string'); } else { - expect(childOpenerLocation).to.be.null() + expect(childOpenerLocation).to.be.null(); } - }) + }); } - }) - }) + }); + }); describe('storage', () => { describe('custom non standard schemes', () => { - const protocolName = 'storage' - let contents: WebContents + const protocolName = 'storage'; + let contents: WebContents; before((done) => { protocol.registerFileProtocol(protocolName, (request, callback) => { - const parsedUrl = url.parse(request.url) - let filename + const parsedUrl = url.parse(request.url); + let filename; switch (parsedUrl.pathname) { - case '/localStorage' : filename = 'local_storage.html'; break - case '/sessionStorage' : filename = 'session_storage.html'; break - case '/WebSQL' : filename = 'web_sql.html'; break - case '/indexedDB' : filename = 'indexed_db.html'; break - case '/cookie' : filename = 'cookie.html'; break - default : filename = '' + case '/localStorage' : filename = 'local_storage.html'; break; + case '/sessionStorage' : filename = 'session_storage.html'; break; + case '/WebSQL' : filename = 'web_sql.html'; break; + case '/indexedDB' : filename = 'indexed_db.html'; break; + case '/cookie' : filename = 'cookie.html'; break; + default : filename = ''; } - callback({ path: `${fixturesPath}/pages/storage/${filename}` }) - }, (error) => done(error)) - }) + callback({ path: `${fixturesPath}/pages/storage/${filename}` }); + }, (error) => done(error)); + }); after((done) => { - protocol.unregisterProtocol(protocolName, () => done()) - }) + protocol.unregisterProtocol(protocolName, () => done()); + }); beforeEach(() => { contents = (webContents as any).create({ nodeIntegration: true - }) - }) + }); + }); afterEach(() => { - (contents as any).destroy() - contents = null as any - }) + (contents as any).destroy(); + contents = null as any; + }); it('cannot access localStorage', (done) => { ipcMain.once('local-storage-response', (event, error) => { - expect(error).to.equal('Failed to read the \'localStorage\' property from \'Window\': Access is denied for this document.') - done() - }) - contents.loadURL(protocolName + '://host/localStorage') - }) + expect(error).to.equal('Failed to read the \'localStorage\' property from \'Window\': Access is denied for this document.'); + done(); + }); + contents.loadURL(protocolName + '://host/localStorage'); + }); it('cannot access sessionStorage', (done) => { ipcMain.once('session-storage-response', (event, error) => { - expect(error).to.equal('Failed to read the \'sessionStorage\' property from \'Window\': Access is denied for this document.') - done() - }) - contents.loadURL(`${protocolName}://host/sessionStorage`) - }) + expect(error).to.equal('Failed to read the \'sessionStorage\' property from \'Window\': Access is denied for this document.'); + done(); + }); + contents.loadURL(`${protocolName}://host/sessionStorage`); + }); it('cannot access WebSQL database', (done) => { ipcMain.once('web-sql-response', (event, error) => { - expect(error).to.equal('Failed to execute \'openDatabase\' on \'Window\': Access to the WebDatabase API is denied in this context.') - done() - }) - contents.loadURL(`${protocolName}://host/WebSQL`) - }) + expect(error).to.equal('Failed to execute \'openDatabase\' on \'Window\': Access to the WebDatabase API is denied in this context.'); + done(); + }); + contents.loadURL(`${protocolName}://host/WebSQL`); + }); it('cannot access indexedDB', (done) => { ipcMain.once('indexed-db-response', (event, error) => { - expect(error).to.equal('Failed to execute \'open\' on \'IDBFactory\': access to the Indexed Database API is denied in this context.') - done() - }) - contents.loadURL(`${protocolName}://host/indexedDB`) - }) + expect(error).to.equal('Failed to execute \'open\' on \'IDBFactory\': access to the Indexed Database API is denied in this context.'); + done(); + }); + contents.loadURL(`${protocolName}://host/indexedDB`); + }); it('cannot access cookie', (done) => { ipcMain.once('cookie-response', (event, error) => { - expect(error).to.equal('Failed to set the \'cookie\' property on \'Document\': Access is denied for this document.') - done() - }) - contents.loadURL(`${protocolName}://host/cookie`) - }) - }) + expect(error).to.equal('Failed to set the \'cookie\' property on \'Document\': Access is denied for this document.'); + done(); + }); + contents.loadURL(`${protocolName}://host/cookie`); + }); + }); describe('can be accessed', () => { - let server: http.Server - let serverUrl: string - let serverCrossSiteUrl: string + let server: http.Server; + let serverUrl: string; + let serverCrossSiteUrl: string; before((done) => { server = http.createServer((req, res) => { const respond = () => { if (req.url === '/redirect-cross-site') { - res.setHeader('Location', `${serverCrossSiteUrl}/redirected`) - res.statusCode = 302 - res.end() + res.setHeader('Location', `${serverCrossSiteUrl}/redirected`); + res.statusCode = 302; + res.end(); } else if (req.url === '/redirected') { - res.end('') + res.end(''); } else { - res.end() + res.end(); } - } - setTimeout(respond, 0) - }) + }; + setTimeout(respond, 0); + }); server.listen(0, '127.0.0.1', () => { - serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - serverCrossSiteUrl = `http://localhost:${(server.address() as AddressInfo).port}` - done() - }) - }) + serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + serverCrossSiteUrl = `http://localhost:${(server.address() as AddressInfo).port}`; + done(); + }); + }); after(() => { - server.close() - server = null as any - }) + server.close(); + server = null as any; + }); - afterEach(closeAllWindows) + afterEach(closeAllWindows); const testLocalStorageAfterXSiteRedirect = (testTitle: string, extraPreferences = {}) => { it(testTitle, (done) => { const w = new BrowserWindow({ show: false, ...extraPreferences - }) - let redirected = false + }); + let redirected = false; w.webContents.on('crashed', () => { - expect.fail('renderer crashed / was killed') - }) + expect.fail('renderer crashed / was killed'); + }); w.webContents.on('did-redirect-navigation', (event, url) => { - expect(url).to.equal(`${serverCrossSiteUrl}/redirected`) - redirected = true - }) + expect(url).to.equal(`${serverCrossSiteUrl}/redirected`); + redirected = true; + }); w.webContents.on('did-finish-load', () => { - expect(redirected).to.be.true('didnt redirect') - done() - }) - w.loadURL(`${serverUrl}/redirect-cross-site`) - }) - } - - testLocalStorageAfterXSiteRedirect('after a cross-site redirect') - testLocalStorageAfterXSiteRedirect('after a cross-site redirect in sandbox mode', { sandbox: true }) - }) - }) + expect(redirected).to.be.true('didnt redirect'); + done(); + }); + w.loadURL(`${serverUrl}/redirect-cross-site`); + }); + }; + + testLocalStorageAfterXSiteRedirect('after a cross-site redirect'); + testLocalStorageAfterXSiteRedirect('after a cross-site redirect in sandbox mode', { sandbox: true }); + }); + }); ifdescribe(features.isPDFViewerEnabled())('PDF Viewer', () => { const pdfSource = url.format({ pathname: path.join(__dirname, 'fixtures', 'cat.pdf').replace(/\\/g, '/'), protocol: 'file', slashes: true - }) + }); it('opens when loading a pdf resource as top level navigation', async () => { - const w = new BrowserWindow({ show: false }) - w.loadURL(pdfSource) - const [, contents] = await emittedOnce(app, 'web-contents-created') - expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html') - await emittedOnce(contents, 'did-finish-load') - }) + const w = new BrowserWindow({ show: false }); + w.loadURL(pdfSource); + const [, contents] = await emittedOnce(app, 'web-contents-created'); + expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html'); + await emittedOnce(contents, 'did-finish-load'); + }); it('opens when loading a pdf resource in a iframe', async () => { - const w = new BrowserWindow({ show: false }) - w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'pdf-in-iframe.html')) - const [, contents] = await emittedOnce(app, 'web-contents-created') - expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html') - await emittedOnce(contents, 'did-finish-load') - }) - }) + const w = new BrowserWindow({ show: false }); + w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'pdf-in-iframe.html')); + const [, contents] = await emittedOnce(app, 'web-contents-created'); + expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html'); + await emittedOnce(contents, 'did-finish-load'); + }); + }); describe('window.history', () => { describe('window.history.pushState', () => { it('should push state after calling history.pushState() from the same url', async () => { - const w = new BrowserWindow({ show: false }) - await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')) + const w = new BrowserWindow({ show: false }); + await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); // History should have current page by now. - expect((w.webContents as any).length()).to.equal(1) + expect((w.webContents as any).length()).to.equal(1); - const waitCommit = emittedOnce(w.webContents, 'navigation-entry-committed') - w.webContents.executeJavaScript('window.history.pushState({}, "")') - await waitCommit + const waitCommit = emittedOnce(w.webContents, 'navigation-entry-committed'); + w.webContents.executeJavaScript('window.history.pushState({}, "")'); + await waitCommit; // Initial page + pushed state. - expect((w.webContents as any).length()).to.equal(2) - }) - }) - }) -}) + expect((w.webContents as any).length()).to.equal(2); + }); + }); + }); +}); describe('font fallback', () => { async function getRenderedFonts (html: string) { - const w = new BrowserWindow({ show: false }) + const w = new BrowserWindow({ show: false }); try { - await w.loadURL(`data:text/html,${html}`) - w.webContents.debugger.attach() - const sendCommand = (method: string, commandParams?: any) => w.webContents.debugger.sendCommand(method, commandParams) - const { nodeId } = (await sendCommand('DOM.getDocument')).root.children[0] - await sendCommand('CSS.enable') - const { fonts } = await sendCommand('CSS.getPlatformFontsForNode', { nodeId }) - return fonts + await w.loadURL(`data:text/html,${html}`); + w.webContents.debugger.attach(); + const sendCommand = (method: string, commandParams?: any) => w.webContents.debugger.sendCommand(method, commandParams); + const { nodeId } = (await sendCommand('DOM.getDocument')).root.children[0]; + await sendCommand('CSS.enable'); + const { fonts } = await sendCommand('CSS.getPlatformFontsForNode', { nodeId }); + return fonts; } finally { - w.close() + w.close(); } } it('should use Helvetica for sans-serif on Mac, and Arial on Windows and Linux', async () => { - const html = 'test' - const fonts = await getRenderedFonts(html) - expect(fonts).to.be.an('array') - expect(fonts).to.have.length(1) - if (process.platform === 'win32') { expect(fonts[0].familyName).to.equal('Arial') } else if (process.platform === 'darwin') { expect(fonts[0].familyName).to.equal('Helvetica') } else if (process.platform === 'linux') { expect(fonts[0].familyName).to.equal('DejaVu Sans') } // I think this depends on the distro? We don't specify a default. - }) + const html = 'test'; + const fonts = await getRenderedFonts(html); + expect(fonts).to.be.an('array'); + expect(fonts).to.have.length(1); + if (process.platform === 'win32') { expect(fonts[0].familyName).to.equal('Arial'); } else if (process.platform === 'darwin') { expect(fonts[0].familyName).to.equal('Helvetica'); } else if (process.platform === 'linux') { expect(fonts[0].familyName).to.equal('DejaVu Sans'); } // I think this depends on the distro? We don't specify a default. + }); ifit(process.platform !== 'linux')('should fall back to Japanese font for sans-serif Japanese script', async function () { const html = ` @@ -1057,29 +1057,29 @@ describe('font fallback', () => { test 智史 - ` - const fonts = await getRenderedFonts(html) - expect(fonts).to.be.an('array') - expect(fonts).to.have.length(1) - if (process.platform === 'win32') { expect(fonts[0].familyName).to.be.oneOf(['Meiryo', 'Yu Gothic']) } else if (process.platform === 'darwin') { expect(fonts[0].familyName).to.equal('Hiragino Kaku Gothic ProN') } - }) -}) + `; + const fonts = await getRenderedFonts(html); + expect(fonts).to.be.an('array'); + expect(fonts).to.have.length(1); + if (process.platform === 'win32') { expect(fonts[0].familyName).to.be.oneOf(['Meiryo', 'Yu Gothic']); } else if (process.platform === 'darwin') { expect(fonts[0].familyName).to.equal('Hiragino Kaku Gothic ProN'); } + }); +}); describe('iframe using HTML fullscreen API while window is OS-fullscreened', () => { const fullscreenChildHtml = promisify(fs.readFile)( path.join(fixturesPath, 'pages', 'fullscreen-oopif.html') - ) - let w: BrowserWindow, server: http.Server + ); + let w: BrowserWindow, server: http.Server; before(() => { server = http.createServer(async (_req, res) => { - res.writeHead(200, { 'Content-Type': 'text/html' }) - res.write(await fullscreenChildHtml) - res.end() - }) + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.write(await fullscreenChildHtml); + res.end(); + }); - server.listen(8989, '127.0.0.1') - }) + server.listen(8989, '127.0.0.1'); + }); beforeEach(() => { w = new BrowserWindow({ @@ -1089,56 +1089,56 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () nodeIntegration: true, nodeIntegrationInSubFrames: true } - }) - }) + }); + }); afterEach(async () => { await closeAllWindows() - ;(w as any) = null - server.close() - }) + ;(w as any) = null; + server.close(); + }); it('can fullscreen from out-of-process iframes (OOPIFs)', done => { ipcMain.once('fullscreenChange', async () => { const fullscreenWidth = await w.webContents.executeJavaScript( "document.querySelector('iframe').offsetWidth" - ) - expect(fullscreenWidth > 0).to.be.true() + ); + expect(fullscreenWidth > 0).to.be.true(); await w.webContents.executeJavaScript( "document.querySelector('iframe').contentWindow.postMessage('exitFullscreen', '*')" - ) + ); - await new Promise(resolve => setTimeout(resolve, 500)) + await new Promise(resolve => setTimeout(resolve, 500)); const width = await w.webContents.executeJavaScript( "document.querySelector('iframe').offsetWidth" - ) - expect(width).to.equal(0) + ); + expect(width).to.equal(0); - done() - }) + done(); + }); const html = - '' - w.loadURL(`data:text/html,${html}`) - }) + ''; + w.loadURL(`data:text/html,${html}`); + }); it('can fullscreen from in-process iframes', done => { ipcMain.once('fullscreenChange', async () => { const fullscreenWidth = await w.webContents.executeJavaScript( "document.querySelector('iframe').offsetWidth" - ) - expect(fullscreenWidth > 0).to.true() + ); + expect(fullscreenWidth > 0).to.true(); - await w.webContents.executeJavaScript('document.exitFullscreen()') + await w.webContents.executeJavaScript('document.exitFullscreen()'); const width = await w.webContents.executeJavaScript( "document.querySelector('iframe').offsetWidth" - ) - expect(width).to.equal(0) - done() - }) - - w.loadFile(path.join(fixturesPath, 'pages', 'fullscreen-ipif.html')) - }) -}) + ); + expect(width).to.equal(0); + done(); + }); + + w.loadFile(path.join(fixturesPath, 'pages', 'fullscreen-ipif.html')); + }); +}); diff --git a/spec-main/events-helpers.ts b/spec-main/events-helpers.ts index 20ab69dd25d8b..3a0c58b5388dc 100644 --- a/spec-main/events-helpers.ts +++ b/spec-main/events-helpers.ts @@ -10,9 +10,9 @@ */ export const waitForEvent = (target: EventTarget, eventName: string) => { return new Promise(resolve => { - target.addEventListener(eventName, resolve, { once: true }) - }) -} + target.addEventListener(eventName, resolve, { once: true }); + }); +}; /** * @param {!EventEmitter} emitter @@ -20,36 +20,36 @@ export const waitForEvent = (target: EventTarget, eventName: string) => { * @return {!Promise} With Event as the first item. */ export const emittedOnce = (emitter: NodeJS.EventEmitter, eventName: string, trigger?: () => void) => { - return emittedNTimes(emitter, eventName, 1, trigger).then(([result]) => result) -} + return emittedNTimes(emitter, eventName, 1, trigger).then(([result]) => result); +}; export const emittedNTimes = async (emitter: NodeJS.EventEmitter, eventName: string, times: number, trigger?: () => void) => { - const events: any[][] = [] + const events: any[][] = []; const p = new Promise(resolve => { const handler = (...args: any[]) => { - events.push(args) + events.push(args); if (events.length === times) { - emitter.removeListener(eventName, handler) - resolve(events) + emitter.removeListener(eventName, handler); + resolve(events); } - } - emitter.on(eventName, handler) - }) + }; + emitter.on(eventName, handler); + }); if (trigger) { - await Promise.resolve(trigger()) + await Promise.resolve(trigger()); } - return p -} + return p; +}; export const emittedUntil = async (emitter: NodeJS.EventEmitter, eventName: string, untilFn: Function) => { const p = new Promise(resolve => { const handler = (...args: any[]) => { if (untilFn(...args)) { - emitter.removeListener(eventName, handler) - resolve(args) + emitter.removeListener(eventName, handler); + resolve(args); } - } - emitter.on(eventName, handler) - }) - return p -} + }; + emitter.on(eventName, handler); + }); + return p; +}; diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 68bd64b57d7a4..da48e590b06bd 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -1,364 +1,364 @@ -import { expect } from 'chai' -import { app, session, BrowserWindow, ipcMain, WebContents, Extension } from 'electron' -import { closeAllWindows, closeWindow } from './window-helpers' -import * as http from 'http' -import { AddressInfo } from 'net' -import * as path from 'path' -import * as fs from 'fs' -import { ifdescribe } from './spec-helpers' -import { emittedOnce, emittedNTimes } from './events-helpers' - -const fixtures = path.join(__dirname, 'fixtures') +import { expect } from 'chai'; +import { app, session, BrowserWindow, ipcMain, WebContents, Extension } from 'electron'; +import { closeAllWindows, closeWindow } from './window-helpers'; +import * as http from 'http'; +import { AddressInfo } from 'net'; +import * as path from 'path'; +import * as fs from 'fs'; +import { ifdescribe } from './spec-helpers'; +import { emittedOnce, emittedNTimes } from './events-helpers'; + +const fixtures = path.join(__dirname, 'fixtures'); ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome extensions', () => { // NB. extensions are only allowed on http://, https:// and ftp:// (!) urls by default. - let server: http.Server - let url: string + let server: http.Server; + let url: string; before(async () => { - server = http.createServer((req, res) => res.end()) + server = http.createServer((req, res) => res.end()); await new Promise(resolve => server.listen(0, '127.0.0.1', () => { - url = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - resolve() - })) - }) + url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + resolve(); + })); + }); after(() => { - server.close() - }) - afterEach(closeAllWindows) + server.close(); + }); + afterEach(closeAllWindows); afterEach(() => { session.defaultSession.getAllExtensions().forEach((e: any) => { - session.defaultSession.removeExtension(e.id) - }) - }) + session.defaultSession.removeExtension(e.id); + }); + }); it('loads an extension', async () => { // NB. we have to use a persist: session (i.e. non-OTR) because the // extension registry is redirected to the main session. so installing an // extension in an in-memory session results in it being installed in the // default session. - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) - await w.loadURL(url) - const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(bg).to.equal('red') - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); + await w.loadURL(url); + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(bg).to.equal('red'); + }); it('serializes a loaded extension', async () => { - const extensionPath = path.join(fixtures, 'extensions', 'red-bg') - const manifest = JSON.parse(fs.readFileSync(path.join(extensionPath, 'manifest.json'), 'utf-8')) - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - const extension = await customSession.loadExtension(extensionPath) - expect(extension.id).to.be.a('string') - expect(extension.name).to.be.a('string') - expect(extension.path).to.be.a('string') - expect(extension.version).to.be.a('string') - expect(extension.url).to.be.a('string') - expect(extension.manifest).to.deep.equal(manifest) - }) + const extensionPath = path.join(fixtures, 'extensions', 'red-bg'); + const manifest = JSON.parse(fs.readFileSync(path.join(extensionPath, 'manifest.json'), 'utf-8')); + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const extension = await customSession.loadExtension(extensionPath); + expect(extension.id).to.be.a('string'); + expect(extension.name).to.be.a('string'); + expect(extension.path).to.be.a('string'); + expect(extension.version).to.be.a('string'); + expect(extension.url).to.be.a('string'); + expect(extension.manifest).to.deep.equal(manifest); + }); it('removes an extension', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); { - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) - await w.loadURL(url) - const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(bg).to.equal('red') + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); + await w.loadURL(url); + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(bg).to.equal('red'); } - customSession.removeExtension(id) + customSession.removeExtension(id); { - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) - await w.loadURL(url) - const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(bg).to.equal('') + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); + await w.loadURL(url); + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(bg).to.equal(''); } - }) + }); it('lists loaded extensions in getAllExtensions', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')) - expect(customSession.getAllExtensions()).to.deep.equal([e]) - customSession.removeExtension(e.id) - expect(customSession.getAllExtensions()).to.deep.equal([]) - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); + expect(customSession.getAllExtensions()).to.deep.equal([e]); + customSession.removeExtension(e.id); + expect(customSession.getAllExtensions()).to.deep.equal([]); + }); it('gets an extension by id', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')) - expect(customSession.getExtension(e.id)).to.deep.equal(e) - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); + expect(customSession.getExtension(e.id)).to.deep.equal(e); + }); it('confines an extension to the session it was loaded in', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')) - const w = new BrowserWindow({ show: false }) // not in the session - await w.loadURL(url) - const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(bg).to.equal('') - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); + const w = new BrowserWindow({ show: false }); // not in the session + await w.loadURL(url); + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(bg).to.equal(''); + }); it('loading an extension in a temporary session throws an error', async () => { - const customSession = session.fromPartition(require('uuid').v4()) - await expect(customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'))).to.eventually.be.rejectedWith('Extensions cannot be loaded in a temporary session') - }) + const customSession = session.fromPartition(require('uuid').v4()); + await expect(customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'))).to.eventually.be.rejectedWith('Extensions cannot be loaded in a temporary session'); + }); describe('chrome.i18n', () => { - let w: BrowserWindow - let extension: Extension + let w: BrowserWindow; + let extension: Extension; const exec = async (name: string) => { - const p = emittedOnce(ipcMain, 'success') - await w.webContents.executeJavaScript(`exec('${name}')`) - const [, result] = await p - return result - } + const p = emittedOnce(ipcMain, 'success'); + await w.webContents.executeJavaScript(`exec('${name}')`); + const [, result] = await p; + return result; + }; beforeEach(async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-i18n')) - w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }) - await w.loadURL(url) - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-i18n')); + w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); + await w.loadURL(url); + }); it('getAcceptLanguages()', async () => { - const result = await exec('getAcceptLanguages') - expect(result).to.be.an('array').and.deep.equal(['en-US']) - }) + const result = await exec('getAcceptLanguages'); + expect(result).to.be.an('array').and.deep.equal(['en-US']); + }); it('getMessage()', async () => { - const result = await exec('getMessage') - expect(result.id).to.be.a('string').and.equal(extension.id) - expect(result.name).to.be.a('string').and.equal('chrome-i18n') - }) - }) + const result = await exec('getMessage'); + expect(result.id).to.be.a('string').and.equal(extension.id); + expect(result.name).to.be.a('string').and.equal('chrome-i18n'); + }); + }); describe('chrome.runtime', () => { - let content: any + let content: any; before(async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-runtime')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-runtime')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); try { - await w.loadURL(url) - content = JSON.parse(await w.webContents.executeJavaScript('document.documentElement.textContent')) - expect(content).to.be.an('object') + await w.loadURL(url); + content = JSON.parse(await w.webContents.executeJavaScript('document.documentElement.textContent')); + expect(content).to.be.an('object'); } finally { - w.destroy() + w.destroy(); } - }) + }); it('getManifest()', () => { - expect(content.manifest).to.be.an('object').with.property('name', 'chrome-runtime') - }) + expect(content.manifest).to.be.an('object').with.property('name', 'chrome-runtime'); + }); it('id', () => { - expect(content.id).to.be.a('string').with.lengthOf(32) - }) + expect(content.id).to.be.a('string').with.lengthOf(32); + }); it('getURL()', () => { - expect(content.url).to.be.a('string').and.match(/^chrome-extension:\/\/.*main.js$/) - }) - }) + expect(content.url).to.be.a('string').and.match(/^chrome-extension:\/\/.*main.js$/); + }); + }); describe('chrome.storage', () => { it('stores and retrieves a key', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-storage')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-storage')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); try { - const p = emittedOnce(ipcMain, 'storage-success') - await w.loadURL(url) - const [, v] = await p - expect(v).to.equal('value') + const p = emittedOnce(ipcMain, 'storage-success'); + await w.loadURL(url); + const [, v] = await p; + expect(v).to.equal('value'); } finally { - w.destroy() + w.destroy(); } - }) - }) + }); + }); describe('chrome.tabs', () => { it('executeScript', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }) - await w.loadURL(url) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); + await w.loadURL(url); - const message = { method: 'executeScript', args: ['1 + 2'] } - w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + const message = { method: 'executeScript', args: ['1 + 2'] }; + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await emittedOnce(w.webContents, 'console-message') - const response = JSON.parse(responseString) + const [,, responseString] = await emittedOnce(w.webContents, 'console-message'); + const response = JSON.parse(responseString); - expect(response).to.equal(3) - }) + expect(response).to.equal(3); + }); it('connect', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }) - await w.loadURL(url) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); + await w.loadURL(url); - const portName = require('uuid').v4() - const message = { method: 'connectTab', args: [portName] } - w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + const portName = require('uuid').v4(); + const message = { method: 'connectTab', args: [portName] }; + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await emittedOnce(w.webContents, 'console-message') - const response = responseString.split(',') - expect(response[0]).to.equal(portName) - expect(response[1]).to.equal('howdy') - }) + const [,, responseString] = await emittedOnce(w.webContents, 'console-message'); + const response = responseString.split(','); + expect(response[0]).to.equal(portName); + expect(response[1]).to.equal('howdy'); + }); it('sendMessage receives the response', async function () { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }) - await w.loadURL(url) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); + await w.loadURL(url); - const message = { method: 'sendMessage', args: ['Hello World!'] } - w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + const message = { method: 'sendMessage', args: ['Hello World!'] }; + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await emittedOnce(w.webContents, 'console-message') - const response = JSON.parse(responseString) + const [,, responseString] = await emittedOnce(w.webContents, 'console-message'); + const response = JSON.parse(responseString); - expect(response.message).to.equal('Hello World!') - expect(response.tabId).to.equal(w.webContents.id) - }) - }) + expect(response.message).to.equal('Hello World!'); + expect(response.tabId).to.equal(w.webContents.id); + }); + }); describe('background pages', () => { it('loads a lazy background page when sending a message', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); try { - w.loadURL(url) - const [, resp] = await emittedOnce(ipcMain, 'bg-page-message-response') - expect(resp.message).to.deep.equal({ some: 'message' }) - expect(resp.sender.id).to.be.a('string') - expect(resp.sender.origin).to.equal(url) - expect(resp.sender.url).to.equal(url + '/') + w.loadURL(url); + const [, resp] = await emittedOnce(ipcMain, 'bg-page-message-response'); + expect(resp.message).to.deep.equal({ some: 'message' }); + expect(resp.sender.id).to.be.a('string'); + expect(resp.sender.origin).to.equal(url); + expect(resp.sender.url).to.equal(url + '/'); } finally { - w.destroy() + w.destroy(); } - }) + }); it('can use extension.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) - await w.loadURL(`chrome-extension://${id}/page-get-background.html`) - const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise') - expect(receivedMessage).to.deep.equal({ some: 'message' }) - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); + await w.loadURL(`chrome-extension://${id}/page-get-background.html`); + const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise'); + expect(receivedMessage).to.deep.equal({ some: 'message' }); + }); it('can use extension.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) - await w.loadURL(`chrome-extension://${id}/page-get-background.html`) - const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise') - expect(receivedMessage).to.deep.equal({ some: 'message' }) - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); + await w.loadURL(`chrome-extension://${id}/page-get-background.html`); + const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise'); + expect(receivedMessage).to.deep.equal({ some: 'message' }); + }); it('can use runtime.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')) - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) - await w.loadURL(`chrome-extension://${id}/page-runtime-get-background.html`) - const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise') - expect(receivedMessage).to.deep.equal({ some: 'message' }) - }) - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); + await w.loadURL(`chrome-extension://${id}/page-runtime-get-background.html`); + const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise'); + expect(receivedMessage).to.deep.equal({ some: 'message' }); + }); + }); describe('devtools extensions', () => { - let showPanelTimeoutId: any = null + let showPanelTimeoutId: any = null; afterEach(() => { - if (showPanelTimeoutId) clearTimeout(showPanelTimeoutId) - }) + if (showPanelTimeoutId) clearTimeout(showPanelTimeoutId); + }); const showLastDevToolsPanel = (w: BrowserWindow) => { w.webContents.once('devtools-opened', () => { const show = () => { - if (w == null || w.isDestroyed()) return - const { devToolsWebContents } = w as unknown as { devToolsWebContents: WebContents | undefined } + if (w == null || w.isDestroyed()) return; + const { devToolsWebContents } = w as unknown as { devToolsWebContents: WebContents | undefined }; if (devToolsWebContents == null || devToolsWebContents.isDestroyed()) { - return + return; } const showLastPanel = () => { // this is executed in the devtools context, where UI is a global - const { UI } = (window as any) - const lastPanelId = UI.inspectorView._tabbedPane._tabs.peekLast().id - UI.inspectorView.showPanel(lastPanelId) - } + const { UI } = (window as any); + const lastPanelId = UI.inspectorView._tabbedPane._tabs.peekLast().id; + UI.inspectorView.showPanel(lastPanelId); + }; devToolsWebContents.executeJavaScript(`(${showLastPanel})()`, false).then(() => { - showPanelTimeoutId = setTimeout(show, 100) - }) - } - showPanelTimeoutId = setTimeout(show, 100) - }) - } + showPanelTimeoutId = setTimeout(show, 100); + }); + }; + showPanelTimeoutId = setTimeout(show, 100); + }); + }; it('loads a devtools extension', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) - customSession.loadExtension(path.join(fixtures, 'extensions', 'devtools-extension')) - const w = new BrowserWindow({ show: true, webPreferences: { session: customSession, nodeIntegration: true } }) - await w.loadURL('data:text/html,hello') - w.webContents.openDevTools() - showLastDevToolsPanel(w) - await emittedOnce(ipcMain, 'winning') - }) - }) + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + customSession.loadExtension(path.join(fixtures, 'extensions', 'devtools-extension')); + const w = new BrowserWindow({ show: true, webPreferences: { session: customSession, nodeIntegration: true } }); + await w.loadURL('data:text/html,hello'); + w.webContents.openDevTools(); + showLastDevToolsPanel(w); + await emittedOnce(ipcMain, 'winning'); + }); + }); describe('deprecation shims', () => { it('loads an extension through BrowserWindow.addExtension', async () => { - BrowserWindow.addExtension(path.join(fixtures, 'extensions', 'red-bg')) - const w = new BrowserWindow({ show: false }) - await w.loadURL(url) - const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(bg).to.equal('red') - }) + BrowserWindow.addExtension(path.join(fixtures, 'extensions', 'red-bg')); + const w = new BrowserWindow({ show: false }); + await w.loadURL(url); + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(bg).to.equal('red'); + }); it('loads an extension through BrowserWindow.addDevToolsExtension', async () => { - BrowserWindow.addDevToolsExtension(path.join(fixtures, 'extensions', 'red-bg')) - const w = new BrowserWindow({ show: false }) - await w.loadURL(url) - const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(bg).to.equal('red') - }) + BrowserWindow.addDevToolsExtension(path.join(fixtures, 'extensions', 'red-bg')); + const w = new BrowserWindow({ show: false }); + await w.loadURL(url); + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(bg).to.equal('red'); + }); it('removes an extension through BrowserWindow.removeExtension', async () => { - await (BrowserWindow.addExtension(path.join(fixtures, 'extensions', 'red-bg')) as any) - BrowserWindow.removeExtension('red-bg') - const w = new BrowserWindow({ show: false }) - await w.loadURL(url) - const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(bg).to.equal('') - }) - }) + await (BrowserWindow.addExtension(path.join(fixtures, 'extensions', 'red-bg')) as any); + BrowserWindow.removeExtension('red-bg'); + const w = new BrowserWindow({ show: false }); + await w.loadURL(url); + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(bg).to.equal(''); + }); + }); describe('chrome extension content scripts', () => { - const fixtures = path.resolve(__dirname, 'fixtures') - const extensionPath = path.resolve(fixtures, 'extensions') + const fixtures = path.resolve(__dirname, 'fixtures'); + const extensionPath = path.resolve(fixtures, 'extensions'); - const addExtension = (name: string) => session.defaultSession.loadExtension(path.resolve(extensionPath, name)) + const addExtension = (name: string) => session.defaultSession.loadExtension(path.resolve(extensionPath, name)); const removeAllExtensions = () => { Object.keys(session.defaultSession.getAllExtensions()).map(extName => { - session.defaultSession.removeExtension(extName) - }) - } + session.defaultSession.removeExtension(extName); + }); + }; - let responseIdCounter = 0 + let responseIdCounter = 0; const executeJavaScriptInFrame = (webContents: WebContents, frameRoutingId: number, code: string) => { return new Promise(resolve => { - const responseId = responseIdCounter++ + const responseId = responseIdCounter++; ipcMain.once(`executeJavaScriptInFrame_${responseId}`, (event, result) => { - resolve(result) - }) - webContents.send('executeJavaScriptInFrame', frameRoutingId, code, responseId) - }) - } + resolve(result); + }); + webContents.send('executeJavaScriptInFrame', frameRoutingId, code, responseId); + }); + }; const generateTests = (sandboxEnabled: boolean, contextIsolationEnabled: boolean) => { describe(`with sandbox ${sandboxEnabled ? 'enabled' : 'disabled'} and context isolation ${contextIsolationEnabled ? 'enabled' : 'disabled'}`, () => { - let w: BrowserWindow + let w: BrowserWindow; describe('supports "run_at" option', () => { beforeEach(async () => { - await closeWindow(w) + await closeWindow(w); w = new BrowserWindow({ show: false, width: 400, @@ -367,57 +367,57 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex contextIsolation: contextIsolationEnabled, sandbox: sandboxEnabled } - }) - }) + }); + }); afterEach(() => { - removeAllExtensions() - return closeWindow(w).then(() => { w = null as unknown as BrowserWindow }) - }) + removeAllExtensions(); + return closeWindow(w).then(() => { w = null as unknown as BrowserWindow; }); + }); it('should run content script at document_start', async () => { - await addExtension('content-script-document-start') + await addExtension('content-script-document-start'); w.webContents.once('dom-ready', async () => { - const result = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(result).to.equal('red') - }) - w.loadURL(url) - }) + const result = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(result).to.equal('red'); + }); + w.loadURL(url); + }); it('should run content script at document_idle', async () => { - await addExtension('content-script-document-idle') - w.loadURL(url) - const result = await w.webContents.executeJavaScript('document.body.style.backgroundColor') - expect(result).to.equal('red') - }) + await addExtension('content-script-document-idle'); + w.loadURL(url); + const result = await w.webContents.executeJavaScript('document.body.style.backgroundColor'); + expect(result).to.equal('red'); + }); it('should run content script at document_end', async () => { - await addExtension('content-script-document-end') + await addExtension('content-script-document-end'); w.webContents.once('did-finish-load', async () => { - const result = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(result).to.equal('red') - }) - w.loadURL(url) - }) - }) + const result = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(result).to.equal('red'); + }); + w.loadURL(url); + }); + }); // TODO(nornagon): real extensions don't load on file: urls, so this // test needs to be updated to serve its content over http. describe.skip('supports "all_frames" option', () => { - const contentScript = path.resolve(fixtures, 'extensions/content-script') + const contentScript = path.resolve(fixtures, 'extensions/content-script'); // Computed style values - const COLOR_RED = 'rgb(255, 0, 0)' - const COLOR_BLUE = 'rgb(0, 0, 255)' - const COLOR_TRANSPARENT = 'rgba(0, 0, 0, 0)' + const COLOR_RED = 'rgb(255, 0, 0)'; + const COLOR_BLUE = 'rgb(0, 0, 255)'; + const COLOR_TRANSPARENT = 'rgba(0, 0, 0, 0)'; before(() => { - BrowserWindow.addExtension(contentScript) - }) + BrowserWindow.addExtension(contentScript); + }); after(() => { - BrowserWindow.removeExtension('content-script-test') - }) + BrowserWindow.removeExtension('content-script-test'); + }); beforeEach(() => { w = new BrowserWindow({ @@ -427,22 +427,22 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex nodeIntegrationInSubFrames: true, preload: path.join(contentScript, 'all_frames-preload.js') } - }) - }) + }); + }); afterEach(() => closeWindow(w).then(() => { - w = null as unknown as BrowserWindow + w = null as unknown as BrowserWindow; }) - ) + ); it('applies matching rules in subframes', async () => { - const detailsPromise = emittedNTimes(w.webContents, 'did-frame-finish-load', 2) - w.loadFile(path.join(contentScript, 'frame-with-frame.html')) - const frameEvents = await detailsPromise + const detailsPromise = emittedNTimes(w.webContents, 'did-frame-finish-load', 2); + w.loadFile(path.join(contentScript, 'frame-with-frame.html')); + const frameEvents = await detailsPromise; await Promise.all( frameEvents.map(async frameEvent => { - const [, isMainFrame, , frameRoutingId] = frameEvent + const [, isMainFrame, , frameRoutingId] = frameEvent; const result: any = await executeJavaScriptInFrame( w.webContents, frameRoutingId, @@ -454,226 +454,226 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex disabledColor: getComputedStyle(b).backgroundColor } })()` - ) - expect(result.enabledColor).to.equal(COLOR_RED) + ); + expect(result.enabledColor).to.equal(COLOR_RED); if (isMainFrame) { - expect(result.disabledColor).to.equal(COLOR_BLUE) + expect(result.disabledColor).to.equal(COLOR_BLUE); } else { - expect(result.disabledColor).to.equal(COLOR_TRANSPARENT) // null color + expect(result.disabledColor).to.equal(COLOR_TRANSPARENT); // null color } }) - ) - }) - }) - }) - } - - generateTests(false, false) - generateTests(false, true) - generateTests(true, false) - generateTests(true, true) - }) + ); + }); + }); + }); + }; + + generateTests(false, false); + generateTests(false, true); + generateTests(true, false); + generateTests(true, true); + }); describe('extension ui pages', () => { afterEach(() => { session.defaultSession.getAllExtensions().forEach(e => { - session.defaultSession.removeExtension(e.id) - }) - }) + session.defaultSession.removeExtension(e.id); + }); + }); it('loads a ui page of an extension', async () => { - const { id } = await session.defaultSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page')) - const w = new BrowserWindow({ show: false }) - await w.loadURL(`chrome-extension://${id}/bare-page.html`) - const textContent = await w.webContents.executeJavaScript('document.body.textContent') - expect(textContent).to.equal('ui page loaded ok\n') - }) + const { id } = await session.defaultSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page')); + const w = new BrowserWindow({ show: false }); + await w.loadURL(`chrome-extension://${id}/bare-page.html`); + const textContent = await w.webContents.executeJavaScript('document.body.textContent'); + expect(textContent).to.equal('ui page loaded ok\n'); + }); it('can load resources', async () => { - const { id } = await session.defaultSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page')) - const w = new BrowserWindow({ show: false }) - await w.loadURL(`chrome-extension://${id}/page-script-load.html`) - const textContent = await w.webContents.executeJavaScript('document.body.textContent') - expect(textContent).to.equal('script loaded ok\n') - }) - }) -}) + const { id } = await session.defaultSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page')); + const w = new BrowserWindow({ show: false }); + await w.loadURL(`chrome-extension://${id}/page-script-load.html`); + const textContent = await w.webContents.executeJavaScript('document.body.textContent'); + expect(textContent).to.equal('script loaded ok\n'); + }); + }); +}); ifdescribe(!process.electronBinding('features').isExtensionsEnabled())('chrome extensions', () => { - const fixtures = path.resolve(__dirname, 'fixtures') - let w: BrowserWindow + const fixtures = path.resolve(__dirname, 'fixtures'); + let w: BrowserWindow; before(() => { - BrowserWindow.addExtension(path.join(fixtures, 'extensions/chrome-api')) - }) + BrowserWindow.addExtension(path.join(fixtures, 'extensions/chrome-api')); + }); after(() => { - BrowserWindow.removeExtension('chrome-api') - }) + BrowserWindow.removeExtension('chrome-api'); + }); beforeEach(() => { - w = new BrowserWindow({ show: false }) - }) + w = new BrowserWindow({ show: false }); + }); - afterEach(() => closeWindow(w).then(() => { w = null as unknown as BrowserWindow })) + afterEach(() => closeWindow(w).then(() => { w = null as unknown as BrowserWindow; })); it('chrome.runtime.connect parses arguments properly', async function () { - await w.loadURL('about:blank') + await w.loadURL('about:blank'); - const promise = emittedOnce(w.webContents, 'console-message') + const promise = emittedOnce(w.webContents, 'console-message'); - const message = { method: 'connect' } - w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + const message = { method: 'connect' }; + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await promise - const response = JSON.parse(responseString) + const [,, responseString] = await promise; + const response = JSON.parse(responseString); - expect(response).to.be.true() - }) + expect(response).to.be.true(); + }); it('runtime.getManifest returns extension manifest', async () => { const actualManifest = (() => { - const data = fs.readFileSync(path.join(fixtures, 'extensions/chrome-api/manifest.json'), 'utf-8') - return JSON.parse(data) - })() + const data = fs.readFileSync(path.join(fixtures, 'extensions/chrome-api/manifest.json'), 'utf-8'); + return JSON.parse(data); + })(); - await w.loadURL('about:blank') + await w.loadURL('about:blank'); - const promise = emittedOnce(w.webContents, 'console-message') + const promise = emittedOnce(w.webContents, 'console-message'); - const message = { method: 'getManifest' } - w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + const message = { method: 'getManifest' }; + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, manifestString] = await promise - const manifest = JSON.parse(manifestString) + const [,, manifestString] = await promise; + const manifest = JSON.parse(manifestString); - expect(manifest.name).to.equal(actualManifest.name) - expect(manifest.content_scripts).to.have.lengthOf(actualManifest.content_scripts.length) - }) + expect(manifest.name).to.equal(actualManifest.name); + expect(manifest.content_scripts).to.have.lengthOf(actualManifest.content_scripts.length); + }); it('chrome.tabs.sendMessage receives the response', async function () { - await w.loadURL('about:blank') + await w.loadURL('about:blank'); - const promise = emittedOnce(w.webContents, 'console-message') + const promise = emittedOnce(w.webContents, 'console-message'); - const message = { method: 'sendMessage', args: ['Hello World!'] } - w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + const message = { method: 'sendMessage', args: ['Hello World!'] }; + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await promise - const response = JSON.parse(responseString) + const [,, responseString] = await promise; + const response = JSON.parse(responseString); - expect(response.message).to.equal('Hello World!') - expect(response.tabId).to.equal(w.webContents.id) - }) + expect(response.message).to.equal('Hello World!'); + expect(response.tabId).to.equal(w.webContents.id); + }); it('chrome.tabs.executeScript receives the response', async function () { - await w.loadURL('about:blank') + await w.loadURL('about:blank'); - const promise = emittedOnce(w.webContents, 'console-message') + const promise = emittedOnce(w.webContents, 'console-message'); - const message = { method: 'executeScript', args: ['1 + 2'] } - w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + const message = { method: 'executeScript', args: ['1 + 2'] }; + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await promise - const response = JSON.parse(responseString) + const [,, responseString] = await promise; + const response = JSON.parse(responseString); - expect(response).to.equal(3) - }) + expect(response).to.equal(3); + }); describe('extensions and dev tools extensions', () => { - let showPanelTimeoutId: NodeJS.Timeout | null = null + let showPanelTimeoutId: NodeJS.Timeout | null = null; const showLastDevToolsPanel = (w: BrowserWindow) => { w.webContents.once('devtools-opened', () => { const show = () => { - if (w == null || w.isDestroyed()) return - const { devToolsWebContents } = w as unknown as { devToolsWebContents: WebContents | undefined } + if (w == null || w.isDestroyed()) return; + const { devToolsWebContents } = w as unknown as { devToolsWebContents: WebContents | undefined }; if (devToolsWebContents == null || devToolsWebContents.isDestroyed()) { - return + return; } const showLastPanel = () => { // this is executed in the devtools context, where UI is a global - const { UI } = (window as any) - const lastPanelId = UI.inspectorView._tabbedPane._tabs.peekLast().id - UI.inspectorView.showPanel(lastPanelId) - } + const { UI } = (window as any); + const lastPanelId = UI.inspectorView._tabbedPane._tabs.peekLast().id; + UI.inspectorView.showPanel(lastPanelId); + }; devToolsWebContents.executeJavaScript(`(${showLastPanel})()`, false).then(() => { - showPanelTimeoutId = setTimeout(show, 100) - }) - } - showPanelTimeoutId = setTimeout(show, 100) - }) - } + showPanelTimeoutId = setTimeout(show, 100); + }); + }; + showPanelTimeoutId = setTimeout(show, 100); + }); + }; afterEach(() => { if (showPanelTimeoutId != null) { - clearTimeout(showPanelTimeoutId) - showPanelTimeoutId = null + clearTimeout(showPanelTimeoutId); + showPanelTimeoutId = null; } - }) + }); describe('BrowserWindow.addDevToolsExtension', () => { describe('for invalid extensions', () => { it('throws errors for missing manifest.json files', () => { - const nonexistentExtensionPath = path.join(__dirname, 'does-not-exist') + const nonexistentExtensionPath = path.join(__dirname, 'does-not-exist'); expect(() => { - BrowserWindow.addDevToolsExtension(nonexistentExtensionPath) - }).to.throw(/ENOENT: no such file or directory/) - }) + BrowserWindow.addDevToolsExtension(nonexistentExtensionPath); + }).to.throw(/ENOENT: no such file or directory/); + }); it('throws errors for invalid manifest.json files', () => { - const badManifestExtensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'bad-manifest') + const badManifestExtensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'bad-manifest'); expect(() => { - BrowserWindow.addDevToolsExtension(badManifestExtensionPath) - }).to.throw(/Unexpected token }/) - }) - }) + BrowserWindow.addDevToolsExtension(badManifestExtensionPath); + }).to.throw(/Unexpected token }/); + }); + }); describe('for a valid extension', () => { - const extensionName = 'foo' + const extensionName = 'foo'; before(() => { - const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo') - BrowserWindow.addDevToolsExtension(extensionPath) - expect(BrowserWindow.getDevToolsExtensions()).to.have.property(extensionName) - }) + const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo'); + BrowserWindow.addDevToolsExtension(extensionPath); + expect(BrowserWindow.getDevToolsExtensions()).to.have.property(extensionName); + }); after(() => { - BrowserWindow.removeDevToolsExtension('foo') - expect(BrowserWindow.getDevToolsExtensions()).to.not.have.property(extensionName) - }) + BrowserWindow.removeDevToolsExtension('foo'); + expect(BrowserWindow.getDevToolsExtensions()).to.not.have.property(extensionName); + }); describe('when the devtools is docked', () => { - let message: any - let w: BrowserWindow + let message: any; + let w: BrowserWindow; before(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); const p = new Promise(resolve => ipcMain.once('answer', (event, message) => { - resolve(message) - })) - showLastDevToolsPanel(w) - w.loadURL('about:blank') - w.webContents.openDevTools({ mode: 'bottom' }) - message = await p - }) - after(closeAllWindows) + resolve(message); + })); + showLastDevToolsPanel(w); + w.loadURL('about:blank'); + w.webContents.openDevTools({ mode: 'bottom' }); + message = await p; + }); + after(closeAllWindows); describe('created extension info', function () { it('has proper "runtimeId"', async function () { - expect(message).to.have.ownProperty('runtimeId') - expect(message.runtimeId).to.equal(extensionName) - }) + expect(message).to.have.ownProperty('runtimeId'); + expect(message.runtimeId).to.equal(extensionName); + }); it('has "tabId" matching webContents id', function () { - expect(message).to.have.ownProperty('tabId') - expect(message.tabId).to.equal(w.webContents.id) - }) + expect(message).to.have.ownProperty('tabId'); + expect(message.tabId).to.equal(w.webContents.id); + }); it('has "i18nString" with proper contents', function () { - expect(message).to.have.ownProperty('i18nString') - expect(message.i18nString).to.equal('foo - bar (baz)') - }) + expect(message).to.have.ownProperty('i18nString'); + expect(message.i18nString).to.equal('foo - bar (baz)'); + }); it('has "storageItems" with proper contents', function () { - expect(message).to.have.ownProperty('storageItems') + expect(message).to.have.ownProperty('storageItems'); expect(message.storageItems).to.deep.equal({ local: { set: { hello: 'world', world: 'hello' }, @@ -685,38 +685,38 @@ ifdescribe(!process.electronBinding('features').isExtensionsEnabled())('chrome e remove: { foo: 'bar' }, clear: {} } - }) - }) - }) - }) + }); + }); + }); + }); describe('when the devtools is undocked', () => { - let message: any - let w: BrowserWindow + let message: any; + let w: BrowserWindow; before(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - showLastDevToolsPanel(w) - w.loadURL('about:blank') - w.webContents.openDevTools({ mode: 'undocked' }) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + showLastDevToolsPanel(w); + w.loadURL('about:blank'); + w.webContents.openDevTools({ mode: 'undocked' }); message = await new Promise(resolve => ipcMain.once('answer', (event, message) => { - resolve(message) - })) - }) - after(closeAllWindows) + resolve(message); + })); + }); + after(closeAllWindows); describe('created extension info', function () { it('has proper "runtimeId"', function () { - expect(message).to.have.ownProperty('runtimeId') - expect(message.runtimeId).to.equal(extensionName) - }) + expect(message).to.have.ownProperty('runtimeId'); + expect(message.runtimeId).to.equal(extensionName); + }); it('has "tabId" matching webContents id', function () { - expect(message).to.have.ownProperty('tabId') - expect(message.tabId).to.equal(w.webContents.id) - }) - }) - }) - }) - }) + expect(message).to.have.ownProperty('tabId'); + expect(message.tabId).to.equal(w.webContents.id); + }); + }); + }); + }); + }); it('works when used with partitions', async () => { const w = new BrowserWindow({ @@ -725,53 +725,53 @@ ifdescribe(!process.electronBinding('features').isExtensionsEnabled())('chrome e nodeIntegration: true, partition: 'temp' } - }) + }); - const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo') - BrowserWindow.addDevToolsExtension(extensionPath) + const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo'); + BrowserWindow.addDevToolsExtension(extensionPath); try { - showLastDevToolsPanel(w) + showLastDevToolsPanel(w); const p: Promise = new Promise(resolve => ipcMain.once('answer', function (event, message) { - resolve(message) - })) + resolve(message); + })); - w.loadURL('about:blank') - w.webContents.openDevTools({ mode: 'bottom' }) - const message = await p - expect(message.runtimeId).to.equal('foo') + w.loadURL('about:blank'); + w.webContents.openDevTools({ mode: 'bottom' }); + const message = await p; + expect(message.runtimeId).to.equal('foo'); } finally { - BrowserWindow.removeDevToolsExtension('foo') - await closeAllWindows() + BrowserWindow.removeDevToolsExtension('foo'); + await closeAllWindows(); } - }) + }); it('serializes the registered extensions on quit', () => { - const extensionName = 'foo' - const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName) - const serializedPath = path.join(app.getPath('userData'), 'DevTools Extensions') + const extensionName = 'foo'; + const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName); + const serializedPath = path.join(app.getPath('userData'), 'DevTools Extensions'); - BrowserWindow.addDevToolsExtension(extensionPath) - app.emit('will-quit') - expect(JSON.parse(fs.readFileSync(serializedPath, 'utf8'))).to.deep.equal([extensionPath]) + BrowserWindow.addDevToolsExtension(extensionPath); + app.emit('will-quit'); + expect(JSON.parse(fs.readFileSync(serializedPath, 'utf8'))).to.deep.equal([extensionPath]); - BrowserWindow.removeDevToolsExtension(extensionName) - app.emit('will-quit') - expect(fs.existsSync(serializedPath)).to.be.false('file exists') - }) + BrowserWindow.removeDevToolsExtension(extensionName); + app.emit('will-quit'); + expect(fs.existsSync(serializedPath)).to.be.false('file exists'); + }); describe('BrowserWindow.addExtension', () => { it('throws errors for missing manifest.json files', () => { expect(() => { - BrowserWindow.addExtension(path.join(__dirname, 'does-not-exist')) - }).to.throw('ENOENT: no such file or directory') - }) + BrowserWindow.addExtension(path.join(__dirname, 'does-not-exist')); + }).to.throw('ENOENT: no such file or directory'); + }); it('throws errors for invalid manifest.json files', () => { expect(() => { - BrowserWindow.addExtension(path.join(__dirname, 'fixtures', 'devtools-extensions', 'bad-manifest')) - }).to.throw('Unexpected token }') - }) - }) - }) -}) + BrowserWindow.addExtension(path.join(__dirname, 'fixtures', 'devtools-extensions', 'bad-manifest')); + }).to.throw('Unexpected token }'); + }); + }); + }); +}); diff --git a/spec-main/fixtures/api/context-bridge/can-bind-preload.js b/spec-main/fixtures/api/context-bridge/can-bind-preload.js index cc0204037bdb2..262c6e80057d6 100644 --- a/spec-main/fixtures/api/context-bridge/can-bind-preload.js +++ b/spec-main/fixtures/api/context-bridge/can-bind-preload.js @@ -1,13 +1,13 @@ -const { contextBridge, ipcRenderer } = require('electron') +const { contextBridge, ipcRenderer } = require('electron'); -console.info(contextBridge) +console.info(contextBridge); -let bound = false +let bound = false; try { - contextBridge.exposeInMainWorld('test', {}) - bound = true + contextBridge.exposeInMainWorld('test', {}); + bound = true; } catch { // Ignore } -ipcRenderer.send('context-bridge-bound', bound) +ipcRenderer.send('context-bridge-bound', bound); diff --git a/spec-main/fixtures/api/ipc-main-listeners/main.js b/spec-main/fixtures/api/ipc-main-listeners/main.js index 52d71d222817e..49e3c6f0f1106 100644 --- a/spec-main/fixtures/api/ipc-main-listeners/main.js +++ b/spec-main/fixtures/api/ipc-main-listeners/main.js @@ -1,8 +1,8 @@ -const { app, ipcMain } = require('electron') +const { app, ipcMain } = require('electron'); app.whenReady().then(() => { - process.stdout.write(JSON.stringify(ipcMain.eventNames())) - process.stdout.end() + process.stdout.write(JSON.stringify(ipcMain.eventNames())); + process.stdout.end(); - app.quit() -}) + app.quit(); +}); diff --git a/spec-main/fixtures/api/leak-exit-browserview.js b/spec-main/fixtures/api/leak-exit-browserview.js index 8b172555b2e3a..16d347afb9256 100644 --- a/spec-main/fixtures/api/leak-exit-browserview.js +++ b/spec-main/fixtures/api/leak-exit-browserview.js @@ -1,6 +1,6 @@ -const { BrowserView, app } = require('electron') +const { BrowserView, app } = require('electron'); app.whenReady().then(function () { new BrowserView({}) // eslint-disable-line - app.quit() -}) + app.quit(); +}); diff --git a/spec-main/fixtures/api/leak-exit-webcontentsview.js b/spec-main/fixtures/api/leak-exit-webcontentsview.js index 74b854c34f400..cc98d941346b6 100644 --- a/spec-main/fixtures/api/leak-exit-webcontentsview.js +++ b/spec-main/fixtures/api/leak-exit-webcontentsview.js @@ -1,7 +1,7 @@ -const { WebContentsView, app, webContents } = require('electron') +const { WebContentsView, app, webContents } = require('electron'); app.whenReady().then(function () { - const web = webContents.create({}) + const web = webContents.create({}); new WebContentsView(web) // eslint-disable-line - app.quit() -}) + app.quit(); +}); diff --git a/spec-main/fixtures/api/net-log/main.js b/spec-main/fixtures/api/net-log/main.js index 84b86aafde4c0..b5463acfdbbf1 100644 --- a/spec-main/fixtures/api/net-log/main.js +++ b/spec-main/fixtures/api/net-log/main.js @@ -1,31 +1,31 @@ -const { app, net, session } = require('electron') +const { app, net, session } = require('electron'); if (process.env.TEST_DUMP_FILE) { - app.commandLine.appendSwitch('log-net-log', process.env.TEST_DUMP_FILE) + app.commandLine.appendSwitch('log-net-log', process.env.TEST_DUMP_FILE); } function request () { return new Promise((resolve) => { - const req = net.request(process.env.TEST_REQUEST_URL) + const req = net.request(process.env.TEST_REQUEST_URL); req.on('response', () => { - resolve() - }) - req.end() - }) + resolve(); + }); + req.end(); + }); } app.whenReady().then(async () => { - const netLog = session.defaultSession.netLog + const netLog = session.defaultSession.netLog; if (process.env.TEST_DUMP_FILE_DYNAMIC) { - await netLog.startLogging(process.env.TEST_DUMP_FILE_DYNAMIC) + await netLog.startLogging(process.env.TEST_DUMP_FILE_DYNAMIC); } - await request() + await request(); if (process.env.TEST_MANUAL_STOP) { - await netLog.stopLogging() + await netLog.stopLogging(); } - app.quit() -}) + app.quit(); +}); diff --git a/spec-main/fixtures/api/service-workers/sw-logs.js b/spec-main/fixtures/api/service-workers/sw-logs.js index 4aadcae644586..464909e617428 100644 --- a/spec-main/fixtures/api/service-workers/sw-logs.js +++ b/spec-main/fixtures/api/service-workers/sw-logs.js @@ -1,6 +1,6 @@ self.addEventListener('install', function (event) { - console.log('log log') - console.info('info log') - console.warn('warn log') - console.error('error log') -}) + console.log('log log'); + console.info('info log'); + console.warn('warn log'); + console.error('error log'); +}); diff --git a/spec-main/fixtures/api/service-workers/sw.js b/spec-main/fixtures/api/service-workers/sw.js index b9ba57b097f9b..89c36ad0cef59 100644 --- a/spec-main/fixtures/api/service-workers/sw.js +++ b/spec-main/fixtures/api/service-workers/sw.js @@ -1,3 +1,3 @@ self.addEventListener('install', function (event) { - console.log('Installed') -}) + console.log('Installed'); +}); diff --git a/spec-main/fixtures/api/test-menu-null/main.js b/spec-main/fixtures/api/test-menu-null/main.js index fced892b1f2c3..13b9c0cd4756f 100644 --- a/spec-main/fixtures/api/test-menu-null/main.js +++ b/spec-main/fixtures/api/test-menu-null/main.js @@ -1,16 +1,16 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron'); -let win +let win; app.whenReady().then(function () { - win = new BrowserWindow({}) - win.setMenu(null) + win = new BrowserWindow({}); + win.setMenu(null); setTimeout(() => { if (win.isMenuBarVisible()) { - console.log('Window has a menu') + console.log('Window has a menu'); } else { - console.log('Window has no menu') + console.log('Window has no menu'); } - app.quit() - }) -}) + app.quit(); + }); +}); diff --git a/spec-main/fixtures/api/test-menu-visibility/main.js b/spec-main/fixtures/api/test-menu-visibility/main.js index aafd79ffaa2e2..76cbec754cb46 100644 --- a/spec-main/fixtures/api/test-menu-visibility/main.js +++ b/spec-main/fixtures/api/test-menu-visibility/main.js @@ -1,16 +1,16 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron'); -let win +let win; app.whenReady().then(function () { - win = new BrowserWindow({}) - win.setMenuBarVisibility(false) + win = new BrowserWindow({}); + win.setMenuBarVisibility(false); setTimeout(() => { if (win.isMenuBarVisible()) { - console.log('Window has a menu') + console.log('Window has a menu'); } else { - console.log('Window has no menu') + console.log('Window has no menu'); } - app.quit() - }) -}) + app.quit(); + }); +}); diff --git a/spec-main/fixtures/auto-update/check/index.js b/spec-main/fixtures/auto-update/check/index.js index 1f82a3acbac60..7b89a831a6859 100644 --- a/spec-main/fixtures/auto-update/check/index.js +++ b/spec-main/fixtures/auto-update/check/index.js @@ -1,23 +1,23 @@ process.on('uncaughtException', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const { autoUpdater } = require('electron') +const { autoUpdater } = require('electron'); autoUpdater.on('error', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const feedUrl = process.argv[1] +const feedUrl = process.argv[1]; autoUpdater.setFeedURL({ url: feedUrl -}) +}); -autoUpdater.checkForUpdates() +autoUpdater.checkForUpdates(); autoUpdater.on('update-not-available', () => { - process.exit(0) -}) + process.exit(0); +}); diff --git a/spec-main/fixtures/auto-update/initial/index.js b/spec-main/fixtures/auto-update/initial/index.js index ef1332ebbac9f..1f1c2571b1645 100644 --- a/spec-main/fixtures/auto-update/initial/index.js +++ b/spec-main/fixtures/auto-update/initial/index.js @@ -1,18 +1,18 @@ process.on('uncaughtException', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const { autoUpdater } = require('electron') +const { autoUpdater } = require('electron'); -const feedUrl = process.argv[1] +const feedUrl = process.argv[1]; -console.log('Setting Feed URL') +console.log('Setting Feed URL'); autoUpdater.setFeedURL({ url: feedUrl -}) +}); -console.log('Feed URL Set:', feedUrl) +console.log('Feed URL Set:', feedUrl); -process.exit(0) +process.exit(0); diff --git a/spec-main/fixtures/auto-update/update/index.js b/spec-main/fixtures/auto-update/update/index.js index aec688cbb96d1..e391362f12f04 100644 --- a/spec-main/fixtures/auto-update/update/index.js +++ b/spec-main/fixtures/auto-update/update/index.js @@ -1,42 +1,42 @@ -const fs = require('fs') -const path = require('path') +const fs = require('fs'); +const path = require('path'); process.on('uncaughtException', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const { app, autoUpdater } = require('electron') +const { app, autoUpdater } = require('electron'); autoUpdater.on('error', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const urlPath = path.resolve(__dirname, '../../../../url.txt') -let feedUrl = process.argv[1] +const urlPath = path.resolve(__dirname, '../../../../url.txt'); +let feedUrl = process.argv[1]; if (!feedUrl || !feedUrl.startsWith('http')) { - feedUrl = `${fs.readFileSync(urlPath, 'utf8')}/${app.getVersion()}` + feedUrl = `${fs.readFileSync(urlPath, 'utf8')}/${app.getVersion()}`; } else { - fs.writeFileSync(urlPath, `${feedUrl}/updated`) + fs.writeFileSync(urlPath, `${feedUrl}/updated`); } autoUpdater.setFeedURL({ url: feedUrl -}) +}); -autoUpdater.checkForUpdates() +autoUpdater.checkForUpdates(); autoUpdater.on('update-available', () => { - console.log('Update Available') -}) + console.log('Update Available'); +}); autoUpdater.on('update-downloaded', () => { - console.log('Update Downloaded') - autoUpdater.quitAndInstall() -}) + console.log('Update Downloaded'); + autoUpdater.quitAndInstall(); +}); autoUpdater.on('update-not-available', () => { - console.error('No update available') - process.exit(1) -}) + console.error('No update available'); + process.exit(1); +}); diff --git a/spec-main/fixtures/chromium/other-window.js b/spec-main/fixtures/chromium/other-window.js index 7303f3ea17ecf..5e516e9dfabcc 100644 --- a/spec-main/fixtures/chromium/other-window.js +++ b/spec-main/fixtures/chromium/other-window.js @@ -1,10 +1,10 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron'); -const ints = (...args) => args.map(a => parseInt(a, 10)) +const ints = (...args) => args.map(a => parseInt(a, 10)); -const [x, y, width, height] = ints(...process.argv.slice(2)) +const [x, y, width, height] = ints(...process.argv.slice(2)); -let w +let w; app.whenReady().then(() => { w = new BrowserWindow({ @@ -12,10 +12,10 @@ app.whenReady().then(() => { y, width, height - }) - console.log('__ready__') -}) + }); + console.log('__ready__'); +}); process.on('SIGTERM', () => { - process.exit(0) -}) + process.exit(0); +}); diff --git a/spec-main/fixtures/devtools-extensions/foo/devtools.js b/spec-main/fixtures/devtools-extensions/foo/devtools.js index efe264240afa2..637f3411d6de1 100644 --- a/spec-main/fixtures/devtools-extensions/foo/devtools.js +++ b/spec-main/fixtures/devtools-extensions/foo/devtools.js @@ -1,2 +1,2 @@ /* global chrome */ -chrome.devtools.panels.create('Foo', 'foo.png', 'index.html') +chrome.devtools.panels.create('Foo', 'foo.png', 'index.html'); diff --git a/spec-main/fixtures/devtools-extensions/foo/panel.js b/spec-main/fixtures/devtools-extensions/foo/panel.js index 62e4b33e044da..d3dad58f3fb83 100644 --- a/spec-main/fixtures/devtools-extensions/foo/panel.js +++ b/spec-main/fixtures/devtools-extensions/foo/panel.js @@ -4,11 +4,11 @@ function testStorageClear (callback) { chrome.storage.sync.get(null, function (syncItems) { chrome.storage.local.clear(function () { chrome.storage.local.get(null, function (localItems) { - callback(syncItems, localItems) - }) - }) - }) - }) + callback(syncItems, localItems); + }); + }); + }); + }); } function testStorageRemove (callback) { @@ -16,11 +16,11 @@ function testStorageRemove (callback) { chrome.storage.sync.get({ foo: 'baz' }, function (syncItems) { chrome.storage.local.remove(['hello'], function () { chrome.storage.local.get(null, function (localItems) { - callback(syncItems, localItems) - }) - }) - }) - }) + callback(syncItems, localItems); + }); + }); + }); + }); } function testStorageSet (callback) { @@ -28,11 +28,11 @@ function testStorageSet (callback) { chrome.storage.sync.get({ foo: 'baz', bar: 'fooo' }, function (syncItems) { chrome.storage.local.set({ hello: 'world', world: 'hello' }, function () { chrome.storage.local.get(null, function (localItems) { - callback(syncItems, localItems) - }) - }) - }) - }) + callback(syncItems, localItems); + }); + }); + }); + }); } function testStorage (callback) { @@ -43,10 +43,10 @@ function testStorage (callback) { syncForSet, localForSet, syncForRemove, localForRemove, syncForClear, localForClear - ) - }) - }) - }) + ); + }); + }); + }); } testStorage(function ( @@ -70,8 +70,8 @@ testStorage(function ( clear: syncForClear } } - }) + }); - const sendMessage = `require('electron').ipcRenderer.send('answer', ${message})` - window.chrome.devtools.inspectedWindow.eval(sendMessage, function () {}) -}) + const sendMessage = `require('electron').ipcRenderer.send('answer', ${message})`; + window.chrome.devtools.inspectedWindow.eval(sendMessage, function () {}); +}); diff --git a/spec-main/fixtures/extensions/chrome-api/background.js b/spec-main/fixtures/extensions/chrome-api/background.js index e546490ce39b8..a1adc1193c7d2 100644 --- a/spec-main/fixtures/extensions/chrome-api/background.js +++ b/spec-main/fixtures/extensions/chrome-api/background.js @@ -1,29 +1,29 @@ /* global chrome */ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - const { method, args = [] } = message - const tabId = sender.tab.id + const { method, args = [] } = message; + const tabId = sender.tab.id; switch (method) { case 'sendMessage': { - const [message] = args - chrome.tabs.sendMessage(tabId, { message, tabId }, undefined, sendResponse) - break + const [message] = args; + chrome.tabs.sendMessage(tabId, { message, tabId }, undefined, sendResponse); + break; } case 'executeScript': { - const [code] = args - chrome.tabs.executeScript(tabId, { code }, ([result]) => sendResponse(result)) - break + const [code] = args; + chrome.tabs.executeScript(tabId, { code }, ([result]) => sendResponse(result)); + break; } case 'connectTab': { - const [name] = args - const port = chrome.tabs.connect(tabId, { name }) - port.postMessage('howdy') - break + const [name] = args; + const port = chrome.tabs.connect(tabId, { name }); + port.postMessage('howdy'); + break; } } // Respond asynchronously - return true -}) + return true; +}); diff --git a/spec-main/fixtures/extensions/chrome-api/main.js b/spec-main/fixtures/extensions/chrome-api/main.js index 3de786d22c2e8..b60a647bdcd99 100644 --- a/spec-main/fixtures/extensions/chrome-api/main.js +++ b/spec-main/fixtures/extensions/chrome-api/main.js @@ -1,47 +1,47 @@ /* global chrome */ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - sendResponse(message) -}) + sendResponse(message); +}); const testMap = { connect () { - let success = false + let success = false; try { - chrome.runtime.connect(chrome.runtime.id) - chrome.runtime.connect(chrome.runtime.id, { name: 'content-script' }) - chrome.runtime.connect({ name: 'content-script' }) - success = true + chrome.runtime.connect(chrome.runtime.id); + chrome.runtime.connect(chrome.runtime.id, { name: 'content-script' }); + chrome.runtime.connect({ name: 'content-script' }); + success = true; } finally { - console.log(JSON.stringify(success)) + console.log(JSON.stringify(success)); } }, getManifest () { - const manifest = chrome.runtime.getManifest() - console.log(JSON.stringify(manifest)) + const manifest = chrome.runtime.getManifest(); + console.log(JSON.stringify(manifest)); }, sendMessage (message) { chrome.runtime.sendMessage({ method: 'sendMessage', args: [message] }, response => { - console.log(JSON.stringify(response)) - }) + console.log(JSON.stringify(response)); + }); }, executeScript (code) { chrome.runtime.sendMessage({ method: 'executeScript', args: [code] }, response => { - console.log(JSON.stringify(response)) - }) + console.log(JSON.stringify(response)); + }); }, connectTab (name) { chrome.runtime.onConnect.addListener(port => { port.onMessage.addListener(message => { - console.log([port.name, message].join()) - }) - }) - chrome.runtime.sendMessage({ method: 'connectTab', args: [name] }) + console.log([port.name, message].join()); + }); + }); + chrome.runtime.sendMessage({ method: 'connectTab', args: [name] }); } -} +}; const dispatchTest = (event) => { - const { method, args = [] } = JSON.parse(event.data) - testMap[method](...args) -} -window.addEventListener('message', dispatchTest, false) + const { method, args = [] } = JSON.parse(event.data); + testMap[method](...args); +}; +window.addEventListener('message', dispatchTest, false); diff --git a/spec-main/fixtures/extensions/content-script-document-end/end.js b/spec-main/fixtures/extensions/content-script-document-end/end.js index 787b050adc8cf..2ce126d3f81bf 100644 --- a/spec-main/fixtures/extensions/content-script-document-end/end.js +++ b/spec-main/fixtures/extensions/content-script-document-end/end.js @@ -1 +1 @@ -document.documentElement.style.backgroundColor = 'red' +document.documentElement.style.backgroundColor = 'red'; diff --git a/spec-main/fixtures/extensions/content-script-document-idle/idle.js b/spec-main/fixtures/extensions/content-script-document-idle/idle.js index b45176a31eaf8..b01f93ba591ab 100644 --- a/spec-main/fixtures/extensions/content-script-document-idle/idle.js +++ b/spec-main/fixtures/extensions/content-script-document-idle/idle.js @@ -1 +1 @@ -document.body.style.backgroundColor = 'red' +document.body.style.backgroundColor = 'red'; diff --git a/spec-main/fixtures/extensions/content-script-document-start/start.js b/spec-main/fixtures/extensions/content-script-document-start/start.js index 787b050adc8cf..2ce126d3f81bf 100644 --- a/spec-main/fixtures/extensions/content-script-document-start/start.js +++ b/spec-main/fixtures/extensions/content-script-document-start/start.js @@ -1 +1 @@ -document.documentElement.style.backgroundColor = 'red' +document.documentElement.style.backgroundColor = 'red'; diff --git a/spec-main/fixtures/extensions/content-script/all_frames-preload.js b/spec-main/fixtures/extensions/content-script/all_frames-preload.js index 54133d143c9fc..424124917ac0e 100644 --- a/spec-main/fixtures/extensions/content-script/all_frames-preload.js +++ b/spec-main/fixtures/extensions/content-script/all_frames-preload.js @@ -1,14 +1,14 @@ -const { ipcRenderer, webFrame } = require('electron') +const { ipcRenderer, webFrame } = require('electron'); if (process.isMainFrame) { // https://github.com/electron/electron/issues/17252 ipcRenderer.on('executeJavaScriptInFrame', (event, frameRoutingId, code, responseId) => { - const frame = webFrame.findFrameByRoutingId(frameRoutingId) + const frame = webFrame.findFrameByRoutingId(frameRoutingId); if (!frame) { - throw new Error(`Can't find frame for routing ID ${frameRoutingId}`) + throw new Error(`Can't find frame for routing ID ${frameRoutingId}`); } frame.executeJavaScript(code, false).then(result => { - event.sender.send(`executeJavaScriptInFrame_${responseId}`, result) - }) - }) + event.sender.send(`executeJavaScriptInFrame_${responseId}`, result); + }); + }); } diff --git a/spec-main/fixtures/extensions/devtools-extension/index.js b/spec-main/fixtures/extensions/devtools-extension/index.js index f3d98a6e0ff17..0cd6e38ed9564 100644 --- a/spec-main/fixtures/extensions/devtools-extension/index.js +++ b/spec-main/fixtures/extensions/devtools-extension/index.js @@ -1,4 +1,4 @@ // eslint-disable-next-line chrome.devtools.inspectedWindow.eval(`require("electron").ipcRenderer.send("winning")`, (result, exc) => { - console.log(result, exc) -}) + console.log(result, exc); +}); diff --git a/spec-main/fixtures/extensions/lazy-background-page/background.js b/spec-main/fixtures/extensions/lazy-background-page/background.js index 13bf8248795ec..5fcd9fdad481a 100644 --- a/spec-main/fixtures/extensions/lazy-background-page/background.js +++ b/spec-main/fixtures/extensions/lazy-background-page/background.js @@ -1,5 +1,5 @@ /* eslint-disable no-undef */ chrome.runtime.onMessage.addListener((message, sender, reply) => { - window.receivedMessage = message - reply({ message, sender }) -}) + window.receivedMessage = message; + reply({ message, sender }); +}); diff --git a/spec-main/fixtures/extensions/lazy-background-page/content_script.js b/spec-main/fixtures/extensions/lazy-background-page/content_script.js index 7bf2d9a6f8cf8..f8f0bf6f8a510 100644 --- a/spec-main/fixtures/extensions/lazy-background-page/content_script.js +++ b/spec-main/fixtures/extensions/lazy-background-page/content_script.js @@ -1,6 +1,6 @@ /* eslint-disable no-undef */ chrome.runtime.sendMessage({ some: 'message' }, (response) => { - const script = document.createElement('script') - script.textContent = `require('electron').ipcRenderer.send('bg-page-message-response', ${JSON.stringify(response)})` - document.documentElement.appendChild(script) -}) + const script = document.createElement('script'); + script.textContent = `require('electron').ipcRenderer.send('bg-page-message-response', ${JSON.stringify(response)})`; + document.documentElement.appendChild(script); +}); diff --git a/spec-main/fixtures/extensions/lazy-background-page/get-background-page.js b/spec-main/fixtures/extensions/lazy-background-page/get-background-page.js index 52b4df957f705..a54c8391f513e 100644 --- a/spec-main/fixtures/extensions/lazy-background-page/get-background-page.js +++ b/spec-main/fixtures/extensions/lazy-background-page/get-background-page.js @@ -1,7 +1,7 @@ /* global chrome */ window.completionPromise = new Promise((resolve) => { - window.completionPromiseResolve = resolve -}) + window.completionPromiseResolve = resolve; +}); chrome.runtime.sendMessage({ some: 'message' }, (response) => { - window.completionPromiseResolve(chrome.extension.getBackgroundPage().receivedMessage) -}) + window.completionPromiseResolve(chrome.extension.getBackgroundPage().receivedMessage); +}); diff --git a/spec-main/fixtures/extensions/lazy-background-page/runtime-get-background-page.js b/spec-main/fixtures/extensions/lazy-background-page/runtime-get-background-page.js index d0a7cf7ddab33..59716d5501dd4 100644 --- a/spec-main/fixtures/extensions/lazy-background-page/runtime-get-background-page.js +++ b/spec-main/fixtures/extensions/lazy-background-page/runtime-get-background-page.js @@ -1,9 +1,9 @@ /* global chrome */ window.completionPromise = new Promise((resolve) => { - window.completionPromiseResolve = resolve -}) + window.completionPromiseResolve = resolve; +}); chrome.runtime.sendMessage({ some: 'message' }, (response) => { chrome.runtime.getBackgroundPage((bgPage) => { - window.completionPromiseResolve(bgPage.receivedMessage) - }) -}) + window.completionPromiseResolve(bgPage.receivedMessage); + }); +}); diff --git a/spec-main/fixtures/extensions/red-bg/main.js b/spec-main/fixtures/extensions/red-bg/main.js index 787b050adc8cf..2ce126d3f81bf 100644 --- a/spec-main/fixtures/extensions/red-bg/main.js +++ b/spec-main/fixtures/extensions/red-bg/main.js @@ -1 +1 @@ -document.documentElement.style.backgroundColor = 'red' +document.documentElement.style.backgroundColor = 'red'; diff --git a/spec-main/fixtures/extensions/ui-page/script.js b/spec-main/fixtures/extensions/ui-page/script.js index b7e2e07cf7538..1941b7484aa92 100644 --- a/spec-main/fixtures/extensions/ui-page/script.js +++ b/spec-main/fixtures/extensions/ui-page/script.js @@ -1 +1 @@ -document.write('script loaded ok') +document.write('script loaded ok'); diff --git a/spec-main/fixtures/module/call.js b/spec-main/fixtures/module/call.js index d09d677199b19..60315154e35e9 100644 --- a/spec-main/fixtures/module/call.js +++ b/spec-main/fixtures/module/call.js @@ -1,7 +1,7 @@ exports.call = function (func) { - return func() -} + return func(); +}; exports.constructor = function () { - this.test = 'test' -} + this.test = 'test'; +}; diff --git a/spec-main/fixtures/module/circular.js b/spec-main/fixtures/module/circular.js index e8629c424acd9..e21b595bebb51 100644 --- a/spec-main/fixtures/module/circular.js +++ b/spec-main/fixtures/module/circular.js @@ -1,3 +1,3 @@ exports.returnArgs = function (...args) { - return args -} + return args; +}; diff --git a/spec-main/fixtures/module/class.js b/spec-main/fixtures/module/class.js index 9b971e52335b6..ca6a83685da50 100644 --- a/spec-main/fixtures/module/class.js +++ b/spec-main/fixtures/module/class.js @@ -1,22 +1,22 @@ -'use strict' +'use strict'; -let value = 'old' +let value = 'old'; class BaseClass { method () { - return 'method' + return 'method'; } get readonly () { - return 'readonly' + return 'readonly'; } get value () { - return value + return value; } set value (val) { - value = val + value = val; } } @@ -26,4 +26,4 @@ class DerivedClass extends BaseClass { module.exports = { base: new BaseClass(), derived: new DerivedClass() -} +}; diff --git a/spec-main/fixtures/module/declare-buffer.js b/spec-main/fixtures/module/declare-buffer.js index 9a054a24b5c08..575d5c55ba709 100644 --- a/spec-main/fixtures/module/declare-buffer.js +++ b/spec-main/fixtures/module/declare-buffer.js @@ -1,2 +1,2 @@ -const Buffer = 'declared Buffer' -module.exports = Buffer +const Buffer = 'declared Buffer'; +module.exports = Buffer; diff --git a/spec-main/fixtures/module/declare-global.js b/spec-main/fixtures/module/declare-global.js index bec8dc534f29e..8f49833aa9177 100644 --- a/spec-main/fixtures/module/declare-global.js +++ b/spec-main/fixtures/module/declare-global.js @@ -1,2 +1,2 @@ -const global = 'declared global' -module.exports = global +const global = 'declared global'; +module.exports = global; diff --git a/spec-main/fixtures/module/declare-process.js b/spec-main/fixtures/module/declare-process.js index 257278a4d0b64..5cc35a3cb10b0 100644 --- a/spec-main/fixtures/module/declare-process.js +++ b/spec-main/fixtures/module/declare-process.js @@ -1,2 +1,2 @@ -const process = 'declared process' -module.exports = process +const process = 'declared process'; +module.exports = process; diff --git a/spec-main/fixtures/module/delete-buffer.js b/spec-main/fixtures/module/delete-buffer.js index b90af7d6181ed..abbacb741497a 100644 --- a/spec-main/fixtures/module/delete-buffer.js +++ b/spec-main/fixtures/module/delete-buffer.js @@ -1,11 +1,11 @@ -const path = require('path') -const { remote } = require('electron') -const { Buffer } = window +const path = require('path'); +const { remote } = require('electron'); +const { Buffer } = window; -delete window.Buffer -delete global.Buffer +delete window.Buffer; +delete global.Buffer; // Test that remote.js doesn't use Buffer global -remote.require(path.join(__dirname, 'print_name.js')).echo(Buffer.from('bar')) +remote.require(path.join(__dirname, 'print_name.js')).echo(Buffer.from('bar')); -window.test = Buffer.from('buffer') +window.test = Buffer.from('buffer'); diff --git a/spec-main/fixtures/module/echo-renamed.js b/spec-main/fixtures/module/echo-renamed.js index 80718356038d9..7b4b195f88f94 100644 --- a/spec-main/fixtures/module/echo-renamed.js +++ b/spec-main/fixtures/module/echo-renamed.js @@ -1,7 +1,7 @@ -let echo +let echo; try { - echo = require('echo') + echo = require('echo'); } catch (e) { - process.exit(1) + process.exit(1); } -process.exit(echo(0)) +process.exit(echo(0)); diff --git a/spec-main/fixtures/module/echo.js b/spec-main/fixtures/module/echo.js index 55283b9b392e1..915d0e3393893 100644 --- a/spec-main/fixtures/module/echo.js +++ b/spec-main/fixtures/module/echo.js @@ -1,6 +1,6 @@ process.on('uncaughtException', function (err) { - process.send(err.message) -}) + process.send(err.message); +}); -const echo = require('echo') -process.send(echo('ok')) +const echo = require('echo'); +process.send(echo('ok')); diff --git a/spec-main/fixtures/module/error-properties.js b/spec-main/fixtures/module/error-properties.js index c3a1e3b3a7f6a..ae7a30c0dcc0b 100644 --- a/spec-main/fixtures/module/error-properties.js +++ b/spec-main/fixtures/module/error-properties.js @@ -1,11 +1,11 @@ class Foo { set bar (value) { - throw new Error('setting error') + throw new Error('setting error'); } get bar () { - throw new Error('getting error') + throw new Error('getting error'); } } -module.exports = new Foo() +module.exports = new Foo(); diff --git a/spec-main/fixtures/module/exception.js b/spec-main/fixtures/module/exception.js index 1465833ff8da4..ca47ca0cf5f24 100644 --- a/spec-main/fixtures/module/exception.js +++ b/spec-main/fixtures/module/exception.js @@ -1,3 +1,3 @@ module.exports = function (error) { - throw error -} + throw error; +}; diff --git a/spec-main/fixtures/module/export-function-with-properties.js b/spec-main/fixtures/module/export-function-with-properties.js index 9df7d79deb246..a38142d20dfe9 100644 --- a/spec-main/fixtures/module/export-function-with-properties.js +++ b/spec-main/fixtures/module/export-function-with-properties.js @@ -1,4 +1,4 @@ function foo () {} -foo.bar = 'baz' +foo.bar = 'baz'; -module.exports = foo +module.exports = foo; diff --git a/spec-main/fixtures/module/function-with-args.js b/spec-main/fixtures/module/function-with-args.js index ed636e5988a2d..b30317e975f03 100644 --- a/spec-main/fixtures/module/function-with-args.js +++ b/spec-main/fixtures/module/function-with-args.js @@ -1,3 +1,3 @@ module.exports = function (cb) { - return cb.length -} + return cb.length; +}; diff --git a/spec-main/fixtures/module/function-with-missing-properties.js b/spec-main/fixtures/module/function-with-missing-properties.js index d247485a17b69..3770eca4f5a8c 100644 --- a/spec-main/fixtures/module/function-with-missing-properties.js +++ b/spec-main/fixtures/module/function-with-missing-properties.js @@ -1,13 +1,13 @@ exports.setup = function () { - const foo = {} + const foo = {}; foo.bar = function () { - return delete foo.bar.baz && delete foo.bar - } + return delete foo.bar.baz && delete foo.bar; + }; foo.bar.baz = function () { - return 3 - } + return 3; + }; - return foo -} + return foo; +}; diff --git a/spec-main/fixtures/module/function-with-properties.js b/spec-main/fixtures/module/function-with-properties.js index 3ae617133e7e9..d6d82d65b0be2 100644 --- a/spec-main/fixtures/module/function-with-properties.js +++ b/spec-main/fixtures/module/function-with-properties.js @@ -1,17 +1,17 @@ function foo () { - return 'hello' + return 'hello'; } -foo.bar = 'baz' +foo.bar = 'baz'; foo.nested = { prop: 'yes' -} +}; foo.method1 = function () { - return 'world' -} + return 'world'; +}; foo.method1.prop1 = function () { - return 123 -} + return 123; +}; module.exports = { foo: foo -} +}; diff --git a/spec-main/fixtures/module/function.js b/spec-main/fixtures/module/function.js index 8a2bb6c421ec8..485d990798bcf 100644 --- a/spec-main/fixtures/module/function.js +++ b/spec-main/fixtures/module/function.js @@ -1 +1 @@ -exports.aFunction = function () { return 1127 } +exports.aFunction = function () { return 1127; }; diff --git a/spec-main/fixtures/module/id.js b/spec-main/fixtures/module/id.js index 2faec9d383216..5bfae457fe0bf 100644 --- a/spec-main/fixtures/module/id.js +++ b/spec-main/fixtures/module/id.js @@ -1 +1 @@ -exports.id = 1127 +exports.id = 1127; diff --git a/spec-main/fixtures/module/no-prototype.js b/spec-main/fixtures/module/no-prototype.js index 000eb183fffb7..54eaeb8477216 100644 --- a/spec-main/fixtures/module/no-prototype.js +++ b/spec-main/fixtures/module/no-prototype.js @@ -1,11 +1,11 @@ -const foo = Object.create(null) -foo.bar = 'baz' -foo.baz = false +const foo = Object.create(null); +foo.bar = 'baz'; +foo.baz = false; module.exports = { foo: foo, bar: 1234, anonymous: new (class {})(), getConstructorName: function (value) { - return value.constructor.name + return value.constructor.name; } -} +}; diff --git a/spec-main/fixtures/module/preload-remote-function.js b/spec-main/fixtures/module/preload-remote-function.js index e9d5c3311c3ef..f33eff6ef611f 100644 --- a/spec-main/fixtures/module/preload-remote-function.js +++ b/spec-main/fixtures/module/preload-remote-function.js @@ -1,5 +1,5 @@ -const { remote, ipcRenderer } = require('electron') +const { remote, ipcRenderer } = require('electron'); remote.getCurrentWindow().rendererFunc = () => { - ipcRenderer.send('done') -} -remote.getCurrentWindow().rendererFunc() + ipcRenderer.send('done'); +}; +remote.getCurrentWindow().rendererFunc(); diff --git a/spec-main/fixtures/module/preload-remote.js b/spec-main/fixtures/module/preload-remote.js index a9014c726ff6c..035487be29ac6 100644 --- a/spec-main/fixtures/module/preload-remote.js +++ b/spec-main/fixtures/module/preload-remote.js @@ -1,5 +1,5 @@ -const { ipcRenderer, remote } = require('electron') +const { ipcRenderer, remote } = require('electron'); window.onload = function () { - ipcRenderer.send('remote', typeof remote) -} + ipcRenderer.send('remote', typeof remote); +}; diff --git a/spec-main/fixtures/module/print_name.js b/spec-main/fixtures/module/print_name.js index 8583af00f4abc..047cab14d2d5f 100644 --- a/spec-main/fixtures/module/print_name.js +++ b/spec-main/fixtures/module/print_name.js @@ -1,10 +1,10 @@ exports.print = function (obj) { - return obj.constructor.name -} + return obj.constructor.name; +}; exports.echo = function (obj) { - return obj -} + return obj; +}; const typedArrays = { Int8Array, @@ -16,21 +16,21 @@ const typedArrays = { Uint32Array, Float32Array, Float64Array -} +}; exports.typedArray = function (type, values) { - const constructor = typedArrays[type] - const array = new constructor(values.length) + const constructor = typedArrays[type]; + const array = new constructor(values.length); for (let i = 0; i < values.length; ++i) { - array[i] = values[i] + array[i] = values[i]; } - return array -} + return array; +}; exports.getNaN = function () { - return NaN -} + return NaN; +}; exports.getInfinity = function () { - return Infinity -} + return Infinity; +}; diff --git a/spec-main/fixtures/module/promise.js b/spec-main/fixtures/module/promise.js index d34058cc80336..b9b568855e301 100644 --- a/spec-main/fixtures/module/promise.js +++ b/spec-main/fixtures/module/promise.js @@ -1,5 +1,5 @@ exports.twicePromise = function (promise) { return promise.then(function (value) { - return value * 2 - }) -} + return value * 2; + }); +}; diff --git a/spec-main/fixtures/module/property.js b/spec-main/fixtures/module/property.js index e570ca9351a90..6d15b3d3f89f9 100644 --- a/spec-main/fixtures/module/property.js +++ b/spec-main/fixtures/module/property.js @@ -1,11 +1,11 @@ -exports.property = 1127 +exports.property = 1127; function func () { } -func.property = 'foo' -exports.func = func +func.property = 'foo'; +exports.func = func; exports.getFunctionProperty = () => { - return `${func.property}-${process.type}` -} + return `${func.property}-${process.type}`; +}; diff --git a/spec-main/fixtures/module/rejected-promise.js b/spec-main/fixtures/module/rejected-promise.js index 93dd9accc0308..74c939b2d86c1 100644 --- a/spec-main/fixtures/module/rejected-promise.js +++ b/spec-main/fixtures/module/rejected-promise.js @@ -1,5 +1,5 @@ exports.reject = function (promise) { return promise.then(function () { - throw Error('rejected') - }) -} + throw Error('rejected'); + }); +}; diff --git a/spec-main/fixtures/module/remote-object-set.js b/spec-main/fixtures/module/remote-object-set.js index 74c574722037d..da4f030928e8c 100644 --- a/spec-main/fixtures/module/remote-object-set.js +++ b/spec-main/fixtures/module/remote-object-set.js @@ -1,11 +1,11 @@ -const { BrowserWindow } = require('electron') +const { BrowserWindow } = require('electron'); class Foo { set bar (value) { if (!(value instanceof BrowserWindow)) { - throw new Error('setting error') + throw new Error('setting error'); } } } -module.exports = new Foo() +module.exports = new Foo(); diff --git a/spec-main/fixtures/module/remote-static.js b/spec-main/fixtures/module/remote-static.js index ed35b1d4476a8..1e005ef5dded4 100644 --- a/spec-main/fixtures/module/remote-static.js +++ b/spec-main/fixtures/module/remote-static.js @@ -1,15 +1,15 @@ class Foo { static foo () { - return 3 + return 3; } baz () { - return 123 + return 123; } } -Foo.bar = 'baz' +Foo.bar = 'baz'; module.exports = { Foo: Foo -} +}; diff --git a/spec-main/fixtures/module/to-string-non-function.js b/spec-main/fixtures/module/to-string-non-function.js index a898fc4892386..1c47cfe290355 100644 --- a/spec-main/fixtures/module/to-string-non-function.js +++ b/spec-main/fixtures/module/to-string-non-function.js @@ -1,4 +1,4 @@ function hello () { } -hello.toString = 'hello' -module.exports = { functionWithToStringProperty: hello } +hello.toString = 'hello'; +module.exports = { functionWithToStringProperty: hello }; diff --git a/spec-main/fixtures/module/unhandled-rejection.js b/spec-main/fixtures/module/unhandled-rejection.js index 6cb870ec88625..bd0a7b6538273 100644 --- a/spec-main/fixtures/module/unhandled-rejection.js +++ b/spec-main/fixtures/module/unhandled-rejection.js @@ -1,3 +1,3 @@ exports.reject = function () { - return Promise.reject(new Error('rejected')) -} + return Promise.reject(new Error('rejected')); +}; diff --git a/spec-main/fixtures/native-addon/echo/lib/echo.js b/spec-main/fixtures/native-addon/echo/lib/echo.js index 8c2c673b4b043..3d204f5e239cf 100644 --- a/spec-main/fixtures/native-addon/echo/lib/echo.js +++ b/spec-main/fixtures/native-addon/echo/lib/echo.js @@ -1 +1 @@ -module.exports = require('../build/Release/echo.node').Print +module.exports = require('../build/Release/echo.node').Print; diff --git a/spec-main/fixtures/sub-frames/preload.js b/spec-main/fixtures/sub-frames/preload.js index 11c77a5281398..52e87657f1d79 100644 --- a/spec-main/fixtures/sub-frames/preload.js +++ b/spec-main/fixtures/sub-frames/preload.js @@ -1,9 +1,9 @@ -const { ipcRenderer, webFrame } = require('electron') +const { ipcRenderer, webFrame } = require('electron'); -window.isolatedGlobal = true +window.isolatedGlobal = true; -ipcRenderer.send('preload-ran', window.location.href, webFrame.routingId) +ipcRenderer.send('preload-ran', window.location.href, webFrame.routingId); ipcRenderer.on('preload-ping', () => { - ipcRenderer.send('preload-pong', webFrame.routingId) -}) + ipcRenderer.send('preload-pong', webFrame.routingId); +}); diff --git a/spec-main/fixtures/sub-frames/webview-iframe-preload.js b/spec-main/fixtures/sub-frames/webview-iframe-preload.js index 926db8bad77a2..2290548866fbc 100644 --- a/spec-main/fixtures/sub-frames/webview-iframe-preload.js +++ b/spec-main/fixtures/sub-frames/webview-iframe-preload.js @@ -1,14 +1,14 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); if (process.isMainFrame) { window.addEventListener('DOMContentLoaded', () => { - const webview = document.createElement('webview') - webview.src = 'about:blank' + const webview = document.createElement('webview'); + webview.src = 'about:blank'; webview.addEventListener('did-finish-load', () => { - ipcRenderer.send('webview-loaded') - }, { once: true }) - document.body.appendChild(webview) - }) + ipcRenderer.send('webview-loaded'); + }, { once: true }); + document.body.appendChild(webview); + }); } else { - ipcRenderer.send('preload-in-frame') + ipcRenderer.send('preload-in-frame'); } diff --git a/spec-main/index.js b/spec-main/index.js index b6075851a7eca..cf9fa06212061 100644 --- a/spec-main/index.js +++ b/spec-main/index.js @@ -1,31 +1,31 @@ -const Module = require('module') -const path = require('path') -const v8 = require('v8') +const Module = require('module'); +const path = require('path'); +const v8 = require('v8'); -Module.globalPaths.push(path.resolve(__dirname, '../spec/node_modules')) +Module.globalPaths.push(path.resolve(__dirname, '../spec/node_modules')); // We want to terminate on errors, not throw up a dialog process.on('uncaughtException', (err) => { - console.error('Unhandled exception in main spec runner:', err) - process.exit(1) -}) + console.error('Unhandled exception in main spec runner:', err); + process.exit(1); +}); // Tell ts-node which tsconfig to use -process.env.TS_NODE_PROJECT = path.resolve(__dirname, '../tsconfig.spec.json') -process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true' +process.env.TS_NODE_PROJECT = path.resolve(__dirname, '../tsconfig.spec.json'); +process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'; -const { app, protocol } = require('electron') +const { app, protocol } = require('electron'); -v8.setFlagsFromString('--expose_gc') -app.commandLine.appendSwitch('js-flags', '--expose_gc') +v8.setFlagsFromString('--expose_gc'); +app.commandLine.appendSwitch('js-flags', '--expose_gc'); // Prevent the spec runner quiting when the first window closes -app.on('window-all-closed', () => null) +app.on('window-all-closed', () => null); // Use fake device for Media Stream to replace actual camera and microphone. -app.commandLine.appendSwitch('use-fake-device-for-media-stream') +app.commandLine.appendSwitch('use-fake-device-for-media-stream'); -global.standardScheme = 'app' -global.zoomScheme = 'zoom' +global.standardScheme = 'app'; +global.zoomScheme = 'zoom'; protocol.registerSchemesAsPrivileged([ { scheme: global.standardScheme, privileges: { standard: true, secure: true } }, { scheme: global.zoomScheme, privileges: { standard: true, secure: true } }, @@ -35,90 +35,90 @@ protocol.registerSchemesAsPrivileged([ { scheme: 'no-fetch', privileges: { corsEnabled: true } }, { scheme: 'foo', privileges: { standard: true } }, { scheme: 'bar', privileges: { standard: true } } -]) +]); app.whenReady().then(() => { - require('ts-node/register') + require('ts-node/register'); const argv = require('yargs') .boolean('ci') .array('files') .string('g').alias('g', 'grep') .boolean('i').alias('i', 'invert') - .argv + .argv; - const Mocha = require('mocha') - const mochaOptions = {} + const Mocha = require('mocha'); + const mochaOptions = {}; if (process.env.MOCHA_REPORTER) { - mochaOptions.reporter = process.env.MOCHA_REPORTER + mochaOptions.reporter = process.env.MOCHA_REPORTER; } if (process.env.MOCHA_MULTI_REPORTERS) { mochaOptions.reporterOptions = { reporterEnabled: process.env.MOCHA_MULTI_REPORTERS - } + }; } - const mocha = new Mocha(mochaOptions) + const mocha = new Mocha(mochaOptions); if (!process.env.MOCHA_REPORTER) { - mocha.ui('bdd').reporter('tap') + mocha.ui('bdd').reporter('tap'); } - mocha.timeout(30000) + mocha.timeout(30000); - if (argv.grep) mocha.grep(argv.grep) - if (argv.invert) mocha.invert() + if (argv.grep) mocha.grep(argv.grep); + if (argv.invert) mocha.invert(); // Read all test files. const walker = require('walkdir').walk(__dirname, { no_recurse: true - }) + }); // This allows you to run specific modules only: // npm run test -match=menu const moduleMatch = process.env.npm_config_match ? new RegExp(process.env.npm_config_match, 'g') - : null + : null; - const testFiles = [] + const testFiles = []; walker.on('file', (file) => { if (/-spec\.[tj]s$/.test(file) && (!moduleMatch || moduleMatch.test(file))) { - testFiles.push(file) + testFiles.push(file); } - }) + }); - const baseElectronDir = path.resolve(__dirname, '..') + const baseElectronDir = path.resolve(__dirname, '..'); walker.on('end', () => { - testFiles.sort() + testFiles.sort(); sortToEnd(testFiles, f => f.includes('crash-reporter')).forEach((file) => { if (!argv.files || argv.files.includes(path.relative(baseElectronDir, file))) { - mocha.addFile(file) + mocha.addFile(file); } - }) + }); const cb = () => { // Ensure the callback is called after runner is defined process.nextTick(() => { - process.exit(runner.failures) - }) - } + process.exit(runner.failures); + }); + }; // Set up chai in the correct order - const chai = require('chai') - chai.use(require('chai-as-promised')) - chai.use(require('dirty-chai')) + const chai = require('chai'); + chai.use(require('chai-as-promised')); + chai.use(require('dirty-chai')); - const runner = mocha.run(cb) - }) -}) + const runner = mocha.run(cb); + }); +}); function partition (xs, f) { - const trues = [] - const falses = [] - xs.forEach(x => (f(x) ? trues : falses).push(x)) - return [trues, falses] + const trues = []; + const falses = []; + xs.forEach(x => (f(x) ? trues : falses).push(x)); + return [trues, falses]; } function sortToEnd (xs, f) { - const [end, beginning] = partition(xs, f) - return beginning.concat(end) + const [end, beginning] = partition(xs, f); + return beginning.concat(end); } diff --git a/spec-main/internal-spec.ts b/spec-main/internal-spec.ts index 320f241789b3a..f91fc5dda1077 100644 --- a/spec-main/internal-spec.ts +++ b/spec-main/internal-spec.ts @@ -1,22 +1,22 @@ -import { expect } from 'chai' +import { expect } from 'chai'; describe('feature-string parsing', () => { it('is indifferent to whitespace around keys and values', () => { - const parseFeaturesString = require('../lib/common/parse-features-string') + const parseFeaturesString = require('../lib/common/parse-features-string'); const checkParse = (string: string, parsed: Record) => { - const features: Record = {} - parseFeaturesString(string, (k: string, v: any) => { features[k] = v }) - expect(features).to.deep.equal(parsed) - } - checkParse('a=yes,c=d', { a: true, c: 'd' }) - checkParse('a=yes ,c=d', { a: true, c: 'd' }) - checkParse('a=yes, c=d', { a: true, c: 'd' }) - checkParse('a=yes , c=d', { a: true, c: 'd' }) - checkParse(' a=yes , c=d', { a: true, c: 'd' }) - checkParse(' a= yes , c=d', { a: true, c: 'd' }) - checkParse(' a = yes , c=d', { a: true, c: 'd' }) - checkParse(' a = yes , c =d', { a: true, c: 'd' }) - checkParse(' a = yes , c = d', { a: true, c: 'd' }) - checkParse(' a = yes , c = d ', { a: true, c: 'd' }) - }) -}) + const features: Record = {}; + parseFeaturesString(string, (k: string, v: any) => { features[k] = v; }); + expect(features).to.deep.equal(parsed); + }; + checkParse('a=yes,c=d', { a: true, c: 'd' }); + checkParse('a=yes ,c=d', { a: true, c: 'd' }); + checkParse('a=yes, c=d', { a: true, c: 'd' }); + checkParse('a=yes , c=d', { a: true, c: 'd' }); + checkParse(' a=yes , c=d', { a: true, c: 'd' }); + checkParse(' a= yes , c=d', { a: true, c: 'd' }); + checkParse(' a = yes , c=d', { a: true, c: 'd' }); + checkParse(' a = yes , c =d', { a: true, c: 'd' }); + checkParse(' a = yes , c = d', { a: true, c: 'd' }); + checkParse(' a = yes , c = d ', { a: true, c: 'd' }); + }); +}); diff --git a/spec-main/modules-spec.ts b/spec-main/modules-spec.ts index da126f485fcfc..217273c96517b 100644 --- a/spec-main/modules-spec.ts +++ b/spec-main/modules-spec.ts @@ -1,153 +1,153 @@ -import { expect } from 'chai' -import * as path from 'path' -import * as fs from 'fs' -import { BrowserWindow } from 'electron' -import { ifdescribe, ifit } from './spec-helpers' -import { closeAllWindows } from './window-helpers' -import * as childProcess from 'child_process' +import { expect } from 'chai'; +import * as path from 'path'; +import * as fs from 'fs'; +import { BrowserWindow } from 'electron'; +import { ifdescribe, ifit } from './spec-helpers'; +import { closeAllWindows } from './window-helpers'; +import * as childProcess from 'child_process'; -const Module = require('module') +const Module = require('module'); -const features = process.electronBinding('features') -const nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS +const features = process.electronBinding('features'); +const nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS; describe('modules support', () => { - const fixtures = path.join(__dirname, 'fixtures') + const fixtures = path.join(__dirname, 'fixtures'); describe('third-party module', () => { ifdescribe(nativeModulesEnabled)('echo', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('can be required in renderer', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - w.loadURL('about:blank') - await expect(w.webContents.executeJavaScript('{ require(\'echo\'); null }')).to.be.fulfilled() - }) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + w.loadURL('about:blank'); + await expect(w.webContents.executeJavaScript('{ require(\'echo\'); null }')).to.be.fulfilled(); + }); ifit(features.isRunAsNodeEnabled())('can be required in node binary', function (done) { - const child = childProcess.fork(path.join(fixtures, 'module', 'echo.js')) + const child = childProcess.fork(path.join(fixtures, 'module', 'echo.js')); child.on('message', (msg) => { - expect(msg).to.equal('ok') - done() - }) - }) + expect(msg).to.equal('ok'); + done(); + }); + }); ifit(process.platform === 'win32')('can be required if electron.exe is renamed', () => { - const testExecPath = path.join(path.dirname(process.execPath), 'test.exe') - fs.copyFileSync(process.execPath, testExecPath) + const testExecPath = path.join(path.dirname(process.execPath), 'test.exe'); + fs.copyFileSync(process.execPath, testExecPath); try { - const fixture = path.join(fixtures, 'module', 'echo-renamed.js') - expect(fs.existsSync(fixture)).to.be.true() - const child = childProcess.spawnSync(testExecPath, [fixture]) - expect(child.status).to.equal(0) + const fixture = path.join(fixtures, 'module', 'echo-renamed.js'); + expect(fs.existsSync(fixture)).to.be.true(); + const child = childProcess.spawnSync(testExecPath, [fixture]); + expect(child.status).to.equal(0); } finally { - fs.unlinkSync(testExecPath) + fs.unlinkSync(testExecPath); } - }) - }) + }); + }); describe('q', () => { describe('Q.when', () => { it('emits the fullfil callback', (done) => { - const Q = require('q') + const Q = require('q'); Q(true).then((val: boolean) => { - expect(val).to.be.true() - done() - }) - }) - }) - }) + expect(val).to.be.true(); + done(); + }); + }); + }); + }); describe('coffeescript', () => { it('can be registered and used to require .coffee files', () => { expect(() => { - require('coffeescript').register() - }).to.not.throw() - expect(require('./fixtures/module/test.coffee')).to.be.true() - }) - }) - }) + require('coffeescript').register(); + }).to.not.throw(); + expect(require('./fixtures/module/test.coffee')).to.be.true(); + }); + }); + }); describe('global variables', () => { describe('process', () => { it('can be declared in a module', () => { - expect(require('./fixtures/module/declare-process')).to.equal('declared process') - }) - }) + expect(require('./fixtures/module/declare-process')).to.equal('declared process'); + }); + }); describe('global', () => { it('can be declared in a module', () => { - expect(require('./fixtures/module/declare-global')).to.equal('declared global') - }) - }) + expect(require('./fixtures/module/declare-global')).to.equal('declared global'); + }); + }); describe('Buffer', () => { it('can be declared in a module', () => { - expect(require('./fixtures/module/declare-buffer')).to.equal('declared Buffer') - }) - }) - }) + expect(require('./fixtures/module/declare-buffer')).to.equal('declared Buffer'); + }); + }); + }); describe('Module._nodeModulePaths', () => { describe('when the path is inside the resources path', () => { it('does not include paths outside of the resources path', () => { - let modulePath = process.resourcesPath + let modulePath = process.resourcesPath; expect(Module._nodeModulePaths(modulePath)).to.deep.equal([ path.join(process.resourcesPath, 'node_modules') - ]) + ]); - modulePath = process.resourcesPath + '-foo' - const nodeModulePaths = Module._nodeModulePaths(modulePath) - expect(nodeModulePaths).to.include(path.join(modulePath, 'node_modules')) - expect(nodeModulePaths).to.include(path.join(modulePath, '..', 'node_modules')) + modulePath = process.resourcesPath + '-foo'; + const nodeModulePaths = Module._nodeModulePaths(modulePath); + expect(nodeModulePaths).to.include(path.join(modulePath, 'node_modules')); + expect(nodeModulePaths).to.include(path.join(modulePath, '..', 'node_modules')); - modulePath = path.join(process.resourcesPath, 'foo') + modulePath = path.join(process.resourcesPath, 'foo'); expect(Module._nodeModulePaths(modulePath)).to.deep.equal([ path.join(process.resourcesPath, 'foo', 'node_modules'), path.join(process.resourcesPath, 'node_modules') - ]) + ]); - modulePath = path.join(process.resourcesPath, 'node_modules', 'foo') + modulePath = path.join(process.resourcesPath, 'node_modules', 'foo'); expect(Module._nodeModulePaths(modulePath)).to.deep.equal([ path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'), path.join(process.resourcesPath, 'node_modules') - ]) + ]); - modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'bar') + modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'bar'); expect(Module._nodeModulePaths(modulePath)).to.deep.equal([ path.join(process.resourcesPath, 'node_modules', 'foo', 'bar', 'node_modules'), path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'), path.join(process.resourcesPath, 'node_modules') - ]) + ]); - modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar') + modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar'); expect(Module._nodeModulePaths(modulePath)).to.deep.equal([ path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar', 'node_modules'), path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'), path.join(process.resourcesPath, 'node_modules') - ]) - }) - }) + ]); + }); + }); describe('when the path is outside the resources path', () => { it('includes paths outside of the resources path', () => { - const modulePath = path.resolve('/foo') + const modulePath = path.resolve('/foo'); expect(Module._nodeModulePaths(modulePath)).to.deep.equal([ path.join(modulePath, 'node_modules'), path.resolve('/node_modules') - ]) - }) - }) - }) + ]); + }); + }); + }); describe('require', () => { describe('when loaded URL is not file: protocol', () => { - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('searches for module under app directory', async () => { - const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) - w.loadURL('about:blank') - const result = await w.webContents.executeJavaScript('typeof require("q").when') - expect(result).to.equal('function') - }) - }) - }) -}) + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }); + w.loadURL('about:blank'); + const result = await w.webContents.executeJavaScript('typeof require("q").when'); + expect(result).to.equal('function'); + }); + }); + }); +}); diff --git a/spec-main/node-spec.ts b/spec-main/node-spec.ts index 078a20f06b3b9..dd019750604f2 100644 --- a/spec-main/node-spec.ts +++ b/spec-main/node-spec.ts @@ -1,265 +1,265 @@ -import { expect } from 'chai' -import * as childProcess from 'child_process' -import * as path from 'path' -import * as util from 'util' -import { emittedOnce } from './events-helpers' -import { ifdescribe, ifit } from './spec-helpers' -import { webContents, WebContents } from 'electron' +import { expect } from 'chai'; +import * as childProcess from 'child_process'; +import * as path from 'path'; +import * as util from 'util'; +import { emittedOnce } from './events-helpers'; +import { ifdescribe, ifit } from './spec-helpers'; +import { webContents, WebContents } from 'electron'; -const features = process.electronBinding('features') +const features = process.electronBinding('features'); describe('node feature', () => { - const fixtures = path.join(__dirname, '..', 'spec', 'fixtures') + const fixtures = path.join(__dirname, '..', 'spec', 'fixtures'); describe('child_process', () => { describe('child_process.fork', () => { it('Works in browser process', (done) => { - const child = childProcess.fork(path.join(fixtures, 'module', 'ping.js')) + const child = childProcess.fork(path.join(fixtures, 'module', 'ping.js')); child.on('message', (msg) => { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) - }) - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); + }); + }); describe('contexts', () => { describe('setTimeout called under Chromium event loop in browser process', () => { it('Can be scheduled in time', (done) => { - setTimeout(done, 0) - }) + setTimeout(done, 0); + }); it('Can be promisified', (done) => { - util.promisify(setTimeout)(0).then(done) - }) - }) + util.promisify(setTimeout)(0).then(done); + }); + }); describe('setInterval called under Chromium event loop in browser process', () => { it('can be scheduled in time', (done) => { - let interval: any = null - let clearing = false + let interval: any = null; + let clearing = false; const clear = () => { - if (interval === null || clearing) return + if (interval === null || clearing) return; // interval might trigger while clearing (remote is slow sometimes) - clearing = true - clearInterval(interval) - clearing = false - interval = null - done() - } - interval = setInterval(clear, 10) - }) - }) - }) + clearing = true; + clearInterval(interval); + clearing = false; + interval = null; + done(); + }; + interval = setInterval(clear, 10); + }); + }); + }); describe('NODE_OPTIONS', () => { - let child: childProcess.ChildProcessWithoutNullStreams - let exitPromise: Promise + let child: childProcess.ChildProcessWithoutNullStreams; + let exitPromise: Promise; it('Fails for options disallowed by Node.js itself', (done) => { after(async () => { - const [code, signal] = await exitPromise - expect(signal).to.equal(null) + const [code, signal] = await exitPromise; + expect(signal).to.equal(null); // Exit code 9 indicates cli flag parsing failure - expect(code).to.equal(9) - child.kill() - }) + expect(code).to.equal(9); + child.kill(); + }); - const env = Object.assign({}, process.env, { NODE_OPTIONS: '--v8-options' }) - child = childProcess.spawn(process.execPath, { env }) - exitPromise = emittedOnce(child, 'exit') + const env = Object.assign({}, process.env, { NODE_OPTIONS: '--v8-options' }); + child = childProcess.spawn(process.execPath, { env }); + exitPromise = emittedOnce(child, 'exit'); - let output = '' - let success = false + let output = ''; + let success = false; const cleanup = () => { - child.stderr.removeListener('data', listener) - child.stdout.removeListener('data', listener) - } + child.stderr.removeListener('data', listener); + child.stdout.removeListener('data', listener); + }; const listener = (data: Buffer) => { - output += data + output += data; if (/electron: --v8-options is not allowed in NODE_OPTIONS/m.test(output)) { - success = true - cleanup() - done() + success = true; + cleanup(); + done(); } - } + }; - child.stderr.on('data', listener) - child.stdout.on('data', listener) + child.stderr.on('data', listener); + child.stdout.on('data', listener); child.on('exit', () => { if (!success) { - cleanup() - done(new Error(`Unexpected output: ${output.toString()}`)) + cleanup(); + done(new Error(`Unexpected output: ${output.toString()}`)); } - }) - }) + }); + }); it('Disallows crypto-related options', (done) => { after(() => { - child.kill() - }) + child.kill(); + }); - const env = Object.assign({}, process.env, { NODE_OPTIONS: '--use-openssl-ca' }) - child = childProcess.spawn(process.execPath, ['--enable-logging'], { env }) + const env = Object.assign({}, process.env, { NODE_OPTIONS: '--use-openssl-ca' }); + child = childProcess.spawn(process.execPath, ['--enable-logging'], { env }); - let output = '' + let output = ''; const cleanup = () => { - child.stderr.removeListener('data', listener) - child.stdout.removeListener('data', listener) - } + child.stderr.removeListener('data', listener); + child.stdout.removeListener('data', listener); + }; const listener = (data: Buffer) => { - output += data + output += data; if (/The NODE_OPTION --use-openssl-ca is not supported in Electron/m.test(output)) { - cleanup() - done() + cleanup(); + done(); } - } + }; - child.stderr.on('data', listener) - child.stdout.on('data', listener) - }) - }) + child.stderr.on('data', listener); + child.stdout.on('data', listener); + }); + }); describe('Node.js cli flags', () => { - let child: childProcess.ChildProcessWithoutNullStreams - let exitPromise: Promise + let child: childProcess.ChildProcessWithoutNullStreams; + let exitPromise: Promise; it('Prohibits crypto-related flags in ELECTRON_RUN_AS_NODE mode', (done) => { after(async () => { - const [code, signal] = await exitPromise - expect(signal).to.equal(null) - expect(code).to.equal(9) - child.kill() - }) + const [code, signal] = await exitPromise; + expect(signal).to.equal(null); + expect(code).to.equal(9); + child.kill(); + }); child = childProcess.spawn(process.execPath, ['--force-fips'], { env: { ELECTRON_RUN_AS_NODE: 'true' } - }) - exitPromise = emittedOnce(child, 'exit') + }); + exitPromise = emittedOnce(child, 'exit'); - let output = '' + let output = ''; const cleanup = () => { - child.stderr.removeListener('data', listener) - child.stdout.removeListener('data', listener) - } + child.stderr.removeListener('data', listener); + child.stdout.removeListener('data', listener); + }; const listener = (data: Buffer) => { - output += data + output += data; if (/.*The Node.js cli flag --force-fips is not supported in Electron/m.test(output)) { - cleanup() - done() + cleanup(); + done(); } - } + }; - child.stderr.on('data', listener) - child.stdout.on('data', listener) - }) - }) + child.stderr.on('data', listener); + child.stdout.on('data', listener); + }); + }); ifdescribe(features.isRunAsNodeEnabled())('inspector', () => { - let child: childProcess.ChildProcessWithoutNullStreams - let exitPromise: Promise + let child: childProcess.ChildProcessWithoutNullStreams; + let exitPromise: Promise; afterEach(async () => { if (child && exitPromise) { - const [code, signal] = await exitPromise - expect(signal).to.equal(null) - expect(code).to.equal(0) + const [code, signal] = await exitPromise; + expect(signal).to.equal(null); + expect(code).to.equal(0); } else if (child) { - child.kill() + child.kill(); } - }) + }); it('Supports starting the v8 inspector with --inspect/--inspect-brk', (done) => { child = childProcess.spawn(process.execPath, ['--inspect-brk', path.join(fixtures, 'module', 'run-as-node.js')], { env: { ELECTRON_RUN_AS_NODE: 'true' } - }) + }); - let output = '' + let output = ''; const cleanup = () => { - child.stderr.removeListener('data', listener) - child.stdout.removeListener('data', listener) - } + child.stderr.removeListener('data', listener); + child.stdout.removeListener('data', listener); + }; const listener = (data: Buffer) => { - output += data + output += data; if (/Debugger listening on ws:/m.test(output)) { - cleanup() - done() + cleanup(); + done(); } - } + }; - child.stderr.on('data', listener) - child.stdout.on('data', listener) - }) + child.stderr.on('data', listener); + child.stdout.on('data', listener); + }); it('Supports starting the v8 inspector with --inspect and a provided port', (done) => { child = childProcess.spawn(process.execPath, ['--inspect=17364', path.join(fixtures, 'module', 'run-as-node.js')], { env: { ELECTRON_RUN_AS_NODE: 'true' } - }) - exitPromise = emittedOnce(child, 'exit') + }); + exitPromise = emittedOnce(child, 'exit'); - let output = '' - const listener = (data: Buffer) => { output += data } + let output = ''; + const listener = (data: Buffer) => { output += data; }; const cleanup = () => { - child.stderr.removeListener('data', listener) - child.stdout.removeListener('data', listener) - } + child.stderr.removeListener('data', listener); + child.stdout.removeListener('data', listener); + }; - child.stderr.on('data', listener) - child.stdout.on('data', listener) + child.stderr.on('data', listener); + child.stdout.on('data', listener); child.on('exit', () => { - cleanup() + cleanup(); if (/^Debugger listening on ws:/m.test(output)) { - expect(output.trim()).to.contain(':17364', 'should be listening on port 17364') - done() + expect(output.trim()).to.contain(':17364', 'should be listening on port 17364'); + done(); } else { - done(new Error(`Unexpected output: ${output.toString()}`)) + done(new Error(`Unexpected output: ${output.toString()}`)); } - }) - }) + }); + }); it('Does not start the v8 inspector when --inspect is after a -- argument', (done) => { - child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'noop.js'), '--', '--inspect']) - exitPromise = emittedOnce(child, 'exit') + child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'noop.js'), '--', '--inspect']); + exitPromise = emittedOnce(child, 'exit'); - let output = '' - const listener = (data: Buffer) => { output += data } - child.stderr.on('data', listener) - child.stdout.on('data', listener) + let output = ''; + const listener = (data: Buffer) => { output += data; }; + child.stderr.on('data', listener); + child.stdout.on('data', listener); child.on('exit', () => { if (output.trim().startsWith('Debugger listening on ws://')) { - done(new Error('Inspector was started when it should not have been')) + done(new Error('Inspector was started when it should not have been')); } else { - done() + done(); } - }) - }) + }); + }); // IPC Electron child process not supported on Windows ifit(process.platform !== 'win32')('Does does not crash when quitting with the inspector connected', function (done) { child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'delay-exit'), '--inspect=0'], { stdio: ['ipc'] - }) as childProcess.ChildProcessWithoutNullStreams - exitPromise = emittedOnce(child, 'exit') + }) as childProcess.ChildProcessWithoutNullStreams; + exitPromise = emittedOnce(child, 'exit'); const cleanup = () => { - child.stderr.removeListener('data', listener) - child.stdout.removeListener('data', listener) - } + child.stderr.removeListener('data', listener); + child.stdout.removeListener('data', listener); + }; - let output = '' - let success = false + let output = ''; + let success = false; function listener (data: Buffer) { - output += data + output += data; if (output.trim().indexOf('Debugger listening on ws://') > -1 && output.indexOf('\n') > -1) { - const socketMatch = output.trim().match(/(ws:\/\/.+:[0-9]+\/.+?)\n/gm) + const socketMatch = output.trim().match(/(ws:\/\/.+:[0-9]+\/.+?)\n/gm); if (socketMatch && socketMatch[0]) { - const w = (webContents as any).create({}) as WebContents + const w = (webContents as any).create({}) as WebContents; w.loadURL('about:blank') .then(() => w.executeJavaScript(`new Promise(resolve => { const connection = new WebSocket(${JSON.stringify(socketMatch[0])}) @@ -269,42 +269,42 @@ describe('node feature', () => { } })`)) .then(() => { - (w as any).destroy() - child.send('plz-quit') - success = true - cleanup() - done() - }) + (w as any).destroy(); + child.send('plz-quit'); + success = true; + cleanup(); + done(); + }); } } } - child.stderr.on('data', listener) - child.stdout.on('data', listener) + child.stderr.on('data', listener); + child.stdout.on('data', listener); child.on('exit', () => { - if (!success) cleanup() - }) - }) + if (!success) cleanup(); + }); + }); it('Supports js binding', (done) => { child = childProcess.spawn(process.execPath, ['--inspect', path.join(fixtures, 'module', 'inspector-binding.js')], { env: { ELECTRON_RUN_AS_NODE: 'true' }, stdio: ['ipc'] - }) as childProcess.ChildProcessWithoutNullStreams - exitPromise = emittedOnce(child, 'exit') + }) as childProcess.ChildProcessWithoutNullStreams; + exitPromise = emittedOnce(child, 'exit'); child.on('message', ({ cmd, debuggerEnabled, success }) => { if (cmd === 'assert') { - expect(debuggerEnabled).to.be.true() - expect(success).to.be.true() - done() + expect(debuggerEnabled).to.be.true(); + expect(success).to.be.true(); + done(); } - }) - }) - }) + }); + }); + }); it('Can find a module using a package.json main field', () => { - const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')]) - expect(result.status).to.equal(0) - }) -}) + const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')]); + expect(result.status).to.equal(0); + }); +}); diff --git a/spec-main/security-warnings-spec.ts b/spec-main/security-warnings-spec.ts index 6d28c77c8c66a..06f4f921ceaff 100644 --- a/spec-main/security-warnings-spec.ts +++ b/spec-main/security-warnings-spec.ts @@ -1,76 +1,76 @@ -import { expect } from 'chai' -import * as http from 'http' -import * as fs from 'fs' -import * as path from 'path' -import * as url from 'url' +import { expect } from 'chai'; +import * as http from 'http'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as url from 'url'; -import { BrowserWindow, WebPreferences } from 'electron' +import { BrowserWindow, WebPreferences } from 'electron'; -import { closeWindow } from './window-helpers' -import { AddressInfo } from 'net' -import { emittedUntil } from './events-helpers' +import { closeWindow } from './window-helpers'; +import { AddressInfo } from 'net'; +import { emittedUntil } from './events-helpers'; const messageContainsSecurityWarning = (event: Event, level: number, message: string) => { - return message.indexOf('Electron Security Warning') > -1 -} + return message.indexOf('Electron Security Warning') > -1; +}; const isLoaded = (event: Event, level: number, message: string) => { - return (message === 'loaded') -} + return (message === 'loaded'); +}; describe('security warnings', () => { - let server: http.Server - let w: BrowserWindow - let useCsp = true - let serverUrl: string + let server: http.Server; + let w: BrowserWindow; + let useCsp = true; + let serverUrl: string; before((done) => { // Create HTTP Server server = http.createServer((request, response) => { - const uri = url.parse(request.url!).pathname! - let filename = path.join(__dirname, '..', 'spec', 'fixtures', 'pages', uri) + const uri = url.parse(request.url!).pathname!; + let filename = path.join(__dirname, '..', 'spec', 'fixtures', 'pages', uri); fs.stat(filename, (error, stats) => { if (error) { - response.writeHead(404, { 'Content-Type': 'text/plain' }) - response.end() - return + response.writeHead(404, { 'Content-Type': 'text/plain' }); + response.end(); + return; } if (stats.isDirectory()) { - filename += '/index.html' + filename += '/index.html'; } fs.readFile(filename, 'binary', (err, file) => { if (err) { - response.writeHead(404, { 'Content-Type': 'text/plain' }) - response.end() - return + response.writeHead(404, { 'Content-Type': 'text/plain' }); + response.end(); + return; } - const cspHeaders = { 'Content-Security-Policy': 'script-src \'self\' \'unsafe-inline\'' } - response.writeHead(200, useCsp ? cspHeaders : undefined) - response.write(file, 'binary') - response.end() - }) - }) + const cspHeaders = { 'Content-Security-Policy': 'script-src \'self\' \'unsafe-inline\'' }; + response.writeHead(200, useCsp ? cspHeaders : undefined); + response.write(file, 'binary'); + response.end(); + }); + }); }).listen(0, '127.0.0.1', () => { - serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - done() - }) - }) + serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + done(); + }); + }); after(() => { // Close server - server.close() - server = null as unknown as any - }) + server.close(); + server = null as unknown as any; + }); afterEach(async () => { - useCsp = true - await closeWindow(w) - w = null as unknown as any - }) + useCsp = true; + await closeWindow(w); + w = null as unknown as any; + }); it('should warn about Node.js integration with remote content', async () => { w = new BrowserWindow({ @@ -78,12 +78,12 @@ describe('security warnings', () => { webPreferences: { nodeIntegration: true } - }) + }); - w.loadURL(`${serverUrl}/base-page-security.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('Node.js Integration with Remote Content') - }) + w.loadURL(`${serverUrl}/base-page-security.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('Node.js Integration with Remote Content'); + }); it('should not warn about Node.js integration with remote content from localhost', async () => { w = new BrowserWindow({ @@ -91,12 +91,12 @@ describe('security warnings', () => { webPreferences: { nodeIntegration: true } - }) + }); - w.loadURL(`${serverUrl}/base-page-security-onload-message.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded) - expect(message).to.not.include('Node.js Integration with Remote Content') - }) + w.loadURL(`${serverUrl}/base-page-security-onload-message.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded); + expect(message).to.not.include('Node.js Integration with Remote Content'); + }); const generateSpecs = (description: string, webPreferences: WebPreferences) => { describe(description, () => { @@ -107,12 +107,12 @@ describe('security warnings', () => { webSecurity: false, ...webPreferences } - }) + }); - w.loadURL(`${serverUrl}/base-page-security.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('Disabled webSecurity') - }) + w.loadURL(`${serverUrl}/base-page-security.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('Disabled webSecurity'); + }); it('should warn about insecure Content-Security-Policy', async () => { w = new BrowserWindow({ @@ -121,13 +121,13 @@ describe('security warnings', () => { enableRemoteModule: false, ...webPreferences } - }) + }); - useCsp = false - w.loadURL(`${serverUrl}/base-page-security.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('Insecure Content-Security-Policy') - }) + useCsp = false; + w.loadURL(`${serverUrl}/base-page-security.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('Insecure Content-Security-Policy'); + }); it('should warn about allowRunningInsecureContent', async () => { w = new BrowserWindow({ @@ -136,12 +136,12 @@ describe('security warnings', () => { allowRunningInsecureContent: true, ...webPreferences } - }) + }); - w.loadURL(`${serverUrl}/base-page-security.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('allowRunningInsecureContent') - }) + w.loadURL(`${serverUrl}/base-page-security.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('allowRunningInsecureContent'); + }); it('should warn about experimentalFeatures', async () => { w = new BrowserWindow({ @@ -150,12 +150,12 @@ describe('security warnings', () => { experimentalFeatures: true, ...webPreferences } - }) + }); - w.loadURL(`${serverUrl}/base-page-security.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('experimentalFeatures') - }) + w.loadURL(`${serverUrl}/base-page-security.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('experimentalFeatures'); + }); it('should warn about enableBlinkFeatures', async () => { w = new BrowserWindow({ @@ -164,69 +164,69 @@ describe('security warnings', () => { enableBlinkFeatures: 'my-cool-feature', ...webPreferences } - }) + }); - w.loadURL(`${serverUrl}/base-page-security.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('enableBlinkFeatures') - }) + w.loadURL(`${serverUrl}/base-page-security.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('enableBlinkFeatures'); + }); it('should warn about allowpopups', async () => { w = new BrowserWindow({ show: false, webPreferences - }) + }); - w.loadURL(`${serverUrl}/webview-allowpopups.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('allowpopups') - }) + w.loadURL(`${serverUrl}/webview-allowpopups.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('allowpopups'); + }); it('should warn about insecure resources', async () => { w = new BrowserWindow({ show: false, webPreferences: { ...webPreferences } - }) + }); - w.loadURL(`${serverUrl}/insecure-resources.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('Insecure Resources') - }) + w.loadURL(`${serverUrl}/insecure-resources.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('Insecure Resources'); + }); it('should not warn about loading insecure-resources.html from localhost', async () => { w = new BrowserWindow({ show: false, webPreferences - }) + }); - w.loadURL(`${serverUrl}/insecure-resources.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.not.include('insecure-resources.html') - }) + w.loadURL(`${serverUrl}/insecure-resources.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.not.include('insecure-resources.html'); + }); it('should warn about enabled remote module with remote content', async () => { w = new BrowserWindow({ show: false, webPreferences - }) + }); - w.loadURL(`${serverUrl}/base-page-security.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning) - expect(message).to.include('enableRemoteModule') - }) + w.loadURL(`${serverUrl}/base-page-security.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning); + expect(message).to.include('enableRemoteModule'); + }); it('should not warn about enabled remote module with remote content from localhost', async () => { w = new BrowserWindow({ show: false, webPreferences - }) - w.loadURL(`${serverUrl}/base-page-security-onload-message.html`) - const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded) - expect(message).to.not.include('enableRemoteModule') - }) - }) - } - - generateSpecs('without sandbox', {}) - generateSpecs('with sandbox', { sandbox: true }) -}) + }); + w.loadURL(`${serverUrl}/base-page-security-onload-message.html`); + const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded); + expect(message).to.not.include('enableRemoteModule'); + }); + }); + }; + + generateSpecs('without sandbox', {}); + generateSpecs('with sandbox', { sandbox: true }); +}); diff --git a/spec-main/spec-helpers.ts b/spec-main/spec-helpers.ts index fad28c9a23f0a..4cdeae33669a1 100644 --- a/spec-main/spec-helpers.ts +++ b/spec-main/spec-helpers.ts @@ -1,4 +1,4 @@ -export const ifit = (condition: boolean) => (condition ? it : it.skip) -export const ifdescribe = (condition: boolean) => (condition ? describe : describe.skip) +export const ifit = (condition: boolean) => (condition ? it : it.skip); +export const ifdescribe = (condition: boolean) => (condition ? describe : describe.skip); -export const delay = (time: number) => new Promise(resolve => setTimeout(resolve, time)) +export const delay = (time: number) => new Promise(resolve => setTimeout(resolve, time)); diff --git a/spec-main/spellchecker-spec.ts b/spec-main/spellchecker-spec.ts index cc28cebb801f2..5ef1c782d074a 100644 --- a/spec-main/spellchecker-spec.ts +++ b/spec-main/spellchecker-spec.ts @@ -1,137 +1,137 @@ -import { BrowserWindow, Session, session } from 'electron' +import { BrowserWindow, Session, session } from 'electron'; -import { expect } from 'chai' -import * as path from 'path' -import { closeWindow } from './window-helpers' -import { emittedOnce } from './events-helpers' -import { ifit, ifdescribe } from './spec-helpers' +import { expect } from 'chai'; +import * as path from 'path'; +import { closeWindow } from './window-helpers'; +import { emittedOnce } from './events-helpers'; +import { ifit, ifdescribe } from './spec-helpers'; -const features = process.electronBinding('features') +const features = process.electronBinding('features'); ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { - let w: BrowserWindow + let w: BrowserWindow; beforeEach(async () => { w = new BrowserWindow({ show: false - }) - await w.loadFile(path.resolve(__dirname, './fixtures/chromium/spellchecker.html')) - }) + }); + await w.loadFile(path.resolve(__dirname, './fixtures/chromium/spellchecker.html')); + }); afterEach(async () => { - await closeWindow(w) - }) + await closeWindow(w); + }); // Context menu test can not run on Windows, and it is not reliable on ARM // CI machines. const shouldRun = process.platform !== 'win32' && process.arch !== 'arm' && - process.arch !== 'arm64' + process.arch !== 'arm64'; ifit(shouldRun)('should detect correctly spelled words as correct', async () => { - await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautiful and lovely"') - await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()') - const contextMenuPromise = emittedOnce(w.webContents, 'context-menu') + await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautiful and lovely"'); + await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()'); + const contextMenuPromise = emittedOnce(w.webContents, 'context-menu'); // Wait for spellchecker to load - await new Promise(resolve => setTimeout(resolve, 500)) + await new Promise(resolve => setTimeout(resolve, 500)); w.webContents.sendInputEvent({ type: 'mouseDown', button: 'right', x: 43, y: 42 - }) - const contextMenuParams: Electron.ContextMenuParams = (await contextMenuPromise)[1] - expect(contextMenuParams.misspelledWord).to.eq('') - expect(contextMenuParams.dictionarySuggestions).to.have.lengthOf(0) - }) + }); + const contextMenuParams: Electron.ContextMenuParams = (await contextMenuPromise)[1]; + expect(contextMenuParams.misspelledWord).to.eq(''); + expect(contextMenuParams.dictionarySuggestions).to.have.lengthOf(0); + }); ifit(shouldRun)('should detect incorrectly spelled words as incorrect', async () => { - await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautifulllll asd asd"') - await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()') - const contextMenuPromise = emittedOnce(w.webContents, 'context-menu') + await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautifulllll asd asd"'); + await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()'); + const contextMenuPromise = emittedOnce(w.webContents, 'context-menu'); // Wait for spellchecker to load - await new Promise(resolve => setTimeout(resolve, 500)) + await new Promise(resolve => setTimeout(resolve, 500)); w.webContents.sendInputEvent({ type: 'mouseDown', button: 'right', x: 43, y: 42 - }) - const contextMenuParams: Electron.ContextMenuParams = (await contextMenuPromise)[1] - expect(contextMenuParams.misspelledWord).to.eq('Beautifulllll') - expect(contextMenuParams.dictionarySuggestions).to.have.length.of.at.least(1) - }) + }); + const contextMenuParams: Electron.ContextMenuParams = (await contextMenuPromise)[1]; + expect(contextMenuParams.misspelledWord).to.eq('Beautifulllll'); + expect(contextMenuParams.dictionarySuggestions).to.have.length.of.at.least(1); + }); describe('custom dictionary word list API', () => { - let ses: Session + let ses: Session; beforeEach(async () => { // ensure a new session runs on each test run - ses = session.fromPartition(`persist:customdictionary-test-${Date.now()}`) - }) + ses = session.fromPartition(`persist:customdictionary-test-${Date.now()}`); + }); afterEach(async () => { if (ses) { - await ses.clearStorageData() - ses.destroy() + await ses.clearStorageData(); + ses.destroy(); } - }) + }); describe('ses.listWordsFromSpellCheckerDictionary', () => { it('should successfully list words in custom dictionary', async () => { - const words = ['foo', 'bar', 'baz'] - const results = words.map(word => ses.addWordToSpellCheckerDictionary(word)) - expect(results).to.eql([true, true, true]) + const words = ['foo', 'bar', 'baz']; + const results = words.map(word => ses.addWordToSpellCheckerDictionary(word)); + expect(results).to.eql([true, true, true]); - const wordList = await ses.listWordsInSpellCheckerDictionary() - expect(wordList).to.have.deep.members(words) - }) + const wordList = await ses.listWordsInSpellCheckerDictionary(); + expect(wordList).to.have.deep.members(words); + }); it('should return an empty array if no words are added', async () => { - const wordList = await ses.listWordsInSpellCheckerDictionary() - expect(wordList).to.have.length(0) - }) - }) + const wordList = await ses.listWordsInSpellCheckerDictionary(); + expect(wordList).to.have.length(0); + }); + }); describe('ses.addWordToSpellCheckerDictionary', () => { it('should successfully add word to custom dictionary', async () => { - const result = ses.addWordToSpellCheckerDictionary('foobar') - expect(result).to.equal(true) - const wordList = await ses.listWordsInSpellCheckerDictionary() - expect(wordList).to.eql(['foobar']) - }) + const result = ses.addWordToSpellCheckerDictionary('foobar'); + expect(result).to.equal(true); + const wordList = await ses.listWordsInSpellCheckerDictionary(); + expect(wordList).to.eql(['foobar']); + }); it('should fail for an empty string', async () => { - const result = ses.addWordToSpellCheckerDictionary('') - expect(result).to.equal(false) - const wordList = await ses.listWordsInSpellCheckerDictionary - expect(wordList).to.have.length(0) - }) + const result = ses.addWordToSpellCheckerDictionary(''); + expect(result).to.equal(false); + const wordList = await ses.listWordsInSpellCheckerDictionary; + expect(wordList).to.have.length(0); + }); // remove API will always return false because we can't add words it('should fail for non-persistent sessions', async () => { - const tempSes = session.fromPartition('temporary') - const result = tempSes.addWordToSpellCheckerDictionary('foobar') - expect(result).to.equal(false) - }) - }) + const tempSes = session.fromPartition('temporary'); + const result = tempSes.addWordToSpellCheckerDictionary('foobar'); + expect(result).to.equal(false); + }); + }); describe('ses.removeWordFromSpellCheckerDictionary', () => { it('should successfully remove words to custom dictionary', async () => { - const result1 = ses.addWordToSpellCheckerDictionary('foobar') - expect(result1).to.equal(true) - const wordList1 = await ses.listWordsInSpellCheckerDictionary() - expect(wordList1).to.eql(['foobar']) - const result2 = ses.removeWordFromSpellCheckerDictionary('foobar') - expect(result2).to.equal(true) - const wordList2 = await ses.listWordsInSpellCheckerDictionary() - expect(wordList2).to.have.length(0) - }) + const result1 = ses.addWordToSpellCheckerDictionary('foobar'); + expect(result1).to.equal(true); + const wordList1 = await ses.listWordsInSpellCheckerDictionary(); + expect(wordList1).to.eql(['foobar']); + const result2 = ses.removeWordFromSpellCheckerDictionary('foobar'); + expect(result2).to.equal(true); + const wordList2 = await ses.listWordsInSpellCheckerDictionary(); + expect(wordList2).to.have.length(0); + }); it('should fail for words not in custom dictionary', () => { - const result2 = ses.removeWordFromSpellCheckerDictionary('foobar') - expect(result2).to.equal(false) - }) - }) - }) -}) + const result2 = ses.removeWordFromSpellCheckerDictionary('foobar'); + expect(result2).to.equal(false); + }); + }); + }); +}); diff --git a/spec-main/types-spec.ts b/spec-main/types-spec.ts index b0de8df6f133a..a635b64da6971 100644 --- a/spec-main/types-spec.ts +++ b/spec-main/types-spec.ts @@ -1,10 +1,10 @@ -import { expect } from 'chai' +import { expect } from 'chai'; describe('bundled @types/node', () => { it('should match the major version of bundled node', () => { - expect(require('../npm/package.json').dependencies).to.have.property('@types/node') - const range = require('../npm/package.json').dependencies['@types/node'] - expect(range).to.match(/^\^.+/, 'should allow any type dep in a major range') - expect(range.slice(1).split('.')[0]).to.equal(process.versions.node.split('.')[0]) - }) -}) + expect(require('../npm/package.json').dependencies).to.have.property('@types/node'); + const range = require('../npm/package.json').dependencies['@types/node']; + expect(range).to.match(/^\^.+/, 'should allow any type dep in a major range'); + expect(range.slice(1).split('.')[0]).to.equal(process.versions.node.split('.')[0]); + }); +}); diff --git a/spec-main/version-bump-spec.ts b/spec-main/version-bump-spec.ts index 0acacbc9bab7e..9bdd228ab6bee 100644 --- a/spec-main/version-bump-spec.ts +++ b/spec-main/version-bump-spec.ts @@ -1,7 +1,7 @@ -import { expect } from 'chai' -import { nextVersion } from '../script/release/version-bumper' -import * as utils from '../script/release/version-utils' -import { ifdescribe } from './spec-helpers' +import { expect } from 'chai'; +import { nextVersion } from '../script/release/version-bumper'; +import * as utils from '../script/release/version-utils'; +import { ifdescribe } from './spec-helpers'; describe('version-bumper', () => { describe('makeVersion', () => { @@ -10,11 +10,11 @@ describe('version-bumper', () => { major: 2, minor: 0, patch: 0 - } + }; - const version = utils.makeVersion(components, '.') - expect(version).to.equal('2.0.0') - }) + const version = utils.makeVersion(components, '.'); + expect(version).to.equal('2.0.0'); + }); it('makes a version with a period delimeter and a partial pre', () => { const components = { @@ -22,11 +22,11 @@ describe('version-bumper', () => { minor: 0, patch: 0, pre: ['nightly', 12345678] - } + }; - const version = utils.makeVersion(components, '.', utils.preType.PARTIAL) - expect(version).to.equal('2.0.0.12345678') - }) + const version = utils.makeVersion(components, '.', utils.preType.PARTIAL); + expect(version).to.equal('2.0.0.12345678'); + }); it('makes a version with a period delimeter and a full pre', () => { const components = { @@ -34,103 +34,103 @@ describe('version-bumper', () => { minor: 0, patch: 0, pre: ['nightly', 12345678] - } + }; - const version = utils.makeVersion(components, '.', utils.preType.FULL) - expect(version).to.equal('2.0.0-nightly.12345678') - }) - }) + const version = utils.makeVersion(components, '.', utils.preType.FULL); + expect(version).to.equal('2.0.0-nightly.12345678'); + }); + }); // On macOS Circle CI we don't have a real git environment due to running // gclient sync on a linux machine. These tests therefore don't run as expected. ifdescribe(!(process.platform === 'linux' && process.arch === 'arm') && process.platform !== 'darwin')('nextVersion', () => { - const nightlyPattern = /[0-9.]*(-nightly.(\d{4})(\d{2})(\d{2}))$/g - const betaPattern = /[0-9.]*(-beta[0-9.]*)/g + const nightlyPattern = /[0-9.]*(-nightly.(\d{4})(\d{2})(\d{2}))$/g; + const betaPattern = /[0-9.]*(-beta[0-9.]*)/g; it('bumps to nightly from stable', async () => { - const version = 'v2.0.0' - const next = await nextVersion('nightly', version) - const matches = next.match(nightlyPattern) - expect(matches).to.have.lengthOf(1) - }) + const version = 'v2.0.0'; + const next = await nextVersion('nightly', version); + const matches = next.match(nightlyPattern); + expect(matches).to.have.lengthOf(1); + }); it('bumps to nightly from beta', async () => { - const version = 'v2.0.0-beta.1' - const next = await nextVersion('nightly', version) - const matches = next.match(nightlyPattern) - expect(matches).to.have.lengthOf(1) - }) + const version = 'v2.0.0-beta.1'; + const next = await nextVersion('nightly', version); + const matches = next.match(nightlyPattern); + expect(matches).to.have.lengthOf(1); + }); it('bumps to nightly from nightly', async () => { - const version = 'v2.0.0-nightly.19950901' - const next = await nextVersion('nightly', version) - const matches = next.match(nightlyPattern) - expect(matches).to.have.lengthOf(1) - }) + const version = 'v2.0.0-nightly.19950901'; + const next = await nextVersion('nightly', version); + const matches = next.match(nightlyPattern); + expect(matches).to.have.lengthOf(1); + }); it('bumps to a nightly version above our switch from N-0-x to N-x-y branch names', async () => { - const version = 'v2.0.0-nightly.19950901' - const next = await nextVersion('nightly', version) + const version = 'v2.0.0-nightly.19950901'; + const next = await nextVersion('nightly', version); // If it starts with v8 then we didn't bump above the 8-x-y branch - expect(next.startsWith('v8')).to.equal(false) - }) + expect(next.startsWith('v8')).to.equal(false); + }); it('throws error when bumping to beta from stable', () => { - const version = 'v2.0.0' + const version = 'v2.0.0'; return expect( nextVersion('beta', version) - ).to.be.rejectedWith('Cannot bump to beta from stable.') - }) + ).to.be.rejectedWith('Cannot bump to beta from stable.'); + }); it('bumps to beta from nightly', async () => { - const version = 'v2.0.0-nightly.19950901' - const next = await nextVersion('beta', version) - const matches = next.match(betaPattern) - expect(matches).to.have.lengthOf(1) - }) + const version = 'v2.0.0-nightly.19950901'; + const next = await nextVersion('beta', version); + const matches = next.match(betaPattern); + expect(matches).to.have.lengthOf(1); + }); it('bumps to beta from beta', async () => { - const version = 'v2.0.0-beta.8' - const next = await nextVersion('beta', version) - expect(next).to.equal('2.0.0-beta.9') - }) + const version = 'v2.0.0-beta.8'; + const next = await nextVersion('beta', version); + expect(next).to.equal('2.0.0-beta.9'); + }); it('bumps to stable from beta', async () => { - const version = 'v2.0.0-beta.1' - const next = await nextVersion('stable', version) - expect(next).to.equal('2.0.0') - }) + const version = 'v2.0.0-beta.1'; + const next = await nextVersion('stable', version); + expect(next).to.equal('2.0.0'); + }); it('bumps to stable from stable', async () => { - const version = 'v2.0.0' - const next = await nextVersion('stable', version) - expect(next).to.equal('2.0.1') - }) + const version = 'v2.0.0'; + const next = await nextVersion('stable', version); + expect(next).to.equal('2.0.1'); + }); it('bumps to minor from stable', async () => { - const version = 'v2.0.0' - const next = await nextVersion('minor', version) - expect(next).to.equal('2.1.0') - }) + const version = 'v2.0.0'; + const next = await nextVersion('minor', version); + expect(next).to.equal('2.1.0'); + }); it('bumps to stable from nightly', async () => { - const version = 'v2.0.0-nightly.19950901' - const next = await nextVersion('stable', version) - expect(next).to.equal('2.0.0') - }) + const version = 'v2.0.0-nightly.19950901'; + const next = await nextVersion('stable', version); + expect(next).to.equal('2.0.0'); + }); it('throws on an invalid version', () => { - const version = 'vI.AM.INVALID' + const version = 'vI.AM.INVALID'; return expect( nextVersion('beta', version) - ).to.be.rejectedWith(`Invalid current version: ${version}`) - }) + ).to.be.rejectedWith(`Invalid current version: ${version}`); + }); it('throws on an invalid bump type', () => { - const version = 'v2.0.0' + const version = 'v2.0.0'; return expect( nextVersion('WRONG', version) - ).to.be.rejectedWith('Invalid bump type.') - }) - }) -}) + ).to.be.rejectedWith('Invalid bump type.'); + }); + }); +}); diff --git a/spec-main/visibility-state-spec.ts b/spec-main/visibility-state-spec.ts index 729ff31fecf9e..69dff5233202c 100644 --- a/spec-main/visibility-state-spec.ts +++ b/spec-main/visibility-state-spec.ts @@ -1,28 +1,28 @@ -import { expect } from 'chai' -import * as cp from 'child_process' -import { BrowserWindow, BrowserWindowConstructorOptions, ipcMain } from 'electron' -import * as path from 'path' +import { expect } from 'chai'; +import * as cp from 'child_process'; +import { BrowserWindow, BrowserWindowConstructorOptions, ipcMain } from 'electron'; +import * as path from 'path'; -import { emittedOnce } from './events-helpers' -import { closeWindow } from './window-helpers' -import { ifdescribe, delay } from './spec-helpers' +import { emittedOnce } from './events-helpers'; +import { closeWindow } from './window-helpers'; +import { ifdescribe, delay } from './spec-helpers'; // visibilityState specs pass on linux with a real window manager but on CI // the environment does not let these specs pass ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { - let w: BrowserWindow + let w: BrowserWindow; afterEach(() => { - return closeWindow(w) - }) + return closeWindow(w); + }); - const load = () => w.loadFile(path.resolve(__dirname, 'fixtures', 'chromium', 'visibilitystate.html')) + const load = () => w.loadFile(path.resolve(__dirname, 'fixtures', 'chromium', 'visibilitystate.html')); const itWithOptions = (name: string, options: BrowserWindowConstructorOptions, fn: Mocha.Func) => { return it(name, async function (...args) { // document.visibilityState tests are very flaky, this is probably because // Electron implements it via async IPC messages. - this.retries(3) + this.retries(3); w = new BrowserWindow({ ...options, @@ -31,47 +31,47 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { ...(options.webPreferences || {}), nodeIntegration: true } - }) - await Promise.resolve(fn.apply(this, args)) - }) - } + }); + await Promise.resolve(fn.apply(this, args)); + }); + }; const getVisibilityState = async (): Promise => { - const response = emittedOnce(ipcMain, 'visibility-state') - w.webContents.send('get-visibility-state') - return (await response)[1] - } + const response = emittedOnce(ipcMain, 'visibility-state'); + w.webContents.send('get-visibility-state'); + return (await response)[1]; + }; itWithOptions('should be visible when the window is initially shown by default', {}, async () => { - await load() - const state = await getVisibilityState() - expect(state).to.equal('visible') - }) + await load(); + const state = await getVisibilityState(); + expect(state).to.equal('visible'); + }); itWithOptions('should be visible when the window is initially shown', { show: true }, async () => { - await load() - const state = await getVisibilityState() - expect(state).to.equal('visible') - }) + await load(); + const state = await getVisibilityState(); + expect(state).to.equal('visible'); + }); itWithOptions('should be hidden when the window is initially hidden', { show: false }, async () => { - await load() - const state = await getVisibilityState() - expect(state).to.equal('hidden') - }) + await load(); + const state = await getVisibilityState(); + expect(state).to.equal('hidden'); + }); itWithOptions('should be visible when the window is initially hidden but shown before the page is loaded', { show: false }, async () => { - w.show() - await load() - const state = await getVisibilityState() - expect(state).to.equal('visible') - }) + w.show(); + await load(); + const state = await getVisibilityState(); + expect(state).to.equal('visible'); + }); itWithOptions('should be hidden when the window is initially shown but hidden before the page is loaded', { show: true @@ -79,59 +79,59 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { // TODO(MarshallOfSound): Figure out if we can work around this 1 tick issue for users if (process.platform === 'darwin') { // Wait for a tick, the window being "shown" takes 1 tick on macOS - await delay(0) + await delay(0); } - w.hide() - await load() - const state = await getVisibilityState() - expect(state).to.equal('hidden') - }) + w.hide(); + await load(); + const state = await getVisibilityState(); + expect(state).to.equal('hidden'); + }); itWithOptions('should be toggle between visible and hidden as the window is hidden and shown', {}, async () => { - await load() - expect(await getVisibilityState()).to.equal('visible') - await emittedOnce(ipcMain, 'visibility-change', () => w.hide()) - expect(await getVisibilityState()).to.equal('hidden') - await emittedOnce(ipcMain, 'visibility-change', () => w.show()) - expect(await getVisibilityState()).to.equal('visible') - }) + await load(); + expect(await getVisibilityState()).to.equal('visible'); + await emittedOnce(ipcMain, 'visibility-change', () => w.hide()); + expect(await getVisibilityState()).to.equal('hidden'); + await emittedOnce(ipcMain, 'visibility-change', () => w.show()); + expect(await getVisibilityState()).to.equal('visible'); + }); itWithOptions('should become hidden when a window is minimized', {}, async () => { - await load() - expect(await getVisibilityState()).to.equal('visible') - await emittedOnce(ipcMain, 'visibility-change', () => w.minimize()) - expect(await getVisibilityState()).to.equal('hidden') - }) + await load(); + expect(await getVisibilityState()).to.equal('visible'); + await emittedOnce(ipcMain, 'visibility-change', () => w.minimize()); + expect(await getVisibilityState()).to.equal('hidden'); + }); itWithOptions('should become visible when a window is restored', {}, async () => { - await load() - expect(await getVisibilityState()).to.equal('visible') - await emittedOnce(ipcMain, 'visibility-change', () => w.minimize()) - await emittedOnce(ipcMain, 'visibility-change', () => w.restore()) - expect(await getVisibilityState()).to.equal('visible') - }) + await load(); + expect(await getVisibilityState()).to.equal('visible'); + await emittedOnce(ipcMain, 'visibility-change', () => w.minimize()); + await emittedOnce(ipcMain, 'visibility-change', () => w.restore()); + expect(await getVisibilityState()).to.equal('visible'); + }); describe('on platforms that support occlusion detection', () => { - let child: cp.ChildProcess + let child: cp.ChildProcess; before(function () { - if (process.platform !== 'darwin') this.skip() - }) + if (process.platform !== 'darwin') this.skip(); + }); const makeOtherWindow = (opts: { x: number; y: number; width: number; height: number; }) => { - child = cp.spawn(process.execPath, [path.resolve(__dirname, 'fixtures', 'chromium', 'other-window.js'), `${opts.x}`, `${opts.y}`, `${opts.width}`, `${opts.height}`]) + child = cp.spawn(process.execPath, [path.resolve(__dirname, 'fixtures', 'chromium', 'other-window.js'), `${opts.x}`, `${opts.y}`, `${opts.width}`, `${opts.height}`]); return new Promise(resolve => { child.stdout!.on('data', (chunk) => { - if (chunk.toString().includes('__ready__')) resolve() - }) - }) - } + if (chunk.toString().includes('__ready__')) resolve(); + }); + }); + }; afterEach(() => { if (child && !child.killed) { - child.kill('SIGTERM') + child.kill('SIGTERM'); } - }) + }); itWithOptions('should be visible when two windows are on screen', { x: 0, @@ -144,11 +144,11 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { y: 0, width: 200, height: 200 - }) - await load() - const state = await getVisibilityState() - expect(state).to.equal('visible') - }) + }); + await load(); + const state = await getVisibilityState(); + expect(state).to.equal('visible'); + }); itWithOptions('should be visible when two windows are on screen that overlap partially', { x: 50, @@ -161,11 +161,11 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { y: 0, width: 200, height: 200 - }) - await load() - const state = await getVisibilityState() - expect(state).to.equal('visible') - }) + }); + await load(); + const state = await getVisibilityState(); + expect(state).to.equal('visible'); + }); itWithOptions('should be hidden when a second window completely conceals the current window', { x: 50, @@ -173,18 +173,18 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { width: 50, height: 50 }, async function () { - this.timeout(240000) - await load() + this.timeout(240000); + await load(); await emittedOnce(ipcMain, 'visibility-change', async () => { await makeOtherWindow({ x: 0, y: 0, width: 300, height: 300 - }) - }) - const state = await getVisibilityState() - expect(state).to.equal('hidden') - }) - }) -}) + }); + }); + const state = await getVisibilityState(); + expect(state).to.equal('hidden'); + }); + }); +}); diff --git a/spec-main/webview-spec.ts b/spec-main/webview-spec.ts index 4f2dc4124e45b..e2d5c5476b96c 100644 --- a/spec-main/webview-spec.ts +++ b/spec-main/webview-spec.ts @@ -1,8 +1,8 @@ -import * as path from 'path' -import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron' -import { closeAllWindows } from './window-helpers' -import { emittedOnce } from './events-helpers' -import { expect } from 'chai' +import * as path from 'path'; +import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron'; +import { closeAllWindows } from './window-helpers'; +import { emittedOnce } from './events-helpers'; +import { expect } from 'chai'; async function loadWebView (w: WebContents, attributes: Record): Promise { await w.executeJavaScript(` @@ -16,13 +16,13 @@ async function loadWebView (w: WebContents, attributes: Record): resolve() }) }) - `) + `); } describe(' tag', function () { - const fixtures = path.join(__dirname, '..', 'spec', 'fixtures') + const fixtures = path.join(__dirname, '..', 'spec', 'fixtures'); - afterEach(closeAllWindows) + afterEach(closeAllWindows); it('works without script tag in page', async () => { const w = new BrowserWindow({ @@ -31,10 +31,10 @@ describe(' tag', function () { webviewTag: true, nodeIntegration: true } - }) - w.loadFile(path.join(fixtures, 'pages', 'webview-no-script.html')) - await emittedOnce(ipcMain, 'pong') - }) + }); + w.loadFile(path.join(fixtures, 'pages', 'webview-no-script.html')); + await emittedOnce(ipcMain, 'pong'); + }); it('works with sandbox', async () => { const w = new BrowserWindow({ @@ -44,10 +44,10 @@ describe(' tag', function () { nodeIntegration: true, sandbox: true } - }) - w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')) - await emittedOnce(ipcMain, 'pong') - }) + }); + w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')); + await emittedOnce(ipcMain, 'pong'); + }); it('works with contextIsolation', async () => { const w = new BrowserWindow({ @@ -57,10 +57,10 @@ describe(' tag', function () { nodeIntegration: true, contextIsolation: true } - }) - w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')) - await emittedOnce(ipcMain, 'pong') - }) + }); + w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')); + await emittedOnce(ipcMain, 'pong'); + }); it('works with contextIsolation + sandbox', async () => { const w = new BrowserWindow({ @@ -71,10 +71,10 @@ describe(' tag', function () { contextIsolation: true, sandbox: true } - }) - w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')) - await emittedOnce(ipcMain, 'pong') - }) + }); + w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')); + await emittedOnce(ipcMain, 'pong'); + }); it('is disabled by default', async () => { const w = new BrowserWindow({ @@ -83,54 +83,54 @@ describe(' tag', function () { preload: path.join(fixtures, 'module', 'preload-webview.js'), nodeIntegration: true } - }) + }); - const webview = emittedOnce(ipcMain, 'webview') - w.loadFile(path.join(fixtures, 'pages', 'webview-no-script.html')) - const [, type] = await webview + const webview = emittedOnce(ipcMain, 'webview'); + w.loadFile(path.join(fixtures, 'pages', 'webview-no-script.html')); + const [, type] = await webview; - expect(type).to.equal('undefined', 'WebView still exists') - }) + expect(type).to.equal('undefined', 'WebView still exists'); + }); // FIXME(deepak1556): Ch69 follow up. xdescribe('document.visibilityState/hidden', () => { afterEach(() => { - ipcMain.removeAllListeners('pong') - }) + ipcMain.removeAllListeners('pong'); + }); it('updates when the window is shown after the ready-to-show event', async () => { - const w = new BrowserWindow({ show: false }) - const readyToShowSignal = emittedOnce(w, 'ready-to-show') - const pongSignal1 = emittedOnce(ipcMain, 'pong') - w.loadFile(path.join(fixtures, 'pages', 'webview-visibilitychange.html')) - await pongSignal1 - const pongSignal2 = emittedOnce(ipcMain, 'pong') - await readyToShowSignal - w.show() - - const [, visibilityState, hidden] = await pongSignal2 - expect(visibilityState).to.equal('visible') - expect(hidden).to.be.false() - }) + const w = new BrowserWindow({ show: false }); + const readyToShowSignal = emittedOnce(w, 'ready-to-show'); + const pongSignal1 = emittedOnce(ipcMain, 'pong'); + w.loadFile(path.join(fixtures, 'pages', 'webview-visibilitychange.html')); + await pongSignal1; + const pongSignal2 = emittedOnce(ipcMain, 'pong'); + await readyToShowSignal; + w.show(); + + const [, visibilityState, hidden] = await pongSignal2; + expect(visibilityState).to.equal('visible'); + expect(hidden).to.be.false(); + }); it('inherits the parent window visibility state and receives visibilitychange events', async () => { - const w = new BrowserWindow({ show: false }) - w.loadFile(path.join(fixtures, 'pages', 'webview-visibilitychange.html')) - const [, visibilityState, hidden] = await emittedOnce(ipcMain, 'pong') - expect(visibilityState).to.equal('hidden') - expect(hidden).to.be.true() + const w = new BrowserWindow({ show: false }); + w.loadFile(path.join(fixtures, 'pages', 'webview-visibilitychange.html')); + const [, visibilityState, hidden] = await emittedOnce(ipcMain, 'pong'); + expect(visibilityState).to.equal('hidden'); + expect(hidden).to.be.true(); // We have to start waiting for the event // before we ask the webContents to resize. - const getResponse = emittedOnce(ipcMain, 'pong') - w.webContents.emit('-window-visibility-change', 'visible') + const getResponse = emittedOnce(ipcMain, 'pong'); + w.webContents.emit('-window-visibility-change', 'visible'); return getResponse.then(([, visibilityState, hidden]) => { - expect(visibilityState).to.equal('visible') - expect(hidden).to.be.false() - }) - }) - }) + expect(visibilityState).to.equal('visible'); + expect(hidden).to.be.false(); + }); + }); + }); describe('did-attach-webview event', () => { it('is emitted when a webview has been attached', async () => { @@ -140,16 +140,16 @@ describe(' tag', function () { webviewTag: true, nodeIntegration: true } - }) - const didAttachWebview = emittedOnce(w.webContents, 'did-attach-webview') - const webviewDomReady = emittedOnce(ipcMain, 'webview-dom-ready') - w.loadFile(path.join(fixtures, 'pages', 'webview-did-attach-event.html')) + }); + const didAttachWebview = emittedOnce(w.webContents, 'did-attach-webview'); + const webviewDomReady = emittedOnce(ipcMain, 'webview-dom-ready'); + w.loadFile(path.join(fixtures, 'pages', 'webview-did-attach-event.html')); - const [, webContents] = await didAttachWebview - const [, id] = await webviewDomReady - expect(webContents.id).to.equal(id) - }) - }) + const [, webContents] = await didAttachWebview; + const [, id] = await webviewDomReady; + expect(webContents.id).to.equal(id); + }); + }); it('loads devtools extensions registered on the parent window', async () => { const w = new BrowserWindow({ @@ -158,50 +158,50 @@ describe(' tag', function () { webviewTag: true, nodeIntegration: true } - }) - BrowserWindow.removeDevToolsExtension('foo') + }); + BrowserWindow.removeDevToolsExtension('foo'); - const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo') - await BrowserWindow.addDevToolsExtension(extensionPath) + const extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo'); + await BrowserWindow.addDevToolsExtension(extensionPath); - w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'webview-devtools.html')) - let childWebContentsId = 0 + w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'webview-devtools.html')); + let childWebContentsId = 0; app.once('web-contents-created', (e, webContents) => { - childWebContentsId = webContents.id + childWebContentsId = webContents.id; webContents.on('devtools-opened', function () { const showPanelIntervalId = setInterval(function () { if (!webContents.isDestroyed() && webContents.devToolsWebContents) { webContents.devToolsWebContents.executeJavaScript('(' + function () { const lastPanelId: any = (window as any).UI.inspectorView._tabbedPane._tabs.peekLast().id; - (window as any).UI.inspectorView.showPanel(lastPanelId) - }.toString() + ')()') + (window as any).UI.inspectorView.showPanel(lastPanelId); + }.toString() + ')()'); } else { - clearInterval(showPanelIntervalId) + clearInterval(showPanelIntervalId); } - }, 100) - }) - }) + }, 100); + }); + }); - const [, { runtimeId, tabId }] = await emittedOnce(ipcMain, 'answer') - expect(runtimeId).to.match(/^[a-z]{32}$/) - expect(tabId).to.equal(childWebContentsId) - }) + const [, { runtimeId, tabId }] = await emittedOnce(ipcMain, 'answer'); + expect(runtimeId).to.match(/^[a-z]{32}$/); + expect(tabId).to.equal(childWebContentsId); + }); describe('zoom behavior', () => { - const zoomScheme = standardScheme - const webviewSession = session.fromPartition('webview-temp') + const zoomScheme = standardScheme; + const webviewSession = session.fromPartition('webview-temp'); before((done) => { - const protocol = webviewSession.protocol + const protocol = webviewSession.protocol; protocol.registerStringProtocol(zoomScheme, (request, callback) => { - callback('hello') - }, (error) => done(error)) - }) + callback('hello'); + }, (error) => done(error)); + }); after((done) => { - const protocol = webviewSession.protocol - protocol.unregisterProtocol(zoomScheme, (error) => done(error)) - }) + const protocol = webviewSession.protocol; + protocol.unregisterProtocol(zoomScheme, (error) => done(error)); + }); it('inherits the zoomFactor of the parent window', async () => { const w = new BrowserWindow({ @@ -211,14 +211,14 @@ describe(' tag', function () { nodeIntegration: true, zoomFactor: 1.2 } - }) - const zoomEventPromise = emittedOnce(ipcMain, 'webview-parent-zoom-level') - w.loadFile(path.join(fixtures, 'pages', 'webview-zoom-factor.html')) + }); + const zoomEventPromise = emittedOnce(ipcMain, 'webview-parent-zoom-level'); + w.loadFile(path.join(fixtures, 'pages', 'webview-zoom-factor.html')); - const [, zoomFactor, zoomLevel] = await zoomEventPromise - expect(zoomFactor).to.equal(1.2) - expect(zoomLevel).to.equal(1) - }) + const [, zoomFactor, zoomLevel] = await zoomEventPromise; + expect(zoomFactor).to.equal(1.2); + expect(zoomLevel).to.equal(1); + }); it('maintains zoom level on navigation', async () => { const w = new BrowserWindow({ @@ -228,27 +228,27 @@ describe(' tag', function () { nodeIntegration: true, zoomFactor: 1.2 } - }) + }); const promise = new Promise((resolve) => { ipcMain.on('webview-zoom-level', (event, zoomLevel, zoomFactor, newHost, final) => { if (!newHost) { - expect(zoomFactor).to.equal(1.44) - expect(zoomLevel).to.equal(2.0) + expect(zoomFactor).to.equal(1.44); + expect(zoomLevel).to.equal(2.0); } else { - expect(zoomFactor).to.equal(1.2) - expect(zoomLevel).to.equal(1) + expect(zoomFactor).to.equal(1.2); + expect(zoomLevel).to.equal(1); } if (final) { - resolve() + resolve(); } - }) - }) + }); + }); - w.loadFile(path.join(fixtures, 'pages', 'webview-custom-zoom-level.html')) + w.loadFile(path.join(fixtures, 'pages', 'webview-custom-zoom-level.html')); - await promise - }) + await promise; + }); it('maintains zoom level when navigating within same page', async () => { const w = new BrowserWindow({ @@ -258,22 +258,22 @@ describe(' tag', function () { nodeIntegration: true, zoomFactor: 1.2 } - }) + }); const promise = new Promise((resolve) => { ipcMain.on('webview-zoom-in-page', (event, zoomLevel, zoomFactor, final) => { - expect(zoomFactor).to.equal(1.44) - expect(zoomLevel).to.equal(2.0) + expect(zoomFactor).to.equal(1.44); + expect(zoomLevel).to.equal(2.0); if (final) { - resolve() + resolve(); } - }) - }) + }); + }); - w.loadFile(path.join(fixtures, 'pages', 'webview-in-page-navigate.html')) + w.loadFile(path.join(fixtures, 'pages', 'webview-in-page-navigate.html')); - await promise - }) + await promise; + }); it('inherits zoom level for the origin when available', async () => { const w = new BrowserWindow({ @@ -283,21 +283,21 @@ describe(' tag', function () { nodeIntegration: true, zoomFactor: 1.2 } - }) - w.loadFile(path.join(fixtures, 'pages', 'webview-origin-zoom-level.html')) + }); + w.loadFile(path.join(fixtures, 'pages', 'webview-origin-zoom-level.html')); - const [, zoomLevel] = await emittedOnce(ipcMain, 'webview-origin-zoom-level') - expect(zoomLevel).to.equal(2.0) - }) - }) + const [, zoomLevel] = await emittedOnce(ipcMain, 'webview-origin-zoom-level'); + expect(zoomLevel).to.equal(2.0); + }); + }); describe('nativeWindowOpen option', () => { - let w: BrowserWindow + let w: BrowserWindow; beforeEach(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }) - await w.loadURL('about:blank') - }) - afterEach(closeAllWindows) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }); + await w.loadURL('about:blank'); + }); + afterEach(closeAllWindows); it('opens window of about:blank with cross-scripting enabled', async () => { // Don't wait for loading to finish. @@ -306,11 +306,11 @@ describe(' tag', function () { nodeintegration: 'on', webpreferences: 'nativeWindowOpen=1', src: `file://${path.join(fixtures, 'api', 'native-window-open-blank.html')}` - }) + }); - const [, content] = await emittedOnce(ipcMain, 'answer') - expect(content).to.equal('Hello') - }) + const [, content] = await emittedOnce(ipcMain, 'answer'); + expect(content).to.equal('Hello'); + }); it('opens window of same domain with cross-scripting enabled', async () => { // Don't wait for loading to finish. @@ -319,11 +319,11 @@ describe(' tag', function () { nodeintegration: 'on', webpreferences: 'nativeWindowOpen=1', src: `file://${path.join(fixtures, 'api', 'native-window-open-file.html')}` - }) + }); - const [, content] = await emittedOnce(ipcMain, 'answer') - expect(content).to.equal('Hello') - }) + const [, content] = await emittedOnce(ipcMain, 'answer'); + expect(content).to.equal('Hello'); + }); it('returns null from window.open when allowpopups is not set', async () => { // Don't wait for loading to finish. @@ -331,11 +331,11 @@ describe(' tag', function () { nodeintegration: 'on', webpreferences: 'nativeWindowOpen=1', src: `file://${path.join(fixtures, 'api', 'native-window-open-no-allowpopups.html')}` - }) + }); - const [, { windowOpenReturnedNull }] = await emittedOnce(ipcMain, 'answer') - expect(windowOpenReturnedNull).to.be.true() - }) + const [, { windowOpenReturnedNull }] = await emittedOnce(ipcMain, 'answer'); + expect(windowOpenReturnedNull).to.be.true(); + }); it('blocks accessing cross-origin frames', async () => { // Don't wait for loading to finish. @@ -344,14 +344,14 @@ describe(' tag', function () { nodeintegration: 'on', webpreferences: 'nativeWindowOpen=1', src: `file://${path.join(fixtures, 'api', 'native-window-open-cross-origin.html')}` - }) + }); - const [, content] = await emittedOnce(ipcMain, 'answer') + const [, content] = await emittedOnce(ipcMain, 'answer'); const expectedContent = - 'Blocked a frame with origin "file://" from accessing a cross-origin frame.' + 'Blocked a frame with origin "file://" from accessing a cross-origin frame.'; - expect(content).to.equal(expectedContent) - }) + expect(content).to.equal(expectedContent); + }); it('emits a new-window event', async () => { // Don't wait for loading to finish. @@ -360,7 +360,7 @@ describe(' tag', function () { nodeintegration: 'on', webpreferences: 'nativeWindowOpen=1', src: `file://${fixtures}/pages/window-open.html` - } + }; const { url, frameName } = await w.webContents.executeJavaScript(` new Promise((resolve, reject) => { const webview = document.createElement('webview') @@ -372,11 +372,11 @@ describe(' tag', function () { resolve({url: e.url, frameName: e.frameName}) }) }) - `) + `); - expect(url).to.equal('http://host/') - expect(frameName).to.equal('host') - }) + expect(url).to.equal('http://host/'); + expect(frameName).to.equal('host'); + }); it('emits a browser-window-created event', async () => { // Don't wait for loading to finish. @@ -384,33 +384,33 @@ describe(' tag', function () { allowpopups: 'on', webpreferences: 'nativeWindowOpen=1', src: `file://${fixtures}/pages/window-open.html` - }) + }); - await emittedOnce(app, 'browser-window-created') - }) + await emittedOnce(app, 'browser-window-created'); + }); it('emits a web-contents-created event', (done) => { app.on('web-contents-created', function listener (event, contents) { if (contents.getType() === 'window') { - app.removeListener('web-contents-created', listener) - done() + app.removeListener('web-contents-created', listener); + done(); } - }) + }); loadWebView(w.webContents, { allowpopups: 'on', webpreferences: 'nativeWindowOpen=1', src: `file://${fixtures}/pages/window-open.html` - }) - }) - }) + }); + }); + }); describe('webpreferences attribute', () => { - let w: BrowserWindow + let w: BrowserWindow; beforeEach(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }) - await w.loadURL('about:blank') - }) - afterEach(closeAllWindows) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }); + await w.loadURL('about:blank'); + }); + afterEach(closeAllWindows); it('can enable context isolation', async () => { loadWebView(w.webContents, { @@ -418,9 +418,9 @@ describe(' tag', function () { preload: `file://${fixtures}/api/isolated-preload.js`, src: `file://${fixtures}/api/isolated.html`, webpreferences: 'contextIsolation=yes' - }) + }); - const [, data] = await emittedOnce(ipcMain, 'isolated-world') + const [, data] = await emittedOnce(ipcMain, 'isolated-world'); expect(data).to.deep.equal({ preloadContext: { preloadProperty: 'number', @@ -441,19 +441,19 @@ describe(' tag', function () { typeofPreloadExecuteJavaScriptProperty: 'number', typeofOpenedWindow: 'object' } - }) - }) - }) + }); + }); + }); describe('permission request handlers', () => { - let w: BrowserWindow + let w: BrowserWindow; beforeEach(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }) - await w.loadURL('about:blank') - }) - afterEach(closeAllWindows) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }); + await w.loadURL('about:blank'); + }); + afterEach(closeAllWindows); - const partition = 'permissionTest' + const partition = 'permissionTest'; function setUpRequestHandler (webContentsId: number, requestedPermission: string) { return new Promise((resolve, reject) => { @@ -462,130 +462,130 @@ describe(' tag', function () { // requestMIDIAccess with sysex requests both midi and midiSysex so // grant the first midi one and then reject the midiSysex one if (requestedPermission === 'midiSysex' && permission === 'midi') { - return callback(true) + return callback(true); } try { - expect(permission).to.equal(requestedPermission) + expect(permission).to.equal(requestedPermission); } catch (e) { - return reject(e) + return reject(e); } - callback(false) - resolve() + callback(false); + resolve(); } - }) - }) + }); + }); } afterEach(() => { - session.fromPartition(partition).setPermissionRequestHandler(null) - }) + session.fromPartition(partition).setPermissionRequestHandler(null); + }); // This is disabled because CI machines don't have cameras or microphones, // so Chrome responds with "NotFoundError" instead of // "PermissionDeniedError". It should be re-enabled if we find a way to mock // the presence of a microphone & camera. xit('emits when using navigator.getUserMedia api', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message') + const errorFromRenderer = emittedOnce(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/media.html`, partition, nodeintegration: 'on' - }) - const [, webViewContents] = await emittedOnce(app, 'web-contents-created') - setUpRequestHandler(webViewContents.id, 'media') - const [, errorName] = await errorFromRenderer - expect(errorName).to.equal('PermissionDeniedError') - }) + }); + const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + setUpRequestHandler(webViewContents.id, 'media'); + const [, errorName] = await errorFromRenderer; + expect(errorName).to.equal('PermissionDeniedError'); + }); it('emits when using navigator.geolocation api', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message') + const errorFromRenderer = emittedOnce(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/geolocation.html`, partition, nodeintegration: 'on' - }) - const [, webViewContents] = await emittedOnce(app, 'web-contents-created') - setUpRequestHandler(webViewContents.id, 'geolocation') - const [, error] = await errorFromRenderer - expect(error).to.equal('User denied Geolocation') - }) + }); + const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + setUpRequestHandler(webViewContents.id, 'geolocation'); + const [, error] = await errorFromRenderer; + expect(error).to.equal('User denied Geolocation'); + }); it('emits when using navigator.requestMIDIAccess without sysex api', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message') + const errorFromRenderer = emittedOnce(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/midi.html`, partition, nodeintegration: 'on' - }) - const [, webViewContents] = await emittedOnce(app, 'web-contents-created') - setUpRequestHandler(webViewContents.id, 'midi') - const [, error] = await errorFromRenderer - expect(error).to.equal('SecurityError') - }) + }); + const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + setUpRequestHandler(webViewContents.id, 'midi'); + const [, error] = await errorFromRenderer; + expect(error).to.equal('SecurityError'); + }); it('emits when using navigator.requestMIDIAccess with sysex api', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message') + const errorFromRenderer = emittedOnce(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/midi-sysex.html`, partition, nodeintegration: 'on' - }) - const [, webViewContents] = await emittedOnce(app, 'web-contents-created') - setUpRequestHandler(webViewContents.id, 'midiSysex') - const [, error] = await errorFromRenderer - expect(error).to.equal('SecurityError') - }) + }); + const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + setUpRequestHandler(webViewContents.id, 'midiSysex'); + const [, error] = await errorFromRenderer; + expect(error).to.equal('SecurityError'); + }); it('emits when accessing external protocol', async () => { loadWebView(w.webContents, { src: 'magnet:test', partition - }) - const [, webViewContents] = await emittedOnce(app, 'web-contents-created') - await setUpRequestHandler(webViewContents.id, 'openExternal') - }) + }); + const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + await setUpRequestHandler(webViewContents.id, 'openExternal'); + }); it('emits when using Notification.requestPermission', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message') + const errorFromRenderer = emittedOnce(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/notification.html`, partition, nodeintegration: 'on' - }) - const [, webViewContents] = await emittedOnce(app, 'web-contents-created') + }); + const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); - await setUpRequestHandler(webViewContents.id, 'notifications') + await setUpRequestHandler(webViewContents.id, 'notifications'); - const [, error] = await errorFromRenderer - expect(error).to.equal('denied') - }) - }) + const [, error] = await errorFromRenderer; + expect(error).to.equal('denied'); + }); + }); describe('enableremotemodule attribute', () => { - let w: BrowserWindow + let w: BrowserWindow; beforeEach(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }) - await w.loadURL('about:blank') - }) - afterEach(closeAllWindows) + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } }); + await w.loadURL('about:blank'); + }); + afterEach(closeAllWindows); const generateSpecs = (description: string, sandbox: boolean) => { describe(description, () => { - const preload = `file://${fixtures}/module/preload-disable-remote.js` - const src = `file://${fixtures}/api/blank.html` + const preload = `file://${fixtures}/module/preload-disable-remote.js`; + const src = `file://${fixtures}/api/blank.html`; it('enables the remote module by default', async () => { loadWebView(w.webContents, { preload, src, sandbox: sandbox.toString() - }) - const [, webViewContents] = await emittedOnce(app, 'web-contents-created') - const [, , message] = await emittedOnce(webViewContents, 'console-message') + }); + const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + const [, , message] = await emittedOnce(webViewContents, 'console-message'); - const typeOfRemote = JSON.parse(message) - expect(typeOfRemote).to.equal('object') - }) + const typeOfRemote = JSON.parse(message); + expect(typeOfRemote).to.equal('object'); + }); it('disables the remote module when false', async () => { loadWebView(w.webContents, { @@ -593,17 +593,17 @@ describe(' tag', function () { src, sandbox: sandbox.toString(), enableremotemodule: 'false' - }) - const [, webViewContents] = await emittedOnce(app, 'web-contents-created') - const [, , message] = await emittedOnce(webViewContents, 'console-message') - - const typeOfRemote = JSON.parse(message) - expect(typeOfRemote).to.equal('undefined') - }) - }) - } - - generateSpecs('without sandbox', false) - generateSpecs('with sandbox', true) - }) -}) + }); + const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + const [, , message] = await emittedOnce(webViewContents, 'console-message'); + + const typeOfRemote = JSON.parse(message); + expect(typeOfRemote).to.equal('undefined'); + }); + }); + }; + + generateSpecs('without sandbox', false); + generateSpecs('with sandbox', true); + }); +}); diff --git a/spec-main/window-helpers.ts b/spec-main/window-helpers.ts index 361f5a10d0cae..a3b3dcccc6c1d 100644 --- a/spec-main/window-helpers.ts +++ b/spec-main/window-helpers.ts @@ -1,6 +1,6 @@ -import { expect } from 'chai' -import { BrowserWindow } from 'electron' -import { emittedOnce } from './events-helpers' +import { expect } from 'chai'; +import { BrowserWindow } from 'electron'; +import { emittedOnce } from './events-helpers'; async function ensureWindowIsClosed (window: BrowserWindow | null) { if (window && !window.isDestroyed()) { @@ -10,14 +10,14 @@ async function ensureWindowIsClosed (window: BrowserWindow | null) { // children which need to be destroyed first. In that case, we // await the 'closed' event which signals the complete shutdown of the // window. - const isClosed = emittedOnce(window, 'closed') - window.destroy() - await isClosed + const isClosed = emittedOnce(window, 'closed'); + window.destroy(); + await isClosed; } else { // If there's no WebContents or if the WebContents is already destroyed, // then the 'closed' event has already been emitted so there's nothing to // wait for. - window.destroy() + window.destroy(); } } } @@ -26,22 +26,22 @@ export const closeWindow = async ( window: BrowserWindow | null = null, { assertNotWindows } = { assertNotWindows: true } ) => { - await ensureWindowIsClosed(window) + await ensureWindowIsClosed(window); if (assertNotWindows) { - const windows = BrowserWindow.getAllWindows() + const windows = BrowserWindow.getAllWindows(); try { - expect(windows).to.have.lengthOf(0) + expect(windows).to.have.lengthOf(0); } finally { for (const win of windows) { - await ensureWindowIsClosed(win) + await ensureWindowIsClosed(win); } } } -} +}; export async function closeAllWindows () { for (const w of BrowserWindow.getAllWindows()) { - await closeWindow(w, { assertNotWindows: false }) + await closeWindow(w, { assertNotWindows: false }); } } diff --git a/spec/api-clipboard-spec.js b/spec/api-clipboard-spec.js index 6cc728a5310dd..ea27336fb4bae 100644 --- a/spec/api-clipboard-spec.js +++ b/spec/api-clipboard-spec.js @@ -1,146 +1,146 @@ -const { expect } = require('chai') -const path = require('path') -const { Buffer } = require('buffer') +const { expect } = require('chai'); +const path = require('path'); +const { Buffer } = require('buffer'); -const { clipboard, nativeImage } = require('electron') +const { clipboard, nativeImage } = require('electron'); describe('clipboard module', () => { - const fixtures = path.resolve(__dirname, 'fixtures') + const fixtures = path.resolve(__dirname, 'fixtures'); // FIXME(zcbenz): Clipboard tests are failing on WOA. beforeEach(function () { if (process.platform === 'win32' && process.arch === 'arm64') { - this.skip() + this.skip(); } - }) + }); describe('clipboard.readImage()', () => { it('returns NativeImage instance', () => { - const p = path.join(fixtures, 'assets', 'logo.png') - const i = nativeImage.createFromPath(p) - clipboard.writeImage(p) - expect(clipboard.readImage().toDataURL()).to.equal(i.toDataURL()) - }) - }) + const p = path.join(fixtures, 'assets', 'logo.png'); + const i = nativeImage.createFromPath(p); + clipboard.writeImage(p); + expect(clipboard.readImage().toDataURL()).to.equal(i.toDataURL()); + }); + }); describe('clipboard.readText()', () => { it('returns unicode string correctly', () => { - const text = '千江有水千江月,万里无云万里天' - clipboard.writeText(text) - expect(clipboard.readText()).to.equal(text) - }) - }) + const text = '千江有水千江月,万里无云万里天'; + clipboard.writeText(text); + expect(clipboard.readText()).to.equal(text); + }); + }); describe('clipboard.readHTML()', () => { it('returns markup correctly', () => { - const text = 'Hi' - const markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi' - clipboard.writeHTML(text) - expect(clipboard.readHTML()).to.equal(markup) - }) - }) + const text = 'Hi'; + const markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi'; + clipboard.writeHTML(text); + expect(clipboard.readHTML()).to.equal(markup); + }); + }); describe('clipboard.readRTF', () => { it('returns rtf text correctly', () => { - const rtf = '{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}' - clipboard.writeRTF(rtf) - expect(clipboard.readRTF()).to.equal(rtf) - }) - }) + const rtf = '{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}'; + clipboard.writeRTF(rtf); + expect(clipboard.readRTF()).to.equal(rtf); + }); + }); describe('clipboard.readBookmark', () => { before(function () { if (process.platform === 'linux') { - this.skip() + this.skip(); } - }) + }); it('returns title and url', () => { - clipboard.writeBookmark('a title', 'https://electronjs.org') + clipboard.writeBookmark('a title', 'https://electronjs.org'); expect(clipboard.readBookmark()).to.deep.equal({ title: 'a title', url: 'https://electronjs.org' - }) + }); - clipboard.writeText('no bookmark') + clipboard.writeText('no bookmark'); expect(clipboard.readBookmark()).to.deep.equal({ title: '', url: '' - }) - }) - }) + }); + }); + }); describe('clipboard.write()', () => { it('returns data correctly', () => { - const text = 'test' - const rtf = '{\\rtf1\\utf8 text}' - const p = path.join(fixtures, 'assets', 'logo.png') - const i = nativeImage.createFromPath(p) - const markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi' - const bookmark = { title: 'a title', url: 'test' } + const text = 'test'; + const rtf = '{\\rtf1\\utf8 text}'; + const p = path.join(fixtures, 'assets', 'logo.png'); + const i = nativeImage.createFromPath(p); + const markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi'; + const bookmark = { title: 'a title', url: 'test' }; clipboard.write({ text: 'test', html: 'Hi', rtf: '{\\rtf1\\utf8 text}', bookmark: 'a title', image: p - }) + }); - expect(clipboard.readText()).to.equal(text) - expect(clipboard.readHTML()).to.equal(markup) - expect(clipboard.readRTF()).to.equal(rtf) - expect(clipboard.readImage().toDataURL()).to.equal(i.toDataURL()) + expect(clipboard.readText()).to.equal(text); + expect(clipboard.readHTML()).to.equal(markup); + expect(clipboard.readRTF()).to.equal(rtf); + expect(clipboard.readImage().toDataURL()).to.equal(i.toDataURL()); if (process.platform !== 'linux') { - expect(clipboard.readBookmark()).to.deep.equal(bookmark) + expect(clipboard.readBookmark()).to.deep.equal(bookmark); } - }) - }) + }); + }); describe('clipboard.read/writeFindText(text)', () => { before(function () { if (process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('reads and write text to the find pasteboard', () => { - clipboard.writeFindText('find this') - expect(clipboard.readFindText()).to.equal('find this') - }) - }) + clipboard.writeFindText('find this'); + expect(clipboard.readFindText()).to.equal('find this'); + }); + }); describe('clipboard.writeBuffer(format, buffer)', () => { it('writes a Buffer for the specified format', function () { if (process.platform !== 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const buffer = Buffer.from('writeBuffer', 'utf8') - clipboard.writeBuffer('public.utf8-plain-text', buffer) - expect(clipboard.readText()).to.equal('writeBuffer') - }) + const buffer = Buffer.from('writeBuffer', 'utf8'); + clipboard.writeBuffer('public.utf8-plain-text', buffer); + expect(clipboard.readText()).to.equal('writeBuffer'); + }); it('throws an error when a non-Buffer is specified', () => { expect(() => { - clipboard.writeBuffer('public.utf8-plain-text', 'hello') - }).to.throw(/buffer must be a node Buffer/) - }) - }) + clipboard.writeBuffer('public.utf8-plain-text', 'hello'); + }).to.throw(/buffer must be a node Buffer/); + }); + }); describe('clipboard.readBuffer(format)', () => { before(function () { if (process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('returns a Buffer of the content for the specified format', () => { - const buffer = Buffer.from('this is binary', 'utf8') - clipboard.writeText(buffer.toString()) - expect(buffer.equals(clipboard.readBuffer('public.utf8-plain-text'))).to.equal(true) - }) - }) -}) + const buffer = Buffer.from('this is binary', 'utf8'); + clipboard.writeText(buffer.toString()); + expect(buffer.equals(clipboard.readBuffer('public.utf8-plain-text'))).to.equal(true); + }); + }); +}); diff --git a/spec/api-native-image-spec.js b/spec/api-native-image-spec.js index 7d772bdbcdf19..23ba0bcd1b38f 100644 --- a/spec/api-native-image-spec.js +++ b/spec/api-native-image-spec.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const { expect } = require('chai') -const { nativeImage } = require('electron') -const { ifdescribe, ifit } = require('./spec-helpers') -const path = require('path') +const { expect } = require('chai'); +const { nativeImage } = require('electron'); +const { ifdescribe, ifit } = require('./spec-helpers'); +const path = require('path'); describe('nativeImage module', () => { const ImageFormat = { PNG: 'png', JPEG: 'jpeg' - } + }; const images = [ { @@ -47,7 +47,7 @@ describe('nativeImage module', () => { height: 3, width: 3 } - ] + ]; /** * @param {?string} filename @@ -55,8 +55,8 @@ describe('nativeImage module', () => { */ const getImagePathFromFilename = (filename) => { return (filename === null) ? null - : path.join(__dirname, 'fixtures', 'assets', filename) - } + : path.join(__dirname, 'fixtures', 'assets', filename); + }; /** * @param {!Object} image @@ -65,12 +65,12 @@ describe('nativeImage module', () => { */ const imageMatchesTheFilters = (image, filters = null) => { if (filters === null) { - return true + return true; } return Object.entries(filters) - .every(([key, value]) => image[key] === value) - } + .every(([key, value]) => image[key] === value); + }; /** * @param {!Object} filters @@ -78,372 +78,372 @@ describe('nativeImage module', () => { */ const getImages = (filters) => { const matchingImages = images - .filter(i => imageMatchesTheFilters(i, filters)) + .filter(i => imageMatchesTheFilters(i, filters)); // Add `.path` property to every image. matchingImages - .forEach(i => { i.path = getImagePathFromFilename(i.filename) }) + .forEach(i => { i.path = getImagePathFromFilename(i.filename); }); - return matchingImages - } + return matchingImages; + }; /** * @param {!Object} filters * @returns {Object} A matching image if any. */ const getImage = (filters) => { - const matchingImages = getImages(filters) + const matchingImages = getImages(filters); - let matchingImage = null + let matchingImage = null; if (matchingImages.length > 0) { - matchingImage = matchingImages[0] + matchingImage = matchingImages[0]; } - return matchingImage - } + return matchingImage; + }; ifdescribe(process.platform === 'darwin')('isMacTemplateImage state', () => { describe('with properties', () => { it('correctly recognizes a template image', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - expect(image.isMacTemplateImage).to.be.false() + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + expect(image.isMacTemplateImage).to.be.false(); - const templateImage = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo_Template.png')) - expect(templateImage.isMacTemplateImage).to.be.true() - }) + const templateImage = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo_Template.png')); + expect(templateImage.isMacTemplateImage).to.be.true(); + }); it('sets a template image', function () { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - expect(image.isMacTemplateImage).to.be.false() + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + expect(image.isMacTemplateImage).to.be.false(); - image.isMacTemplateImage = true - expect(image.isMacTemplateImage).to.be.true() - }) - }) + image.isMacTemplateImage = true; + expect(image.isMacTemplateImage).to.be.true(); + }); + }); describe('with functions', () => { it('correctly recognizes a template image', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - expect(image.isTemplateImage()).to.be.false() + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + expect(image.isTemplateImage()).to.be.false(); - const templateImage = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo_Template.png')) - expect(templateImage.isTemplateImage()).to.be.true() - }) + const templateImage = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo_Template.png')); + expect(templateImage.isTemplateImage()).to.be.true(); + }); it('sets a template image', function () { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - expect(image.isTemplateImage()).to.be.false() + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + expect(image.isTemplateImage()).to.be.false(); - image.setTemplateImage(true) - expect(image.isTemplateImage()).to.be.true() - }) - }) - }) + image.setTemplateImage(true); + expect(image.isTemplateImage()).to.be.true(); + }); + }); + }); describe('createEmpty()', () => { it('returns an empty image', () => { - const empty = nativeImage.createEmpty() - expect(empty.isEmpty()).to.be.true() - expect(empty.getAspectRatio()).to.equal(1) - expect(empty.toDataURL()).to.equal('data:image/png;base64,') - expect(empty.toDataURL({ scaleFactor: 2.0 })).to.equal('data:image/png;base64,') - expect(empty.getSize()).to.deep.equal({ width: 0, height: 0 }) - expect(empty.getBitmap()).to.be.empty() - expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty() - expect(empty.toBitmap()).to.be.empty() - expect(empty.toBitmap({ scaleFactor: 2.0 })).to.be.empty() - expect(empty.toJPEG(100)).to.be.empty() - expect(empty.toPNG()).to.be.empty() - expect(empty.toPNG({ scaleFactor: 2.0 })).to.be.empty() + const empty = nativeImage.createEmpty(); + expect(empty.isEmpty()).to.be.true(); + expect(empty.getAspectRatio()).to.equal(1); + expect(empty.toDataURL()).to.equal('data:image/png;base64,'); + expect(empty.toDataURL({ scaleFactor: 2.0 })).to.equal('data:image/png;base64,'); + expect(empty.getSize()).to.deep.equal({ width: 0, height: 0 }); + expect(empty.getBitmap()).to.be.empty(); + expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty(); + expect(empty.toBitmap()).to.be.empty(); + expect(empty.toBitmap({ scaleFactor: 2.0 })).to.be.empty(); + expect(empty.toJPEG(100)).to.be.empty(); + expect(empty.toPNG()).to.be.empty(); + expect(empty.toPNG({ scaleFactor: 2.0 })).to.be.empty(); if (process.platform === 'darwin') { - expect(empty.getNativeHandle()).to.be.empty() + expect(empty.getNativeHandle()).to.be.empty(); } - }) - }) + }); + }); describe('createFromBitmap(buffer, options)', () => { it('returns an empty image when the buffer is empty', () => { - expect(nativeImage.createFromBitmap(Buffer.from([]), { width: 0, height: 0 }).isEmpty()).to.be.true() - }) + expect(nativeImage.createFromBitmap(Buffer.from([]), { width: 0, height: 0 }).isEmpty()).to.be.true(); + }); it('returns an image created from the given buffer', () => { - const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) + const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); - const imageB = nativeImage.createFromBitmap(imageA.toBitmap(), imageA.getSize()) - expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 }) + const imageB = nativeImage.createFromBitmap(imageA.toBitmap(), imageA.getSize()); + expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 }); - const imageC = nativeImage.createFromBuffer(imageA.toBitmap(), { ...imageA.getSize(), scaleFactor: 2.0 }) - expect(imageC.getSize()).to.deep.equal({ width: 269, height: 95 }) - }) + const imageC = nativeImage.createFromBuffer(imageA.toBitmap(), { ...imageA.getSize(), scaleFactor: 2.0 }); + expect(imageC.getSize()).to.deep.equal({ width: 269, height: 95 }); + }); it('throws on invalid arguments', () => { - expect(() => nativeImage.createFromBitmap(null, {})).to.throw('buffer must be a node Buffer') - expect(() => nativeImage.createFromBitmap([12, 14, 124, 12], {})).to.throw('buffer must be a node Buffer') - expect(() => nativeImage.createFromBitmap(Buffer.from([]), {})).to.throw('width is required') - expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1 })).to.throw('height is required') - expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1, height: 1 })).to.throw('invalid buffer size') - }) - }) + expect(() => nativeImage.createFromBitmap(null, {})).to.throw('buffer must be a node Buffer'); + expect(() => nativeImage.createFromBitmap([12, 14, 124, 12], {})).to.throw('buffer must be a node Buffer'); + expect(() => nativeImage.createFromBitmap(Buffer.from([]), {})).to.throw('width is required'); + expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1 })).to.throw('height is required'); + expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1, height: 1 })).to.throw('invalid buffer size'); + }); + }); describe('createFromBuffer(buffer, options)', () => { it('returns an empty image when the buffer is empty', () => { - expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty()).to.be.true() - }) + expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty()).to.be.true(); + }); it('returns an empty image when the buffer is too small', () => { - const image = nativeImage.createFromBuffer(Buffer.from([1, 2, 3, 4]), { width: 100, height: 100 }) - expect(image.isEmpty()).to.be.true() - }) + const image = nativeImage.createFromBuffer(Buffer.from([1, 2, 3, 4]), { width: 100, height: 100 }); + expect(image.isEmpty()).to.be.true(); + }); it('returns an image created from the given buffer', () => { - const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) + const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); - const imageB = nativeImage.createFromBuffer(imageA.toPNG()) - expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 }) - expect(imageA.toBitmap().equals(imageB.toBitmap())).to.be.true() + const imageB = nativeImage.createFromBuffer(imageA.toPNG()); + expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 }); + expect(imageA.toBitmap().equals(imageB.toBitmap())).to.be.true(); - const imageC = nativeImage.createFromBuffer(imageA.toJPEG(100)) - expect(imageC.getSize()).to.deep.equal({ width: 538, height: 190 }) + const imageC = nativeImage.createFromBuffer(imageA.toJPEG(100)); + expect(imageC.getSize()).to.deep.equal({ width: 538, height: 190 }); const imageD = nativeImage.createFromBuffer(imageA.toBitmap(), - { width: 538, height: 190 }) - expect(imageD.getSize()).to.deep.equal({ width: 538, height: 190 }) + { width: 538, height: 190 }); + expect(imageD.getSize()).to.deep.equal({ width: 538, height: 190 }); const imageE = nativeImage.createFromBuffer(imageA.toBitmap(), - { width: 100, height: 200 }) - expect(imageE.getSize()).to.deep.equal({ width: 100, height: 200 }) + { width: 100, height: 200 }); + expect(imageE.getSize()).to.deep.equal({ width: 100, height: 200 }); - const imageF = nativeImage.createFromBuffer(imageA.toBitmap()) - expect(imageF.isEmpty()).to.be.true() + const imageF = nativeImage.createFromBuffer(imageA.toBitmap()); + expect(imageF.isEmpty()).to.be.true(); const imageG = nativeImage.createFromBuffer(imageA.toPNG(), - { width: 100, height: 200 }) - expect(imageG.getSize()).to.deep.equal({ width: 538, height: 190 }) + { width: 100, height: 200 }); + expect(imageG.getSize()).to.deep.equal({ width: 538, height: 190 }); const imageH = nativeImage.createFromBuffer(imageA.toJPEG(100), - { width: 100, height: 200 }) - expect(imageH.getSize()).to.deep.equal({ width: 538, height: 190 }) + { width: 100, height: 200 }); + expect(imageH.getSize()).to.deep.equal({ width: 538, height: 190 }); const imageI = nativeImage.createFromBuffer(imageA.toBitmap(), - { width: 538, height: 190, scaleFactor: 2.0 }) - expect(imageI.getSize()).to.deep.equal({ width: 269, height: 95 }) - }) + { width: 538, height: 190, scaleFactor: 2.0 }); + expect(imageI.getSize()).to.deep.equal({ width: 269, height: 95 }); + }); it('throws on invalid arguments', () => { - expect(() => nativeImage.createFromBuffer(null)).to.throw('buffer must be a node Buffer') - expect(() => nativeImage.createFromBuffer([12, 14, 124, 12])).to.throw('buffer must be a node Buffer') - }) - }) + expect(() => nativeImage.createFromBuffer(null)).to.throw('buffer must be a node Buffer'); + expect(() => nativeImage.createFromBuffer([12, 14, 124, 12])).to.throw('buffer must be a node Buffer'); + }); + }); describe('createFromDataURL(dataURL)', () => { it('returns an empty image from the empty string', () => { - expect(nativeImage.createFromDataURL('').isEmpty()).to.be.true() - }) + expect(nativeImage.createFromDataURL('').isEmpty()).to.be.true(); + }); it('returns an image created from the given string', () => { - const imagesData = getImages({ hasDataUrl: true }) + const imagesData = getImages({ hasDataUrl: true }); for (const imageData of imagesData) { - const imageFromPath = nativeImage.createFromPath(imageData.path) - const imageFromDataUrl = nativeImage.createFromDataURL(imageData.dataUrl) + const imageFromPath = nativeImage.createFromPath(imageData.path); + const imageFromDataUrl = nativeImage.createFromDataURL(imageData.dataUrl); - expect(imageFromDataUrl.isEmpty()).to.be.false() - expect(imageFromDataUrl.getSize()).to.deep.equal(imageFromPath.getSize()) + expect(imageFromDataUrl.isEmpty()).to.be.false(); + expect(imageFromDataUrl.getSize()).to.deep.equal(imageFromPath.getSize()); expect(imageFromDataUrl.toBitmap()).to.satisfy( - bitmap => imageFromPath.toBitmap().equals(bitmap)) - expect(imageFromDataUrl.toDataURL()).to.equal(imageFromPath.toDataURL()) + bitmap => imageFromPath.toBitmap().equals(bitmap)); + expect(imageFromDataUrl.toDataURL()).to.equal(imageFromPath.toDataURL()); } - }) - }) + }); + }); describe('toDataURL()', () => { it('returns a PNG data URL', () => { - const imagesData = getImages({ hasDataUrl: true }) + const imagesData = getImages({ hasDataUrl: true }); for (const imageData of imagesData) { - const imageFromPath = nativeImage.createFromPath(imageData.path) + const imageFromPath = nativeImage.createFromPath(imageData.path); - const scaleFactors = [1.0, 2.0] + const scaleFactors = [1.0, 2.0]; for (const scaleFactor of scaleFactors) { - expect(imageFromPath.toDataURL({ scaleFactor })).to.equal(imageData.dataUrl) + expect(imageFromPath.toDataURL({ scaleFactor })).to.equal(imageData.dataUrl); } } - }) + }); it('returns a data URL at 1x scale factor by default', () => { - const imageData = getImage({ filename: 'logo.png' }) - const image = nativeImage.createFromPath(imageData.path) + const imageData = getImage({ filename: 'logo.png' }); + const image = nativeImage.createFromPath(imageData.path); const imageOne = nativeImage.createFromBuffer(image.toPNG(), { width: image.getSize().width, height: image.getSize().height, scaleFactor: 2.0 - }) + }); expect(imageOne.getSize()).to.deep.equal( - { width: imageData.width / 2, height: imageData.height / 2 }) + { width: imageData.width / 2, height: imageData.height / 2 }); - const imageTwo = nativeImage.createFromDataURL(imageOne.toDataURL()) + const imageTwo = nativeImage.createFromDataURL(imageOne.toDataURL()); expect(imageTwo.getSize()).to.deep.equal( - { width: imageData.width, height: imageData.height }) + { width: imageData.width, height: imageData.height }); - expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true() - }) + expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true(); + }); it('supports a scale factor', () => { - const imageData = getImage({ filename: 'logo.png' }) - const image = nativeImage.createFromPath(imageData.path) - const expectedSize = { width: imageData.width, height: imageData.height } + const imageData = getImage({ filename: 'logo.png' }); + const image = nativeImage.createFromPath(imageData.path); + const expectedSize = { width: imageData.width, height: imageData.height }; const imageFromDataUrlOne = nativeImage.createFromDataURL( - image.toDataURL({ scaleFactor: 1.0 })) - expect(imageFromDataUrlOne.getSize()).to.deep.equal(expectedSize) + image.toDataURL({ scaleFactor: 1.0 })); + expect(imageFromDataUrlOne.getSize()).to.deep.equal(expectedSize); const imageFromDataUrlTwo = nativeImage.createFromDataURL( - image.toDataURL({ scaleFactor: 2.0 })) - expect(imageFromDataUrlTwo.getSize()).to.deep.equal(expectedSize) - }) - }) + image.toDataURL({ scaleFactor: 2.0 })); + expect(imageFromDataUrlTwo.getSize()).to.deep.equal(expectedSize); + }); + }); describe('toPNG()', () => { it('returns a buffer at 1x scale factor by default', () => { - const imageData = getImage({ filename: 'logo.png' }) - const imageA = nativeImage.createFromPath(imageData.path) + const imageData = getImage({ filename: 'logo.png' }); + const imageA = nativeImage.createFromPath(imageData.path); const imageB = nativeImage.createFromBuffer(imageA.toPNG(), { width: imageA.getSize().width, height: imageA.getSize().height, scaleFactor: 2.0 - }) + }); expect(imageB.getSize()).to.deep.equal( - { width: imageData.width / 2, height: imageData.height / 2 }) + { width: imageData.width / 2, height: imageData.height / 2 }); - const imageC = nativeImage.createFromBuffer(imageB.toPNG()) + const imageC = nativeImage.createFromBuffer(imageB.toPNG()); expect(imageC.getSize()).to.deep.equal( - { width: imageData.width, height: imageData.height }) + { width: imageData.width, height: imageData.height }); - expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true() - }) + expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true(); + }); it('supports a scale factor', () => { - const imageData = getImage({ filename: 'logo.png' }) - const image = nativeImage.createFromPath(imageData.path) + const imageData = getImage({ filename: 'logo.png' }); + const image = nativeImage.createFromPath(imageData.path); const imageFromBufferOne = nativeImage.createFromBuffer( - image.toPNG({ scaleFactor: 1.0 })) + image.toPNG({ scaleFactor: 1.0 })); expect(imageFromBufferOne.getSize()).to.deep.equal( - { width: imageData.width, height: imageData.height }) + { width: imageData.width, height: imageData.height }); const imageFromBufferTwo = nativeImage.createFromBuffer( - image.toPNG({ scaleFactor: 2.0 }), { scaleFactor: 2.0 }) + image.toPNG({ scaleFactor: 2.0 }), { scaleFactor: 2.0 }); expect(imageFromBufferTwo.getSize()).to.deep.equal( - { width: imageData.width / 2, height: imageData.height / 2 }) - }) - }) + { width: imageData.width / 2, height: imageData.height / 2 }); + }); + }); describe('createFromPath(path)', () => { it('returns an empty image for invalid paths', () => { - expect(nativeImage.createFromPath('').isEmpty()).to.be.true() - expect(nativeImage.createFromPath('does-not-exist.png').isEmpty()).to.be.true() - expect(nativeImage.createFromPath('does-not-exist.ico').isEmpty()).to.be.true() - expect(nativeImage.createFromPath(__dirname).isEmpty()).to.be.true() - expect(nativeImage.createFromPath(__filename).isEmpty()).to.be.true() - }) + expect(nativeImage.createFromPath('').isEmpty()).to.be.true(); + expect(nativeImage.createFromPath('does-not-exist.png').isEmpty()).to.be.true(); + expect(nativeImage.createFromPath('does-not-exist.ico').isEmpty()).to.be.true(); + expect(nativeImage.createFromPath(__dirname).isEmpty()).to.be.true(); + expect(nativeImage.createFromPath(__filename).isEmpty()).to.be.true(); + }); it('loads images from paths relative to the current working directory', () => { - const imagePath = path.relative('.', path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - const image = nativeImage.createFromPath(imagePath) - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }) - }) + const imagePath = path.relative('.', path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const image = nativeImage.createFromPath(imagePath); + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }); + }); it('loads images from paths with `.` segments', () => { - const imagePath = `${path.join(__dirname, 'fixtures')}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}` - const image = nativeImage.createFromPath(imagePath) - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }) - }) + const imagePath = `${path.join(__dirname, 'fixtures')}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}`; + const image = nativeImage.createFromPath(imagePath); + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }); + }); it('loads images from paths with `..` segments', () => { - const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}` - const image = nativeImage.createFromPath(imagePath) - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }) - }) + const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`; + const image = nativeImage.createFromPath(imagePath); + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }); + }); it('Gets an NSImage pointer on macOS', function () { if (process.platform !== 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}` - const image = nativeImage.createFromPath(imagePath) - const nsimage = image.getNativeHandle() + const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`; + const image = nativeImage.createFromPath(imagePath); + const nsimage = image.getNativeHandle(); - expect(nsimage).to.have.lengthOf(8) + expect(nsimage).to.have.lengthOf(8); // If all bytes are null, that's Bad - const allBytesAreNotNull = nsimage.reduce((acc, x) => acc || (x !== 0), false) - expect(allBytesAreNotNull) - }) + const allBytesAreNotNull = nsimage.reduce((acc, x) => acc || (x !== 0), false); + expect(allBytesAreNotNull); + }); it('loads images from .ico files on Windows', function () { if (process.platform !== 'win32') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico') - const image = nativeImage.createFromPath(imagePath) - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 256, height: 256 }) - }) - }) + const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico'); + const image = nativeImage.createFromPath(imagePath); + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 256, height: 256 }); + }); + }); describe('createFromNamedImage(name)', () => { it('returns empty for invalid options', () => { - const image = nativeImage.createFromNamedImage('totally_not_real') - expect(image.isEmpty()).to.be.true() - }) + const image = nativeImage.createFromNamedImage('totally_not_real'); + expect(image.isEmpty()).to.be.true(); + }); it('returns empty on non-darwin platforms', function () { if (process.platform === 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const image = nativeImage.createFromNamedImage('NSActionTemplate') - expect(image.isEmpty()).to.be.true() - }) + const image = nativeImage.createFromNamedImage('NSActionTemplate'); + expect(image.isEmpty()).to.be.true(); + }); it('returns a valid image on darwin', function () { if (process.platform !== 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const image = nativeImage.createFromNamedImage('NSActionTemplate') - expect(image.isEmpty()).to.be.false() - }) + const image = nativeImage.createFromNamedImage('NSActionTemplate'); + expect(image.isEmpty()).to.be.false(); + }); it('returns allows an HSL shift for a valid image on darwin', function () { if (process.platform !== 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8]) - expect(image.isEmpty()).to.be.false() - }) - }) + const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8]); + expect(image.isEmpty()).to.be.false(); + }); + }); describe('resize(options)', () => { it('returns a resized image', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); for (const [resizeTo, expectedSize] of new Map([ [{}, { width: 538, height: 190 }], [{ width: 269 }, { width: 269, height: 95 }], @@ -455,167 +455,167 @@ describe('nativeImage module', () => { [{ width: 0, height: 0 }, { width: 0, height: 0 }], [{ width: -1, height: -1 }, { width: 0, height: 0 }] ])) { - const actualSize = image.resize(resizeTo).getSize() - expect(actualSize).to.deep.equal(expectedSize) + const actualSize = image.resize(resizeTo).getSize(); + expect(actualSize).to.deep.equal(expectedSize); } - }) + }); it('returns an empty image when called on an empty image', () => { - expect(nativeImage.createEmpty().resize({ width: 1, height: 1 }).isEmpty()).to.be.true() - expect(nativeImage.createEmpty().resize({ width: 0, height: 0 }).isEmpty()).to.be.true() - }) + expect(nativeImage.createEmpty().resize({ width: 1, height: 1 }).isEmpty()).to.be.true(); + expect(nativeImage.createEmpty().resize({ width: 0, height: 0 }).isEmpty()).to.be.true(); + }); it('supports a quality option', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - const good = image.resize({ width: 100, height: 100, quality: 'good' }) - const better = image.resize({ width: 100, height: 100, quality: 'better' }) - const best = image.resize({ width: 100, height: 100, quality: 'best' }) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const good = image.resize({ width: 100, height: 100, quality: 'good' }); + const better = image.resize({ width: 100, height: 100, quality: 'better' }); + const best = image.resize({ width: 100, height: 100, quality: 'best' }); - expect(good.toPNG()).to.have.lengthOf.at.most(better.toPNG().length) - expect(better.toPNG()).to.have.lengthOf.below(best.toPNG().length) - }) - }) + expect(good.toPNG()).to.have.lengthOf.at.most(better.toPNG().length); + expect(better.toPNG()).to.have.lengthOf.below(best.toPNG().length); + }); + }); describe('crop(bounds)', () => { it('returns an empty image when called on an empty image', () => { - expect(nativeImage.createEmpty().crop({ width: 1, height: 2, x: 0, y: 0 }).isEmpty()).to.be.true() - expect(nativeImage.createEmpty().crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true() - }) + expect(nativeImage.createEmpty().crop({ width: 1, height: 2, x: 0, y: 0 }).isEmpty()).to.be.true(); + expect(nativeImage.createEmpty().crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true(); + }); it('returns an empty image when the bounds are invalid', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - expect(image.crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true() - expect(image.crop({ width: -1, height: 10, x: 0, y: 0 }).isEmpty()).to.be.true() - expect(image.crop({ width: 10, height: -35, x: 0, y: 0 }).isEmpty()).to.be.true() - expect(image.crop({ width: 100, height: 100, x: 1000, y: 1000 }).isEmpty()).to.be.true() - }) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + expect(image.crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true(); + expect(image.crop({ width: -1, height: 10, x: 0, y: 0 }).isEmpty()).to.be.true(); + expect(image.crop({ width: 10, height: -35, x: 0, y: 0 }).isEmpty()).to.be.true(); + expect(image.crop({ width: 100, height: 100, x: 1000, y: 1000 }).isEmpty()).to.be.true(); + }); it('returns a cropped image', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - const cropA = image.crop({ width: 25, height: 64, x: 0, y: 0 }) - const cropB = image.crop({ width: 25, height: 64, x: 30, y: 40 }) - expect(cropA.getSize()).to.deep.equal({ width: 25, height: 64 }) - expect(cropB.getSize()).to.deep.equal({ width: 25, height: 64 }) - expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false() - }) - }) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const cropA = image.crop({ width: 25, height: 64, x: 0, y: 0 }); + const cropB = image.crop({ width: 25, height: 64, x: 30, y: 40 }); + expect(cropA.getSize()).to.deep.equal({ width: 25, height: 64 }); + expect(cropB.getSize()).to.deep.equal({ width: 25, height: 64 }); + expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false(); + }); + }); describe('getAspectRatio()', () => { it('returns an aspect ratio of an empty image', () => { - expect(nativeImage.createEmpty().getAspectRatio()).to.equal(1.0) - }) + expect(nativeImage.createEmpty().getAspectRatio()).to.equal(1.0); + }); it('returns an aspect ratio of an image', () => { - const imageData = getImage({ filename: 'logo.png' }) + const imageData = getImage({ filename: 'logo.png' }); // imageData.width / imageData.height = 2.831578947368421 - const expectedAspectRatio = 2.8315789699554443 + const expectedAspectRatio = 2.8315789699554443; - const image = nativeImage.createFromPath(imageData.path) - expect(image.getAspectRatio()).to.equal(expectedAspectRatio) - }) - }) + const image = nativeImage.createFromPath(imageData.path); + expect(image.getAspectRatio()).to.equal(expectedAspectRatio); + }); + }); describe('addRepresentation()', () => { it('does not add representation when the buffer is too small', () => { - const image = nativeImage.createEmpty() + const image = nativeImage.createEmpty(); image.addRepresentation({ buffer: Buffer.from([1, 2, 3, 4]), width: 100, height: 100 - }) + }); - expect(image.isEmpty()).to.be.true() - }) + expect(image.isEmpty()).to.be.true(); + }); it('supports adding a buffer representation for a scale factor', () => { - const image = nativeImage.createEmpty() + const image = nativeImage.createEmpty(); - const imageDataOne = getImage({ width: 1, height: 1 }) + const imageDataOne = getImage({ width: 1, height: 1 }); image.addRepresentation({ scaleFactor: 1.0, buffer: nativeImage.createFromPath(imageDataOne.path).toPNG() - }) + }); - const imageDataTwo = getImage({ width: 2, height: 2 }) + const imageDataTwo = getImage({ width: 2, height: 2 }); image.addRepresentation({ scaleFactor: 2.0, buffer: nativeImage.createFromPath(imageDataTwo.path).toPNG() - }) + }); - const imageDataThree = getImage({ width: 3, height: 3 }) + const imageDataThree = getImage({ width: 3, height: 3 }); image.addRepresentation({ scaleFactor: 3.0, buffer: nativeImage.createFromPath(imageDataThree.path).toPNG() - }) + }); image.addRepresentation({ scaleFactor: 4.0, buffer: 'invalid' - }) + }); - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 1, height: 1 }) + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 1, height: 1 }); - expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl) - expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl) - expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl) - expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl) - }) + expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl); + expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl); + expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl); + expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl); + }); it('supports adding a data URL representation for a scale factor', () => { - const image = nativeImage.createEmpty() + const image = nativeImage.createEmpty(); - const imageDataOne = getImage({ width: 1, height: 1 }) + const imageDataOne = getImage({ width: 1, height: 1 }); image.addRepresentation({ scaleFactor: 1.0, dataURL: imageDataOne.dataUrl - }) + }); - const imageDataTwo = getImage({ width: 2, height: 2 }) + const imageDataTwo = getImage({ width: 2, height: 2 }); image.addRepresentation({ scaleFactor: 2.0, dataURL: imageDataTwo.dataUrl - }) + }); - const imageDataThree = getImage({ width: 3, height: 3 }) + const imageDataThree = getImage({ width: 3, height: 3 }); image.addRepresentation({ scaleFactor: 3.0, dataURL: imageDataThree.dataUrl - }) + }); image.addRepresentation({ scaleFactor: 4.0, dataURL: 'invalid' - }) + }); - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 1, height: 1 }) + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 1, height: 1 }); - expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl) - expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl) - expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl) - expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl) - }) + expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl); + expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl); + expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl); + expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl); + }); it('supports adding a representation to an existing image', () => { - const imageDataOne = getImage({ width: 1, height: 1 }) - const image = nativeImage.createFromPath(imageDataOne.path) + const imageDataOne = getImage({ width: 1, height: 1 }); + const image = nativeImage.createFromPath(imageDataOne.path); - const imageDataTwo = getImage({ width: 2, height: 2 }) + const imageDataTwo = getImage({ width: 2, height: 2 }); image.addRepresentation({ scaleFactor: 2.0, dataURL: imageDataTwo.dataUrl - }) + }); - const imageDataThree = getImage({ width: 3, height: 3 }) + const imageDataThree = getImage({ width: 3, height: 3 }); image.addRepresentation({ scaleFactor: 2.0, dataURL: imageDataThree.dataUrl - }) + }); - expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl) - expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl) - }) - }) -}) + expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl); + expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl); + }); + }); +}); diff --git a/spec/api-process-spec.js b/spec/api-process-spec.js index a6ae4df692eae..6eada5c945feb 100644 --- a/spec/api-process-spec.js +++ b/spec/api-process-spec.js @@ -1,118 +1,118 @@ -const { ipcRenderer } = require('electron') -const fs = require('fs') -const path = require('path') +const { ipcRenderer } = require('electron'); +const fs = require('fs'); +const path = require('path'); -const { expect } = require('chai') +const { expect } = require('chai'); describe('process module', () => { describe('process.getCreationTime()', () => { it('returns a creation time', () => { - const creationTime = process.getCreationTime() - expect(creationTime).to.be.a('number').and.be.at.least(0) - }) - }) + const creationTime = process.getCreationTime(); + expect(creationTime).to.be.a('number').and.be.at.least(0); + }); + }); describe('process.getCPUUsage()', () => { it('returns a cpu usage object', () => { - const cpuUsage = process.getCPUUsage() - expect(cpuUsage.percentCPUUsage).to.be.a('number') - expect(cpuUsage.idleWakeupsPerSecond).to.be.a('number') - }) - }) + const cpuUsage = process.getCPUUsage(); + expect(cpuUsage.percentCPUUsage).to.be.a('number'); + expect(cpuUsage.idleWakeupsPerSecond).to.be.a('number'); + }); + }); describe('process.getIOCounters()', () => { before(function () { if (process.platform === 'darwin') { - this.skip() + this.skip(); } - }) + }); it('returns an io counters object', () => { - const ioCounters = process.getIOCounters() - expect(ioCounters.readOperationCount).to.be.a('number') - expect(ioCounters.writeOperationCount).to.be.a('number') - expect(ioCounters.otherOperationCount).to.be.a('number') - expect(ioCounters.readTransferCount).to.be.a('number') - expect(ioCounters.writeTransferCount).to.be.a('number') - expect(ioCounters.otherTransferCount).to.be.a('number') - }) - }) + const ioCounters = process.getIOCounters(); + expect(ioCounters.readOperationCount).to.be.a('number'); + expect(ioCounters.writeOperationCount).to.be.a('number'); + expect(ioCounters.otherOperationCount).to.be.a('number'); + expect(ioCounters.readTransferCount).to.be.a('number'); + expect(ioCounters.writeTransferCount).to.be.a('number'); + expect(ioCounters.otherTransferCount).to.be.a('number'); + }); + }); describe('process.getBlinkMemoryInfo()', () => { it('returns blink memory information object', () => { - const heapStats = process.getBlinkMemoryInfo() - expect(heapStats.allocated).to.be.a('number') - expect(heapStats.total).to.be.a('number') - }) - }) + const heapStats = process.getBlinkMemoryInfo(); + expect(heapStats.allocated).to.be.a('number'); + expect(heapStats.total).to.be.a('number'); + }); + }); describe('process.getProcessMemoryInfo()', async () => { it('resolves promise successfully with valid data', async () => { - const memoryInfo = await process.getProcessMemoryInfo() - expect(memoryInfo).to.be.an('object') + const memoryInfo = await process.getProcessMemoryInfo(); + expect(memoryInfo).to.be.an('object'); if (process.platform === 'linux' || process.platform === 'windows') { - expect(memoryInfo.residentSet).to.be.a('number').greaterThan(0) + expect(memoryInfo.residentSet).to.be.a('number').greaterThan(0); } - expect(memoryInfo.private).to.be.a('number').greaterThan(0) + expect(memoryInfo.private).to.be.a('number').greaterThan(0); // Shared bytes can be zero - expect(memoryInfo.shared).to.be.a('number').greaterThan(-1) - }) - }) + expect(memoryInfo.shared).to.be.a('number').greaterThan(-1); + }); + }); describe('process.getSystemMemoryInfo()', () => { it('returns system memory info object', () => { - const systemMemoryInfo = process.getSystemMemoryInfo() - expect(systemMemoryInfo.free).to.be.a('number') - expect(systemMemoryInfo.total).to.be.a('number') - }) - }) + const systemMemoryInfo = process.getSystemMemoryInfo(); + expect(systemMemoryInfo.free).to.be.a('number'); + expect(systemMemoryInfo.total).to.be.a('number'); + }); + }); describe('process.getSystemVersion()', () => { it('returns a string', () => { - expect(process.getSystemVersion()).to.be.a('string') - }) - }) + expect(process.getSystemVersion()).to.be.a('string'); + }); + }); describe('process.getHeapStatistics()', () => { it('returns heap statistics object', () => { - const heapStats = process.getHeapStatistics() - expect(heapStats.totalHeapSize).to.be.a('number') - expect(heapStats.totalHeapSizeExecutable).to.be.a('number') - expect(heapStats.totalPhysicalSize).to.be.a('number') - expect(heapStats.totalAvailableSize).to.be.a('number') - expect(heapStats.usedHeapSize).to.be.a('number') - expect(heapStats.heapSizeLimit).to.be.a('number') - expect(heapStats.mallocedMemory).to.be.a('number') - expect(heapStats.peakMallocedMemory).to.be.a('number') - expect(heapStats.doesZapGarbage).to.be.a('boolean') - }) - }) + const heapStats = process.getHeapStatistics(); + expect(heapStats.totalHeapSize).to.be.a('number'); + expect(heapStats.totalHeapSizeExecutable).to.be.a('number'); + expect(heapStats.totalPhysicalSize).to.be.a('number'); + expect(heapStats.totalAvailableSize).to.be.a('number'); + expect(heapStats.usedHeapSize).to.be.a('number'); + expect(heapStats.heapSizeLimit).to.be.a('number'); + expect(heapStats.mallocedMemory).to.be.a('number'); + expect(heapStats.peakMallocedMemory).to.be.a('number'); + expect(heapStats.doesZapGarbage).to.be.a('boolean'); + }); + }); describe('process.takeHeapSnapshot()', () => { it('returns true on success', async () => { - const filePath = path.join(await ipcRenderer.invoke('get-temp-dir'), 'test.heapsnapshot') + const filePath = path.join(await ipcRenderer.invoke('get-temp-dir'), 'test.heapsnapshot'); const cleanup = () => { try { - fs.unlinkSync(filePath) + fs.unlinkSync(filePath); } catch (e) { // ignore error } - } + }; try { - const success = process.takeHeapSnapshot(filePath) - expect(success).to.be.true() - const stats = fs.statSync(filePath) - expect(stats.size).not.to.be.equal(0) + const success = process.takeHeapSnapshot(filePath); + expect(success).to.be.true(); + const stats = fs.statSync(filePath); + expect(stats.size).not.to.be.equal(0); } finally { - cleanup() + cleanup(); } - }) + }); it('returns false on failure', () => { - const success = process.takeHeapSnapshot('') - expect(success).to.be.false() - }) - }) -}) + const success = process.takeHeapSnapshot(''); + expect(success).to.be.false(); + }); + }); +}); diff --git a/spec/api-shell-spec.js b/spec/api-shell-spec.js index 3b1b6d8f99932..9f38b407c6cfc 100644 --- a/spec/api-shell-spec.js +++ b/spec/api-shell-spec.js @@ -1,13 +1,13 @@ -const { expect } = require('chai') +const { expect } = require('chai'); -const fs = require('fs') -const path = require('path') -const os = require('os') -const http = require('http') -const { shell } = require('electron') +const fs = require('fs'); +const path = require('path'); +const os = require('os'); +const http = require('http'); +const { shell } = require('electron'); describe('shell module', () => { - const fixtures = path.resolve(__dirname, 'fixtures') + const fixtures = path.resolve(__dirname, 'fixtures'); const shortcutOptions = { target: 'C:\\target', description: 'description', @@ -16,59 +16,59 @@ describe('shell module', () => { appUserModelId: 'appUserModelId', icon: 'icon', iconIndex: 1 - } + }; describe('shell.readShortcutLink(shortcutPath)', () => { beforeEach(function () { - if (process.platform !== 'win32') this.skip() - }) + if (process.platform !== 'win32') this.skip(); + }); it('throws when failed', () => { expect(() => { - shell.readShortcutLink('not-exist') - }).to.throw('Failed to read shortcut link') - }) + shell.readShortcutLink('not-exist'); + }).to.throw('Failed to read shortcut link'); + }); it('reads all properties of a shortcut', () => { - const shortcut = shell.readShortcutLink(path.join(fixtures, 'assets', 'shortcut.lnk')) - expect(shortcut).to.deep.equal(shortcutOptions) - }) - }) + const shortcut = shell.readShortcutLink(path.join(fixtures, 'assets', 'shortcut.lnk')); + expect(shortcut).to.deep.equal(shortcutOptions); + }); + }); describe('shell.writeShortcutLink(shortcutPath[, operation], options)', () => { beforeEach(function () { - if (process.platform !== 'win32') this.skip() - }) + if (process.platform !== 'win32') this.skip(); + }); - const tmpShortcut = path.join(os.tmpdir(), `${Date.now()}.lnk`) + const tmpShortcut = path.join(os.tmpdir(), `${Date.now()}.lnk`); afterEach(() => { - fs.unlinkSync(tmpShortcut) - }) + fs.unlinkSync(tmpShortcut); + }); it('writes the shortcut', () => { - expect(shell.writeShortcutLink(tmpShortcut, { target: 'C:\\' })).to.be.true() - expect(fs.existsSync(tmpShortcut)).to.be.true() - }) + expect(shell.writeShortcutLink(tmpShortcut, { target: 'C:\\' })).to.be.true(); + expect(fs.existsSync(tmpShortcut)).to.be.true(); + }); it('correctly sets the fields', () => { - expect(shell.writeShortcutLink(tmpShortcut, shortcutOptions)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions) - }) + expect(shell.writeShortcutLink(tmpShortcut, shortcutOptions)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions); + }); it('updates the shortcut', () => { - expect(shell.writeShortcutLink(tmpShortcut, 'update', shortcutOptions)).to.be.false() - expect(shell.writeShortcutLink(tmpShortcut, 'create', shortcutOptions)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions) - const change = { target: 'D:\\' } - expect(shell.writeShortcutLink(tmpShortcut, 'update', change)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(Object.assign(shortcutOptions, change)) - }) + expect(shell.writeShortcutLink(tmpShortcut, 'update', shortcutOptions)).to.be.false(); + expect(shell.writeShortcutLink(tmpShortcut, 'create', shortcutOptions)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions); + const change = { target: 'D:\\' }; + expect(shell.writeShortcutLink(tmpShortcut, 'update', change)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(Object.assign(shortcutOptions, change)); + }); it('replaces the shortcut', () => { - expect(shell.writeShortcutLink(tmpShortcut, 'replace', shortcutOptions)).to.be.false() - expect(shell.writeShortcutLink(tmpShortcut, 'create', shortcutOptions)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions) + expect(shell.writeShortcutLink(tmpShortcut, 'replace', shortcutOptions)).to.be.false(); + expect(shell.writeShortcutLink(tmpShortcut, 'create', shortcutOptions)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions); const change = { target: 'D:\\', description: 'description2', @@ -77,9 +77,9 @@ describe('shell module', () => { appUserModelId: 'appUserModelId2', icon: 'icon2', iconIndex: 2 - } - expect(shell.writeShortcutLink(tmpShortcut, 'replace', change)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(change) - }) - }) -}) + }; + expect(shell.writeShortcutLink(tmpShortcut, 'replace', change)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(change); + }); + }); +}); diff --git a/spec/api-web-frame-spec.js b/spec/api-web-frame-spec.js index 8fb52fd822a55..01c8a69912930 100644 --- a/spec/api-web-frame-spec.js +++ b/spec/api-web-frame-spec.js @@ -1,109 +1,109 @@ -const { expect } = require('chai') -const { webFrame } = require('electron') +const { expect } = require('chai'); +const { webFrame } = require('electron'); describe('webFrame module', function () { it('top is self for top frame', () => { - expect(webFrame.top.context).to.equal(webFrame.context) - }) + expect(webFrame.top.context).to.equal(webFrame.context); + }); it('opener is null for top frame', () => { - expect(webFrame.opener).to.be.null() - }) + expect(webFrame.opener).to.be.null(); + }); it('firstChild is null for top frame', () => { - expect(webFrame.firstChild).to.be.null() - }) + expect(webFrame.firstChild).to.be.null(); + }); it('getFrameForSelector() does not crash when not found', () => { - expect(webFrame.getFrameForSelector('unexist-selector')).to.be.null() - }) + expect(webFrame.getFrameForSelector('unexist-selector')).to.be.null(); + }); it('findFrameByName() does not crash when not found', () => { - expect(webFrame.findFrameByName('unexist-name')).to.be.null() - }) + expect(webFrame.findFrameByName('unexist-name')).to.be.null(); + }); it('findFrameByRoutingId() does not crash when not found', () => { - expect(webFrame.findFrameByRoutingId(-1)).to.be.null() - }) + expect(webFrame.findFrameByRoutingId(-1)).to.be.null(); + }); describe('executeJavaScript', () => { - let childFrameElement, childFrame + let childFrameElement, childFrame; before(() => { - childFrameElement = document.createElement('iframe') - document.body.appendChild(childFrameElement) - childFrame = webFrame.firstChild - }) + childFrameElement = document.createElement('iframe'); + document.body.appendChild(childFrameElement); + childFrame = webFrame.firstChild; + }); after(() => { - childFrameElement.remove() - }) + childFrameElement.remove(); + }); it('executeJavaScript() yields results via a promise and a sync callback', done => { - let callbackResult, callbackError + let callbackResult, callbackError; childFrame .executeJavaScript('1 + 1', (result, error) => { - callbackResult = result - callbackError = error + callbackResult = result; + callbackError = error; }) .then( promiseResult => { - expect(promiseResult).to.equal(2) - done() + expect(promiseResult).to.equal(2); + done(); }, promiseError => { - done(promiseError) + done(promiseError); } - ) + ); - expect(callbackResult).to.equal(2) - expect(callbackError).to.be.undefined() - }) + expect(callbackResult).to.equal(2); + expect(callbackError).to.be.undefined(); + }); it('executeJavaScriptInIsolatedWorld() yields results via a promise and a sync callback', done => { - let callbackResult, callbackError + let callbackResult, callbackError; childFrame .executeJavaScriptInIsolatedWorld(999, [{ code: '1 + 1' }], (result, error) => { - callbackResult = result - callbackError = error + callbackResult = result; + callbackError = error; }) .then( promiseResult => { - expect(promiseResult).to.equal(2) - done() + expect(promiseResult).to.equal(2); + done(); }, promiseError => { - done(promiseError) + done(promiseError); } - ) + ); - expect(callbackResult).to.equal(2) - expect(callbackError).to.be.undefined() - }) + expect(callbackResult).to.equal(2); + expect(callbackError).to.be.undefined(); + }); it('executeJavaScript() yields errors via a promise and a sync callback', done => { - let callbackResult, callbackError + let callbackResult, callbackError; childFrame .executeJavaScript('thisShouldProduceAnError()', (result, error) => { - callbackResult = result - callbackError = error + callbackResult = result; + callbackError = error; }) .then( promiseResult => { - done(new Error('error is expected')) + done(new Error('error is expected')); }, promiseError => { - expect(promiseError).to.be.an('error') - done() + expect(promiseError).to.be.an('error'); + done(); } - ) + ); - expect(callbackResult).to.be.undefined() - expect(callbackError).to.be.an('error') - }) + expect(callbackResult).to.be.undefined(); + expect(callbackError).to.be.an('error'); + }); // executeJavaScriptInIsolatedWorld is failing to detect exec errors and is neither // rejecting nor passing the error to the callback. This predates the reintroduction @@ -133,8 +133,8 @@ describe('webFrame module', function () { // }) it('executeJavaScript(InIsolatedWorld) can be used without a callback', async () => { - expect(await webFrame.executeJavaScript('1 + 1')).to.equal(2) - expect(await webFrame.executeJavaScriptInIsolatedWorld(999, [{ code: '1 + 1' }])).to.equal(2) - }) - }) -}) + expect(await webFrame.executeJavaScript('1 + 1')).to.equal(2); + expect(await webFrame.executeJavaScriptInIsolatedWorld(999, [{ code: '1 + 1' }])).to.equal(2); + }); + }); +}); diff --git a/spec/asar-spec.js b/spec/asar-spec.js index 47ed7d71938de..fe253f9e3f044 100644 --- a/spec/asar-spec.js +++ b/spec/asar-spec.js @@ -1,1325 +1,1325 @@ -const { expect } = require('chai') -const ChildProcess = require('child_process') -const fs = require('fs') -const path = require('path') -const temp = require('temp').track() -const util = require('util') -const nativeImage = require('electron').nativeImage +const { expect } = require('chai'); +const ChildProcess = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const temp = require('temp').track(); +const util = require('util'); +const nativeImage = require('electron').nativeImage; -const features = process.electronBinding('features') +const features = process.electronBinding('features'); async function expectToThrowErrorWithCode (func, code) { - let error + let error; try { - await func() + await func(); } catch (e) { - error = e + error = e; } - expect(error).is.an('Error') - expect(error).to.have.property('code').which.equals(code) + expect(error).is.an('Error'); + expect(error).to.have.property('code').which.equals(code); } describe('asar package', function () { - const fixtures = path.join(__dirname, 'fixtures') - const asarDir = path.join(fixtures, 'test.asar') + const fixtures = path.join(__dirname, 'fixtures'); + const asarDir = path.join(fixtures, 'test.asar'); describe('node api', function () { it('supports paths specified as a Buffer', function () { - const file = Buffer.from(path.join(asarDir, 'a.asar', 'file1')) - expect(fs.existsSync(file)).to.be.true() - }) + const file = Buffer.from(path.join(asarDir, 'a.asar', 'file1')); + expect(fs.existsSync(file)).to.be.true(); + }); describe('fs.readFileSync', function () { it('does not leak fd', function () { - let readCalls = 1 + let readCalls = 1; while (readCalls <= 10000) { - fs.readFileSync(path.join(process.resourcesPath, 'default_app.asar', 'main.js')) - readCalls++ + fs.readFileSync(path.join(process.resourcesPath, 'default_app.asar', 'main.js')); + readCalls++; } - }) + }); it('reads a normal file', function () { - const file1 = path.join(asarDir, 'a.asar', 'file1') - expect(fs.readFileSync(file1).toString().trim()).to.equal('file1') - const file2 = path.join(asarDir, 'a.asar', 'file2') - expect(fs.readFileSync(file2).toString().trim()).to.equal('file2') - const file3 = path.join(asarDir, 'a.asar', 'file3') - expect(fs.readFileSync(file3).toString().trim()).to.equal('file3') - }) + const file1 = path.join(asarDir, 'a.asar', 'file1'); + expect(fs.readFileSync(file1).toString().trim()).to.equal('file1'); + const file2 = path.join(asarDir, 'a.asar', 'file2'); + expect(fs.readFileSync(file2).toString().trim()).to.equal('file2'); + const file3 = path.join(asarDir, 'a.asar', 'file3'); + expect(fs.readFileSync(file3).toString().trim()).to.equal('file3'); + }); it('reads from a empty file', function () { - const file = path.join(asarDir, 'empty.asar', 'file1') - const buffer = fs.readFileSync(file) - expect(buffer).to.be.empty() - expect(buffer.toString()).to.equal('') - }) + const file = path.join(asarDir, 'empty.asar', 'file1'); + const buffer = fs.readFileSync(file); + expect(buffer).to.be.empty(); + expect(buffer.toString()).to.equal(''); + }); it('reads a linked file', function () { - const p = path.join(asarDir, 'a.asar', 'link1') - expect(fs.readFileSync(p).toString().trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'link1'); + expect(fs.readFileSync(p).toString().trim()).to.equal('file1'); + }); it('reads a file from linked directory', function () { - const p1 = path.join(asarDir, 'a.asar', 'link2', 'file1') - expect(fs.readFileSync(p1).toString().trim()).to.equal('file1') - const p2 = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - expect(fs.readFileSync(p2).toString().trim()).to.equal('file1') - }) + const p1 = path.join(asarDir, 'a.asar', 'link2', 'file1'); + expect(fs.readFileSync(p1).toString().trim()).to.equal('file1'); + const p2 = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + expect(fs.readFileSync(p2).toString().trim()).to.equal('file1'); + }); it('throws ENOENT error when can not find file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.readFileSync(p) - }).to.throw(/ENOENT/) - }) + fs.readFileSync(p); + }).to.throw(/ENOENT/); + }); it('passes ENOENT error to callback when can not find file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - let async = false + const p = path.join(asarDir, 'a.asar', 'not-exist'); + let async = false; fs.readFile(p, function (error) { - expect(async).to.be.true() - expect(error).to.match(/ENOENT/) - }) - async = true - }) + expect(async).to.be.true(); + expect(error).to.match(/ENOENT/); + }); + async = true; + }); it('reads a normal file with unpacked files', function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - expect(fs.readFileSync(p).toString().trim()).to.equal('a') - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + expect(fs.readFileSync(p).toString().trim()).to.equal('a'); + }); it('reads a file in filesystem', function () { - const p = path.resolve(asarDir, 'file') - expect(fs.readFileSync(p).toString().trim()).to.equal('file') - }) - }) + const p = path.resolve(asarDir, 'file'); + expect(fs.readFileSync(p).toString().trim()).to.equal('file'); + }); + }); describe('fs.readFile', function () { it('reads a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); fs.readFile(p, function (err, content) { - expect(err).to.be.null() - expect(String(content).trim()).to.equal('file1') - done() - }) - }) + expect(err).to.be.null(); + expect(String(content).trim()).to.equal('file1'); + done(); + }); + }); it('reads from a empty file', function (done) { - const p = path.join(asarDir, 'empty.asar', 'file1') + const p = path.join(asarDir, 'empty.asar', 'file1'); fs.readFile(p, function (err, content) { - expect(err).to.be.null() - expect(String(content)).to.equal('') - done() - }) - }) + expect(err).to.be.null(); + expect(String(content)).to.equal(''); + done(); + }); + }); it('reads from a empty file with encoding', function (done) { - const p = path.join(asarDir, 'empty.asar', 'file1') + const p = path.join(asarDir, 'empty.asar', 'file1'); fs.readFile(p, 'utf8', function (err, content) { - expect(err).to.be.null() - expect(content).to.equal('') - done() - }) - }) + expect(err).to.be.null(); + expect(content).to.equal(''); + done(); + }); + }); it('reads a linked file', function (done) { - const p = path.join(asarDir, 'a.asar', 'link1') + const p = path.join(asarDir, 'a.asar', 'link1'); fs.readFile(p, function (err, content) { - expect(err).to.be.null() - expect(String(content).trim()).to.equal('file1') - done() - }) - }) + expect(err).to.be.null(); + expect(String(content).trim()).to.equal('file1'); + done(); + }); + }); it('reads a file from linked directory', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); fs.readFile(p, function (err, content) { - expect(err).to.be.null() - expect(String(content).trim()).to.equal('file1') - done() - }) - }) + expect(err).to.be.null(); + expect(String(content).trim()).to.equal('file1'); + done(); + }); + }); it('throws ENOENT error when can not find file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.readFile(p, function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.readFile', function () { it('reads a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - const content = await fs.promises.readFile(p) - expect(String(content).trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + const content = await fs.promises.readFile(p); + expect(String(content).trim()).to.equal('file1'); + }); it('reads from a empty file', async function () { - const p = path.join(asarDir, 'empty.asar', 'file1') - const content = await fs.promises.readFile(p) - expect(String(content)).to.equal('') - }) + const p = path.join(asarDir, 'empty.asar', 'file1'); + const content = await fs.promises.readFile(p); + expect(String(content)).to.equal(''); + }); it('reads from a empty file with encoding', async function () { - const p = path.join(asarDir, 'empty.asar', 'file1') - const content = await fs.promises.readFile(p, 'utf8') - expect(content).to.equal('') - }) + const p = path.join(asarDir, 'empty.asar', 'file1'); + const content = await fs.promises.readFile(p, 'utf8'); + expect(content).to.equal(''); + }); it('reads a linked file', async function () { - const p = path.join(asarDir, 'a.asar', 'link1') - const content = await fs.promises.readFile(p) - expect(String(content).trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'link1'); + const content = await fs.promises.readFile(p); + expect(String(content).trim()).to.equal('file1'); + }); it('reads a file from linked directory', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - const content = await fs.promises.readFile(p) - expect(String(content).trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + const content = await fs.promises.readFile(p); + expect(String(content).trim()).to.equal('file1'); + }); it('throws ENOENT error when can not find file', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.readFile(p), 'ENOENT') - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.readFile(p), 'ENOENT'); + }); + }); describe('fs.copyFile', function () { it('copies a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') - const dest = temp.path() + const p = path.join(asarDir, 'a.asar', 'file1'); + const dest = temp.path(); fs.copyFile(p, dest, function (err) { - expect(err).to.be.null() - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - done() - }) - }) + expect(err).to.be.null(); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + done(); + }); + }); it('copies a unpacked file', function (done) { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - const dest = temp.path() + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + const dest = temp.path(); fs.copyFile(p, dest, function (err) { - expect(err).to.be.null() - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - done() - }) - }) - }) + expect(err).to.be.null(); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + done(); + }); + }); + }); describe('fs.promises.copyFile', function () { it('copies a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - const dest = temp.path() - await fs.promises.copyFile(p, dest) - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + const dest = temp.path(); + await fs.promises.copyFile(p, dest); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + }); it('copies a unpacked file', async function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - const dest = temp.path() - await fs.promises.copyFile(p, dest) - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - }) - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + const dest = temp.path(); + await fs.promises.copyFile(p, dest); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + }); + }); describe('fs.copyFileSync', function () { it('copies a normal file', function () { - const p = path.join(asarDir, 'a.asar', 'file1') - const dest = temp.path() - fs.copyFileSync(p, dest) - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + const dest = temp.path(); + fs.copyFileSync(p, dest); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + }); it('copies a unpacked file', function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - const dest = temp.path() - fs.copyFileSync(p, dest) - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - }) - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + const dest = temp.path(); + fs.copyFileSync(p, dest); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + }); + }); describe('fs.lstatSync', function () { it('handles path with trailing slash correctly', function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - fs.lstatSync(p) - fs.lstatSync(p + '/') - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + fs.lstatSync(p); + fs.lstatSync(p + '/'); + }); it('returns information of root', function () { - const p = path.join(asarDir, 'a.asar') - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar'); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of root with stats as bigint', function () { - const p = path.join(asarDir, 'a.asar') - const stats = fs.lstatSync(p, { bigint: false }) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar'); + const stats = fs.lstatSync(p, { bigint: false }); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of a normal file', function () { - const ref2 = ['file1', 'file2', 'file3', path.join('dir1', 'file1'), path.join('link2', 'file1')] + const ref2 = ['file1', 'file2', 'file3', path.join('dir1', 'file1'), path.join('link2', 'file1')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.true() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(6) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.true(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(6); } - }) + }); it('returns information of a normal directory', function () { - const ref2 = ['dir1', 'dir2', 'dir3'] + const ref2 = ['dir1', 'dir2', 'dir3']; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); } - }) + }); it('returns information of a linked file', function () { - const ref2 = ['link1', path.join('dir1', 'link1'), path.join('link2', 'link2')] + const ref2 = ['link1', path.join('dir1', 'link1'), path.join('link2', 'link2')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); } - }) + }); it('returns information of a linked directory', function () { - const ref2 = ['link2', path.join('dir1', 'link2'), path.join('link2', 'link2')] + const ref2 = ['link2', path.join('dir1', 'link2'), path.join('link2', 'link2')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); } - }) + }); it('throws ENOENT error when can not find file', function () { - const ref2 = ['file4', 'file5', path.join('dir1', 'file4')] + const ref2 = ['file4', 'file5', path.join('dir1', 'file4')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); expect(() => { - fs.lstatSync(p) - }).to.throw(/ENOENT/) + fs.lstatSync(p); + }).to.throw(/ENOENT/); } - }) - }) + }); + }); describe('fs.lstat', function () { it('handles path with trailing slash correctly', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - fs.lstat(p + '/', done) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + fs.lstat(p + '/', done); + }); it('returns information of root', function (done) { - const p = path.join(asarDir, 'a.asar') + const p = path.join(asarDir, 'a.asar'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('returns information of root with stats as bigint', function (done) { - const p = path.join(asarDir, 'a.asar') + const p = path.join(asarDir, 'a.asar'); fs.lstat(p, { bigint: false }, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('returns information of a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'file1') + const p = path.join(asarDir, 'a.asar', 'link2', 'file1'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.true() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(6) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.true(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(6); + done(); + }); + }); it('returns information of a normal directory', function (done) { - const p = path.join(asarDir, 'a.asar', 'dir1') + const p = path.join(asarDir, 'a.asar', 'dir1'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('returns information of a linked file', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link1') + const p = path.join(asarDir, 'a.asar', 'link2', 'link1'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('returns information of a linked directory', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('throws ENOENT error when can not find file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file4') + const p = path.join(asarDir, 'a.asar', 'file4'); fs.lstat(p, function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.lstat', function () { it('handles path with trailing slash correctly', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - await fs.promises.lstat(p + '/') - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + await fs.promises.lstat(p + '/'); + }); it('returns information of root', async function () { - const p = path.join(asarDir, 'a.asar') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of root with stats as bigint', async function () { - const p = path.join(asarDir, 'a.asar') - const stats = await fs.promises.lstat(p, { bigint: false }) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar'); + const stats = await fs.promises.lstat(p, { bigint: false }); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'file1') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.true() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(6) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'file1'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.true(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(6); + }); it('returns information of a normal directory', async function () { - const p = path.join(asarDir, 'a.asar', 'dir1') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar', 'dir1'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of a linked file', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link1') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link1'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); + }); it('returns information of a linked directory', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); + }); it('throws ENOENT error when can not find file', async function () { - const p = path.join(asarDir, 'a.asar', 'file4') - await expectToThrowErrorWithCode(() => fs.promises.lstat(p), 'ENOENT') - }) - }) + const p = path.join(asarDir, 'a.asar', 'file4'); + await expectToThrowErrorWithCode(() => fs.promises.lstat(p), 'ENOENT'); + }); + }); describe('fs.realpathSync', () => { it('returns real path root', () => { - const parent = fs.realpathSync(asarDir) - const p = 'a.asar' - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = 'a.asar'; + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal file', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'file1') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'file1'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal directory', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'dir1') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'dir1'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a linked file', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link1') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + }); it('returns real path of a linked directory', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link2') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + }); it('returns real path of an unpacked file', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('unpack.asar', 'a.txt') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('unpack.asar', 'a.txt'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('throws ENOENT error when can not find file', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'not-exist') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'not-exist'); expect(() => { - fs.realpathSync(path.join(parent, p)) - }).to.throw(/ENOENT/) - }) - }) + fs.realpathSync(path.join(parent, p)); + }).to.throw(/ENOENT/); + }); + }); describe('fs.realpathSync.native', () => { it('returns real path root', () => { - const parent = fs.realpathSync.native(asarDir) - const p = 'a.asar' - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync.native(asarDir); + const p = 'a.asar'; + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal file', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'file1') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'file1'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal directory', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'dir1') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'dir1'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a linked file', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'link2', 'link1') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + }); it('returns real path of a linked directory', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'link2', 'link2') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + }); it('returns real path of an unpacked file', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('unpack.asar', 'a.txt') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('unpack.asar', 'a.txt'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('throws ENOENT error when can not find file', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'not-exist') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'not-exist'); expect(() => { - fs.realpathSync.native(path.join(parent, p)) - }).to.throw(/ENOENT/) - }) - }) + fs.realpathSync.native(path.join(parent, p)); + }).to.throw(/ENOENT/); + }); + }); describe('fs.realpath', () => { it('returns real path root', done => { - const parent = fs.realpathSync(asarDir) - const p = 'a.asar' + const parent = fs.realpathSync(asarDir); + const p = 'a.asar'; fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a normal file', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'file1') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'file1'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a normal directory', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'dir1') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'dir1'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a linked file', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link1') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + done(); + }); + }); it('returns real path of a linked directory', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link2') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + done(); + }); + }); it('returns real path of an unpacked file', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('unpack.asar', 'a.txt') + const parent = fs.realpathSync(asarDir); + const p = path.join('unpack.asar', 'a.txt'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('throws ENOENT error when can not find file', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'not-exist') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'not-exist'); fs.realpath(path.join(parent, p), err => { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.realpath', () => { it('returns real path root', async () => { - const parent = fs.realpathSync(asarDir) - const p = 'a.asar' - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = 'a.asar'; + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal file', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'file1') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'file1'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal directory', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'dir1') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'dir1'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a linked file', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link1') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + }); it('returns real path of a linked directory', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link2') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + }); it('returns real path of an unpacked file', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('unpack.asar', 'a.txt') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('unpack.asar', 'a.txt'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('throws ENOENT error when can not find file', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.realpath(path.join(parent, p)), 'ENOENT') - }) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.realpath(path.join(parent, p)), 'ENOENT'); + }); + }); describe('fs.realpath.native', () => { it('returns real path root', done => { - const parent = fs.realpathSync.native(asarDir) - const p = 'a.asar' + const parent = fs.realpathSync.native(asarDir); + const p = 'a.asar'; fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a normal file', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'file1') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'file1'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a normal directory', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'dir1') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'dir1'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a linked file', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'link2', 'link1') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + done(); + }); + }); it('returns real path of a linked directory', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'link2', 'link2') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + done(); + }); + }); it('returns real path of an unpacked file', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('unpack.asar', 'a.txt') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('unpack.asar', 'a.txt'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('throws ENOENT error when can not find file', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'not-exist') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'not-exist'); fs.realpath.native(path.join(parent, p), err => { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.readdirSync', function () { it('reads dirs from root', function () { - const p = path.join(asarDir, 'a.asar') - const dirs = fs.readdirSync(p) - expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']) - }) + const p = path.join(asarDir, 'a.asar'); + const dirs = fs.readdirSync(p); + expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); + }); it('reads dirs from a normal dir', function () { - const p = path.join(asarDir, 'a.asar', 'dir1') - const dirs = fs.readdirSync(p) - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - }) + const p = path.join(asarDir, 'a.asar', 'dir1'); + const dirs = fs.readdirSync(p); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + }); it('reads dirs from a linked dir', function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') - const dirs = fs.readdirSync(p) - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); + const dirs = fs.readdirSync(p); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + }); it('throws ENOENT error when can not find file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.readdirSync(p) - }).to.throw(/ENOENT/) - }) - }) + fs.readdirSync(p); + }).to.throw(/ENOENT/); + }); + }); describe('fs.readdir', function () { it('reads dirs from root', function (done) { - const p = path.join(asarDir, 'a.asar') + const p = path.join(asarDir, 'a.asar'); fs.readdir(p, function (err, dirs) { - expect(err).to.be.null() - expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']) - done() - }) - }) + expect(err).to.be.null(); + expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); + done(); + }); + }); it('reads dirs from a normal dir', function (done) { - const p = path.join(asarDir, 'a.asar', 'dir1') + const p = path.join(asarDir, 'a.asar', 'dir1'); fs.readdir(p, function (err, dirs) { - expect(err).to.be.null() - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - done() - }) - }) + expect(err).to.be.null(); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + done(); + }); + }); it('reads dirs from a linked dir', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); fs.readdir(p, function (err, dirs) { - expect(err).to.be.null() - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - done() - }) - }) + expect(err).to.be.null(); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + done(); + }); + }); it('throws ENOENT error when can not find file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.readdir(p, function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.readdir', function () { it('reads dirs from root', async function () { - const p = path.join(asarDir, 'a.asar') - const dirs = await fs.promises.readdir(p) - expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']) - }) + const p = path.join(asarDir, 'a.asar'); + const dirs = await fs.promises.readdir(p); + expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); + }); it('reads dirs from a normal dir', async function () { - const p = path.join(asarDir, 'a.asar', 'dir1') - const dirs = await fs.promises.readdir(p) - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - }) + const p = path.join(asarDir, 'a.asar', 'dir1'); + const dirs = await fs.promises.readdir(p); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + }); it('reads dirs from a linked dir', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') - const dirs = await fs.promises.readdir(p) - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); + const dirs = await fs.promises.readdir(p); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + }); it('throws ENOENT error when can not find file', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.readdir(p), 'ENOENT') - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.readdir(p), 'ENOENT'); + }); + }); describe('fs.openSync', function () { it('opens a normal/linked/under-linked-directory file', function () { - const ref2 = ['file1', 'link1', path.join('link2', 'file1')] + const ref2 = ['file1', 'link1', path.join('link2', 'file1')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const fd = fs.openSync(p, 'r') - const buffer = Buffer.alloc(6) - fs.readSync(fd, buffer, 0, 6, 0) - expect(String(buffer).trim()).to.equal('file1') - fs.closeSync(fd) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const fd = fs.openSync(p, 'r'); + const buffer = Buffer.alloc(6); + fs.readSync(fd, buffer, 0, 6, 0); + expect(String(buffer).trim()).to.equal('file1'); + fs.closeSync(fd); } - }) + }); it('throws ENOENT error when can not find file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.openSync(p) - }).to.throw(/ENOENT/) - }) - }) + fs.openSync(p); + }).to.throw(/ENOENT/); + }); + }); describe('fs.open', function () { it('opens a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); fs.open(p, 'r', function (err, fd) { - expect(err).to.be.null() - const buffer = Buffer.alloc(6) + expect(err).to.be.null(); + const buffer = Buffer.alloc(6); fs.read(fd, buffer, 0, 6, 0, function (err) { - expect(err).to.be.null() - expect(String(buffer).trim()).to.equal('file1') - fs.close(fd, done) - }) - }) - }) + expect(err).to.be.null(); + expect(String(buffer).trim()).to.equal('file1'); + fs.close(fd, done); + }); + }); + }); it('throws ENOENT error when can not find file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.open(p, 'r', function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.open', function () { it('opens a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - const fh = await fs.promises.open(p, 'r') - const buffer = Buffer.alloc(6) - await fh.read(buffer, 0, 6, 0) - expect(String(buffer).trim()).to.equal('file1') - await fh.close() - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + const fh = await fs.promises.open(p, 'r'); + const buffer = Buffer.alloc(6); + await fh.read(buffer, 0, 6, 0); + expect(String(buffer).trim()).to.equal('file1'); + await fh.close(); + }); it('throws ENOENT error when can not find file', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.open(p, 'r'), 'ENOENT') - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.open(p, 'r'), 'ENOENT'); + }); + }); describe('fs.mkdir', function () { it('throws error when calling inside asar archive', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.mkdir(p, function (err) { - expect(err.code).to.equal('ENOTDIR') - done() - }) - }) - }) + expect(err.code).to.equal('ENOTDIR'); + done(); + }); + }); + }); describe('fs.promises.mkdir', function () { it('throws error when calling inside asar archive', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.mkdir(p), 'ENOTDIR') - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.mkdir(p), 'ENOTDIR'); + }); + }); describe('fs.mkdirSync', function () { it('throws error when calling inside asar archive', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.mkdirSync(p) - }).to.throw(/ENOTDIR/) - }) - }) + fs.mkdirSync(p); + }).to.throw(/ENOTDIR/); + }); + }); describe('fs.exists', function () { it('handles an existing file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); // eslint-disable-next-line fs.exists(p, function (exists) { - expect(exists).to.be.true() - done() - }) - }) + expect(exists).to.be.true(); + done(); + }); + }); it('handles a non-existent file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); // eslint-disable-next-line fs.exists(p, function (exists) { - expect(exists).to.be.false() - done() - }) - }) + expect(exists).to.be.false(); + done(); + }); + }); it('promisified version handles an existing file', (done) => { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); // eslint-disable-next-line util.promisify(fs.exists)(p).then(exists => { - expect(exists).to.be.true() - done() - }) - }) + expect(exists).to.be.true(); + done(); + }); + }); it('promisified version handles a non-existent file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); // eslint-disable-next-line util.promisify(fs.exists)(p).then(exists => { - expect(exists).to.be.false() - done() - }) - }) - }) + expect(exists).to.be.false(); + done(); + }); + }); + }); describe('fs.existsSync', function () { it('handles an existing file', function () { - const p = path.join(asarDir, 'a.asar', 'file1') - expect(fs.existsSync(p)).to.be.true() - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + expect(fs.existsSync(p)).to.be.true(); + }); it('handles a non-existent file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - expect(fs.existsSync(p)).to.be.false() - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + expect(fs.existsSync(p)).to.be.false(); + }); + }); describe('fs.access', function () { it('accesses a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); fs.access(p, function (err) { - expect(err).to.be.undefined() - done() - }) - }) + expect(err).to.be.undefined(); + done(); + }); + }); it('throws an error when called with write mode', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) { - expect(err.code).to.equal('EACCES') - done() - }) - }) + expect(err.code).to.equal('EACCES'); + done(); + }); + }); it('throws an error when called on non-existent file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.access(p, function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); it('allows write mode for unpacked files', function (done) { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) { - expect(err).to.be.null() - done() - }) - }) - }) + expect(err).to.be.null(); + done(); + }); + }); + }); describe('fs.promises.access', function () { it('accesses a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - await fs.promises.access(p) - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + await fs.promises.access(p); + }); it('throws an error when called with write mode', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - await expectToThrowErrorWithCode(() => fs.promises.access(p, fs.constants.R_OK | fs.constants.W_OK), 'EACCES') - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + await expectToThrowErrorWithCode(() => fs.promises.access(p, fs.constants.R_OK | fs.constants.W_OK), 'EACCES'); + }); it('throws an error when called on non-existent file', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.access(p), 'ENOENT') - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.access(p), 'ENOENT'); + }); it('allows write mode for unpacked files', async function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - await fs.promises.access(p, fs.constants.R_OK | fs.constants.W_OK) - }) - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + await fs.promises.access(p, fs.constants.R_OK | fs.constants.W_OK); + }); + }); describe('fs.accessSync', function () { it('accesses a normal file', function () { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); expect(() => { - fs.accessSync(p) - }).to.not.throw() - }) + fs.accessSync(p); + }).to.not.throw(); + }); it('throws an error when called with write mode', function () { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); expect(() => { - fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK) - }).to.throw(/EACCES/) - }) + fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK); + }).to.throw(/EACCES/); + }); it('throws an error when called on non-existent file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.accessSync(p) - }).to.throw(/ENOENT/) - }) + fs.accessSync(p); + }).to.throw(/ENOENT/); + }); it('allows write mode for unpacked files', function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); expect(() => { - fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK) - }).to.not.throw() - }) - }) + fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK); + }).to.not.throw(); + }); + }); describe('child_process.fork', function () { before(function () { if (!features.isRunAsNodeEnabled()) { - this.skip() + this.skip(); } - }) + }); it('opens a normal js file', function (done) { - const child = ChildProcess.fork(path.join(asarDir, 'a.asar', 'ping.js')) + const child = ChildProcess.fork(path.join(asarDir, 'a.asar', 'ping.js')); child.on('message', function (msg) { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); it('supports asar in the forked js', function (done) { - const file = path.join(asarDir, 'a.asar', 'file1') - const child = ChildProcess.fork(path.join(fixtures, 'module', 'asar.js')) + const file = path.join(asarDir, 'a.asar', 'file1'); + const child = ChildProcess.fork(path.join(fixtures, 'module', 'asar.js')); child.on('message', function (content) { - expect(content).to.equal(fs.readFileSync(file).toString()) - done() - }) - child.send(file) - }) - }) + expect(content).to.equal(fs.readFileSync(file).toString()); + done(); + }); + child.send(file); + }); + }); describe('child_process.exec', function () { - const echo = path.join(asarDir, 'echo.asar', 'echo') + const echo = path.join(asarDir, 'echo.asar', 'echo'); it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) { ChildProcess.exec('echo ' + echo + ' foo bar', function (error, stdout) { - expect(error).to.be.null() - expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n') - done() - }) - }) + expect(error).to.be.null(); + expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n'); + done(); + }); + }); it('can be promisified', () => { return util.promisify(ChildProcess.exec)('echo ' + echo + ' foo bar').then(({ stdout }) => { - expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n') - }) - }) - }) + expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n'); + }); + }); + }); describe('child_process.execSync', function () { - const echo = path.join(asarDir, 'echo.asar', 'echo') + const echo = path.join(asarDir, 'echo.asar', 'echo'); it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) { - const stdout = ChildProcess.execSync('echo ' + echo + ' foo bar') - expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n') - done() - }) - }) + const stdout = ChildProcess.execSync('echo ' + echo + ' foo bar'); + expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n'); + done(); + }); + }); describe('child_process.execFile', function () { - const execFile = ChildProcess.execFile - const execFileSync = ChildProcess.execFileSync - const echo = path.join(asarDir, 'echo.asar', 'echo') + const execFile = ChildProcess.execFile; + const execFileSync = ChildProcess.execFileSync; + const echo = path.join(asarDir, 'echo.asar', 'echo'); before(function () { if (process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('executes binaries', function (done) { execFile(echo, ['test'], function (error, stdout) { - expect(error).to.be.null() - expect(stdout).to.equal('test\n') - done() - }) - }) + expect(error).to.be.null(); + expect(stdout).to.equal('test\n'); + done(); + }); + }); it('executes binaries without callback', function (done) { - const process = execFile(echo, ['test']) + const process = execFile(echo, ['test']); process.on('close', function (code) { - expect(code).to.equal(0) - done() - }) + expect(code).to.equal(0); + done(); + }); process.on('error', function () { - expect.fail() - done() - }) - }) + expect.fail(); + done(); + }); + }); it('execFileSync executes binaries', function () { - const output = execFileSync(echo, ['test']) - expect(String(output)).to.equal('test\n') - }) + const output = execFileSync(echo, ['test']); + expect(String(output)).to.equal('test\n'); + }); it('can be promisified', () => { return util.promisify(ChildProcess.execFile)(echo, ['test']).then(({ stdout }) => { - expect(stdout).to.equal('test\n') - }) - }) - }) + expect(stdout).to.equal('test\n'); + }); + }); + }); describe('internalModuleReadJSON', function () { - const internalModuleReadJSON = process.binding('fs').internalModuleReadJSON + const internalModuleReadJSON = process.binding('fs').internalModuleReadJSON; it('read a normal file', function () { - const file1 = path.join(asarDir, 'a.asar', 'file1') - expect(internalModuleReadJSON(file1).toString().trim()).to.equal('file1') - const file2 = path.join(asarDir, 'a.asar', 'file2') - expect(internalModuleReadJSON(file2).toString().trim()).to.equal('file2') - const file3 = path.join(asarDir, 'a.asar', 'file3') - expect(internalModuleReadJSON(file3).toString().trim()).to.equal('file3') - }) + const file1 = path.join(asarDir, 'a.asar', 'file1'); + expect(internalModuleReadJSON(file1).toString().trim()).to.equal('file1'); + const file2 = path.join(asarDir, 'a.asar', 'file2'); + expect(internalModuleReadJSON(file2).toString().trim()).to.equal('file2'); + const file3 = path.join(asarDir, 'a.asar', 'file3'); + expect(internalModuleReadJSON(file3).toString().trim()).to.equal('file3'); + }); it('reads a normal file with unpacked files', function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - expect(internalModuleReadJSON(p).toString().trim()).to.equal('a') - }) - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + expect(internalModuleReadJSON(p).toString().trim()).to.equal('a'); + }); + }); describe('util.promisify', function () { it('can promisify all fs functions', function () { - const originalFs = require('original-fs') - const { hasOwnProperty } = Object.prototype + const originalFs = require('original-fs'); + const { hasOwnProperty } = Object.prototype; for (const [propertyName, originalValue] of Object.entries(originalFs)) { // Some properties exist but have a value of `undefined` on some platforms. // E.g. `fs.lchmod`, which in only available on MacOS, see // https://nodejs.org/docs/latest-v10.x/api/fs.html#fs_fs_lchmod_path_mode_callback // Also check for `null`s, `hasOwnProperty()` can't handle them. - if (typeof originalValue === 'undefined' || originalValue === null) continue + if (typeof originalValue === 'undefined' || originalValue === null) continue; if (hasOwnProperty.call(originalValue, util.promisify.custom)) { expect(fs).to.have.own.property(propertyName) - .that.has.own.property(util.promisify.custom) + .that.has.own.property(util.promisify.custom); } } - }) - }) + }); + }); describe('process.noAsar', function () { - const errorName = process.platform === 'win32' ? 'ENOENT' : 'ENOTDIR' + const errorName = process.platform === 'win32' ? 'ENOENT' : 'ENOTDIR'; beforeEach(function () { - process.noAsar = true - }) + process.noAsar = true; + }); afterEach(function () { - process.noAsar = false - }) + process.noAsar = false; + }); it('disables asar support in sync API', function () { - const file = path.join(asarDir, 'a.asar', 'file1') - const dir = path.join(asarDir, 'a.asar', 'dir1') + const file = path.join(asarDir, 'a.asar', 'file1'); + const dir = path.join(asarDir, 'a.asar', 'dir1'); expect(() => { - fs.readFileSync(file) - }).to.throw(new RegExp(errorName)) + fs.readFileSync(file); + }).to.throw(new RegExp(errorName)); expect(() => { - fs.lstatSync(file) - }).to.throw(new RegExp(errorName)) + fs.lstatSync(file); + }).to.throw(new RegExp(errorName)); expect(() => { - fs.realpathSync(file) - }).to.throw(new RegExp(errorName)) + fs.realpathSync(file); + }).to.throw(new RegExp(errorName)); expect(() => { - fs.readdirSync(dir) - }).to.throw(new RegExp(errorName)) - }) + fs.readdirSync(dir); + }).to.throw(new RegExp(errorName)); + }); it('disables asar support in async API', function (done) { - const file = path.join(asarDir, 'a.asar', 'file1') - const dir = path.join(asarDir, 'a.asar', 'dir1') + const file = path.join(asarDir, 'a.asar', 'file1'); + const dir = path.join(asarDir, 'a.asar', 'dir1'); fs.readFile(file, function (error) { - expect(error.code).to.equal(errorName) + expect(error.code).to.equal(errorName); fs.lstat(file, function (error) { - expect(error.code).to.equal(errorName) + expect(error.code).to.equal(errorName); fs.realpath(file, function (error) { - expect(error.code).to.equal(errorName) + expect(error.code).to.equal(errorName); fs.readdir(dir, function (error) { - expect(error.code).to.equal(errorName) - done() - }) - }) - }) - }) - }) + expect(error.code).to.equal(errorName); + done(); + }); + }); + }); + }); + }); it('disables asar support in promises API', async function () { - const file = path.join(asarDir, 'a.asar', 'file1') - const dir = path.join(asarDir, 'a.asar', 'dir1') - await expect(fs.promises.readFile(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)) - await expect(fs.promises.lstat(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)) - await expect(fs.promises.realpath(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)) - await expect(fs.promises.readdir(dir)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)) - }) + const file = path.join(asarDir, 'a.asar', 'file1'); + const dir = path.join(asarDir, 'a.asar', 'dir1'); + await expect(fs.promises.readFile(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)); + await expect(fs.promises.lstat(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)); + await expect(fs.promises.realpath(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)); + await expect(fs.promises.readdir(dir)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)); + }); it('treats *.asar as normal file', function () { - const originalFs = require('original-fs') - const asar = path.join(asarDir, 'a.asar') - const content1 = fs.readFileSync(asar) - const content2 = originalFs.readFileSync(asar) - expect(content1.compare(content2)).to.equal(0) + const originalFs = require('original-fs'); + const asar = path.join(asarDir, 'a.asar'); + const content1 = fs.readFileSync(asar); + const content2 = originalFs.readFileSync(asar); + expect(content1.compare(content2)).to.equal(0); expect(() => { - fs.readdirSync(asar) - }).to.throw(/ENOTDIR/) - }) + fs.readdirSync(asar); + }).to.throw(/ENOTDIR/); + }); it('is reset to its original value when execSync throws an error', function () { - process.noAsar = false + process.noAsar = false; expect(() => { - ChildProcess.execSync(path.join(__dirname, 'does-not-exist.txt')) - }).to.throw() - expect(process.noAsar).to.be.false() - }) - }) + ChildProcess.execSync(path.join(__dirname, 'does-not-exist.txt')); + }).to.throw(); + expect(process.noAsar).to.be.false(); + }); + }); describe('process.env.ELECTRON_NO_ASAR', function () { before(function () { if (!features.isRunAsNodeEnabled()) { - this.skip() + this.skip(); } - }) + }); it('disables asar support in forked processes', function (done) { const forked = ChildProcess.fork(path.join(__dirname, 'fixtures', 'module', 'no-asar.js'), [], { env: { ELECTRON_NO_ASAR: true } - }) + }); forked.on('message', function (stats) { - expect(stats.isFile).to.be.true() - expect(stats.size).to.equal(778) - done() - }) - }) + expect(stats.isFile).to.be.true(); + expect(stats.size).to.equal(778); + done(); + }); + }); it('disables asar support in spawned processes', function (done) { const spawned = ChildProcess.spawn(process.execPath, [path.join(__dirname, 'fixtures', 'module', 'no-asar.js')], { @@ -1327,140 +1327,140 @@ describe('asar package', function () { ELECTRON_NO_ASAR: true, ELECTRON_RUN_AS_NODE: true } - }) + }); - let output = '' + let output = ''; spawned.stdout.on('data', function (data) { - output += data - }) + output += data; + }); spawned.stdout.on('close', function () { - const stats = JSON.parse(output) - expect(stats.isFile).to.be.true() - expect(stats.size).to.equal(778) - done() - }) - }) - }) - }) + const stats = JSON.parse(output); + expect(stats.isFile).to.be.true(); + expect(stats.size).to.equal(778); + done(); + }); + }); + }); + }); describe('asar protocol', function () { it('can request a file in package', function (done) { - const p = path.resolve(asarDir, 'a.asar', 'file1') + const p = path.resolve(asarDir, 'a.asar', 'file1'); $.get('file://' + p, function (data) { - expect(data.trim()).to.equal('file1') - done() - }) - }) + expect(data.trim()).to.equal('file1'); + done(); + }); + }); it('can request a file in package with unpacked files', function (done) { - const p = path.resolve(asarDir, 'unpack.asar', 'a.txt') + const p = path.resolve(asarDir, 'unpack.asar', 'a.txt'); $.get('file://' + p, function (data) { - expect(data.trim()).to.equal('a') - done() - }) - }) + expect(data.trim()).to.equal('a'); + done(); + }); + }); it('can request a linked file in package', function (done) { - const p = path.resolve(asarDir, 'a.asar', 'link2', 'link1') + const p = path.resolve(asarDir, 'a.asar', 'link2', 'link1'); $.get('file://' + p, function (data) { - expect(data.trim()).to.equal('file1') - done() - }) - }) + expect(data.trim()).to.equal('file1'); + done(); + }); + }); it('can request a file in filesystem', function (done) { - const p = path.resolve(asarDir, 'file') + const p = path.resolve(asarDir, 'file'); $.get('file://' + p, function (data) { - expect(data.trim()).to.equal('file') - done() - }) - }) + expect(data.trim()).to.equal('file'); + done(); + }); + }); it('gets 404 when file is not found', function (done) { - const p = path.resolve(asarDir, 'a.asar', 'no-exist') + const p = path.resolve(asarDir, 'a.asar', 'no-exist'); $.ajax({ url: 'file://' + p, error: function (err) { - expect(err.status).to.equal(404) - done() + expect(err.status).to.equal(404); + done(); } - }) - }) - }) + }); + }); + }); describe('original-fs module', function () { - const originalFs = require('original-fs') + const originalFs = require('original-fs'); it('treats .asar as file', function () { - const file = path.join(asarDir, 'a.asar') - const stats = originalFs.statSync(file) - expect(stats.isFile()).to.be.true() - }) + const file = path.join(asarDir, 'a.asar'); + const stats = originalFs.statSync(file); + expect(stats.isFile()).to.be.true(); + }); it('is available in forked scripts', function (done) { if (!features.isRunAsNodeEnabled()) { - this.skip() - done() + this.skip(); + done(); } - const child = ChildProcess.fork(path.join(fixtures, 'module', 'original-fs.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'original-fs.js')); child.on('message', function (msg) { - expect(msg).to.equal('object') - done() - }) - child.send('message') - }) + expect(msg).to.equal('object'); + done(); + }); + child.send('message'); + }); it('can be used with streams', () => { - originalFs.createReadStream(path.join(asarDir, 'a.asar')) - }) + originalFs.createReadStream(path.join(asarDir, 'a.asar')); + }); it('has the same APIs as fs', function () { - expect(Object.keys(require('fs'))).to.deep.equal(Object.keys(require('original-fs'))) - expect(Object.keys(require('fs').promises)).to.deep.equal(Object.keys(require('original-fs').promises)) - }) - }) + expect(Object.keys(require('fs'))).to.deep.equal(Object.keys(require('original-fs'))); + expect(Object.keys(require('fs').promises)).to.deep.equal(Object.keys(require('original-fs').promises)); + }); + }); describe('graceful-fs module', function () { - const gfs = require('graceful-fs') + const gfs = require('graceful-fs'); it('recognize asar archvies', function () { - const p = path.join(asarDir, 'a.asar', 'link1') - expect(gfs.readFileSync(p).toString().trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'link1'); + expect(gfs.readFileSync(p).toString().trim()).to.equal('file1'); + }); it('does not touch global fs object', function () { - expect(fs.readdir).to.not.equal(gfs.readdir) - }) - }) + expect(fs.readdir).to.not.equal(gfs.readdir); + }); + }); describe('mkdirp module', function () { - const mkdirp = require('mkdirp') + const mkdirp = require('mkdirp'); it('throws error when calling inside asar archive', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - mkdirp.sync(p) - }).to.throw(/ENOTDIR/) - }) - }) + mkdirp.sync(p); + }).to.throw(/ENOTDIR/); + }); + }); describe('native-image', function () { it('reads image from asar archive', function () { - const p = path.join(asarDir, 'logo.asar', 'logo.png') - const logo = nativeImage.createFromPath(p) + const p = path.join(asarDir, 'logo.asar', 'logo.png'); + const logo = nativeImage.createFromPath(p); expect(logo.getSize()).to.deep.equal({ width: 55, height: 55 - }) - }) + }); + }); it('reads image from asar archive with unpacked files', function () { - const p = path.join(asarDir, 'unpack.asar', 'atom.png') - const logo = nativeImage.createFromPath(p) + const p = path.join(asarDir, 'unpack.asar', 'atom.png'); + const logo = nativeImage.createFromPath(p); expect(logo.getSize()).to.deep.equal({ width: 1024, height: 1024 - }) - }) - }) -}) + }); + }); + }); +}); diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 141123863fef3..f9eb1c6dc43f9 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -1,34 +1,34 @@ -const { expect } = require('chai') -const fs = require('fs') -const http = require('http') -const path = require('path') -const ws = require('ws') -const url = require('url') -const ChildProcess = require('child_process') -const { ipcRenderer } = require('electron') -const { emittedOnce } = require('./events-helpers') -const { resolveGetters } = require('./expect-helpers') -const features = process.electronBinding('features') +const { expect } = require('chai'); +const fs = require('fs'); +const http = require('http'); +const path = require('path'); +const ws = require('ws'); +const url = require('url'); +const ChildProcess = require('child_process'); +const { ipcRenderer } = require('electron'); +const { emittedOnce } = require('./events-helpers'); +const { resolveGetters } = require('./expect-helpers'); +const features = process.electronBinding('features'); /* Most of the APIs here don't use standard callbacks */ /* eslint-disable standard/no-callback-literal */ describe('chromium feature', () => { - const fixtures = path.resolve(__dirname, 'fixtures') - let listener = null + const fixtures = path.resolve(__dirname, 'fixtures'); + let listener = null; afterEach(() => { if (listener != null) { - window.removeEventListener('message', listener) + window.removeEventListener('message', listener); } - listener = null - }) + listener = null; + }); describe('heap snapshot', () => { it('does not crash', function () { - process.electronBinding('v8_util').takeHeapSnapshot() - }) - }) + process.electronBinding('v8_util').takeHeapSnapshot(); + }); + }); describe('navigator.webkitGetUserMedia', () => { it('calls its callbacks', (done) => { @@ -36,74 +36,74 @@ describe('chromium feature', () => { audio: true, video: false }, () => done(), - () => done()) - }) - }) + () => done()); + }); + }); describe('navigator.language', () => { it('should not be empty', () => { - expect(navigator.language).to.not.equal('') - }) - }) + expect(navigator.language).to.not.equal(''); + }); + }); describe('navigator.geolocation', () => { before(function () { if (!features.isFakeLocationProviderEnabled()) { - return this.skip() + return this.skip(); } - }) + }); it('returns position when permission is granted', (done) => { navigator.geolocation.getCurrentPosition((position) => { - expect(position).to.have.a.property('coords') - expect(position).to.have.a.property('timestamp') - done() + expect(position).to.have.a.property('coords'); + expect(position).to.have.a.property('timestamp'); + done(); }, (error) => { - done(error) - }) - }) - }) + done(error); + }); + }); + }); describe('window.open', () => { it('returns a BrowserWindowProxy object', () => { - const b = window.open('about:blank', '', 'show=no') - expect(b.closed).to.be.false() - expect(b.constructor.name).to.equal('BrowserWindowProxy') - b.close() - }) + const b = window.open('about:blank', '', 'show=no'); + expect(b.closed).to.be.false(); + expect(b.constructor.name).to.equal('BrowserWindowProxy'); + b.close(); + }); it('accepts "nodeIntegration" as feature', (done) => { - let b = null + let b = null; listener = (event) => { - expect(event.data.isProcessGlobalUndefined).to.be.true() - b.close() - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-opener-node.html`, '', 'nodeIntegration=no,show=no') - }) + expect(event.data.isProcessGlobalUndefined).to.be.true(); + b.close(); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-opener-node.html`, '', 'nodeIntegration=no,show=no'); + }); it('inherit options of parent window', (done) => { - let b = null + let b = null; listener = (event) => { - const width = outerWidth - const height = outerHeight - expect(event.data).to.equal(`size: ${width} ${height}`) - b.close() - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no') - }) + const width = outerWidth; + const height = outerHeight; + expect(event.data).to.equal(`size: ${width} ${height}`); + b.close(); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no'); + }); it('disables node integration when it is disabled on the parent window', (done) => { - let b = null + let b = null; listener = (event) => { - expect(event.data.isProcessGlobalUndefined).to.be.true() - b.close() - done() - } - window.addEventListener('message', listener) + expect(event.data.isProcessGlobalUndefined).to.be.true(); + b.close(); + done(); + }; + window.addEventListener('message', listener); const windowUrl = require('url').format({ pathname: `${fixtures}/pages/window-opener-no-node-integration.html`, @@ -112,18 +112,18 @@ describe('chromium feature', () => { p: `${fixtures}/pages/window-opener-node.html` }, slashes: true - }) - b = window.open(windowUrl, '', 'nodeIntegration=no,show=no') - }) + }); + b = window.open(windowUrl, '', 'nodeIntegration=no,show=no'); + }); it('disables the tag when it is disabled on the parent window', (done) => { - let b = null + let b = null; listener = (event) => { - expect(event.data.isWebViewGlobalUndefined).to.be.true() - b.close() - done() - } - window.addEventListener('message', listener) + expect(event.data.isWebViewGlobalUndefined).to.be.true(); + b.close(); + done(); + }; + window.addEventListener('message', listener); const windowUrl = require('url').format({ pathname: `${fixtures}/pages/window-opener-no-webview-tag.html`, @@ -132,89 +132,89 @@ describe('chromium feature', () => { p: `${fixtures}/pages/window-opener-webview.html` }, slashes: true - }) - b = window.open(windowUrl, '', 'webviewTag=no,nodeIntegration=yes,show=no') - }) + }); + b = window.open(windowUrl, '', 'webviewTag=no,nodeIntegration=yes,show=no'); + }); it('does not override child options', (done) => { - let b = null + let b = null; const size = { width: 350, height: 450 - } + }; listener = (event) => { - expect(event.data).to.equal(`size: ${size.width} ${size.height}`) - b.close() - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no,width=' + size.width + ',height=' + size.height) - }) + expect(event.data).to.equal(`size: ${size.width} ${size.height}`); + b.close(); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no,width=' + size.width + ',height=' + size.height); + }); it('throws an exception when the arguments cannot be converted to strings', () => { expect(() => { - window.open('', { toString: null }) - }).to.throw('Cannot convert object to primitive value') + window.open('', { toString: null }); + }).to.throw('Cannot convert object to primitive value'); expect(() => { - window.open('', '', { toString: 3 }) - }).to.throw('Cannot convert object to primitive value') - }) + window.open('', '', { toString: 3 }); + }).to.throw('Cannot convert object to primitive value'); + }); it('does not throw an exception when the features include webPreferences', () => { - let b = null + let b = null; expect(() => { - b = window.open('', '', 'webPreferences=') - }).to.not.throw() - b.close() - }) - }) + b = window.open('', '', 'webPreferences='); + }).to.not.throw(); + b.close(); + }); + }); describe('window.opener', () => { it('is not null for window opened by window.open', (done) => { - let b = null + let b = null; listener = (event) => { - expect(event.data).to.equal('object') - b.close() - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-opener.html`, '', 'show=no') - }) - }) + expect(event.data).to.equal('object'); + b.close(); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-opener.html`, '', 'show=no'); + }); + }); describe('window.postMessage', () => { it('throws an exception when the targetOrigin cannot be converted to a string', () => { - const b = window.open('') + const b = window.open(''); expect(() => { - b.postMessage('test', { toString: null }) - }).to.throw('Cannot convert object to primitive value') - b.close() - }) - }) + b.postMessage('test', { toString: null }); + }).to.throw('Cannot convert object to primitive value'); + b.close(); + }); + }); describe('window.opener.postMessage', () => { it('sets source and origin correctly', (done) => { - let b = null + let b = null; listener = (event) => { - window.removeEventListener('message', listener) - b.close() - expect(event.source).to.equal(b) - expect(event.origin).to.equal('file://') - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-opener-postMessage.html`, '', 'show=no') - }) + window.removeEventListener('message', listener); + b.close(); + expect(event.source).to.equal(b); + expect(event.origin).to.equal('file://'); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-opener-postMessage.html`, '', 'show=no'); + }); it('supports windows opened from a ', (done) => { - const webview = new WebView() + const webview = new WebView(); webview.addEventListener('console-message', (e) => { - webview.remove() - expect(e.message).to.equal('message') - done() - }) - webview.allowpopups = true + webview.remove(); + expect(e.message).to.equal('message'); + done(); + }); + webview.allowpopups = true; webview.src = url.format({ pathname: `${fixtures}/pages/webview-opener-postMessage.html`, protocol: 'file', @@ -222,158 +222,158 @@ describe('chromium feature', () => { p: `${fixtures}/pages/window-opener-postMessage.html` }, slashes: true - }) - document.body.appendChild(webview) - }) + }); + document.body.appendChild(webview); + }); describe('targetOrigin argument', () => { - let serverURL - let server + let serverURL; + let server; beforeEach((done) => { server = http.createServer((req, res) => { - res.writeHead(200) - const filePath = path.join(fixtures, 'pages', 'window-opener-targetOrigin.html') - res.end(fs.readFileSync(filePath, 'utf8')) - }) + res.writeHead(200); + const filePath = path.join(fixtures, 'pages', 'window-opener-targetOrigin.html'); + res.end(fs.readFileSync(filePath, 'utf8')); + }); server.listen(0, '127.0.0.1', () => { - serverURL = `http://127.0.0.1:${server.address().port}` - done() - }) - }) + serverURL = `http://127.0.0.1:${server.address().port}`; + done(); + }); + }); afterEach(() => { - server.close() - }) + server.close(); + }); it('delivers messages that match the origin', (done) => { - let b = null + let b = null; listener = (event) => { - window.removeEventListener('message', listener) - b.close() - expect(event.data).to.equal('deliver') - done() - } - window.addEventListener('message', listener) - b = window.open(serverURL, '', 'show=no') - }) - }) - }) + window.removeEventListener('message', listener); + b.close(); + expect(event.data).to.equal('deliver'); + done(); + }; + window.addEventListener('message', listener); + b = window.open(serverURL, '', 'show=no'); + }); + }); + }); describe('webgl', () => { before(function () { if (process.platform === 'win32') { - this.skip() + this.skip(); } - }) + }); it('can be get as context in canvas', () => { if (process.platform === 'linux') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const webgl = document.createElement('canvas').getContext('webgl') - expect(webgl).to.not.be.null() - }) - }) + const webgl = document.createElement('canvas').getContext('webgl'); + expect(webgl).to.not.be.null(); + }); + }); describe('web workers', () => { it('Worker can work', (done) => { - const worker = new Worker('../fixtures/workers/worker.js') - const message = 'ping' + const worker = new Worker('../fixtures/workers/worker.js'); + const message = 'ping'; worker.onmessage = (event) => { - expect(event.data).to.equal(message) - worker.terminate() - done() - } - worker.postMessage(message) - }) + expect(event.data).to.equal(message); + worker.terminate(); + done(); + }; + worker.postMessage(message); + }); it('Worker has no node integration by default', (done) => { - const worker = new Worker('../fixtures/workers/worker_node.js') + const worker = new Worker('../fixtures/workers/worker_node.js'); worker.onmessage = (event) => { - expect(event.data).to.equal('undefined undefined undefined undefined') - worker.terminate() - done() - } - }) + expect(event.data).to.equal('undefined undefined undefined undefined'); + worker.terminate(); + done(); + }; + }); it('Worker has node integration with nodeIntegrationInWorker', (done) => { - const webview = new WebView() + const webview = new WebView(); webview.addEventListener('ipc-message', (e) => { - expect(e.channel).to.equal('object function object function') - webview.remove() - done() - }) - webview.src = `file://${fixtures}/pages/worker.html` - webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker') - document.body.appendChild(webview) - }) + expect(e.channel).to.equal('object function object function'); + webview.remove(); + done(); + }); + webview.src = `file://${fixtures}/pages/worker.html`; + webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker'); + document.body.appendChild(webview); + }); // FIXME: disabled during chromium update due to crash in content::WorkerScriptFetchInitiator::CreateScriptLoaderOnIO xdescribe('SharedWorker', () => { it('can work', (done) => { - const worker = new SharedWorker('../fixtures/workers/shared_worker.js') - const message = 'ping' + const worker = new SharedWorker('../fixtures/workers/shared_worker.js'); + const message = 'ping'; worker.port.onmessage = (event) => { - expect(event.data).to.equal(message) - done() - } - worker.port.postMessage(message) - }) + expect(event.data).to.equal(message); + done(); + }; + worker.port.postMessage(message); + }); it('has no node integration by default', (done) => { - const worker = new SharedWorker('../fixtures/workers/shared_worker_node.js') + const worker = new SharedWorker('../fixtures/workers/shared_worker_node.js'); worker.port.onmessage = (event) => { - expect(event.data).to.equal('undefined undefined undefined undefined') - done() - } - }) + expect(event.data).to.equal('undefined undefined undefined undefined'); + done(); + }; + }); it('has node integration with nodeIntegrationInWorker', (done) => { - const webview = new WebView() + const webview = new WebView(); webview.addEventListener('console-message', (e) => { - console.log(e) - }) + console.log(e); + }); webview.addEventListener('ipc-message', (e) => { - expect(e.channel).to.equal('object function object function') - webview.remove() - done() - }) - webview.src = `file://${fixtures}/pages/shared_worker.html` - webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker') - document.body.appendChild(webview) - }) - }) - }) + expect(e.channel).to.equal('object function object function'); + webview.remove(); + done(); + }); + webview.src = `file://${fixtures}/pages/shared_worker.html`; + webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker'); + document.body.appendChild(webview); + }); + }); + }); describe('iframe', () => { - let iframe = null + let iframe = null; beforeEach(() => { - iframe = document.createElement('iframe') - }) + iframe = document.createElement('iframe'); + }); afterEach(() => { - document.body.removeChild(iframe) - }) + document.body.removeChild(iframe); + }); it('does not have node integration', (done) => { - iframe.src = `file://${fixtures}/pages/set-global.html` - document.body.appendChild(iframe) + iframe.src = `file://${fixtures}/pages/set-global.html`; + document.body.appendChild(iframe); iframe.onload = () => { - expect(iframe.contentWindow.test).to.equal('undefined undefined undefined') - done() - } - }) - }) + expect(iframe.contentWindow.test).to.equal('undefined undefined undefined'); + done(); + }; + }); + }); describe('storage', () => { describe('DOM storage quota increase', () => { ['localStorage', 'sessionStorage'].forEach((storageName) => { - const storage = window[storageName] + const storage = window[storageName]; it(`allows saving at least 40MiB in ${storageName}`, (done) => { // Although JavaScript strings use UTF-16, the underlying // storage provider may encode strings differently, muddling the @@ -383,142 +383,142 @@ describe('chromium feature', () => { // to exceed the original 10MiB quota yet stay within the // new 100MiB quota. // Note that both the key name and value affect the total size. - const testKeyName = '_electronDOMStorageQuotaIncreasedTest' - const length = 40 * Math.pow(2, 20) - testKeyName.length - storage.setItem(testKeyName, 'X'.repeat(length)) + const testKeyName = '_electronDOMStorageQuotaIncreasedTest'; + const length = 40 * Math.pow(2, 20) - testKeyName.length; + storage.setItem(testKeyName, 'X'.repeat(length)); // Wait at least one turn of the event loop to help avoid false positives // Although not entirely necessary, the previous version of this test case // failed to detect a real problem (perhaps related to DOM storage data caching) // wherein calling `getItem` immediately after `setItem` would appear to work // but then later (e.g. next tick) it would not. setTimeout(() => { - expect(storage.getItem(testKeyName)).to.have.lengthOf(length) - storage.removeItem(testKeyName) - done() - }, 1) - }) + expect(storage.getItem(testKeyName)).to.have.lengthOf(length); + storage.removeItem(testKeyName); + done(); + }, 1); + }); it(`throws when attempting to use more than 128MiB in ${storageName}`, () => { expect(() => { - const testKeyName = '_electronDOMStorageQuotaStillEnforcedTest' - const length = 128 * Math.pow(2, 20) - testKeyName.length + const testKeyName = '_electronDOMStorageQuotaStillEnforcedTest'; + const length = 128 * Math.pow(2, 20) - testKeyName.length; try { - storage.setItem(testKeyName, 'X'.repeat(length)) + storage.setItem(testKeyName, 'X'.repeat(length)); } finally { - storage.removeItem(testKeyName) + storage.removeItem(testKeyName); } - }).to.throw() - }) - }) - }) + }).to.throw(); + }); + }); + }); it('requesting persitent quota works', (done) => { navigator.webkitPersistentStorage.requestQuota(1024 * 1024, (grantedBytes) => { - expect(grantedBytes).to.equal(1048576) - done() - }) - }) - }) + expect(grantedBytes).to.equal(1048576); + done(); + }); + }); + }); describe('websockets', () => { - let wss = null - let server = null - const WebSocketServer = ws.Server + let wss = null; + let server = null; + const WebSocketServer = ws.Server; afterEach(() => { - wss.close() - server.close() - }) + wss.close(); + server.close(); + }); it('has user agent', (done) => { - server = http.createServer() + server = http.createServer(); server.listen(0, '127.0.0.1', () => { - const port = server.address().port - wss = new WebSocketServer({ server: server }) - wss.on('error', done) + const port = server.address().port; + wss = new WebSocketServer({ server: server }); + wss.on('error', done); wss.on('connection', (ws, upgradeReq) => { if (upgradeReq.headers['user-agent']) { - done() + done(); } else { - done('user agent is empty') + done('user agent is empty'); } - }) - const socket = new WebSocket(`ws://127.0.0.1:${port}`) - }) - }) - }) + }); + const socket = new WebSocket(`ws://127.0.0.1:${port}`); + }); + }); + }); describe('Promise', () => { it('resolves correctly in Node.js calls', (done) => { class XElement extends HTMLElement {} - customElements.define('x-element', XElement) + customElements.define('x-element', XElement); setImmediate(() => { - let called = false + let called = false; Promise.resolve().then(() => { - done(called ? undefined : new Error('wrong sequence')) - }) - document.createElement('x-element') - called = true - }) - }) + done(called ? undefined : new Error('wrong sequence')); + }); + document.createElement('x-element'); + called = true; + }); + }); it('resolves correctly in Electron calls', (done) => { class YElement extends HTMLElement {} - customElements.define('y-element', YElement) + customElements.define('y-element', YElement); ipcRenderer.invoke('ping').then(() => { - let called = false + let called = false; Promise.resolve().then(() => { - done(called ? undefined : new Error('wrong sequence')) - }) - document.createElement('y-element') - called = true - }) - }) - }) + done(called ? undefined : new Error('wrong sequence')); + }); + document.createElement('y-element'); + called = true; + }); + }); + }); describe('fetch', () => { it('does not crash', (done) => { const server = http.createServer((req, res) => { - res.end('test') - server.close() - }) + res.end('test'); + server.close(); + }); server.listen(0, '127.0.0.1', () => { - const port = server.address().port + const port = server.address().port; fetch(`http://127.0.0.1:${port}`).then((res) => res.body.getReader()) .then((reader) => { reader.read().then((r) => { - reader.cancel() - done() - }) - }).catch((e) => done(e)) - }) - }) - }) + reader.cancel(); + done(); + }); + }).catch((e) => done(e)); + }); + }); + }); describe('window.alert(message, title)', () => { it('throws an exception when the arguments cannot be converted to strings', () => { expect(() => { - window.alert({ toString: null }) - }).to.throw('Cannot convert object to primitive value') - }) - }) + window.alert({ toString: null }); + }).to.throw('Cannot convert object to primitive value'); + }); + }); describe('window.confirm(message, title)', () => { it('throws an exception when the arguments cannot be converted to strings', () => { expect(() => { - window.confirm({ toString: null }, 'title') - }).to.throw('Cannot convert object to primitive value') - }) - }) + window.confirm({ toString: null }, 'title'); + }).to.throw('Cannot convert object to primitive value'); + }); + }); describe('window.history', () => { describe('window.history.go(offset)', () => { it('throws an exception when the argumnet cannot be converted to a string', () => { expect(() => { - window.history.go({ toString: null }) - }).to.throw('Cannot convert object to primitive value') - }) - }) - }) + window.history.go({ toString: null }); + }).to.throw('Cannot convert object to primitive value'); + }); + }); + }); // TODO(nornagon): this is broken on CI, it triggers: // [FATAL:speech_synthesis.mojom-shared.h(237)] The outgoing message will @@ -527,50 +527,50 @@ describe('chromium feature', () => { describe.skip('SpeechSynthesis', () => { before(function () { if (!features.isTtsEnabled()) { - this.skip() + this.skip(); } - }) + }); it('should emit lifecycle events', async () => { const sentence = `long sentence which will take at least a few seconds to - utter so that it's possible to pause and resume before the end` - const utter = new SpeechSynthesisUtterance(sentence) + utter so that it's possible to pause and resume before the end`; + const utter = new SpeechSynthesisUtterance(sentence); // Create a dummy utterence so that speech synthesis state // is initialized for later calls. - speechSynthesis.speak(new SpeechSynthesisUtterance()) - speechSynthesis.cancel() - speechSynthesis.speak(utter) + speechSynthesis.speak(new SpeechSynthesisUtterance()); + speechSynthesis.cancel(); + speechSynthesis.speak(utter); // paused state after speak() - expect(speechSynthesis.paused).to.be.false() - await new Promise((resolve) => { utter.onstart = resolve }) + expect(speechSynthesis.paused).to.be.false(); + await new Promise((resolve) => { utter.onstart = resolve; }); // paused state after start event - expect(speechSynthesis.paused).to.be.false() + expect(speechSynthesis.paused).to.be.false(); - speechSynthesis.pause() + speechSynthesis.pause(); // paused state changes async, right before the pause event - expect(speechSynthesis.paused).to.be.false() - await new Promise((resolve) => { utter.onpause = resolve }) - expect(speechSynthesis.paused).to.be.true() + expect(speechSynthesis.paused).to.be.false(); + await new Promise((resolve) => { utter.onpause = resolve; }); + expect(speechSynthesis.paused).to.be.true(); - speechSynthesis.resume() - await new Promise((resolve) => { utter.onresume = resolve }) + speechSynthesis.resume(); + await new Promise((resolve) => { utter.onresume = resolve; }); // paused state after resume event - expect(speechSynthesis.paused).to.be.false() + expect(speechSynthesis.paused).to.be.false(); - await new Promise((resolve) => { utter.onend = resolve }) - }) - }) -}) + await new Promise((resolve) => { utter.onend = resolve; }); + }); + }); +}); describe('console functions', () => { it('should exist', () => { - expect(console.log, 'log').to.be.a('function') - expect(console.error, 'error').to.be.a('function') - expect(console.warn, 'warn').to.be.a('function') - expect(console.info, 'info').to.be.a('function') - expect(console.debug, 'debug').to.be.a('function') - expect(console.trace, 'trace').to.be.a('function') - expect(console.time, 'time').to.be.a('function') - expect(console.timeEnd, 'timeEnd').to.be.a('function') - }) -}) + expect(console.log, 'log').to.be.a('function'); + expect(console.error, 'error').to.be.a('function'); + expect(console.warn, 'warn').to.be.a('function'); + expect(console.info, 'info').to.be.a('function'); + expect(console.debug, 'debug').to.be.a('function'); + expect(console.trace, 'trace').to.be.a('function'); + expect(console.time, 'time').to.be.a('function'); + expect(console.timeEnd, 'timeEnd').to.be.a('function'); + }); +}); diff --git a/spec/events-helpers.js b/spec/events-helpers.js index 64a4fba447eb3..507f68eb113f1 100644 --- a/spec/events-helpers.js +++ b/spec/events-helpers.js @@ -10,9 +10,9 @@ */ const waitForEvent = (target, eventName) => { return new Promise(resolve => { - target.addEventListener(eventName, resolve, { once: true }) - }) -} + target.addEventListener(eventName, resolve, { once: true }); + }); +}; /** * @param {!EventEmitter} emitter @@ -20,23 +20,23 @@ const waitForEvent = (target, eventName) => { * @return {!Promise} With Event as the first item. */ const emittedOnce = (emitter, eventName) => { - return emittedNTimes(emitter, eventName, 1).then(([result]) => result) -} + return emittedNTimes(emitter, eventName, 1).then(([result]) => result); +}; const emittedNTimes = (emitter, eventName, times) => { - const events = [] + const events = []; return new Promise(resolve => { const handler = (...args) => { - events.push(args) + events.push(args); if (events.length === times) { - emitter.removeListener(eventName, handler) - resolve(events) + emitter.removeListener(eventName, handler); + resolve(events); } - } - emitter.on(eventName, handler) - }) -} + }; + emitter.on(eventName, handler); + }); +}; -exports.emittedOnce = emittedOnce -exports.emittedNTimes = emittedNTimes -exports.waitForEvent = waitForEvent +exports.emittedOnce = emittedOnce; +exports.emittedNTimes = emittedNTimes; +exports.waitForEvent = waitForEvent; diff --git a/spec/expect-helpers.js b/spec/expect-helpers.js index 659f63d6c0598..18d72d1ec66b8 100644 --- a/spec/expect-helpers.js +++ b/spec/expect-helpers.js @@ -1,18 +1,18 @@ function resolveSingleObjectGetters (object) { if (object && typeof object === 'object') { - const newObject = {} + const newObject = {}; for (const key in object) { // eslint-disable-line guard-for-in - newObject[key] = resolveGetters(object[key])[0] + newObject[key] = resolveGetters(object[key])[0]; } - return newObject + return newObject; } - return object + return object; } function resolveGetters (...args) { - return args.map(resolveSingleObjectGetters) + return args.map(resolveSingleObjectGetters); } module.exports = { resolveGetters -} +}; diff --git a/spec/fixtures/api/app-path/lib/index.js b/spec/fixtures/api/app-path/lib/index.js index d1a5732edca51..a4510fd01c7a8 100644 --- a/spec/fixtures/api/app-path/lib/index.js +++ b/spec/fixtures/api/app-path/lib/index.js @@ -1,10 +1,10 @@ -const { app } = require('electron') +const { app } = require('electron'); const payload = { appPath: app.getAppPath() -} +}; -process.stdout.write(JSON.stringify(payload)) -process.stdout.end() +process.stdout.write(JSON.stringify(payload)); +process.stdout.end(); -process.exit() +process.exit(); diff --git a/spec/fixtures/api/command-line/main.js b/spec/fixtures/api/command-line/main.js index b5fb79144770e..6b262a022d80c 100644 --- a/spec/fixtures/api/command-line/main.js +++ b/spec/fixtures/api/command-line/main.js @@ -1,13 +1,13 @@ -const { app } = require('electron') +const { app } = require('electron'); app.whenReady().then(() => { const payload = { hasSwitch: app.commandLine.hasSwitch('foobar'), getSwitchValue: app.commandLine.getSwitchValue('foobar') - } + }; - process.stdout.write(JSON.stringify(payload)) - process.stdout.end() + process.stdout.write(JSON.stringify(payload)); + process.stdout.end(); - app.quit() -}) + app.quit(); +}); diff --git a/spec/fixtures/api/cookie-app/main.js b/spec/fixtures/api/cookie-app/main.js index b16402a98c0f3..f6791e923d876 100644 --- a/spec/fixtures/api/cookie-app/main.js +++ b/spec/fixtures/api/cookie-app/main.js @@ -1,42 +1,42 @@ -const { app, session } = require('electron') +const { app, session } = require('electron'); app.whenReady().then(async function () { - const url = 'http://foo.bar' - const persistentSession = session.fromPartition('persist:ence-test') - const name = 'test' - const value = 'true' + const url = 'http://foo.bar'; + const persistentSession = session.fromPartition('persist:ence-test'); + const name = 'test'; + const value = 'true'; const set = () => persistentSession.cookies.set({ url, name, value, expirationDate: Date.now() + 60000 - }) + }); const get = () => persistentSession.cookies.get({ url - }) + }); const maybeRemove = async (pred) => { if (pred()) { - await persistentSession.cookies.remove(url, name) + await persistentSession.cookies.remove(url, name); } - } + }; try { - await maybeRemove(() => process.env.PHASE === 'one') - const one = await get() - await set() - const two = await get() - await maybeRemove(() => process.env.PHASE === 'two') - const three = await get() + await maybeRemove(() => process.env.PHASE === 'one'); + const one = await get(); + await set(); + const two = await get(); + await maybeRemove(() => process.env.PHASE === 'two'); + const three = await get(); - process.stdout.write(`${one.length}${two.length}${three.length}`) + process.stdout.write(`${one.length}${two.length}${three.length}`); } catch (e) { - process.stdout.write('ERROR') + process.stdout.write('ERROR'); } finally { - process.stdout.end() + process.stdout.end(); - app.quit() + app.quit(); } -}) +}); diff --git a/spec/fixtures/api/default-menu/main.js b/spec/fixtures/api/default-menu/main.js index 5688e5b458451..16e84badc9136 100644 --- a/spec/fixtures/api/default-menu/main.js +++ b/spec/fixtures/api/default-menu/main.js @@ -1,32 +1,32 @@ -const { app, Menu } = require('electron') +const { app, Menu } = require('electron'); function output (value) { - process.stdout.write(JSON.stringify(value)) - process.stdout.end() + process.stdout.write(JSON.stringify(value)); + process.stdout.end(); - app.quit() + app.quit(); } try { - let expectedMenu + let expectedMenu; if (app.commandLine.hasSwitch('custom-menu')) { - expectedMenu = new Menu() - Menu.setApplicationMenu(expectedMenu) + expectedMenu = new Menu(); + Menu.setApplicationMenu(expectedMenu); } else if (app.commandLine.hasSwitch('null-menu')) { - expectedMenu = null - Menu.setApplicationMenu(null) + expectedMenu = null; + Menu.setApplicationMenu(null); } app.whenReady().then(() => { setImmediate(() => { try { - output(Menu.getApplicationMenu() === expectedMenu) + output(Menu.getApplicationMenu() === expectedMenu); } catch (error) { - output(null) + output(null); } - }) - }) + }); + }); } catch (error) { - output(null) + output(null); } diff --git a/spec/fixtures/api/electron-main-module/app/index.js b/spec/fixtures/api/electron-main-module/app/index.js index c56845bca42d5..873192591cd3d 100644 --- a/spec/fixtures/api/electron-main-module/app/index.js +++ b/spec/fixtures/api/electron-main-module/app/index.js @@ -1,8 +1,8 @@ try { - require('some-module') + require('some-module'); } catch (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } -process.exit(0) +process.exit(0); diff --git a/spec/fixtures/api/electron-module-app/.gitignore b/spec/fixtures/api/electron-module-app/.gitignore new file mode 100644 index 0000000000000..736e8ae58ad87 --- /dev/null +++ b/spec/fixtures/api/electron-module-app/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/spec/fixtures/api/electron-module-app/node_modules/foo/index.js b/spec/fixtures/api/electron-module-app/node_modules/foo/index.js index 11d763e517426..41f3ba446f69d 100644 --- a/spec/fixtures/api/electron-module-app/node_modules/foo/index.js +++ b/spec/fixtures/api/electron-module-app/node_modules/foo/index.js @@ -1 +1 @@ -exports.bar = function () {} +exports.bar = function () {}; diff --git a/spec/fixtures/api/exit-closes-all-windows-app/main.js b/spec/fixtures/api/exit-closes-all-windows-app/main.js index e61a74dd96127..2a427b10ff87f 100644 --- a/spec/fixtures/api/exit-closes-all-windows-app/main.js +++ b/spec/fixtures/api/exit-closes-all-windows-app/main.js @@ -1,19 +1,19 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron'); -const windows = [] +const windows = []; function createWindow (id) { - const window = new BrowserWindow({ show: false }) - window.loadURL(`data:,window${id}`) - windows.push(window) + const window = new BrowserWindow({ show: false }); + window.loadURL(`data:,window${id}`); + windows.push(window); } app.whenReady().then(() => { for (let i = 1; i <= 5; i++) { - createWindow(i) + createWindow(i); } setImmediate(function () { - app.exit(123) - }) -}) + app.exit(123); + }); +}); diff --git a/spec/fixtures/api/gpu-info.js b/spec/fixtures/api/gpu-info.js index 8c3d929d708df..3e7bc88b02d8b 100644 --- a/spec/fixtures/api/gpu-info.js +++ b/spec/fixtures/api/gpu-info.js @@ -1,21 +1,21 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron'); -app.commandLine.appendSwitch('--disable-software-rasterizer') +app.commandLine.appendSwitch('--disable-software-rasterizer'); app.whenReady().then(() => { - const infoType = process.argv.pop() - const w = new BrowserWindow({ show: false }) + const infoType = process.argv.pop(); + const w = new BrowserWindow({ show: false }); w.webContents.once('did-finish-load', () => { app.getGPUInfo(infoType).then( (gpuInfo) => { - console.log(JSON.stringify(gpuInfo)) - app.exit(0) + console.log(JSON.stringify(gpuInfo)); + app.exit(0); }, (error) => { - console.error(error) - app.exit(1) + console.error(error); + app.exit(1); } - ) - }) - w.loadURL('data:text/html;') -}) + ); + }); + w.loadURL('data:text/html;'); +}); diff --git a/spec/fixtures/api/isolated-fetch-preload.js b/spec/fixtures/api/isolated-fetch-preload.js index 283af99fe16e1..e2c7b75857f77 100644 --- a/spec/fixtures/api/isolated-fetch-preload.js +++ b/spec/fixtures/api/isolated-fetch-preload.js @@ -1,6 +1,6 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); // Ensure fetch works from isolated world origin fetch('https://localhost:1234').catch(err => { - ipcRenderer.send('isolated-fetch-error', err.message) -}) + ipcRenderer.send('isolated-fetch-error', err.message); +}); diff --git a/spec/fixtures/api/isolated-preload.js b/spec/fixtures/api/isolated-preload.js index 20c2181bacbaf..c0ca37505ae6f 100644 --- a/spec/fixtures/api/isolated-preload.js +++ b/spec/fixtures/api/isolated-preload.js @@ -1,8 +1,8 @@ -const { ipcRenderer, webFrame } = require('electron') +const { ipcRenderer, webFrame } = require('electron'); -window.foo = 3 +window.foo = 3; -webFrame.executeJavaScript('window.preloadExecuteJavaScriptProperty = 1234;') +webFrame.executeJavaScript('window.preloadExecuteJavaScriptProperty = 1234;'); window.addEventListener('message', (event) => { ipcRenderer.send('isolated-world', { @@ -16,5 +16,5 @@ window.addEventListener('message', (event) => { typeofPreloadExecuteJavaScriptProperty: typeof window.preloadExecuteJavaScriptProperty }, pageContext: event.data - }) -}) + }); +}); diff --git a/spec/fixtures/api/leak-exit-webcontents.js b/spec/fixtures/api/leak-exit-webcontents.js index f2f06c8db6001..d601c18fc14eb 100644 --- a/spec/fixtures/api/leak-exit-webcontents.js +++ b/spec/fixtures/api/leak-exit-webcontents.js @@ -1,6 +1,6 @@ -const { app, webContents } = require('electron') +const { app, webContents } = require('electron'); app.whenReady().then(function () { - webContents.create({}) + webContents.create({}); - app.quit() -}) + app.quit(); +}); diff --git a/spec/fixtures/api/loaded-from-dataurl.js b/spec/fixtures/api/loaded-from-dataurl.js index c4dbdd044bdb2..ccb1454acfc27 100644 --- a/spec/fixtures/api/loaded-from-dataurl.js +++ b/spec/fixtures/api/loaded-from-dataurl.js @@ -1 +1 @@ -require('electron').ipcRenderer.send('answer', 'test') +require('electron').ipcRenderer.send('answer', 'test'); diff --git a/spec/fixtures/api/locale-check/main.js b/spec/fixtures/api/locale-check/main.js index 054920c66b2fe..e287f8efa393a 100644 --- a/spec/fixtures/api/locale-check/main.js +++ b/spec/fixtures/api/locale-check/main.js @@ -1,8 +1,8 @@ -const { app } = require('electron') +const { app } = require('electron'); app.whenReady().then(() => { - process.stdout.write(app.getLocale()) - process.stdout.end() + process.stdout.write(app.getLocale()); + process.stdout.end(); - app.quit() -}) + app.quit(); +}); diff --git a/spec/fixtures/api/mixed-sandbox-app/electron-app-mixed-sandbox-preload.js b/spec/fixtures/api/mixed-sandbox-app/electron-app-mixed-sandbox-preload.js index abe0eeea87edc..fb2d6a30bcf3d 100644 --- a/spec/fixtures/api/mixed-sandbox-app/electron-app-mixed-sandbox-preload.js +++ b/spec/fixtures/api/mixed-sandbox-app/electron-app-mixed-sandbox-preload.js @@ -1 +1 @@ -require('electron').ipcRenderer.send('argv', process.argv) +require('electron').ipcRenderer.send('argv', process.argv); diff --git a/spec/fixtures/api/mixed-sandbox-app/main.js b/spec/fixtures/api/mixed-sandbox-app/main.js index 86cacad01e136..bb61f2b628c6c 100644 --- a/spec/fixtures/api/mixed-sandbox-app/main.js +++ b/spec/fixtures/api/mixed-sandbox-app/main.js @@ -1,40 +1,40 @@ -const { app, BrowserWindow, ipcMain } = require('electron') -const net = require('net') -const path = require('path') +const { app, BrowserWindow, ipcMain } = require('electron'); +const net = require('net'); +const path = require('path'); process.on('uncaughtException', () => { - app.exit(1) -}) + app.exit(1); +}); if (process.argv.includes('--app-enable-sandbox')) { - app.enableSandbox() + app.enableSandbox(); } -let currentWindowSandboxed = false +let currentWindowSandboxed = false; app.whenReady().then(() => { function testWindow (isSandboxed, callback) { - currentWindowSandboxed = isSandboxed + currentWindowSandboxed = isSandboxed; const currentWindow = new BrowserWindow({ show: false, webPreferences: { preload: path.join(__dirname, 'electron-app-mixed-sandbox-preload.js'), sandbox: isSandboxed } - }) - currentWindow.loadURL('about:blank') + }); + currentWindow.loadURL('about:blank'); currentWindow.webContents.once('devtools-opened', () => { if (isSandboxed) { - argv.sandboxDevtools = true + argv.sandboxDevtools = true; } else { - argv.noSandboxDevtools = true + argv.noSandboxDevtools = true; } if (callback) { - callback() + callback(); } - finish() - }) - currentWindow.webContents.openDevTools() + finish(); + }); + currentWindow.webContents.openDevTools(); } const argv = { @@ -42,36 +42,36 @@ app.whenReady().then(() => { noSandbox: null, sandboxDevtools: null, noSandboxDevtools: null - } + }; - let connected = false + let connected = false; testWindow(true, () => { - testWindow() - }) + testWindow(); + }); function finish () { if (connected && argv.sandbox != null && argv.noSandbox != null && argv.noSandboxDevtools != null && argv.sandboxDevtools != null) { client.once('end', () => { - app.exit(0) - }) - client.end(JSON.stringify(argv)) + app.exit(0); + }); + client.end(JSON.stringify(argv)); } } - const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox' + const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox'; const client = net.connect(socketPath, () => { - connected = true - finish() - }) + connected = true; + finish(); + }); ipcMain.on('argv', (event, value) => { if (currentWindowSandboxed) { - argv.sandbox = value + argv.sandbox = value; } else { - argv.noSandbox = value + argv.noSandbox = value; } - finish() - }) -}) + finish(); + }); +}); diff --git a/spec/fixtures/api/native-window-open-isolated-preload.js b/spec/fixtures/api/native-window-open-isolated-preload.js index 9491e4efcc465..de5d41564b42f 100644 --- a/spec/fixtures/api/native-window-open-isolated-preload.js +++ b/spec/fixtures/api/native-window-open-isolated-preload.js @@ -1,5 +1,5 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.addEventListener('message', (event) => { - ipcRenderer.send('answer', event.data) -}) + ipcRenderer.send('answer', event.data); +}); diff --git a/spec/fixtures/api/new-window-preload.js b/spec/fixtures/api/new-window-preload.js index cd681ac12a798..46b6cce25bdce 100644 --- a/spec/fixtures/api/new-window-preload.js +++ b/spec/fixtures/api/new-window-preload.js @@ -1,4 +1,4 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); -ipcRenderer.send('answer', process.argv) -window.close() +ipcRenderer.send('answer', process.argv); +window.close(); diff --git a/spec/fixtures/api/new-window-webview-preload.js b/spec/fixtures/api/new-window-webview-preload.js index ba5d5ded82db1..1336da90a9fa9 100644 --- a/spec/fixtures/api/new-window-webview-preload.js +++ b/spec/fixtures/api/new-window-webview-preload.js @@ -1,3 +1,3 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); -window.ipcRenderer = ipcRenderer +window.ipcRenderer = ipcRenderer; diff --git a/spec/fixtures/api/quit-app/main.js b/spec/fixtures/api/quit-app/main.js index 7f4321aefe898..1df2b675fac13 100644 --- a/spec/fixtures/api/quit-app/main.js +++ b/spec/fixtures/api/quit-app/main.js @@ -1,12 +1,12 @@ -const { app } = require('electron') +const { app } = require('electron'); app.whenReady().then(function () { // This setImmediate call gets the spec passing on Linux setImmediate(function () { - app.exit(123) - }) -}) + app.exit(123); + }); +}); process.on('exit', function (code) { - console.log('Exit event with code: ' + code) -}) + console.log('Exit event with code: ' + code); +}); diff --git a/spec/fixtures/api/relaunch/main.js b/spec/fixtures/api/relaunch/main.js index c149ef78c563c..48da277383c3c 100644 --- a/spec/fixtures/api/relaunch/main.js +++ b/spec/fixtures/api/relaunch/main.js @@ -1,23 +1,23 @@ -const { app } = require('electron') -const net = require('net') +const { app } = require('electron'); +const net = require('net'); -const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch' +const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'; process.on('uncaughtException', () => { - app.exit(1) -}) + app.exit(1); +}); app.whenReady().then(() => { - const lastArg = process.argv[process.argv.length - 1] - const client = net.connect(socketPath) + const lastArg = process.argv[process.argv.length - 1]; + const client = net.connect(socketPath); client.once('connect', () => { - client.end(String(lastArg === '--second')) - }) + client.end(String(lastArg === '--second')); + }); client.once('end', () => { - app.exit(0) - }) + app.exit(0); + }); if (lastArg !== '--second') { - app.relaunch({ args: process.argv.slice(1).concat('--second') }) + app.relaunch({ args: process.argv.slice(1).concat('--second') }); } -}) +}); diff --git a/spec/fixtures/api/shared-worker/shared-worker1.js b/spec/fixtures/api/shared-worker/shared-worker1.js index 468144a9d02e5..f749b244662b9 100644 --- a/spec/fixtures/api/shared-worker/shared-worker1.js +++ b/spec/fixtures/api/shared-worker/shared-worker1.js @@ -1,4 +1,4 @@ self.onconnect = function (e) { - const port = e.ports[0] - port.postMessage('ready') -} + const port = e.ports[0]; + port.postMessage('ready'); +}; diff --git a/spec/fixtures/api/shared-worker/shared-worker2.js b/spec/fixtures/api/shared-worker/shared-worker2.js index 468144a9d02e5..f749b244662b9 100644 --- a/spec/fixtures/api/shared-worker/shared-worker2.js +++ b/spec/fixtures/api/shared-worker/shared-worker2.js @@ -1,4 +1,4 @@ self.onconnect = function (e) { - const port = e.ports[0] - port.postMessage('ready') -} + const port = e.ports[0]; + port.postMessage('ready'); +}; diff --git a/spec/fixtures/api/singleton/main.js b/spec/fixtures/api/singleton/main.js index cdd374bdb6a84..81660003b4ac4 100644 --- a/spec/fixtures/api/singleton/main.js +++ b/spec/fixtures/api/singleton/main.js @@ -1,18 +1,18 @@ -const { app } = require('electron') +const { app } = require('electron'); app.whenReady().then(() => { - console.log('started') // ping parent -}) + console.log('started'); // ping parent +}); -const gotTheLock = app.requestSingleInstanceLock() +const gotTheLock = app.requestSingleInstanceLock(); app.on('second-instance', (event, args) => { setImmediate(() => { - console.log(JSON.stringify(args)) - app.exit(0) - }) -}) + console.log(JSON.stringify(args)); + app.exit(0); + }); +}); if (!gotTheLock) { - app.exit(1) + app.exit(1); } diff --git a/spec/fixtures/api/site-instance-overrides/main.js b/spec/fixtures/api/site-instance-overrides/main.js index 5ce1d0f22eba8..76fe316b8a6ed 100644 --- a/spec/fixtures/api/site-instance-overrides/main.js +++ b/spec/fixtures/api/site-instance-overrides/main.js @@ -1,28 +1,28 @@ -const { app, BrowserWindow, ipcMain } = require('electron') -const path = require('path') +const { app, BrowserWindow, ipcMain } = require('electron'); +const path = require('path'); -process.noDeprecation = true +process.noDeprecation = true; process.on('uncaughtException', (e) => { - console.error(e) - process.exit(1) -}) + console.error(e); + process.exit(1); +}); -app.allowRendererProcessReuse = JSON.parse(process.argv[2]) +app.allowRendererProcessReuse = JSON.parse(process.argv[2]); -const pids = [] -let win +const pids = []; +let win; ipcMain.on('pid', (event, pid) => { - pids.push(pid) + pids.push(pid); if (pids.length === 2) { - console.log(JSON.stringify(pids)) - if (win) win.close() - app.quit() + console.log(JSON.stringify(pids)); + if (win) win.close(); + app.quit(); } else { - if (win) win.reload() + if (win) win.reload(); } -}) +}); app.whenReady().then(() => { win = new BrowserWindow({ @@ -30,6 +30,6 @@ app.whenReady().then(() => { webPreferences: { preload: path.resolve(__dirname, 'preload.js') } - }) - win.loadFile('index.html') -}) + }); + win.loadFile('index.html'); +}); diff --git a/spec/fixtures/api/site-instance-overrides/preload.js b/spec/fixtures/api/site-instance-overrides/preload.js index 721369deb3449..cfe37266b5e8b 100644 --- a/spec/fixtures/api/site-instance-overrides/preload.js +++ b/spec/fixtures/api/site-instance-overrides/preload.js @@ -1,3 +1,3 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); -ipcRenderer.send('pid', process.pid) +ipcRenderer.send('pid', process.pid); diff --git a/spec/fixtures/api/window-all-closed/main.js b/spec/fixtures/api/window-all-closed/main.js index 9e2fcdfa1bb7c..c19dc483b54ce 100644 --- a/spec/fixtures/api/window-all-closed/main.js +++ b/spec/fixtures/api/window-all-closed/main.js @@ -1,20 +1,20 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron'); -let handled = false +let handled = false; if (app.commandLine.hasSwitch('handle-event')) { app.on('window-all-closed', () => { - handled = true - app.quit() - }) + handled = true; + app.quit(); + }); } app.on('quit', () => { - process.stdout.write(JSON.stringify(handled)) - process.stdout.end() -}) + process.stdout.write(JSON.stringify(handled)); + process.stdout.end(); +}); app.whenReady().then(() => { - const win = new BrowserWindow() - win.close() -}) + const win = new BrowserWindow(); + win.close(); +}); diff --git a/spec/fixtures/api/window-open-preload.js b/spec/fixtures/api/window-open-preload.js index d86205a5b0e88..035a0bcfcc0f8 100644 --- a/spec/fixtures/api/window-open-preload.js +++ b/spec/fixtures/api/window-open-preload.js @@ -1,9 +1,9 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); setImmediate(function () { if (window.location.toString() === 'bar://page/') { - const windowOpenerIsNull = window.opener == null - ipcRenderer.send('answer', process.argv, typeof global.process, windowOpenerIsNull) - window.close() + const windowOpenerIsNull = window.opener == null; + ipcRenderer.send('answer', process.argv, typeof global.process, windowOpenerIsNull); + window.close(); } -}) +}); diff --git a/spec/fixtures/module/access-blink-apis.js b/spec/fixtures/module/access-blink-apis.js index 19f2d51cc8712..625451dac3b52 100644 --- a/spec/fixtures/module/access-blink-apis.js +++ b/spec/fixtures/module/access-blink-apis.js @@ -1,17 +1,17 @@ -window.delayed = true +window.delayed = true; global.getGlobalNames = () => { return Object.getOwnPropertyNames(global) .filter(key => typeof global[key] === 'function') .filter(key => key !== 'WebView') - .sort() -} + .sort(); +}; -const atPreload = global.getGlobalNames() +const atPreload = global.getGlobalNames(); window.addEventListener('load', () => { window.test = { atPreload, atLoad: global.getGlobalNames() - } -}) + }; +}); diff --git a/spec/fixtures/module/asar.js b/spec/fixtures/module/asar.js index 624daaeaf7a28..6a973ad386045 100644 --- a/spec/fixtures/module/asar.js +++ b/spec/fixtures/module/asar.js @@ -1,4 +1,4 @@ -const fs = require('fs') +const fs = require('fs'); process.on('message', function (file) { - process.send(fs.readFileSync(file).toString()) -}) + process.send(fs.readFileSync(file).toString()); +}); diff --git a/spec/fixtures/module/check-arguments.js b/spec/fixtures/module/check-arguments.js index 96f788e5b1a44..8a5ef8dde197d 100644 --- a/spec/fixtures/module/check-arguments.js +++ b/spec/fixtures/module/check-arguments.js @@ -1,4 +1,4 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.onload = function () { - ipcRenderer.send('answer', process.argv) -} + ipcRenderer.send('answer', process.argv); +}; diff --git a/spec/fixtures/module/crash.js b/spec/fixtures/module/crash.js index 2e15f1c6b19bb..a550fc4649fa6 100644 --- a/spec/fixtures/module/crash.js +++ b/spec/fixtures/module/crash.js @@ -8,12 +8,12 @@ process.crashReporter.start({ extra2: 'extra2', _version: process.argv[3] } -}) +}); if (process.platform !== 'linux') { - process.crashReporter.addExtraParameter('newExtra', 'newExtra') - process.crashReporter.addExtraParameter('removeExtra', 'removeExtra') - process.crashReporter.removeExtraParameter('removeExtra') + process.crashReporter.addExtraParameter('newExtra', 'newExtra'); + process.crashReporter.addExtraParameter('removeExtra', 'removeExtra'); + process.crashReporter.removeExtraParameter('removeExtra'); } -process.nextTick(() => process.crash()) +process.nextTick(() => process.crash()); diff --git a/spec/fixtures/module/create_socket.js b/spec/fixtures/module/create_socket.js index 5e1f49255d504..d5eca125a541e 100644 --- a/spec/fixtures/module/create_socket.js +++ b/spec/fixtures/module/create_socket.js @@ -1,4 +1,4 @@ -const net = require('net') -const server = net.createServer(function () {}) -server.listen(process.argv[2]) -process.exit(0) +const net = require('net'); +const server = net.createServer(function () {}); +server.listen(process.argv[2]); +process.exit(0); diff --git a/spec/fixtures/module/delay-exit.js b/spec/fixtures/module/delay-exit.js index fa7895449b32f..8fbe9d64f68d7 100644 --- a/spec/fixtures/module/delay-exit.js +++ b/spec/fixtures/module/delay-exit.js @@ -1,3 +1,3 @@ -const { app } = require('electron') +const { app } = require('electron'); -process.on('message', () => app.quit()) +process.on('message', () => app.quit()); diff --git a/spec/fixtures/module/empty.js b/spec/fixtures/module/empty.js index 3f0e6f4f0707d..7dafbcc94f339 100644 --- a/spec/fixtures/module/empty.js +++ b/spec/fixtures/module/empty.js @@ -1,5 +1,5 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.addEventListener('message', (event) => { - ipcRenderer.send('leak-result', event.data) -}) + ipcRenderer.send('leak-result', event.data); +}); diff --git a/spec/fixtures/module/fork_ping.js b/spec/fixtures/module/fork_ping.js index a2c8782600ec1..e9b28bde1d61c 100644 --- a/spec/fixtures/module/fork_ping.js +++ b/spec/fixtures/module/fork_ping.js @@ -1,16 +1,16 @@ -const path = require('path') +const path = require('path'); process.on('uncaughtException', function (error) { - process.send(error.stack) -}) + process.send(error.stack); +}); -const child = require('child_process').fork(path.join(__dirname, '/ping.js')) +const child = require('child_process').fork(path.join(__dirname, '/ping.js')); process.on('message', function (msg) { - child.send(msg) -}) + child.send(msg); +}); child.on('message', function (msg) { - process.send(msg) -}) + process.send(msg); +}); child.on('exit', function (code) { - process.exit(code) -}) + process.exit(code); +}); diff --git a/spec/fixtures/module/get-global-preload.js b/spec/fixtures/module/get-global-preload.js index c1010dd472b2b..1f02aa132bc56 100644 --- a/spec/fixtures/module/get-global-preload.js +++ b/spec/fixtures/module/get-global-preload.js @@ -1 +1 @@ -require('electron').ipcRenderer.send('vars', window.preload1, window.preload2, window.preload3) +require('electron').ipcRenderer.send('vars', window.preload1, window.preload2, window.preload3); diff --git a/spec/fixtures/module/hello-child.js b/spec/fixtures/module/hello-child.js index 09ac18900e162..a40ab2165751b 100644 --- a/spec/fixtures/module/hello-child.js +++ b/spec/fixtures/module/hello-child.js @@ -1,6 +1,6 @@ class Hello { say () { - return 'hi child window' + return 'hi child window'; } } -module.exports = Hello +module.exports = Hello; diff --git a/spec/fixtures/module/hello.js b/spec/fixtures/module/hello.js index 9debd60618ecc..49d2cb827ef29 100644 --- a/spec/fixtures/module/hello.js +++ b/spec/fixtures/module/hello.js @@ -1,6 +1,6 @@ class Hello { say () { - return 'hi' + return 'hi'; } } -module.exports = Hello +module.exports = Hello; diff --git a/spec/fixtures/module/inspector-binding.js b/spec/fixtures/module/inspector-binding.js index c91ee8d9fcb88..64a5986e8c75a 100644 --- a/spec/fixtures/module/inspector-binding.js +++ b/spec/fixtures/module/inspector-binding.js @@ -1,18 +1,18 @@ -const inspector = require('inspector') -const path = require('path') -const { pathToFileURL } = require('url') +const inspector = require('inspector'); +const path = require('path'); +const { pathToFileURL } = require('url'); // This test case will set a breakpoint 4 lines below function debuggedFunction () { - let i - let accum = 0 + let i; + let accum = 0; for (i = 0; i < 5; i++) { - accum += i + accum += i; } - return accum + return accum; } -let scopeCallback = null +let scopeCallback = null; function checkScope (session, scopeId) { session.post('Runtime.getProperties', { @@ -20,57 +20,57 @@ function checkScope (session, scopeId) { ownProperties: false, accessorPropertiesOnly: false, generatePreview: true - }, scopeCallback) + }, scopeCallback); } function debuggerPausedCallback (session, notification) { - const params = notification.params - const callFrame = params.callFrames[0] - const scopeId = callFrame.scopeChain[0].object.objectId - checkScope(session, scopeId) + const params = notification.params; + const callFrame = params.callFrames[0]; + const scopeId = callFrame.scopeChain[0].object.objectId; + checkScope(session, scopeId); } function testSampleDebugSession () { - let cur = 0 - const failures = [] + let cur = 0; + const failures = []; const expects = { i: [0, 1, 2, 3, 4], accum: [0, 0, 1, 3, 6] - } + }; scopeCallback = function (error, result) { - if (error) failures.push(error) - const i = cur++ - let v, actual, expected + if (error) failures.push(error); + const i = cur++; + let v, actual, expected; for (v of result.result) { - actual = v.value.value - expected = expects[v.name][i] + actual = v.value.value; + expected = expects[v.name][i]; if (actual !== expected) { failures.push(`Iteration ${i} variable: ${v.name} ` + - `expected: ${expected} actual: ${actual}`) + `expected: ${expected} actual: ${actual}`); } } - } - const session = new inspector.Session() - session.connect() + }; + const session = new inspector.Session(); + session.connect(); session.on('Debugger.paused', - (notification) => debuggerPausedCallback(session, notification)) - let cbAsSecondArgCalled = false - session.post('Debugger.enable', () => { cbAsSecondArgCalled = true }) + (notification) => debuggerPausedCallback(session, notification)); + let cbAsSecondArgCalled = false; + session.post('Debugger.enable', () => { cbAsSecondArgCalled = true; }); session.post('Debugger.setBreakpointByUrl', { lineNumber: 9, url: pathToFileURL(path.resolve(__dirname, __filename)).toString(), columnNumber: 0, condition: '' - }) + }); - debuggedFunction() - scopeCallback = null - session.disconnect() + debuggedFunction(); + scopeCallback = null; + session.disconnect(); process.send({ cmd: 'assert', debuggerEnabled: cbAsSecondArgCalled, success: (cur === 5) && (failures.length === 0) - }) + }); } -testSampleDebugSession() +testSampleDebugSession(); diff --git a/spec/fixtures/module/isolated-ping.js b/spec/fixtures/module/isolated-ping.js index 7088d346c8ef1..90392e46fe4de 100644 --- a/spec/fixtures/module/isolated-ping.js +++ b/spec/fixtures/module/isolated-ping.js @@ -1,2 +1,2 @@ -const { ipcRenderer } = require('electron') -ipcRenderer.send('pong') +const { ipcRenderer } = require('electron'); +ipcRenderer.send('pong'); diff --git a/spec/fixtures/module/locale-compare.js b/spec/fixtures/module/locale-compare.js index 4027540044347..48c4ebbdc8a47 100644 --- a/spec/fixtures/module/locale-compare.js +++ b/spec/fixtures/module/locale-compare.js @@ -3,5 +3,5 @@ process.on('message', function () { 'a'.localeCompare('a'), 'ä'.localeCompare('z', 'de'), 'ä'.localeCompare('a', 'sv', { sensitivity: 'base' }) - ]) -}) + ]); +}); diff --git a/spec/fixtures/module/no-asar.js b/spec/fixtures/module/no-asar.js index 400a8525ffa47..8835a22c42d1e 100644 --- a/spec/fixtures/module/no-asar.js +++ b/spec/fixtures/module/no-asar.js @@ -1,15 +1,15 @@ -const fs = require('fs') -const path = require('path') +const fs = require('fs'); +const path = require('path'); -const stats = fs.statSync(path.join(__dirname, '..', 'test.asar', 'a.asar')) +const stats = fs.statSync(path.join(__dirname, '..', 'test.asar', 'a.asar')); const details = { isFile: stats.isFile(), size: stats.size -} +}; if (process.send != null) { - process.send(details) + process.send(details); } else { - console.log(JSON.stringify(details)) + console.log(JSON.stringify(details)); } diff --git a/spec/fixtures/module/noop.js b/spec/fixtures/module/noop.js index 23d7e9991574b..dcbbff6c93458 100644 --- a/spec/fixtures/module/noop.js +++ b/spec/fixtures/module/noop.js @@ -1 +1 @@ -process.exit(0) +process.exit(0); diff --git a/spec/fixtures/module/original-fs.js b/spec/fixtures/module/original-fs.js index 341dcb2e0de80..7a527c6335853 100644 --- a/spec/fixtures/module/original-fs.js +++ b/spec/fixtures/module/original-fs.js @@ -1,3 +1,3 @@ process.on('message', function () { - process.send(typeof require('original-fs')) -}) + process.send(typeof require('original-fs')); +}); diff --git a/spec/fixtures/module/ping.js b/spec/fixtures/module/ping.js index 90b3d1fb20a18..7479ac7419fa6 100644 --- a/spec/fixtures/module/ping.js +++ b/spec/fixtures/module/ping.js @@ -1,4 +1,4 @@ process.on('message', function (msg) { - process.send(msg) - process.exit(0) -}) + process.send(msg); + process.exit(0); +}); diff --git a/spec/fixtures/module/preload-context.js b/spec/fixtures/module/preload-context.js index 3d3f8bc9755cc..4dbc3a9a58d3b 100644 --- a/spec/fixtures/module/preload-context.js +++ b/spec/fixtures/module/preload-context.js @@ -5,6 +5,6 @@ const types = { electron: typeof electron, window: typeof window, localVar: typeof window.test -} +}; -console.log(JSON.stringify(types)) +console.log(JSON.stringify(types)); diff --git a/spec/fixtures/module/preload-disable-remote.js b/spec/fixtures/module/preload-disable-remote.js index 008acec7b5d60..9b6b96cbf28f5 100644 --- a/spec/fixtures/module/preload-disable-remote.js +++ b/spec/fixtures/module/preload-disable-remote.js @@ -1,8 +1,8 @@ setImmediate(function () { try { - const { remote } = require('electron') - console.log(JSON.stringify(typeof remote)) + const { remote } = require('electron'); + console.log(JSON.stringify(typeof remote)); } catch (e) { - console.log(e.message) + console.log(e.message); } -}) +}); diff --git a/spec/fixtures/module/preload-error-exception.js b/spec/fixtures/module/preload-error-exception.js index 710907d35a33a..ec3582c249628 100644 --- a/spec/fixtures/module/preload-error-exception.js +++ b/spec/fixtures/module/preload-error-exception.js @@ -1 +1 @@ -throw new Error('Hello World!') +throw new Error('Hello World!'); diff --git a/spec/fixtures/module/preload-ipc-ping-pong.js b/spec/fixtures/module/preload-ipc-ping-pong.js index 6ea0b32fdad95..41d4c75382230 100644 --- a/spec/fixtures/module/preload-ipc-ping-pong.js +++ b/spec/fixtures/module/preload-ipc-ping-pong.js @@ -1,9 +1,9 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); ipcRenderer.on('ping', function (event, payload) { - ipcRenderer.sendTo(event.senderId, 'pong', payload) -}) + ipcRenderer.sendTo(event.senderId, 'pong', payload); +}); ipcRenderer.on('ping-æøåü', function (event, payload) { - ipcRenderer.sendTo(event.senderId, 'pong-æøåü', payload) -}) + ipcRenderer.sendTo(event.senderId, 'pong-æøåü', payload); +}); diff --git a/spec/fixtures/module/preload-ipc.js b/spec/fixtures/module/preload-ipc.js index 3f97d1a56b3bb..390fa920dfa09 100644 --- a/spec/fixtures/module/preload-ipc.js +++ b/spec/fixtures/module/preload-ipc.js @@ -1,4 +1,4 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); ipcRenderer.on('ping', function (event, message) { - ipcRenderer.sendToHost('pong', message) -}) + ipcRenderer.sendToHost('pong', message); +}); diff --git a/spec/fixtures/module/preload-node-off-wrapper.js b/spec/fixtures/module/preload-node-off-wrapper.js index dbe1330adcf3c..614f0db1c68bd 100644 --- a/spec/fixtures/module/preload-node-off-wrapper.js +++ b/spec/fixtures/module/preload-node-off-wrapper.js @@ -1,3 +1,3 @@ setImmediate(function () { - require('./preload-required-module') -}) + require('./preload-required-module'); +}); diff --git a/spec/fixtures/module/preload-node-off.js b/spec/fixtures/module/preload-node-off.js index 65db2a7e4dc05..0b21b2ae32d5d 100644 --- a/spec/fixtures/module/preload-node-off.js +++ b/spec/fixtures/module/preload-node-off.js @@ -5,9 +5,9 @@ setImmediate(function () { setImmediate: typeof setImmediate, global: typeof global, Buffer: typeof Buffer - } - console.log(JSON.stringify(types)) + }; + console.log(JSON.stringify(types)); } catch (e) { - console.log(e.message) + console.log(e.message); } -}) +}); diff --git a/spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js b/spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js index 3e631a7ffb64c..e72a4a4058db4 100644 --- a/spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js +++ b/spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js @@ -1,15 +1,15 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); document.addEventListener('DOMContentLoaded', (event) => { - const outerFrame = document.querySelector('#outer-frame') + const outerFrame = document.querySelector('#outer-frame'); if (outerFrame) { outerFrame.onload = function () { - const pdframe = outerFrame.contentWindow.document.getElementById('pdf-frame') + const pdframe = outerFrame.contentWindow.document.getElementById('pdf-frame'); if (pdframe) { pdframe.contentWindow.addEventListener('pdf-loaded', (event) => { - ipcRenderer.send('pdf-loaded', event.detail) - }) + ipcRenderer.send('pdf-loaded', event.detail); + }); } - } + }; } -}) +}); diff --git a/spec/fixtures/module/preload-pdf-loaded-in-subframe.js b/spec/fixtures/module/preload-pdf-loaded-in-subframe.js index 40ba24a4d958c..dd7a7aaa42d6d 100644 --- a/spec/fixtures/module/preload-pdf-loaded-in-subframe.js +++ b/spec/fixtures/module/preload-pdf-loaded-in-subframe.js @@ -1,10 +1,10 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); document.addEventListener('DOMContentLoaded', (event) => { - const subframe = document.querySelector('#pdf-frame') + const subframe = document.querySelector('#pdf-frame'); if (subframe) { subframe.contentWindow.addEventListener('pdf-loaded', (event) => { - ipcRenderer.send('pdf-loaded', event.detail) - }) + ipcRenderer.send('pdf-loaded', event.detail); + }); } -}) +}); diff --git a/spec/fixtures/module/preload-pdf-loaded.js b/spec/fixtures/module/preload-pdf-loaded.js index 9393898b50846..aa5c8fb4ffac6 100644 --- a/spec/fixtures/module/preload-pdf-loaded.js +++ b/spec/fixtures/module/preload-pdf-loaded.js @@ -1,5 +1,5 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.addEventListener('pdf-loaded', function (event) { - ipcRenderer.send('pdf-loaded', event.detail) -}) + ipcRenderer.send('pdf-loaded', event.detail); +}); diff --git a/spec/fixtures/module/preload-required-module.js b/spec/fixtures/module/preload-required-module.js index 75b2bf4add880..23ff1780021cd 100644 --- a/spec/fixtures/module/preload-required-module.js +++ b/spec/fixtures/module/preload-required-module.js @@ -5,8 +5,8 @@ try { global: typeof global, Buffer: typeof Buffer, 'global.Buffer': typeof global.Buffer - } - console.log(JSON.stringify(types)) + }; + console.log(JSON.stringify(types)); } catch (e) { - console.log(e.message) + console.log(e.message); } diff --git a/spec/fixtures/module/preload-sandbox.js b/spec/fixtures/module/preload-sandbox.js index d62e0e402abbc..524771046a0eb 100644 --- a/spec/fixtures/module/preload-sandbox.js +++ b/spec/fixtures/module/preload-sandbox.js @@ -1,25 +1,25 @@ (function () { - const { setImmediate } = require('timers') - const { ipcRenderer } = require('electron') - window.ipcRenderer = ipcRenderer - window.setImmediate = setImmediate - window.require = require + const { setImmediate } = require('timers'); + const { ipcRenderer } = require('electron'); + window.ipcRenderer = ipcRenderer; + window.setImmediate = setImmediate; + window.require = require; function invoke (code) { try { - return code() + return code(); } catch { - return null + return null; } } process.once('loaded', () => { - ipcRenderer.send('process-loaded') - }) + ipcRenderer.send('process-loaded'); + }); if (location.protocol === 'file:') { - window.test = 'preload' - window.process = process + window.test = 'preload'; + window.process = process; if (process.env.sandboxmain) { window.test = { osSandbox: !process.argv.includes('--no-sandbox'), @@ -42,20 +42,20 @@ type: process.type, version: process.version, versions: process.versions - } + }; } } else if (location.href !== 'about:blank') { addEventListener('DOMContentLoaded', () => { ipcRenderer.on('touch-the-opener', () => { - let errorMessage = null + let errorMessage = null; try { - const openerDoc = opener.document // eslint-disable-line no-unused-vars + const openerDoc = opener.document; // eslint-disable-line no-unused-vars } catch (error) { - errorMessage = error.message + errorMessage = error.message; } - ipcRenderer.send('answer', errorMessage) - }) - ipcRenderer.send('child-loaded', window.opener == null, document.body.innerHTML, location.href) - }) + ipcRenderer.send('answer', errorMessage); + }); + ipcRenderer.send('child-loaded', window.opener == null, document.body.innerHTML, location.href); + }); } -})() +})(); diff --git "a/spec/fixtures/module/preload-sandbox\303\246\303\270 \303\245\303\274.js" "b/spec/fixtures/module/preload-sandbox\303\246\303\270 \303\245\303\274.js" index 3799c8e69c7af..596e9ca4c12ea 100644 --- "a/spec/fixtures/module/preload-sandbox\303\246\303\270 \303\245\303\274.js" +++ "b/spec/fixtures/module/preload-sandbox\303\246\303\270 \303\245\303\274.js" @@ -1,6 +1,6 @@ (function () { - window.require = require + window.require = require; if (location.protocol === 'file:') { - window.test = 'preload' + window.test = 'preload'; } -})() +})(); diff --git a/spec/fixtures/module/preload-set-global.js b/spec/fixtures/module/preload-set-global.js index 6737b06982dc8..ff1dff5e793a6 100644 --- a/spec/fixtures/module/preload-set-global.js +++ b/spec/fixtures/module/preload-set-global.js @@ -1 +1 @@ -window.foo = 'bar' +window.foo = 'bar'; diff --git a/spec/fixtures/module/preload-webview.js b/spec/fixtures/module/preload-webview.js index b1386a95280c9..1789551556b57 100644 --- a/spec/fixtures/module/preload-webview.js +++ b/spec/fixtures/module/preload-webview.js @@ -1,5 +1,5 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.onload = function () { - ipcRenderer.send('webview', typeof WebView) -} + ipcRenderer.send('webview', typeof WebView); +}; diff --git a/spec/fixtures/module/preload.js b/spec/fixtures/module/preload.js index 6b77bde032940..aa7bba4cae8f0 100644 --- a/spec/fixtures/module/preload.js +++ b/spec/fixtures/module/preload.js @@ -3,5 +3,5 @@ const types = { module: typeof module, process: typeof process, Buffer: typeof Buffer -} -console.log(JSON.stringify(types)) +}; +console.log(JSON.stringify(types)); diff --git a/spec/fixtures/module/process-stdout.js b/spec/fixtures/module/process-stdout.js index 953750a247f99..f45b5d60408d3 100644 --- a/spec/fixtures/module/process-stdout.js +++ b/spec/fixtures/module/process-stdout.js @@ -1 +1 @@ -process.stdout.write('pipes stdio') +process.stdout.write('pipes stdio'); diff --git a/spec/fixtures/module/process_args.js b/spec/fixtures/module/process_args.js index 56e3906c55345..88191450f1cb3 100644 --- a/spec/fixtures/module/process_args.js +++ b/spec/fixtures/module/process_args.js @@ -1,4 +1,4 @@ process.on('message', function () { - process.send(process.argv) - process.exit(0) -}) + process.send(process.argv); + process.exit(0); +}); diff --git a/spec/fixtures/module/run-as-node.js b/spec/fixtures/module/run-as-node.js index 20812f0463eb5..4d033c7e8d196 100644 --- a/spec/fixtures/module/run-as-node.js +++ b/spec/fixtures/module/run-as-node.js @@ -2,4 +2,4 @@ console.log(JSON.stringify({ processLog: typeof process.log, processType: typeof process.type, window: typeof window -})) +})); diff --git a/spec/fixtures/module/send-later.js b/spec/fixtures/module/send-later.js index 34250aedce3fb..5b4a22097275c 100644 --- a/spec/fixtures/module/send-later.js +++ b/spec/fixtures/module/send-later.js @@ -1,4 +1,4 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.onload = function () { - ipcRenderer.send('answer', typeof window.process, typeof window.Buffer) -} + ipcRenderer.send('answer', typeof window.process, typeof window.Buffer); +}; diff --git a/spec/fixtures/module/set-global-preload-1.js b/spec/fixtures/module/set-global-preload-1.js index 22dfdf918506e..92e8741de1771 100644 --- a/spec/fixtures/module/set-global-preload-1.js +++ b/spec/fixtures/module/set-global-preload-1.js @@ -1 +1 @@ -window.preload1 = 'preload-1' +window.preload1 = 'preload-1'; diff --git a/spec/fixtures/module/set-global-preload-2.js b/spec/fixtures/module/set-global-preload-2.js index 7542009f7b09a..af100270d070c 100644 --- a/spec/fixtures/module/set-global-preload-2.js +++ b/spec/fixtures/module/set-global-preload-2.js @@ -1 +1 @@ -window.preload2 = window.preload1 + '-2' +window.preload2 = window.preload1 + '-2'; diff --git a/spec/fixtures/module/set-global-preload-3.js b/spec/fixtures/module/set-global-preload-3.js index 9cfef949277ed..8491be46727cf 100644 --- a/spec/fixtures/module/set-global-preload-3.js +++ b/spec/fixtures/module/set-global-preload-3.js @@ -1 +1 @@ -window.preload3 = window.preload2 + '-3' +window.preload3 = window.preload2 + '-3'; diff --git a/spec/fixtures/module/set-global.js b/spec/fixtures/module/set-global.js index 5ad98817e0bd5..c63ed6cf4486f 100644 --- a/spec/fixtures/module/set-global.js +++ b/spec/fixtures/module/set-global.js @@ -1 +1 @@ -if (!window.test) window.test = 'preload' +if (!window.test) window.test = 'preload'; diff --git a/spec/fixtures/module/set-immediate.js b/spec/fixtures/module/set-immediate.js index 69563fd0a832f..263801c3b9bc4 100644 --- a/spec/fixtures/module/set-immediate.js +++ b/spec/fixtures/module/set-immediate.js @@ -1,11 +1,11 @@ process.on('uncaughtException', function (error) { - process.send(error.message) - process.exit(1) -}) + process.send(error.message); + process.exit(1); +}); process.on('message', function () { setImmediate(function () { - process.send('ok') - process.exit(0) - }) -}) + process.send('ok'); + process.exit(0); + }); +}); diff --git a/spec/fixtures/no-proprietary-codecs.js b/spec/fixtures/no-proprietary-codecs.js index 8fc08c4237704..0522847bb3ac1 100644 --- a/spec/fixtures/no-proprietary-codecs.js +++ b/spec/fixtures/no-proprietary-codecs.js @@ -4,13 +4,13 @@ // proprietary codecs to ensure Electron uses it instead of the version // that does include proprietary codecs. -const { app, BrowserWindow, ipcMain } = require('electron') -const path = require('path') +const { app, BrowserWindow, ipcMain } = require('electron'); +const path = require('path'); -const MEDIA_ERR_SRC_NOT_SUPPORTED = 4 -const FIVE_MINUTES = 5 * 60 * 1000 +const MEDIA_ERR_SRC_NOT_SUPPORTED = 4; +const FIVE_MINUTES = 5 * 60 * 1000; -let window +let window; app.whenReady().then(() => { window = new BrowserWindow({ @@ -18,33 +18,33 @@ app.whenReady().then(() => { webPreferences: { nodeIntegration: true } - }) + }); window.webContents.on('crashed', (event, killed) => { - console.log(`WebContents crashed (killed=${killed})`) - app.exit(1) - }) + console.log(`WebContents crashed (killed=${killed})`); + app.exit(1); + }); - window.loadFile(path.resolve(__dirname, 'test.asar', 'video.asar', 'index.html')) + window.loadFile(path.resolve(__dirname, 'test.asar', 'video.asar', 'index.html')); ipcMain.on('asar-video', (event, message, error) => { if (message === 'ended') { - console.log('Video played, proprietary codecs are included') - app.exit(1) - return + console.log('Video played, proprietary codecs are included'); + app.exit(1); + return; } if (message === 'error' && error === MEDIA_ERR_SRC_NOT_SUPPORTED) { - app.exit(0) - return + app.exit(0); + return; } - console.log(`Unexpected response from page: ${message} ${error}`) - app.exit(1) - }) + console.log(`Unexpected response from page: ${message} ${error}`); + app.exit(1); + }); setTimeout(() => { - console.log('No IPC message after 5 minutes') - app.exit(1) - }, FIVE_MINUTES) -}) + console.log('No IPC message after 5 minutes'); + app.exit(1); + }, FIVE_MINUTES); +}); diff --git a/spec/fixtures/pages/service-worker/service-worker.js b/spec/fixtures/pages/service-worker/service-worker.js index a068d74046dc0..1de4a9e4740eb 100644 --- a/spec/fixtures/pages/service-worker/service-worker.js +++ b/spec/fixtures/pages/service-worker/service-worker.js @@ -1,9 +1,9 @@ self.addEventListener('fetch', function (event) { - const requestUrl = new URL(event.request.url) + const requestUrl = new URL(event.request.url); if (requestUrl.pathname === '/echo' && event.request.headers.has('X-Mock-Response')) { - const mockResponse = new Response('Hello from serviceWorker!') - event.respondWith(mockResponse) + const mockResponse = new Response('Hello from serviceWorker!'); + event.respondWith(mockResponse); } -}) +}); diff --git a/spec/fixtures/snapshot-items-available/main.js b/spec/fixtures/snapshot-items-available/main.js index 3604a6bbfaf2e..4114a2b936231 100644 --- a/spec/fixtures/snapshot-items-available/main.js +++ b/spec/fixtures/snapshot-items-available/main.js @@ -1,26 +1,26 @@ // Verifies that objects contained in custom snapshot are accessible in Electron. -const { app } = require('electron') +const { app } = require('electron'); app.whenReady().then(() => { - let returnCode = 0 + let returnCode = 0; try { - const testValue = f() // eslint-disable-line no-undef + const testValue = f(); // eslint-disable-line no-undef if (testValue === 86) { - console.log('ok test snapshot successfully loaded.') + console.log('ok test snapshot successfully loaded.'); } else { - console.log('not ok test snapshot could not be successfully loaded.') - returnCode = 1 + console.log('not ok test snapshot could not be successfully loaded.'); + returnCode = 1; } } catch (ex) { - console.log('Error running custom snapshot', ex) - returnCode = 1 + console.log('Error running custom snapshot', ex); + returnCode = 1; } setImmediate(function () { - app.exit(returnCode) - }) -}) + app.exit(returnCode); + }); +}); process.on('exit', function (code) { - console.log('test snapshot exited with code: ' + code) -}) + console.log('test snapshot exited with code: ' + code); +}); diff --git a/spec/fixtures/testsnap.js b/spec/fixtures/testsnap.js index 394aa9f4d8028..3ac89c522a8bb 100644 --- a/spec/fixtures/testsnap.js +++ b/spec/fixtures/testsnap.js @@ -1,3 +1,3 @@ // taken from https://chromium.googlesource.com/v8/v8.git/+/HEAD/test/cctest/test-serialize.cc#1127 -function f () { return g() * 2 } // eslint-disable-line no-unused-vars -function g () { return 43 } +function f () { return g() * 2; } // eslint-disable-line no-unused-vars +function g () { return 43; } diff --git a/spec/fixtures/workers/shared_worker.js b/spec/fixtures/workers/shared_worker.js index ad0776fc07f80..eea323af4d353 100644 --- a/spec/fixtures/workers/shared_worker.js +++ b/spec/fixtures/workers/shared_worker.js @@ -1,7 +1,7 @@ this.onconnect = function (event) { - const port = event.ports[0] - port.start() + const port = event.ports[0]; + port.start(); port.onmessage = function (event) { - port.postMessage(event.data) - } -} + port.postMessage(event.data); + }; +}; diff --git a/spec/fixtures/workers/shared_worker_node.js b/spec/fixtures/workers/shared_worker_node.js index 03f4eced53b21..d9229f7c74911 100644 --- a/spec/fixtures/workers/shared_worker_node.js +++ b/spec/fixtures/workers/shared_worker_node.js @@ -1,5 +1,5 @@ self.onconnect = function (event) { - const port = event.ports[0] - port.start() - port.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')) -} + const port = event.ports[0]; + port.start(); + port.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')); +}; diff --git a/spec/fixtures/workers/worker.js b/spec/fixtures/workers/worker.js index 884c17ac86ea6..488838ceb46c3 100644 --- a/spec/fixtures/workers/worker.js +++ b/spec/fixtures/workers/worker.js @@ -1,3 +1,3 @@ this.onmessage = function (msg) { - this.postMessage(msg.data) -} + this.postMessage(msg.data); +}; diff --git a/spec/fixtures/workers/worker_node.js b/spec/fixtures/workers/worker_node.js index 5d59d2d0c3404..61f387744ec89 100644 --- a/spec/fixtures/workers/worker_node.js +++ b/spec/fixtures/workers/worker_node.js @@ -1 +1 @@ -self.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')) +self.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')); diff --git a/spec/node-spec.js b/spec/node-spec.js index e78419934d481..b870b30f9804d 100644 --- a/spec/node-spec.js +++ b/spec/node-spec.js @@ -1,146 +1,146 @@ -const ChildProcess = require('child_process') -const { expect } = require('chai') -const fs = require('fs') -const path = require('path') -const os = require('os') -const { ipcRenderer } = require('electron') -const features = process.electronBinding('features') +const ChildProcess = require('child_process'); +const { expect } = require('chai'); +const fs = require('fs'); +const path = require('path'); +const os = require('os'); +const { ipcRenderer } = require('electron'); +const features = process.electronBinding('features'); -const { emittedOnce } = require('./events-helpers') -const { ifit } = require('./spec-helpers') +const { emittedOnce } = require('./events-helpers'); +const { ifit } = require('./spec-helpers'); describe('node feature', () => { - const fixtures = path.join(__dirname, 'fixtures') + const fixtures = path.join(__dirname, 'fixtures'); describe('child_process', () => { beforeEach(function () { if (!features.isRunAsNodeEnabled()) { - this.skip() + this.skip(); } - }) + }); describe('child_process.fork', () => { it('works in current process', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'ping.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'ping.js')); child.on('message', msg => { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); it('preserves args', (done) => { - const args = ['--expose_gc', '-test', '1'] - const child = ChildProcess.fork(path.join(fixtures, 'module', 'process_args.js'), args) + const args = ['--expose_gc', '-test', '1']; + const child = ChildProcess.fork(path.join(fixtures, 'module', 'process_args.js'), args); child.on('message', (msg) => { - expect(args).to.deep.equal(msg.slice(2)) - done() - }) - child.send('message') - }) + expect(args).to.deep.equal(msg.slice(2)); + done(); + }); + child.send('message'); + }); it('works in forked process', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js')); child.on('message', (msg) => { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); it('works in forked process when options.env is specifed', (done) => { const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], { path: process.env.PATH - }) + }); child.on('message', (msg) => { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); it('has String::localeCompare working in script', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'locale-compare.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'locale-compare.js')); child.on('message', (msg) => { - expect(msg).to.deep.equal([0, -1, 1]) - done() - }) - child.send('message') - }) + expect(msg).to.deep.equal([0, -1, 1]); + done(); + }); + child.send('message'); + }); it('has setImmediate working in script', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'set-immediate.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'set-immediate.js')); child.on('message', (msg) => { - expect(msg).to.equal('ok') - done() - }) - child.send('message') - }) + expect(msg).to.equal('ok'); + done(); + }); + child.send('message'); + }); it('pipes stdio', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'process-stdout.js'), { silent: true }) - let data = '' + const child = ChildProcess.fork(path.join(fixtures, 'module', 'process-stdout.js'), { silent: true }); + let data = ''; child.stdout.on('data', (chunk) => { - data += String(chunk) - }) + data += String(chunk); + }); child.on('close', (code) => { - expect(code).to.equal(0) - expect(data).to.equal('pipes stdio') - done() - }) - }) + expect(code).to.equal(0); + expect(data).to.equal('pipes stdio'); + done(); + }); + }); it('works when sending a message to a process forked with the --eval argument', (done) => { - const source = "process.on('message', (message) => { process.send(message) })" - const forked = ChildProcess.fork('--eval', [source]) + const source = "process.on('message', (message) => { process.send(message) })"; + const forked = ChildProcess.fork('--eval', [source]); forked.once('message', (message) => { - expect(message).to.equal('hello') - done() - }) - forked.send('hello') - }) + expect(message).to.equal('hello'); + done(); + }); + forked.send('hello'); + }); it('has the electron version in process.versions', (done) => { - const source = 'process.send(process.versions)' - const forked = ChildProcess.fork('--eval', [source]) + const source = 'process.send(process.versions)'; + const forked = ChildProcess.fork('--eval', [source]); forked.on('message', (message) => { expect(message) .to.have.own.property('electron') .that.is.a('string') - .and.matches(/^\d+\.\d+\.\d+(\S*)?$/) - done() - }) - }) - }) + .and.matches(/^\d+\.\d+\.\d+(\S*)?$/); + done(); + }); + }); + }); describe('child_process.spawn', () => { - let child + let child; afterEach(() => { - if (child != null) child.kill() - }) + if (child != null) child.kill(); + }); it('supports spawning Electron as a node process via the ELECTRON_RUN_AS_NODE env var', (done) => { child = ChildProcess.spawn(process.execPath, [path.join(__dirname, 'fixtures', 'module', 'run-as-node.js')], { env: { ELECTRON_RUN_AS_NODE: true } - }) + }); - let output = '' + let output = ''; child.stdout.on('data', data => { - output += data - }) + output += data; + }); child.stdout.on('close', () => { expect(JSON.parse(output)).to.deep.equal({ processLog: process.platform === 'win32' ? 'function' : 'undefined', processType: 'undefined', window: 'undefined' - }) - done() - }) - }) - }) + }); + done(); + }); + }); + }); describe('child_process.exec', () => { (process.platform === 'linux' ? it : it.skip)('allows executing a setuid binary from non-sandboxed renderer', () => { @@ -150,274 +150,274 @@ describe('node feature', () => { // are running in. If this test fails with an error like 'effective uid // is not 0', then it's likely that our patch to prevent the flag from // being set has become ineffective. - const stdout = ChildProcess.execSync('sudo --help') - expect(stdout).to.not.be.empty() - }) - }) - }) + const stdout = ChildProcess.execSync('sudo --help'); + expect(stdout).to.not.be.empty(); + }); + }); + }); describe('contexts', () => { describe('setTimeout in fs callback', () => { it('does not crash', (done) => { fs.readFile(__filename, () => { - setTimeout(done, 0) - }) - }) - }) + setTimeout(done, 0); + }); + }); + }); describe('error thrown in renderer process node context', () => { it('gets emitted as a process uncaughtException event', (done) => { - const error = new Error('boo!') - const listeners = process.listeners('uncaughtException') - process.removeAllListeners('uncaughtException') + const error = new Error('boo!'); + const listeners = process.listeners('uncaughtException'); + process.removeAllListeners('uncaughtException'); process.on('uncaughtException', (thrown) => { try { - expect(thrown).to.equal(error) - done() + expect(thrown).to.equal(error); + done(); } catch (e) { - done(e) + done(e); } finally { - process.removeAllListeners('uncaughtException') - listeners.forEach((listener) => process.on('uncaughtException', listener)) + process.removeAllListeners('uncaughtException'); + listeners.forEach((listener) => process.on('uncaughtException', listener)); } - }) + }); fs.readFile(__filename, () => { - throw error - }) - }) - }) + throw error; + }); + }); + }); describe('error thrown in main process node context', () => { it('gets emitted as a process uncaughtException event', () => { - const error = ipcRenderer.sendSync('handle-uncaught-exception', 'hello') - expect(error).to.equal('hello') - }) - }) + const error = ipcRenderer.sendSync('handle-uncaught-exception', 'hello'); + expect(error).to.equal('hello'); + }); + }); describe('promise rejection in main process node context', () => { it('gets emitted as a process unhandledRejection event', () => { - const error = ipcRenderer.sendSync('handle-unhandled-rejection', 'hello') - expect(error).to.equal('hello') - }) - }) + const error = ipcRenderer.sendSync('handle-unhandled-rejection', 'hello'); + expect(error).to.equal('hello'); + }); + }); describe('setTimeout called under blink env in renderer process', () => { it('can be scheduled in time', (done) => { - setTimeout(done, 10) - }) + setTimeout(done, 10); + }); it('works from the timers module', (done) => { - require('timers').setTimeout(done, 10) - }) - }) + require('timers').setTimeout(done, 10); + }); + }); describe('setInterval called under blink env in renderer process', () => { it('can be scheduled in time', (done) => { const id = setInterval(() => { - clearInterval(id) - done() - }, 10) - }) + clearInterval(id); + done(); + }, 10); + }); it('can be scheduled in time from timers module', (done) => { - const { setInterval, clearInterval } = require('timers') + const { setInterval, clearInterval } = require('timers'); const id = setInterval(() => { - clearInterval(id) - done() - }, 10) - }) - }) - }) + clearInterval(id); + done(); + }, 10); + }); + }); + }); describe('message loop', () => { describe('process.nextTick', () => { - it('emits the callback', (done) => process.nextTick(done)) + it('emits the callback', (done) => process.nextTick(done)); it('works in nested calls', (done) => { process.nextTick(() => { - process.nextTick(() => process.nextTick(done)) - }) - }) - }) + process.nextTick(() => process.nextTick(done)); + }); + }); + }); describe('setImmediate', () => { - it('emits the callback', (done) => setImmediate(done)) + it('emits the callback', (done) => setImmediate(done)); it('works in nested calls', (done) => { setImmediate(() => { - setImmediate(() => setImmediate(done)) - }) - }) - }) - }) + setImmediate(() => setImmediate(done)); + }); + }); + }); + }); describe('net.connect', () => { before(function () { if (!features.isRunAsNodeEnabled() || process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('emit error when connect to a socket path without listeners', (done) => { - const socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock') - const script = path.join(fixtures, 'module', 'create_socket.js') - const child = ChildProcess.fork(script, [socketPath]) + const socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock'); + const script = path.join(fixtures, 'module', 'create_socket.js'); + const child = ChildProcess.fork(script, [socketPath]); child.on('exit', (code) => { - expect(code).to.equal(0) - const client = require('net').connect(socketPath) + expect(code).to.equal(0); + const client = require('net').connect(socketPath); client.on('error', (error) => { - expect(error.code).to.equal('ECONNREFUSED') - done() - }) - }) - }) - }) + expect(error.code).to.equal('ECONNREFUSED'); + done(); + }); + }); + }); + }); describe('Buffer', () => { it('can be created from WebKit external string', () => { - const p = document.createElement('p') - p.innerText = '闲云潭影日悠悠,物换星移几度秋' - const b = Buffer.from(p.innerText) - expect(b.toString()).to.equal('闲云潭影日悠悠,物换星移几度秋') - expect(Buffer.byteLength(p.innerText)).to.equal(45) - }) + const p = document.createElement('p'); + p.innerText = '闲云潭影日悠悠,物换星移几度秋'; + const b = Buffer.from(p.innerText); + expect(b.toString()).to.equal('闲云潭影日悠悠,物换星移几度秋'); + expect(Buffer.byteLength(p.innerText)).to.equal(45); + }); it('correctly parses external one-byte UTF8 string', () => { - const p = document.createElement('p') - p.innerText = 'Jøhänñéß' - const b = Buffer.from(p.innerText) - expect(b.toString()).to.equal('Jøhänñéß') - expect(Buffer.byteLength(p.innerText)).to.equal(13) - }) + const p = document.createElement('p'); + p.innerText = 'Jøhänñéß'; + const b = Buffer.from(p.innerText); + expect(b.toString()).to.equal('Jøhänñéß'); + expect(Buffer.byteLength(p.innerText)).to.equal(13); + }); it('does not crash when creating large Buffers', () => { - let buffer = Buffer.from(new Array(4096).join(' ')) - expect(buffer.length).to.equal(4095) - buffer = Buffer.from(new Array(4097).join(' ')) - expect(buffer.length).to.equal(4096) - }) + let buffer = Buffer.from(new Array(4096).join(' ')); + expect(buffer.length).to.equal(4095); + buffer = Buffer.from(new Array(4097).join(' ')); + expect(buffer.length).to.equal(4096); + }); it('does not crash for crypto operations', () => { - const crypto = require('crypto') - const data = 'lG9E+/g4JmRmedDAnihtBD4Dfaha/GFOjd+xUOQI05UtfVX3DjUXvrS98p7kZQwY3LNhdiFo7MY5rGft8yBuDhKuNNag9vRx/44IuClDhdQ=' - const key = 'q90K9yBqhWZnAMCMTOJfPQ==' - const cipherText = '{"error_code":114,"error_message":"Tham số không hợp lệ","data":null}' + const crypto = require('crypto'); + const data = 'lG9E+/g4JmRmedDAnihtBD4Dfaha/GFOjd+xUOQI05UtfVX3DjUXvrS98p7kZQwY3LNhdiFo7MY5rGft8yBuDhKuNNag9vRx/44IuClDhdQ='; + const key = 'q90K9yBqhWZnAMCMTOJfPQ=='; + const cipherText = '{"error_code":114,"error_message":"Tham số không hợp lệ","data":null}'; for (let i = 0; i < 10000; ++i) { - const iv = Buffer.from('0'.repeat(32), 'hex') - const input = Buffer.from(data, 'base64') - const decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(key, 'base64'), iv) - const result = Buffer.concat([decipher.update(input), decipher.final()]).toString('utf8') - expect(cipherText).to.equal(result) + const iv = Buffer.from('0'.repeat(32), 'hex'); + const input = Buffer.from(data, 'base64'); + const decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(key, 'base64'), iv); + const result = Buffer.concat([decipher.update(input), decipher.final()]).toString('utf8'); + expect(cipherText).to.equal(result); } - }) - }) + }); + }); describe('process.stdout', () => { it('does not throw an exception when accessed', () => { - expect(() => process.stdout).to.not.throw() - }) + expect(() => process.stdout).to.not.throw(); + }); it('does not throw an exception when calling write()', () => { expect(() => { - process.stdout.write('test') - }).to.not.throw() - }) + process.stdout.write('test'); + }).to.not.throw(); + }); // TODO: figure out why process.stdout.isTTY is true on Darwin but not Linux/Win. ifit(process.platform !== 'darwin')('isTTY should be undefined in the renderer process', function () { - expect(process.stdout.isTTY).to.be.undefined() - }) - }) + expect(process.stdout.isTTY).to.be.undefined(); + }); + }); describe('process.stdin', () => { it('does not throw an exception when accessed', () => { - expect(() => process.stdin).to.not.throw() - }) + expect(() => process.stdin).to.not.throw(); + }); it('returns null when read from', () => { - expect(process.stdin.read()).to.be.null() - }) - }) + expect(process.stdin.read()).to.be.null(); + }); + }); describe('process.version', () => { it('should not have -pre', () => { - expect(process.version.endsWith('-pre')).to.be.false() - }) - }) + expect(process.version.endsWith('-pre')).to.be.false(); + }); + }); describe('vm.runInNewContext', () => { it('should not crash', () => { - require('vm').runInNewContext('') - }) - }) + require('vm').runInNewContext(''); + }); + }); describe('crypto', () => { it('should list the ripemd160 hash in getHashes', () => { - expect(require('crypto').getHashes()).to.include('ripemd160') - }) + expect(require('crypto').getHashes()).to.include('ripemd160'); + }); it('should be able to create a ripemd160 hash and use it', () => { - const hash = require('crypto').createHash('ripemd160') - hash.update('electron-ripemd160') - expect(hash.digest('hex')).to.equal('fa7fec13c624009ab126ebb99eda6525583395fe') - }) + const hash = require('crypto').createHash('ripemd160'); + hash.update('electron-ripemd160'); + expect(hash.digest('hex')).to.equal('fa7fec13c624009ab126ebb99eda6525583395fe'); + }); it('should list aes-{128,256}-cfb in getCiphers', () => { - expect(require('crypto').getCiphers()).to.include.members(['aes-128-cfb', 'aes-256-cfb']) - }) + expect(require('crypto').getCiphers()).to.include.members(['aes-128-cfb', 'aes-256-cfb']); + }); it('should be able to create an aes-128-cfb cipher', () => { - require('crypto').createCipheriv('aes-128-cfb', '0123456789abcdef', '0123456789abcdef') - }) + require('crypto').createCipheriv('aes-128-cfb', '0123456789abcdef', '0123456789abcdef'); + }); it('should be able to create an aes-256-cfb cipher', () => { - require('crypto').createCipheriv('aes-256-cfb', '0123456789abcdef0123456789abcdef', '0123456789abcdef') - }) + require('crypto').createCipheriv('aes-256-cfb', '0123456789abcdef0123456789abcdef', '0123456789abcdef'); + }); it('should list des-ede-cbc in getCiphers', () => { - expect(require('crypto').getCiphers()).to.include('des-ede-cbc') - }) + expect(require('crypto').getCiphers()).to.include('des-ede-cbc'); + }); it('should be able to create an des-ede-cbc cipher', () => { - const key = Buffer.from('0123456789abcdeff1e0d3c2b5a49786', 'hex') - const iv = Buffer.from('fedcba9876543210', 'hex') - require('crypto').createCipheriv('des-ede-cbc', key, iv) - }) + const key = Buffer.from('0123456789abcdeff1e0d3c2b5a49786', 'hex'); + const iv = Buffer.from('fedcba9876543210', 'hex'); + require('crypto').createCipheriv('des-ede-cbc', key, iv); + }); it('should not crash when getting an ECDH key', () => { - const ecdh = require('crypto').createECDH('prime256v1') - expect(ecdh.generateKeys()).to.be.an.instanceof(Buffer) - expect(ecdh.getPrivateKey()).to.be.an.instanceof(Buffer) - }) + const ecdh = require('crypto').createECDH('prime256v1'); + expect(ecdh.generateKeys()).to.be.an.instanceof(Buffer); + expect(ecdh.getPrivateKey()).to.be.an.instanceof(Buffer); + }); it('should not crash when generating DH keys or fetching DH fields', () => { - const dh = require('crypto').createDiffieHellman('modp15') - expect(dh.generateKeys()).to.be.an.instanceof(Buffer) - expect(dh.getPublicKey()).to.be.an.instanceof(Buffer) - expect(dh.getPrivateKey()).to.be.an.instanceof(Buffer) - expect(dh.getPrime()).to.be.an.instanceof(Buffer) - expect(dh.getGenerator()).to.be.an.instanceof(Buffer) - }) + const dh = require('crypto').createDiffieHellman('modp15'); + expect(dh.generateKeys()).to.be.an.instanceof(Buffer); + expect(dh.getPublicKey()).to.be.an.instanceof(Buffer); + expect(dh.getPrivateKey()).to.be.an.instanceof(Buffer); + expect(dh.getPrime()).to.be.an.instanceof(Buffer); + expect(dh.getGenerator()).to.be.an.instanceof(Buffer); + }); it('should not crash when creating an ECDH cipher', () => { - const crypto = require('crypto') - const dh = crypto.createECDH('prime256v1') - dh.generateKeys() - dh.setPrivateKey(dh.getPrivateKey()) - }) - }) + const crypto = require('crypto'); + const dh = crypto.createECDH('prime256v1'); + dh.generateKeys(); + dh.setPrivateKey(dh.getPrivateKey()); + }); + }); it('includes the electron version in process.versions', () => { expect(process.versions) .to.have.own.property('electron') .that.is.a('string') - .and.matches(/^\d+\.\d+\.\d+(\S*)?$/) - }) + .and.matches(/^\d+\.\d+\.\d+(\S*)?$/); + }); it('includes the chrome version in process.versions', () => { expect(process.versions) .to.have.own.property('chrome') .that.is.a('string') - .and.matches(/^\d+\.\d+\.\d+\.\d+$/) - }) -}) + .and.matches(/^\d+\.\d+\.\d+\.\d+$/); + }); +}); diff --git a/spec/spec-helpers.js b/spec/spec-helpers.js index f54f043dccf69..ae7d955e28fdc 100644 --- a/spec/spec-helpers.js +++ b/spec/spec-helpers.js @@ -1,2 +1,2 @@ -exports.ifit = (condition) => (condition ? it : it.skip) -exports.ifdescribe = (condition) => (condition ? describe : describe.skip) +exports.ifit = (condition) => (condition ? it : it.skip); +exports.ifdescribe = (condition) => (condition ? describe : describe.skip); diff --git a/spec/static/main.js b/spec/static/main.js index 6a392ea2e4ea7..acf54b609d9a3 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -1,36 +1,36 @@ // Deprecated APIs are still supported and should be tested. -process.throwDeprecation = false +process.throwDeprecation = false; -const electron = require('electron') -const { app, BrowserWindow, crashReporter, dialog, ipcMain, protocol, webContents, session } = electron +const electron = require('electron'); +const { app, BrowserWindow, crashReporter, dialog, ipcMain, protocol, webContents, session } = electron; try { - require('fs').rmdirSync(app.getPath('userData'), { recursive: true }) + require('fs').rmdirSync(app.getPath('userData'), { recursive: true }); } catch (e) { - console.warn('Warning: couldn\'t clear user data directory:', e) + console.warn('Warning: couldn\'t clear user data directory:', e); } -const fs = require('fs') -const path = require('path') -const util = require('util') -const v8 = require('v8') +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const v8 = require('v8'); const argv = require('yargs') .boolean('ci') .array('files') .string('g').alias('g', 'grep') .boolean('i').alias('i', 'invert') - .argv + .argv; -let window = null +let window = null; -v8.setFlagsFromString('--expose_gc') -app.commandLine.appendSwitch('js-flags', '--expose_gc') -app.commandLine.appendSwitch('ignore-certificate-errors') -app.commandLine.appendSwitch('disable-renderer-backgrounding') +v8.setFlagsFromString('--expose_gc'); +app.commandLine.appendSwitch('js-flags', '--expose_gc'); +app.commandLine.appendSwitch('ignore-certificate-errors'); +app.commandLine.appendSwitch('disable-renderer-backgrounding'); // Disable security warnings (the security warnings test will enable them) -process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true +process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true; // Accessing stdout in the main process will result in the process.stdout // throwing UnknownSystemError in renderer process sometimes. This line makes @@ -43,62 +43,62 @@ process.stdout console ipcMain.on('message', function (event, ...args) { - event.sender.send('message', ...args) -}) + event.sender.send('message', ...args); +}); -ipcMain.handle('get-modules', () => Object.keys(electron)) -ipcMain.handle('get-temp-dir', () => app.getPath('temp')) -ipcMain.handle('ping', () => null) +ipcMain.handle('get-modules', () => Object.keys(electron)); +ipcMain.handle('get-temp-dir', () => app.getPath('temp')); +ipcMain.handle('ping', () => null); // Write output to file if OUTPUT_TO_FILE is defined. -const outputToFile = process.env.OUTPUT_TO_FILE +const outputToFile = process.env.OUTPUT_TO_FILE; const print = function (_, method, args) { - const output = util.format.apply(null, args) + const output = util.format.apply(null, args); if (outputToFile) { - fs.appendFileSync(outputToFile, output + '\n') + fs.appendFileSync(outputToFile, output + '\n'); } else { - console[method](output) + console[method](output); } -} -ipcMain.on('console-call', print) +}; +ipcMain.on('console-call', print); ipcMain.on('process.exit', function (event, code) { - process.exit(code) -}) + process.exit(code); +}); ipcMain.on('eval', function (event, script) { event.returnValue = eval(script) // eslint-disable-line -}) +}); ipcMain.on('echo', function (event, msg) { - event.returnValue = msg -}) + event.returnValue = msg; +}); -process.removeAllListeners('uncaughtException') +process.removeAllListeners('uncaughtException'); process.on('uncaughtException', function (error) { - console.error(error, error.stack) - process.exit(1) -}) + console.error(error, error.stack); + process.exit(1); +}); -global.nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS +global.nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS; app.on('window-all-closed', function () { - app.quit() -}) + app.quit(); +}); app.on('gpu-process-crashed', (event, killed) => { - console.log(`GPU process crashed (killed=${killed})`) -}) + console.log(`GPU process crashed (killed=${killed})`); +}); app.on('renderer-process-crashed', (event, contents, killed) => { - console.log(`webContents ${contents.id} crashed: ${contents.getURL()} (killed=${killed})`) -}) + console.log(`webContents ${contents.id} crashed: ${contents.getURL()} (killed=${killed})`); +}); app.whenReady().then(async function () { - await session.defaultSession.clearCache() - await session.defaultSession.clearStorageData() + await session.defaultSession.clearCache(); + await session.defaultSession.clearStorageData(); // Test if using protocol module would crash. - electron.protocol.registerStringProtocol('test-if-crashes', function () {}) + electron.protocol.registerStringProtocol('test-if-crashes', function () {}); window = new BrowserWindow({ title: 'Electron Tests', @@ -111,77 +111,77 @@ app.whenReady().then(async function () { enableRemoteModule: false, webviewTag: true } - }) + }); window.loadFile('static/index.html', { query: { grep: argv.grep, invert: argv.invert ? 'true' : '', files: argv.files ? argv.files.join(',') : undefined } - }) + }); window.on('unresponsive', function () { const chosen = dialog.showMessageBox(window, { type: 'warning', buttons: ['Close', 'Keep Waiting'], message: 'Window is not responsing', detail: 'The window is not responding. Would you like to force close it or just keep waiting?' - }) - if (chosen === 0) window.destroy() - }) + }); + if (chosen === 0) window.destroy(); + }); window.webContents.on('crashed', function () { - console.error('Renderer process crashed') - process.exit(1) - }) -}) + console.error('Renderer process crashed'); + process.exit(1); + }); +}); ipcMain.on('prevent-next-will-attach-webview', (event) => { - event.sender.once('will-attach-webview', event => event.preventDefault()) -}) + event.sender.once('will-attach-webview', event => event.preventDefault()); +}); ipcMain.on('disable-node-on-next-will-attach-webview', (event, id) => { event.sender.once('will-attach-webview', (event, webPreferences, params) => { - params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}` - webPreferences.nodeIntegration = false - }) -}) + params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}`; + webPreferences.nodeIntegration = false; + }); +}); ipcMain.on('disable-preload-on-next-will-attach-webview', (event, id) => { event.sender.once('will-attach-webview', (event, webPreferences, params) => { - params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'webview-stripped-preload.html')}` - delete webPreferences.preload - delete webPreferences.preloadURL - }) -}) + params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'webview-stripped-preload.html')}`; + delete webPreferences.preload; + delete webPreferences.preloadURL; + }); +}); ipcMain.on('handle-uncaught-exception', (event, message) => { suspendListeners(process, 'uncaughtException', (error) => { - event.returnValue = error.message - }) + event.returnValue = error.message; + }); fs.readFile(__filename, () => { - throw new Error(message) - }) -}) + throw new Error(message); + }); +}); ipcMain.on('handle-unhandled-rejection', (event, message) => { suspendListeners(process, 'unhandledRejection', (error) => { - event.returnValue = error.message - }) + event.returnValue = error.message; + }); fs.readFile(__filename, () => { - Promise.reject(new Error(message)) - }) -}) + Promise.reject(new Error(message)); + }); +}); // Suspend listeners until the next event and then restore them const suspendListeners = (emitter, eventName, callback) => { - const listeners = emitter.listeners(eventName) - emitter.removeAllListeners(eventName) + const listeners = emitter.listeners(eventName); + emitter.removeAllListeners(eventName); emitter.once(eventName, (...args) => { - emitter.removeAllListeners(eventName) + emitter.removeAllListeners(eventName); listeners.forEach((listener) => { - emitter.on(eventName, listener) - }) + emitter.on(eventName, listener); + }); // eslint-disable-next-line standard/no-callback-literal - callback(...args) - }) -} + callback(...args); + }); +}; diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 1f2ce7455a769..ba81229a2f331 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -1,147 +1,147 @@ -const { expect } = require('chai') -const path = require('path') -const http = require('http') -const url = require('url') -const { ipcRenderer } = require('electron') -const { emittedOnce, waitForEvent } = require('./events-helpers') -const { ifdescribe, ifit } = require('./spec-helpers') +const { expect } = require('chai'); +const path = require('path'); +const http = require('http'); +const url = require('url'); +const { ipcRenderer } = require('electron'); +const { emittedOnce, waitForEvent } = require('./events-helpers'); +const { ifdescribe, ifit } = require('./spec-helpers'); -const features = process.electronBinding('features') -const nativeModulesEnabled = process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS +const features = process.electronBinding('features'); +const nativeModulesEnabled = process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS; /* Most of the APIs here don't use standard callbacks */ /* eslint-disable standard/no-callback-literal */ describe(' tag', function () { - this.timeout(3 * 60 * 1000) + this.timeout(3 * 60 * 1000); - const fixtures = path.join(__dirname, 'fixtures') - let webview = null + const fixtures = path.join(__dirname, 'fixtures'); + let webview = null; const loadWebView = async (webview, attributes = {}) => { for (const [name, value] of Object.entries(attributes)) { - webview.setAttribute(name, value) + webview.setAttribute(name, value); } - document.body.appendChild(webview) - await waitForEvent(webview, 'did-finish-load') - return webview - } + document.body.appendChild(webview); + await waitForEvent(webview, 'did-finish-load'); + return webview; + }; const startLoadingWebViewAndWaitForMessage = async (webview, attributes = {}) => { - loadWebView(webview, attributes) // Don't wait for load to be finished. - const event = await waitForEvent(webview, 'console-message') - return event.message - } + loadWebView(webview, attributes); // Don't wait for load to be finished. + const event = await waitForEvent(webview, 'console-message'); + return event.message; + }; beforeEach(() => { - webview = new WebView() - }) + webview = new WebView(); + }); afterEach(() => { if (!document.body.contains(webview)) { - document.body.appendChild(webview) + document.body.appendChild(webview); } - webview.remove() - }) + webview.remove(); + }); describe('src attribute', () => { it('specifies the page to load', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/a.html` - }) - expect(message).to.equal('a') - }) + }); + expect(message).to.equal('a'); + }); it('navigates to new page when changed', async () => { await loadWebView(webview, { src: `file://${fixtures}/pages/a.html` - }) + }); - webview.src = `file://${fixtures}/pages/b.html` + webview.src = `file://${fixtures}/pages/b.html`; - const { message } = await waitForEvent(webview, 'console-message') - expect(message).to.equal('b') - }) + const { message } = await waitForEvent(webview, 'console-message'); + expect(message).to.equal('b'); + }); it('resolves relative URLs', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { src: '../fixtures/pages/e.html' - }) - expect(message).to.equal('Window script is loaded before preload script') - }) + }); + expect(message).to.equal('Window script is loaded before preload script'); + }); it('ignores empty values', () => { - expect(webview.src).to.equal('') + expect(webview.src).to.equal(''); for (const emptyValue of ['', null, undefined]) { - webview.src = emptyValue - expect(webview.src).to.equal('') + webview.src = emptyValue; + expect(webview.src).to.equal(''); } - }) + }); it('does not wait until loadURL is resolved', async () => { - await loadWebView(webview, { src: 'about:blank' }) + await loadWebView(webview, { src: 'about:blank' }); - const before = Date.now() - webview.src = 'https://github.com' - const now = Date.now() + const before = Date.now(); + webview.src = 'https://github.com'; + const now = Date.now(); // Setting src is essentially sending a sync IPC message, which should // not exceed more than a few ms. // // This is for testing #18638. - expect(now - before).to.be.below(100) - }) - }) + expect(now - before).to.be.below(100); + }); + }); describe('nodeintegration attribute', () => { it('inserts no node symbols when not set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/c.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'undefined', module: 'undefined', process: 'undefined', global: 'undefined' - }) - }) + }); + }); it('inserts node symbols when set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/d.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('loads node symbols after POST navigation when set', async function () { // FIXME Figure out why this is timing out on AppVeyor if (process.env.APPVEYOR === 'True') { - this.skip() - return + this.skip(); + return; } const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/post.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('disables node integration on child windows when it is disabled on the webview', async () => { const src = url.format({ @@ -151,282 +151,282 @@ describe(' tag', function () { p: `${fixtures}/pages/window-opener-node.html` }, slashes: true - }) + }); loadWebView(webview, { allowpopups: 'on', src - }) - const { message } = await waitForEvent(webview, 'console-message') - expect(JSON.parse(message).isProcessGlobalUndefined).to.be.true() + }); + const { message } = await waitForEvent(webview, 'console-message'); + expect(JSON.parse(message).isProcessGlobalUndefined).to.be.true(); }); (nativeModulesEnabled ? it : it.skip)('loads native modules when navigation happens', async function () { await loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/native-module.html` - }) + }); - webview.reload() + webview.reload(); - const { message } = await waitForEvent(webview, 'console-message') - expect(message).to.equal('function') - }) - }) + const { message } = await waitForEvent(webview, 'console-message'); + expect(message).to.equal('function'); + }); + }); describe('preload attribute', () => { it('loads the script before other scripts in window', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload.js`, src: `file://${fixtures}/pages/e.html` - }) + }); - expect(message).to.be.a('string') - expect(message).to.be.not.equal('Window script is loaded before preload script') - }) + expect(message).to.be.a('string'); + expect(message).to.be.not.equal('Window script is loaded before preload script'); + }); it('preload script can still use "process" and "Buffer" when nodeintegration is off', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload-node-off.js`, src: `file://${fixtures}/api/blank.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ process: 'object', Buffer: 'function' - }) - }) + }); + }); it('runs in the correct scope when sandboxed', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload-context.js`, src: `file://${fixtures}/api/blank.html`, webpreferences: 'sandbox=yes' - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', // arguments passed to it should be availale electron: 'undefined', // objects from the scope it is called from should not be available window: 'object', // the window object should be available localVar: 'undefined' // but local variables should not be exposed to the window - }) - }) + }); + }); it('preload script can require modules that still use "process" and "Buffer" when nodeintegration is off', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload-node-off-wrapper.js`, src: `file://${fixtures}/api/blank.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ process: 'object', Buffer: 'function' - }) - }) + }); + }); it('receives ipc message in preload script', async () => { await loadWebView(webview, { preload: `${fixtures}/module/preload-ipc.js`, src: `file://${fixtures}/pages/e.html` - }) + }); - const message = 'boom!' - webview.send('ping', message) + const message = 'boom!'; + webview.send('ping', message); - const { channel, args } = await waitForEvent(webview, 'ipc-message') - expect(channel).to.equal('pong') - expect(args).to.deep.equal([message]) - }) + const { channel, args } = await waitForEvent(webview, 'ipc-message'); + expect(channel).to.equal('pong'); + expect(args).to.deep.equal([message]); + }); it('works without script tag in page', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload.js`, src: `file://${fixtures}pages/base-page.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object', Buffer: 'function' - }) - }) + }); + }); it('resolves relative URLs', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: '../fixtures/module/preload.js', src: `file://${fixtures}/pages/e.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object', Buffer: 'function' - }) - }) + }); + }); it('ignores empty values', () => { - expect(webview.preload).to.equal('') + expect(webview.preload).to.equal(''); for (const emptyValue of ['', null, undefined]) { - webview.preload = emptyValue - expect(webview.preload).to.equal('') + webview.preload = emptyValue; + expect(webview.preload).to.equal(''); } - }) - }) + }); + }); describe('httpreferrer attribute', () => { it('sets the referrer url', (done) => { - const referrer = 'http://github.com/' + const referrer = 'http://github.com/'; const server = http.createServer((req, res) => { - res.end() - server.close() - expect(req.headers.referer).to.equal(referrer) - done() + res.end(); + server.close(); + expect(req.headers.referer).to.equal(referrer); + done(); }).listen(0, '127.0.0.1', () => { - const port = server.address().port + const port = server.address().port; loadWebView(webview, { httpreferrer: referrer, src: `http://127.0.0.1:${port}` - }) - }) - }) - }) + }); + }); + }); + }); describe('useragent attribute', () => { it('sets the user agent', async () => { - const referrer = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko' + const referrer = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'; const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/useragent.html`, useragent: referrer - }) - expect(message).to.equal(referrer) - }) - }) + }); + expect(message).to.equal(referrer); + }); + }); describe('disablewebsecurity attribute', () => { it('does not disable web security when not set', async () => { - const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js') - const src = ` ` - const encoded = btoa(unescape(encodeURIComponent(src))) + const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js'); + const src = ` `; + const encoded = btoa(unescape(encodeURIComponent(src))); const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `data:text/html;base64,${encoded}` - }) - expect(message).to.be.a('string') - expect(message).to.contain('Not allowed to load local resource') - }) + }); + expect(message).to.be.a('string'); + expect(message).to.contain('Not allowed to load local resource'); + }); it('disables web security when set', async () => { - const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js') - const src = ` ` - const encoded = btoa(unescape(encodeURIComponent(src))) + const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js'); + const src = ` `; + const encoded = btoa(unescape(encodeURIComponent(src))); const message = await startLoadingWebViewAndWaitForMessage(webview, { disablewebsecurity: '', src: `data:text/html;base64,${encoded}` - }) - expect(message).to.equal('ok') - }) + }); + expect(message).to.equal('ok'); + }); it('does not break node integration', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { disablewebsecurity: '', nodeintegration: 'on', src: `file://${fixtures}/pages/d.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('does not break preload script', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { disablewebsecurity: '', preload: `${fixtures}/module/preload.js`, src: `file://${fixtures}/pages/e.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object', Buffer: 'function' - }) - }) - }) + }); + }); + }); describe('partition attribute', () => { it('inserts no node symbols when not set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { partition: 'test1', src: `file://${fixtures}/pages/c.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'undefined', module: 'undefined', process: 'undefined', global: 'undefined' - }) - }) + }); + }); it('inserts node symbols when set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'on', partition: 'test2', src: `file://${fixtures}/pages/d.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('isolates storage for different id', async () => { - window.localStorage.setItem('test', 'one') + window.localStorage.setItem('test', 'one'); const message = await startLoadingWebViewAndWaitForMessage(webview, { partition: 'test3', src: `file://${fixtures}/pages/partition/one.html` - }) + }); - const parsedMessage = JSON.parse(message) + const parsedMessage = JSON.parse(message); expect(parsedMessage).to.include({ numberOfEntries: 0, testValue: null - }) - }) + }); + }); it('uses current session storage when no id is provided', async () => { - const testValue = 'one' - window.localStorage.setItem('test', testValue) + const testValue = 'one'; + window.localStorage.setItem('test', testValue); const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/partition/one.html` - }) + }); - const parsedMessage = JSON.parse(message) + const parsedMessage = JSON.parse(message); expect(parsedMessage).to.include({ numberOfEntries: 1, testValue - }) - }) - }) + }); + }); + }); describe('allowpopups attribute', () => { const generateSpecs = (description, webpreferences = '') => { @@ -435,494 +435,494 @@ describe(' tag', function () { const message = await startLoadingWebViewAndWaitForMessage(webview, { webpreferences, src: `file://${fixtures}/pages/window-open-hide.html` - }) - expect(message).to.equal('null') - }) + }); + expect(message).to.equal('null'); + }); it('can open new window when set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { webpreferences, allowpopups: 'on', src: `file://${fixtures}/pages/window-open-hide.html` - }) - expect(message).to.equal('window') - }) - }) - } + }); + expect(message).to.equal('window'); + }); + }); + }; - generateSpecs('without sandbox') - generateSpecs('with sandbox', 'sandbox=yes') - generateSpecs('with nativeWindowOpen', 'nativeWindowOpen=yes') - }) + generateSpecs('without sandbox'); + generateSpecs('with sandbox', 'sandbox=yes'); + generateSpecs('with nativeWindowOpen', 'nativeWindowOpen=yes'); + }); describe('webpreferences attribute', () => { it('can enable nodeintegration', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/d.html`, webpreferences: 'nodeIntegration' - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); ifit(features.isRemoteModuleEnabled())('can disable the remote module', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload-disable-remote.js`, src: `file://${fixtures}/api/blank.html`, webpreferences: 'enableRemoteModule=no' - }) + }); - const typeOfRemote = JSON.parse(message) - expect(typeOfRemote).to.equal('undefined') - }) + const typeOfRemote = JSON.parse(message); + expect(typeOfRemote).to.equal('undefined'); + }); it('can disables web security and enable nodeintegration', async () => { - const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js') - const src = ` ` - const encoded = btoa(unescape(encodeURIComponent(src))) + const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js'); + const src = ` `; + const encoded = btoa(unescape(encodeURIComponent(src))); const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `data:text/html;base64,${encoded}`, webpreferences: 'webSecurity=no, nodeIntegration=yes' - }) + }); - expect(message).to.equal('function') - }) - }) + expect(message).to.equal('function'); + }); + }); describe('new-window event', () => { it('emits when window.open is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/window-open.html` - }) - const { url, frameName } = await waitForEvent(webview, 'new-window') + }); + const { url, frameName } = await waitForEvent(webview, 'new-window'); - expect(url).to.equal('http://host/') - expect(frameName).to.equal('host') - }) + expect(url).to.equal('http://host/'); + expect(frameName).to.equal('host'); + }); it('emits when link with target is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/target-name.html` - }) - const { url, frameName } = await waitForEvent(webview, 'new-window') + }); + const { url, frameName } = await waitForEvent(webview, 'new-window'); - expect(url).to.equal('http://host/') - expect(frameName).to.equal('target') - }) - }) + expect(url).to.equal('http://host/'); + expect(frameName).to.equal('target'); + }); + }); describe('ipc-message event', () => { it('emits when guest sends an ipc message to browser', async () => { loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/ipc-message.html` - }) - const { channel, args } = await waitForEvent(webview, 'ipc-message') + }); + const { channel, args } = await waitForEvent(webview, 'ipc-message'); - expect(channel).to.equal('channel') - expect(args).to.deep.equal(['arg1', 'arg2']) - }) - }) + expect(channel).to.equal('channel'); + expect(args).to.deep.equal(['arg1', 'arg2']); + }); + }); describe('page-title-set event', () => { it('emits when title is set', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/a.html` - }) - const { title, explicitSet } = await waitForEvent(webview, 'page-title-set') + }); + const { title, explicitSet } = await waitForEvent(webview, 'page-title-set'); - expect(title).to.equal('test') - expect(explicitSet).to.be.true() - }) - }) + expect(title).to.equal('test'); + expect(explicitSet).to.be.true(); + }); + }); describe('page-favicon-updated event', () => { it('emits when favicon urls are received', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/a.html` - }) - const { favicons } = await waitForEvent(webview, 'page-favicon-updated') + }); + const { favicons } = await waitForEvent(webview, 'page-favicon-updated'); - expect(favicons).to.be.an('array').of.length(2) + expect(favicons).to.be.an('array').of.length(2); if (process.platform === 'win32') { - expect(favicons[0]).to.match(/^file:\/\/\/[A-Z]:\/favicon.png$/i) + expect(favicons[0]).to.match(/^file:\/\/\/[A-Z]:\/favicon.png$/i); } else { - expect(favicons[0]).to.equal('file:///favicon.png') + expect(favicons[0]).to.equal('file:///favicon.png'); } - }) - }) + }); + }); describe('will-navigate event', () => { it('emits when a url that leads to oustide of the page is clicked', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/webview-will-navigate.html` - }) - const { url } = await waitForEvent(webview, 'will-navigate') + }); + const { url } = await waitForEvent(webview, 'will-navigate'); - expect(url).to.equal('http://host/') - }) - }) + expect(url).to.equal('http://host/'); + }); + }); describe('did-navigate event', () => { - let p = path.join(fixtures, 'pages', 'webview-will-navigate.html') - p = p.replace(/\\/g, '/') + let p = path.join(fixtures, 'pages', 'webview-will-navigate.html'); + p = p.replace(/\\/g, '/'); const pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p - }) + }); it('emits when a url that leads to outside of the page is clicked', async () => { - loadWebView(webview, { src: pageUrl }) - const { url } = await waitForEvent(webview, 'did-navigate') + loadWebView(webview, { src: pageUrl }); + const { url } = await waitForEvent(webview, 'did-navigate'); - expect(url).to.equal(pageUrl) - }) - }) + expect(url).to.equal(pageUrl); + }); + }); describe('did-navigate-in-page event', () => { it('emits when an anchor link is clicked', async () => { - let p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page.html') - p = p.replace(/\\/g, '/') + let p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page.html'); + p = p.replace(/\\/g, '/'); const pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p - }) - loadWebView(webview, { src: pageUrl }) - const event = await waitForEvent(webview, 'did-navigate-in-page') - expect(event.url).to.equal(`${pageUrl}#test_content`) - }) + }); + loadWebView(webview, { src: pageUrl }); + const event = await waitForEvent(webview, 'did-navigate-in-page'); + expect(event.url).to.equal(`${pageUrl}#test_content`); + }); it('emits when window.history.replaceState is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/webview-did-navigate-in-page-with-history.html` - }) - const { url } = await waitForEvent(webview, 'did-navigate-in-page') - expect(url).to.equal('http://host/') - }) + }); + const { url } = await waitForEvent(webview, 'did-navigate-in-page'); + expect(url).to.equal('http://host/'); + }); it('emits when window.location.hash is changed', async () => { - let p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page-with-hash.html') - p = p.replace(/\\/g, '/') + let p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page-with-hash.html'); + p = p.replace(/\\/g, '/'); const pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p - }) - loadWebView(webview, { src: pageUrl }) - const event = await waitForEvent(webview, 'did-navigate-in-page') - expect(event.url).to.equal(`${pageUrl}#test`) - }) - }) + }); + loadWebView(webview, { src: pageUrl }); + const event = await waitForEvent(webview, 'did-navigate-in-page'); + expect(event.url).to.equal(`${pageUrl}#test`); + }); + }); describe('close event', () => { it('should fire when interior page calls window.close', async () => { - loadWebView(webview, { src: `file://${fixtures}/pages/close.html` }) - await waitForEvent(webview, 'close') - }) - }) + loadWebView(webview, { src: `file://${fixtures}/pages/close.html` }); + await waitForEvent(webview, 'close'); + }); + }); // FIXME(zcbenz): Disabled because of moving to OOPIF webview. xdescribe('setDevToolsWebContents() API', () => { it('sets webContents of webview as devtools', async () => { - const webview2 = new WebView() - loadWebView(webview2) + const webview2 = new WebView(); + loadWebView(webview2); // Setup an event handler for further usage. - const waitForDomReady = waitForEvent(webview2, 'dom-ready') + const waitForDomReady = waitForEvent(webview2, 'dom-ready'); - loadWebView(webview, { src: 'about:blank' }) - await waitForEvent(webview, 'dom-ready') - webview.getWebContents().setDevToolsWebContents(webview2.getWebContents()) - webview.getWebContents().openDevTools() + loadWebView(webview, { src: 'about:blank' }); + await waitForEvent(webview, 'dom-ready'); + webview.getWebContents().setDevToolsWebContents(webview2.getWebContents()); + webview.getWebContents().openDevTools(); - await waitForDomReady + await waitForDomReady; // Its WebContents should be a DevTools. - const devtools = webview2.getWebContents() - expect(devtools.getURL().startsWith('devtools://devtools')).to.be.true() + const devtools = webview2.getWebContents(); + expect(devtools.getURL().startsWith('devtools://devtools')).to.be.true(); - const name = await devtools.executeJavaScript('InspectorFrontendHost.constructor.name') - document.body.removeChild(webview2) + const name = await devtools.executeJavaScript('InspectorFrontendHost.constructor.name'); + document.body.removeChild(webview2); - expect(name).to.be.equal('InspectorFrontendHostImpl') - }) - }) + expect(name).to.be.equal('InspectorFrontendHostImpl'); + }); + }); describe('devtools-opened event', () => { it('should fire when webview.openDevTools() is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` - }) - await waitForEvent(webview, 'dom-ready') + }); + await waitForEvent(webview, 'dom-ready'); - webview.openDevTools() - await waitForEvent(webview, 'devtools-opened') + webview.openDevTools(); + await waitForEvent(webview, 'devtools-opened'); - webview.closeDevTools() - }) - }) + webview.closeDevTools(); + }); + }); describe('devtools-closed event', () => { it('should fire when webview.closeDevTools() is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` - }) - await waitForEvent(webview, 'dom-ready') + }); + await waitForEvent(webview, 'dom-ready'); - webview.openDevTools() - await waitForEvent(webview, 'devtools-opened') + webview.openDevTools(); + await waitForEvent(webview, 'devtools-opened'); - webview.closeDevTools() - await waitForEvent(webview, 'devtools-closed') - }) - }) + webview.closeDevTools(); + await waitForEvent(webview, 'devtools-closed'); + }); + }); describe('devtools-focused event', () => { it('should fire when webview.openDevTools() is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` - }) + }); - const waitForDevToolsFocused = waitForEvent(webview, 'devtools-focused') + const waitForDevToolsFocused = waitForEvent(webview, 'devtools-focused'); - await waitForEvent(webview, 'dom-ready') - webview.openDevTools() + await waitForEvent(webview, 'dom-ready'); + webview.openDevTools(); - await waitForDevToolsFocused - webview.closeDevTools() - }) - }) + await waitForDevToolsFocused; + webview.closeDevTools(); + }); + }); describe('.reload()', () => { it('should emit beforeunload handler', async () => { await loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/beforeunload-false.html` - }) + }); // Event handler has to be added before reload. - const waitForOnbeforeunload = waitForEvent(webview, 'ipc-message') + const waitForOnbeforeunload = waitForEvent(webview, 'ipc-message'); - webview.reload() + webview.reload(); - const { channel } = await waitForOnbeforeunload - expect(channel).to.equal('onbeforeunload') - }) - }) + const { channel } = await waitForOnbeforeunload; + expect(channel).to.equal('onbeforeunload'); + }); + }); describe('.goForward()', () => { it('should work after a replaced history entry', (done) => { - let loadCount = 1 + let loadCount = 1; const listener = (e) => { if (loadCount === 1) { - expect(e.channel).to.equal('history') - expect(e.args[0]).to.equal(1) - expect(webview.canGoBack()).to.be.false() - expect(webview.canGoForward()).to.be.false() + expect(e.channel).to.equal('history'); + expect(e.args[0]).to.equal(1); + expect(webview.canGoBack()).to.be.false(); + expect(webview.canGoForward()).to.be.false(); } else if (loadCount === 2) { - expect(e.channel).to.equal('history') - expect(e.args[0]).to.equal(2) - expect(webview.canGoBack()).to.be.false() - expect(webview.canGoForward()).to.be.true() - webview.removeEventListener('ipc-message', listener) + expect(e.channel).to.equal('history'); + expect(e.args[0]).to.equal(2); + expect(webview.canGoBack()).to.be.false(); + expect(webview.canGoForward()).to.be.true(); + webview.removeEventListener('ipc-message', listener); } - } + }; const loadListener = () => { if (loadCount === 1) { - webview.src = `file://${fixtures}/pages/base-page.html` + webview.src = `file://${fixtures}/pages/base-page.html`; } else if (loadCount === 2) { - expect(webview.canGoBack()).to.be.true() - expect(webview.canGoForward()).to.be.false() + expect(webview.canGoBack()).to.be.true(); + expect(webview.canGoForward()).to.be.false(); - webview.goBack() + webview.goBack(); } else if (loadCount === 3) { - webview.goForward() + webview.goForward(); } else if (loadCount === 4) { - expect(webview.canGoBack()).to.be.true() - expect(webview.canGoForward()).to.be.false() + expect(webview.canGoBack()).to.be.true(); + expect(webview.canGoForward()).to.be.false(); - webview.removeEventListener('did-finish-load', loadListener) - done() + webview.removeEventListener('did-finish-load', loadListener); + done(); } - loadCount += 1 - } + loadCount += 1; + }; - webview.addEventListener('ipc-message', listener) - webview.addEventListener('did-finish-load', loadListener) + webview.addEventListener('ipc-message', listener); + webview.addEventListener('did-finish-load', loadListener); loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/history-replace.html` - }) - }) - }) + }); + }); + }); // FIXME: https://github.com/electron/electron/issues/19397 xdescribe('.clearHistory()', () => { it('should clear the navigation history', async () => { - const message = waitForEvent(webview, 'ipc-message') + const message = waitForEvent(webview, 'ipc-message'); await loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/history.html` - }) - const event = await message + }); + const event = await message; - expect(event.channel).to.equal('history') - expect(event.args[0]).to.equal(2) - expect(webview.canGoBack()).to.be.true() + expect(event.channel).to.equal('history'); + expect(event.args[0]).to.equal(2); + expect(webview.canGoBack()).to.be.true(); - webview.clearHistory() - expect(webview.canGoBack()).to.be.false() - }) - }) + webview.clearHistory(); + expect(webview.canGoBack()).to.be.false(); + }); + }); describe('basic auth', () => { - const auth = require('basic-auth') + const auth = require('basic-auth'); it('should authenticate with correct credentials', (done) => { - const message = 'Authenticated' + const message = 'Authenticated'; const server = http.createServer((req, res) => { - const credentials = auth(req) + const credentials = auth(req); if (credentials.name === 'test' && credentials.pass === 'test') { - res.end(message) + res.end(message); } else { - res.end('failed') + res.end('failed'); } - server.close() - }) + server.close(); + }); server.listen(0, '127.0.0.1', () => { - const port = server.address().port + const port = server.address().port; webview.addEventListener('ipc-message', (e) => { - expect(e.channel).to.equal(message) - done() - }) + expect(e.channel).to.equal(message); + done(); + }); loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/basic-auth.html?port=${port}` - }) - }) - }) - }) + }); + }); + }); + }); describe('dom-ready event', () => { it('emits when document is loaded', (done) => { - const server = http.createServer(() => {}) + const server = http.createServer(() => {}); server.listen(0, '127.0.0.1', () => { - const port = server.address().port + const port = server.address().port; webview.addEventListener('dom-ready', () => { - done() - }) + done(); + }); loadWebView(webview, { src: `file://${fixtures}/pages/dom-ready.html?port=${port}` - }) - }) - }) + }); + }); + }); it('throws a custom error when an API method is called before the event is emitted', () => { const expectedErrorMessage = 'The WebView must be attached to the DOM ' + - 'and the dom-ready event emitted before this method can be called.' - expect(() => { webview.stop() }).to.throw(expectedErrorMessage) - }) - }) + 'and the dom-ready event emitted before this method can be called.'; + expect(() => { webview.stop(); }).to.throw(expectedErrorMessage); + }); + }); describe('executeJavaScript', () => { it('should support user gesture', async () => { await loadWebView(webview, { src: `file://${fixtures}/pages/fullscreen.html` - }) + }); // Event handler has to be added before js execution. - const waitForEnterHtmlFullScreen = waitForEvent(webview, 'enter-html-full-screen') + const waitForEnterHtmlFullScreen = waitForEvent(webview, 'enter-html-full-screen'); - const jsScript = "document.querySelector('video').webkitRequestFullscreen()" - webview.executeJavaScript(jsScript, true) + const jsScript = "document.querySelector('video').webkitRequestFullscreen()"; + webview.executeJavaScript(jsScript, true); - return waitForEnterHtmlFullScreen - }) + return waitForEnterHtmlFullScreen; + }); it('can return the result of the executed script', async () => { await loadWebView(webview, { src: 'about:blank' - }) + }); - const jsScript = "'4'+2" - const expectedResult = '42' + const jsScript = "'4'+2"; + const expectedResult = '42'; - const result = await webview.executeJavaScript(jsScript) - expect(result).to.equal(expectedResult) - }) - }) + const result = await webview.executeJavaScript(jsScript); + expect(result).to.equal(expectedResult); + }); + }); it('supports inserting CSS', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }) - await webview.insertCSS('body { background-repeat: round; }') - const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")') - expect(result).to.equal('round') - }) + await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); + await webview.insertCSS('body { background-repeat: round; }'); + const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")'); + expect(result).to.equal('round'); + }); it('supports removing inserted CSS', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }) - const key = await webview.insertCSS('body { background-repeat: round; }') - await webview.removeInsertedCSS(key) - const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")') - expect(result).to.equal('repeat') - }) + await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); + const key = await webview.insertCSS('body { background-repeat: round; }'); + await webview.removeInsertedCSS(key); + const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")'); + expect(result).to.equal('repeat'); + }); describe('sendInputEvent', () => { it('can send keyboard event', async () => { loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/onkeyup.html` - }) - await waitForEvent(webview, 'dom-ready') + }); + await waitForEvent(webview, 'dom-ready'); - const waitForIpcMessage = waitForEvent(webview, 'ipc-message') + const waitForIpcMessage = waitForEvent(webview, 'ipc-message'); webview.sendInputEvent({ type: 'keyup', keyCode: 'c', modifiers: ['shift'] - }) + }); - const { channel, args } = await waitForIpcMessage - expect(channel).to.equal('keyup') - expect(args).to.deep.equal(['C', 'KeyC', 67, true, false]) - }) + const { channel, args } = await waitForIpcMessage; + expect(channel).to.equal('keyup'); + expect(args).to.deep.equal(['C', 'KeyC', 67, true, false]); + }); it('can send mouse event', async () => { loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/onmouseup.html` - }) - await waitForEvent(webview, 'dom-ready') + }); + await waitForEvent(webview, 'dom-ready'); - const waitForIpcMessage = waitForEvent(webview, 'ipc-message') + const waitForIpcMessage = waitForEvent(webview, 'ipc-message'); webview.sendInputEvent({ type: 'mouseup', modifiers: ['ctrl'], x: 10, y: 20 - }) + }); - const { channel, args } = await waitForIpcMessage - expect(channel).to.equal('mouseup') - expect(args).to.deep.equal([10, 20, false, true]) - }) - }) + const { channel, args } = await waitForIpcMessage; + expect(channel).to.equal('mouseup'); + expect(args).to.deep.equal([10, 20, false, true]); + }); + }); describe('media-started-playing media-paused events', () => { it('emits when audio starts and stops playing', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }) + await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); // With the new autoplay policy, audio elements must be unmuted // see https://goo.gl/xX8pDD. @@ -931,90 +931,90 @@ describe(' tag', function () { audio.src = "../assets/tone.wav" document.body.appendChild(audio); audio.play() - ` - webview.executeJavaScript(source, true) - await waitForEvent(webview, 'media-started-playing') + `; + webview.executeJavaScript(source, true); + await waitForEvent(webview, 'media-started-playing'); - webview.executeJavaScript('document.querySelector("audio").pause()', true) - await waitForEvent(webview, 'media-paused') - }) - }) + webview.executeJavaScript('document.querySelector("audio").pause()', true); + await waitForEvent(webview, 'media-paused'); + }); + }); describe('found-in-page event', () => { it('emits when a request is made', (done) => { - let requestId = null - const activeMatchOrdinal = [] + let requestId = null; + const activeMatchOrdinal = []; const listener = (e) => { - expect(e.result.requestId).to.equal(requestId) - expect(e.result.matches).to.equal(3) - activeMatchOrdinal.push(e.result.activeMatchOrdinal) + expect(e.result.requestId).to.equal(requestId); + expect(e.result.matches).to.equal(3); + activeMatchOrdinal.push(e.result.activeMatchOrdinal); if (e.result.activeMatchOrdinal === e.result.matches) { - expect(activeMatchOrdinal).to.deep.equal([1, 2, 3]) - webview.stopFindInPage('clearSelection') - done() + expect(activeMatchOrdinal).to.deep.equal([1, 2, 3]); + webview.stopFindInPage('clearSelection'); + done(); } else { - listener2() + listener2(); } - } + }; const listener2 = () => { - requestId = webview.findInPage('virtual') - } - webview.addEventListener('found-in-page', listener) - webview.addEventListener('did-finish-load', listener2) - loadWebView(webview, { src: `file://${fixtures}/pages/content.html` }) + requestId = webview.findInPage('virtual'); + }; + webview.addEventListener('found-in-page', listener); + webview.addEventListener('did-finish-load', listener2); + loadWebView(webview, { src: `file://${fixtures}/pages/content.html` }); // TODO(deepak1556): With https://codereview.chromium.org/2836973002 // focus of the webContents is required when triggering the api. // Remove this workaround after determining the cause for // incorrect focus. - webview.focus() - }) - }) + webview.focus(); + }); + }); describe('did-change-theme-color event', () => { it('emits when theme color changes', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/theme-color.html` - }) - await waitForEvent(webview, 'did-change-theme-color') - }) - }) + }); + await waitForEvent(webview, 'did-change-theme-color'); + }); + }); describe('.getWebContentsId', () => { it('can return the WebContents ID', async () => { - const src = 'about:blank' - await loadWebView(webview, { src }) + const src = 'about:blank'; + await loadWebView(webview, { src }); - expect(webview.getWebContentsId()).to.be.a('number') - }) - }) + expect(webview.getWebContentsId()).to.be.a('number'); + }); + }); describe('.capturePage()', () => { before(function () { // TODO(miniak): figure out why this is failing on windows if (process.platform === 'win32') { - this.skip() + this.skip(); } - }) + }); it('returns a Promise with a NativeImage', async () => { - const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E' - await loadWebView(webview, { src }) + const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; + await loadWebView(webview, { src }); - const image = await webview.capturePage() - const imgBuffer = image.toPNG() + const image = await webview.capturePage(); + const imgBuffer = image.toPNG(); // Check the 25th byte in the PNG. // Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha - expect(imgBuffer[25]).to.equal(6) - }) - }) + expect(imgBuffer[25]).to.equal(6); + }); + }); describe('.printToPDF()', () => { before(() => { if (!features.isPrintingEnabled()) { - this.skip() + this.skip(); } - }) + }); it('rejects on incorrectly typed parameters', async () => { const badTypes = { @@ -1026,144 +1026,144 @@ describe(' tag', function () { printSelectionOnly: 1, printBackground: 2, pageSize: 'IAmAPageSize' - } + }; // These will hard crash in Chromium unless we type-check for (const [key, value] of Object.entries(badTypes)) { - const param = { [key]: value } + const param = { [key]: value }; - const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E' - await loadWebView(webview, { src }) - await expect(webview.printToPDF(param)).to.eventually.be.rejected() + const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; + await loadWebView(webview, { src }); + await expect(webview.printToPDF(param)).to.eventually.be.rejected(); } - }) + }); it('can print to PDF', async () => { - const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E' - await loadWebView(webview, { src }) + const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; + await loadWebView(webview, { src }); - const data = await webview.printToPDF({}) - expect(data).to.be.an.instanceof(Uint8Array).that.is.not.empty() - }) - }) + const data = await webview.printToPDF({}); + expect(data).to.be.an.instanceof(Uint8Array).that.is.not.empty(); + }); + }); describe('will-attach-webview event', () => { it('does not emit when src is not changed', (done) => { - loadWebView(webview) + loadWebView(webview); setTimeout(() => { const expectedErrorMessage = 'The WebView must be attached to the DOM ' + - 'and the dom-ready event emitted before this method can be called.' - expect(() => { webview.stop() }).to.throw(expectedErrorMessage) - done() - }) - }) + 'and the dom-ready event emitted before this method can be called.'; + expect(() => { webview.stop(); }).to.throw(expectedErrorMessage); + done(); + }); + }); it('supports changing the web preferences', async () => { - ipcRenderer.send('disable-node-on-next-will-attach-webview') + ipcRenderer.send('disable-node-on-next-will-attach-webview'); const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'yes', src: `file://${fixtures}/pages/a.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'undefined', module: 'undefined', process: 'undefined', global: 'undefined' - }) - }) + }); + }); it('supports preventing a webview from being created', async () => { - ipcRenderer.send('prevent-next-will-attach-webview') + ipcRenderer.send('prevent-next-will-attach-webview'); loadWebView(webview, { src: `file://${fixtures}/pages/c.html` - }) - await waitForEvent(webview, 'destroyed') - }) + }); + await waitForEvent(webview, 'destroyed'); + }); it('supports removing the preload script', async () => { - ipcRenderer.send('disable-preload-on-next-will-attach-webview') + ipcRenderer.send('disable-preload-on-next-will-attach-webview'); const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'yes', preload: path.join(fixtures, 'module', 'preload-set-global.js'), src: `file://${fixtures}/pages/a.html` - }) + }); - expect(message).to.equal('undefined') - }) - }) + expect(message).to.equal('undefined'); + }); + }); describe('DOM events', () => { - let div + let div; beforeEach(() => { - div = document.createElement('div') - div.style.width = '100px' - div.style.height = '10px' - div.style.overflow = 'hidden' - webview.style.height = '100%' - webview.style.width = '100%' - }) + div = document.createElement('div'); + div.style.width = '100px'; + div.style.height = '10px'; + div.style.overflow = 'hidden'; + webview.style.height = '100%'; + webview.style.width = '100%'; + }); afterEach(() => { - if (div != null) div.remove() - }) + if (div != null) div.remove(); + }); const generateSpecs = (description, sandbox) => { describe(description, () => { // TODO(nornagon): disabled during chromium roll 2019-06-11 due to a // 'ResizeObserver loop limit exceeded' error on Windows xit('emits resize events', async () => { - const firstResizeSignal = waitForEvent(webview, 'resize') - const domReadySignal = waitForEvent(webview, 'dom-ready') + const firstResizeSignal = waitForEvent(webview, 'resize'); + const domReadySignal = waitForEvent(webview, 'dom-ready'); - webview.src = `file://${fixtures}/pages/a.html` - webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}` - div.appendChild(webview) - document.body.appendChild(div) + webview.src = `file://${fixtures}/pages/a.html`; + webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}`; + div.appendChild(webview); + document.body.appendChild(div); - const firstResizeEvent = await firstResizeSignal - expect(firstResizeEvent.target).to.equal(webview) - expect(firstResizeEvent.newWidth).to.equal(100) - expect(firstResizeEvent.newHeight).to.equal(10) + const firstResizeEvent = await firstResizeSignal; + expect(firstResizeEvent.target).to.equal(webview); + expect(firstResizeEvent.newWidth).to.equal(100); + expect(firstResizeEvent.newHeight).to.equal(10); - await domReadySignal + await domReadySignal; - const secondResizeSignal = waitForEvent(webview, 'resize') + const secondResizeSignal = waitForEvent(webview, 'resize'); - const newWidth = 1234 - const newHeight = 789 - div.style.width = `${newWidth}px` - div.style.height = `${newHeight}px` + const newWidth = 1234; + const newHeight = 789; + div.style.width = `${newWidth}px`; + div.style.height = `${newHeight}px`; - const secondResizeEvent = await secondResizeSignal - expect(secondResizeEvent.target).to.equal(webview) - expect(secondResizeEvent.newWidth).to.equal(newWidth) - expect(secondResizeEvent.newHeight).to.equal(newHeight) - }) + const secondResizeEvent = await secondResizeSignal; + expect(secondResizeEvent.target).to.equal(webview); + expect(secondResizeEvent.newWidth).to.equal(newWidth); + expect(secondResizeEvent.newHeight).to.equal(newHeight); + }); it('emits focus event', async () => { - const domReadySignal = waitForEvent(webview, 'dom-ready') - webview.src = `file://${fixtures}/pages/a.html` - webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}` - document.body.appendChild(webview) + const domReadySignal = waitForEvent(webview, 'dom-ready'); + webview.src = `file://${fixtures}/pages/a.html`; + webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}`; + document.body.appendChild(webview); - await domReadySignal + await domReadySignal; // If this test fails, check if webview.focus() still works. - const focusSignal = waitForEvent(webview, 'focus') - webview.focus() - - await focusSignal - }) - }) - } - - generateSpecs('without sandbox', false) - generateSpecs('with sandbox', true) - }) -}) + const focusSignal = waitForEvent(webview, 'focus'); + webview.focus(); + + await focusSignal; + }); + }); + }; + + generateSpecs('without sandbox', false); + generateSpecs('with sandbox', true); + }); +});