Advertisement
Guest User

Untitled

a guest
Jul 18th, 2019
2,022
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 284.04 KB | None | 0 0
  1.  Ì È Ä {"files":{"browser":{"files":{"api":{"files":{"app.js":{"size":3279,"offset":"0"},"auto-updater":{"files":{"auto-updater-native.js":{"size":315,"offset":"3532"},"auto-updater-win.js":{"size":2247,"offset":"3847"},"squirrel-update-win.js":{"size":3855,"offset":"6094"}}},"auto-updater.js":{"size":253,"offset":"3279"},"browser-view.js":{"size":411,"offset":"9949"},"browser-window.js":{"size":5670,"offset":"10360"},"content-tracing.js":{"size":73,"offset":"16030"},"crash-reporter.js":{"size":395,"offset":"16103"},"dialog.js":{"size":9080,"offset":"16498"},"exports":{"files":{"electron.js":{"size":530,"offset":"25578"}}},"global-shortcut.js":{"size":88,"offset":"26108"},"in-app-purchase.js":{"size":619,"offset":"26196"},"ipc-main.js":{"size":216,"offset":"26815"},"menu-item-roles.js":{"size":6795,"offset":"27031"},"menu-item.js":{"size":2803,"offset":"33826"},"menu-utils.js":{"size":4586,"offset":"36629"},"menu.js":{"size":8529,"offset":"41215"},"module-list.js":{"size":1908,"offset":"49744"},"navigation-controller.js":{"size":5661,"offset":"51652"},"net-log.js":{"size":830,"offset":"57313"},"net.js":{"size":10281,"offset":"58143"},"notification.js":{"size":283,"offset":"68424"},"power-monitor.js":{"size":793,"offset":"68707"},"power-save-blocker.js":{"size":93,"offset":"69500"},"protocol.js":{"size":820,"offset":"69593"},"screen.js":{"size":270,"offset":"70413"},"session.js":{"size":613,"offset":"70683"},"system-preferences.js":{"size":348,"offset":"71296"},"top-level-window.js":{"size":699,"offset":"71644"},"touch-bar.js":{"size":10768,"offset":"72343"},"tray.js":{"size":196,"offset":"83111"},"view.js":{"size":239,"offset":"83307"},"web-contents-view.js":{"size":379,"offset":"83546"},"web-contents.js":{"size":15583,"offset":"83925"}}},"chrome-extension.js":{"size":14751,"offset":"99508"},"desktop-capturer.js":{"size":2455,"offset":"114259"},"guest-view-manager.js":{"size":15187,"offset":"116714"},"guest-window-manager.js":{"size":13148,"offset":"131901"},"init.js":{"size":5945,"offset":"145049"},"ipc-main-internal.js":{"size":216,"offset":"150994"},"objects-registry.js":{"size":3411,"offset":"151210"},"rpc-server.js":{"size":18747,"offset":"154621"}}},"common":{"files":{"api":{"files":{"clipboard.js":{"size":1182,"offset":"173368"},"deprecate.js":{"size":2606,"offset":"174550"},"deprecations.js":{"size":242,"offset":"177156"},"exports":{"files":{"electron.js":{"size":920,"offset":"177398"}}},"is-promise.js":{"size":334,"offset":"178318"},"module-list.js":{"size":471,"offset":"178652"},"native-image.js":{"size":70,"offset":"179123"},"shell.js":{"size":63,"offset":"179193"}}},"atom-binding-setup.js":{"size":371,"offset":"179256"},"buffer-utils.js":{"size":1509,"offset":"179627"},"crash-reporter.js":{"size":3092,"offset":"181136"},"error-utils.js":{"size":1199,"offset":"184228"},"init.js":{"size":1889,"offset":"185427"},"parse-features-string.js":{"size":747,"offset":"187316"},"reset-search-paths.js":{"size":1744,"offset":"188063"},"web-view-methods.js":{"size":1271,"offset":"189807"}}},"renderer":{"files":{"api":{"files":{"crash-reporter.js":{"size":368,"offset":"191078"},"desktop-capturer.js":{"size":1421,"offset":"191446"},"exports":{"files":{"electron.js":{"size":541,"offset":"192867"}}},"ipc-renderer.js":{"size":834,"offset":"193408"},"module-list.js":{"size":735,"offset":"194242"},"remote.js":{"size":11723,"offset":"194977"},"screen.js":{"size":146,"offset":"206700"},"web-frame.js":{"size":1696,"offset":"206846"}}},"callbacks-registry.js":{"size":1510,"offset":"208542"},"chrome-api.js":{"size":6056,"offset":"210052"},"content-scripts-injector.js":{"size":3360,"offset":"216108"},"extensions":{"files":{"event.js":{"size":446,"offset":"219468"},"i18n.js":{"size":2683,"offset":"219914"},"storage.js":{"size":3731,"offset":"222597"},"web-navigation.js":{"size":621,"offset":"226328"}}},"init.js":{"size":6149,"offset":"226949"},"inspector.js":{"size":3457,"offset":"233098"},"ipc-renderer-internal.js":{"size":759,"offset":"236555"},"override.js":{"size":783,"offset":"237314"},"remote.js":{"size":220,"offset":"238097"},"security-warnings.js":{"size":11985,"offset":"238317"},"web-frame-init.js":{"size":847,"offset":"250302"},"web-view":{"files":{"guest-view-internal.js":{"size":4383,"offset":"251149"},"web-view-attributes.js":{"size":9820,"offset":"255532"},"web-view-constants.js":{"size":1150,"offset":"265352"},"web-view.js":{"size":12358,"offset":"266502"}}},"window-setup.js":{"size":6179,"offset":"278860"}}},"worker":{"files":{"init.js":{"size":1244,"offset":"285039"}}}}}'use strict'
  2.  
  3. const bindings = process.atomBinding('app')
  4. const path = require('path')
  5. const { app, App } = bindings
  6.  
  7. // Only one app object permitted.
  8. module.exports = app
  9.  
  10. const electron = require('electron')
  11. const { deprecate, Menu } = electron
  12. const { EventEmitter } = require('events')
  13.  
  14. let dockMenu = null
  15.  
  16. // App is an EventEmitter.
  17. Object.setPrototypeOf(App.prototype, EventEmitter.prototype)
  18. EventEmitter.call(app)
  19.  
  20. Object.assign(app, {
  21. setApplicationMenu (menu) {
  22. return Menu.setApplicationMenu(menu)
  23. },
  24. getApplicationMenu () {
  25. return Menu.getApplicationMenu()
  26. },
  27. commandLine: {
  28. appendSwitch (...args) {
  29. const castedArgs = args.map((arg) => {
  30. return typeof arg !== 'string' ? `${arg}` : arg
  31. })
  32. return bindings.appendSwitch(...castedArgs)
  33. },
  34. appendArgument (...args) {
  35. const castedArgs = args.map((arg) => {
  36. return typeof arg !== 'string' ? `${arg}` : arg
  37. })
  38. return bindings.appendArgument(...castedArgs)
  39. }
  40. }
  41. })
  42.  
  43. const nativeFn = app.getAppMetrics
  44. app.getAppMetrics = () => {
  45. const metrics = nativeFn.call(app)
  46. for (const metric of metrics) {
  47. if ('memory' in metric) {
  48. deprecate.removeProperty(metric, 'memory')
  49. }
  50. }
  51.  
  52. return metrics
  53. }
  54.  
  55. app.isPackaged = (() => {
  56. const execFile = path.basename(process.execPath).toLowerCase()
  57. if (process.platform === 'win32') {
  58. return execFile !== 'electron.exe'
  59. }
  60. return execFile !== 'electron'
  61. })()
  62.  
  63. if (process.platform === 'darwin') {
  64. app.dock = {
  65. bounce (type = 'informational') {
  66. return bindings.dockBounce(type)
  67. },
  68. cancelBounce: bindings.dockCancelBounce,
  69. downloadFinished: bindings.dockDownloadFinished,
  70. setBadge: bindings.dockSetBadgeText,
  71. getBadge: bindings.dockGetBadgeText,
  72. hide: bindings.dockHide,
  73. show: bindings.dockShow,
  74. isVisible: bindings.dockIsVisible,
  75. setMenu (menu) {
  76. dockMenu = menu
  77. bindings.dockSetMenu(menu)
  78. },
  79. getMenu () {
  80. return dockMenu
  81. },
  82. setIcon: bindings.dockSetIcon
  83. }
  84. }
  85.  
  86. if (process.platform === 'linux') {
  87. app.launcher = {
  88. setBadgeCount: bindings.unityLauncherSetBadgeCount,
  89. getBadgeCount: bindings.unityLauncherGetBadgeCount,
  90. isCounterBadgeAvailable: bindings.unityLauncherAvailable,
  91. isUnityRunning: bindings.unityLauncherAvailable
  92. }
  93. }
  94.  
  95. app.allowNTLMCredentialsForAllDomains = function (allow) {
  96. if (!process.noDeprecations) {
  97. deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains')
  98. }
  99. const domains = allow ? '*' : ''
  100. if (!this.isReady()) {
  101. this.commandLine.appendSwitch('auth-server-whitelist', domains)
  102. } else {
  103. electron.session.defaultSession.allowNTLMCredentialsForDomains(domains)
  104. }
  105. }
  106.  
  107. // Routes the events to webContents.
  108. const events = ['login', 'certificate-error', 'select-client-certificate']
  109. for (const name of events) {
  110. app.on(name, (event, webContents, ...args) => {
  111. webContents.emit(name, event, ...args)
  112. })
  113. }
  114.  
  115. // Wrappers for native classes.
  116. const { DownloadItem } = process.atomBinding('download_item')
  117. Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype)
  118. 'use strict'
  119.  
  120. if (process.platform === 'win32') {
  121. module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win')
  122. } else {
  123. module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native')
  124. }
  125. 'use strict'
  126.  
  127. const EventEmitter = require('events').EventEmitter
  128. const { autoUpdater, AutoUpdater } = process.atomBinding('auto_updater')
  129.  
  130. // AutoUpdater is an EventEmitter.
  131. Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype)
  132. EventEmitter.call(autoUpdater)
  133.  
  134. module.exports = autoUpdater
  135. 'use strict'
  136.  
  137. const { app } = require('electron')
  138. const { EventEmitter } = require('events')
  139. const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win')
  140.  
  141. class AutoUpdater extends EventEmitter {
  142. quitAndInstall () {
  143. if (!this.updateAvailable) {
  144. return this.emitError('No update available, can\'t quit and install')
  145. }
  146. squirrelUpdate.processStart()
  147. app.quit()
  148. }
  149.  
  150. getFeedURL () {
  151. return this.updateURL
  152. }
  153.  
  154. setFeedURL (options) {
  155. let updateURL
  156. if (typeof options === 'object') {
  157. if (typeof options.url === 'string') {
  158. updateURL = options.url
  159. } else {
  160. throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call')
  161. }
  162. } else if (typeof options === 'string') {
  163. updateURL = options
  164. } else {
  165. throw new Error('Expected an options object with a \'url\' property to be provided')
  166. }
  167. this.updateURL = updateURL
  168. }
  169.  
  170. checkForUpdates () {
  171. if (!this.updateURL) {
  172. return this.emitError('Update URL is not set')
  173. }
  174. if (!squirrelUpdate.supported()) {
  175. return this.emitError('Can not find Squirrel')
  176. }
  177. this.emit('checking-for-update')
  178. squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => {
  179. if (error != null) {
  180. return this.emitError(error)
  181. }
  182. if (update == null) {
  183. return this.emit('update-not-available')
  184. }
  185. this.updateAvailable = true
  186. this.emit('update-available')
  187. squirrelUpdate.update(this.updateURL, (error) => {
  188. if (error != null) {
  189. return this.emitError(error)
  190. }
  191. const { releaseNotes, version } = update
  192. // Date is not available on Windows, so fake it.
  193. const date = new Date()
  194. this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
  195. this.quitAndInstall()
  196. })
  197. })
  198. })
  199. }
  200.  
  201. // Private: Emit both error object and message, this is to keep compatibility
  202. // with Old APIs.
  203. emitError (message) {
  204. this.emit('error', new Error(message), message)
  205. }
  206. }
  207.  
  208. module.exports = new AutoUpdater()
  209. 'use strict'
  210.  
  211. const fs = require('fs')
  212. const path = require('path')
  213. const spawn = require('child_process').spawn
  214.  
  215. // i.e. my-app/app-0.1.13/
  216. const appFolder = path.dirname(process.execPath)
  217.  
  218. // i.e. my-app/Update.exe
  219. const updateExe = path.resolve(appFolder, '..', 'Update.exe')
  220. const exeName = path.basename(process.execPath)
  221. let spawnedArgs = []
  222. let spawnedProcess
  223.  
  224. const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i])
  225.  
  226. // Spawn a command and invoke the callback when it completes with an error
  227. // and the output from standard out.
  228. const spawnUpdate = function (args, detached, callback) {
  229. let error, errorEmitted, stderr, stdout
  230.  
  231. try {
  232. // Ensure we don't spawn multiple squirrel processes
  233. // Process spawned, same args: Attach events to alread running process
  234. // Process spawned, different args: Return with error
  235. // No process spawned: Spawn new process
  236. if (spawnedProcess && !isSameArgs(args)) {
  237. // Disabled for backwards compatibility:
  238. // eslint-disable-next-line standard/no-callback-literal
  239. return callback(`AutoUpdater process with arguments ${args} is already running`)
  240. } else if (!spawnedProcess) {
  241. spawnedProcess = spawn(updateExe, args, {
  242. detached: detached,
  243. windowsHide: true
  244. })
  245. spawnedArgs = args || []
  246. }
  247. } catch (error1) {
  248. error = error1
  249.  
  250. // Shouldn't happen, but still guard it.
  251. process.nextTick(function () {
  252. return callback(error)
  253. })
  254. return
  255. }
  256. stdout = ''
  257. stderr = ''
  258.  
  259. spawnedProcess.stdout.on('data', (data) => { stdout += data })
  260. spawnedProcess.stderr.on('data', (data) => { stderr += data })
  261.  
  262. errorEmitted = false
  263. spawnedProcess.on('error', (error) => {
  264. errorEmitted = true
  265. callback(error)
  266. })
  267.  
  268. return spawnedProcess.on('exit', function (code, signal) {
  269. spawnedProcess = undefined
  270. spawnedArgs = []
  271.  
  272. // We may have already emitted an error.
  273. if (errorEmitted) {
  274. return
  275. }
  276.  
  277. // Process terminated with error.
  278. if (code !== 0) {
  279. // Disabled for backwards compatibility:
  280. // eslint-disable-next-line standard/no-callback-literal
  281. return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`)
  282. }
  283.  
  284. // Success.
  285. callback(null, stdout)
  286. })
  287. }
  288.  
  289. // Start an instance of the installed app.
  290. exports.processStart = function () {
  291. return spawnUpdate(['--processStartAndWait', exeName], true, function () {})
  292. }
  293.  
  294. // Download the releases specified by the URL and write new results to stdout.
  295. exports.checkForUpdate = function (updateURL, callback) {
  296. return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) {
  297. let ref, ref1, update
  298. if (error != null) {
  299. return callback(error)
  300. }
  301. try {
  302. // Last line of output is the JSON details about the releases
  303. const json = stdout.trim().split('\n').pop()
  304. update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0
  305. } catch (jsonError) {
  306. // Disabled for backwards compatibility:
  307. // eslint-disable-next-line standard/no-callback-literal
  308. return callback(`Invalid result:\n${stdout}`)
  309. }
  310. return callback(null, update)
  311. })
  312. }
  313.  
  314. // Update the application to the latest remote version specified by URL.
  315. exports.update = function (updateURL, callback) {
  316. return spawnUpdate(['--update', updateURL], false, callback)
  317. }
  318.  
  319. // Is the Update.exe installed with the current application?
  320. exports.supported = function () {
  321. try {
  322. fs.accessSync(updateExe, fs.R_OK)
  323. return true
  324. } catch (error) {
  325. return false
  326. }
  327. }
  328. 'use strict'
  329.  
  330. const { EventEmitter } = require('events')
  331. const { BrowserView } = process.atomBinding('browser_view')
  332.  
  333. Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype)
  334.  
  335. BrowserView.fromWebContents = (webContents) => {
  336. for (const view of BrowserView.getAllViews()) {
  337. if (view.webContents.equal(webContents)) return view
  338. }
  339.  
  340. return null
  341. }
  342.  
  343. module.exports = BrowserView
  344. 'use strict'
  345.  
  346. const electron = require('electron')
  347. const { WebContentsView, TopLevelWindow } = electron
  348. const { BrowserWindow } = process.atomBinding('window')
  349.  
  350. Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype)
  351.  
  352. BrowserWindow.prototype._init = function () {
  353. // Call parent class's _init.
  354. TopLevelWindow.prototype._init.call(this)
  355.  
  356. // Avoid recursive require.
  357. const { app } = electron
  358.  
  359. // Create WebContentsView.
  360. this.setContentView(new WebContentsView(this.webContents))
  361.  
  362. const nativeSetBounds = this.setBounds
  363. this.setBounds = (bounds, ...opts) => {
  364. bounds = {
  365. ...this.getBounds(),
  366. ...bounds
  367. }
  368. nativeSetBounds.call(this, bounds, ...opts)
  369. }
  370.  
  371. // window.resizeTo(...)
  372. // window.moveTo(...)
  373. this.webContents.on('move', (event, size) => {
  374. this.setBounds(size)
  375. })
  376.  
  377. // Hide the auto-hide menu when webContents is focused.
  378. this.webContents.on('activate', () => {
  379. if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) {
  380. this.setMenuBarVisibility(false)
  381. }
  382. })
  383.  
  384. // Change window title to page title.
  385. this.webContents.on('page-title-updated', (event, title) => {
  386. // Route the event to BrowserWindow.
  387. this.emit('page-title-updated', event, title)
  388. if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title)
  389. })
  390.  
  391. // Sometimes the webContents doesn't get focus when window is shown, so we
  392. // have to force focusing on webContents in this case. The safest way is to
  393. // focus it when we first start to load URL, if we do it earlier it won't
  394. // have effect, if we do it later we might move focus in the page.
  395. //
  396. // Though this hack is only needed on macOS when the app is launched from
  397. // Finder, we still do it on all platforms in case of other bugs we don't
  398. // know.
  399. this.webContents.once('load-url', function () {
  400. this.focus()
  401. })
  402.  
  403. // Redirect focus/blur event to app instance too.
  404. this.on('blur', (event) => {
  405. app.emit('browser-window-blur', event, this)
  406. })
  407. this.on('focus', (event) => {
  408. app.emit('browser-window-focus', event, this)
  409. })
  410.  
  411. // Subscribe to visibilityState changes and pass to renderer process.
  412. let isVisible = this.isVisible() && !this.isMinimized()
  413. const visibilityChanged = () => {
  414. const newState = this.isVisible() && !this.isMinimized()
  415. if (isVisible !== newState) {
  416. isVisible = newState
  417. const visibilityState = isVisible ? 'visible' : 'hidden'
  418. this.webContents.emit('-window-visibility-change', visibilityState)
  419. }
  420. }
  421.  
  422. const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']
  423. for (const event of visibilityEvents) {
  424. this.on(event, visibilityChanged)
  425. }
  426.  
  427. // Notify the creation of the window.
  428. app.emit('browser-window-created', {}, this)
  429.  
  430. Object.defineProperty(this, 'devToolsWebContents', {
  431. enumerable: true,
  432. configurable: false,
  433. get () {
  434. return this.webContents.devToolsWebContents
  435. }
  436. })
  437. }
  438.  
  439. const isBrowserWindow = (win) => {
  440. return win && win.constructor.name === 'BrowserWindow'
  441. }
  442.  
  443. BrowserWindow.fromId = (id) => {
  444. const win = TopLevelWindow.fromId(id)
  445. return isBrowserWindow(win) ? win : null
  446. }
  447.  
  448. BrowserWindow.getAllWindows = () => {
  449. return TopLevelWindow.getAllWindows().filter(isBrowserWindow)
  450. }
  451.  
  452. BrowserWindow.getFocusedWindow = () => {
  453. for (const window of BrowserWindow.getAllWindows()) {
  454. if (window.isFocused() || window.isDevToolsFocused()) return window
  455. }
  456. return null
  457. }
  458.  
  459. BrowserWindow.fromWebContents = (webContents) => {
  460. for (const window of BrowserWindow.getAllWindows()) {
  461. if (window.webContents.equal(webContents)) return window
  462. }
  463. }
  464.  
  465. BrowserWindow.fromBrowserView = (browserView) => {
  466. for (const window of BrowserWindow.getAllWindows()) {
  467. if (window.getBrowserView() === browserView) return window
  468. }
  469.  
  470. return null
  471. }
  472.  
  473. BrowserWindow.fromDevToolsWebContents = (webContents) => {
  474. for (const window of BrowserWindow.getAllWindows()) {
  475. const { devToolsWebContents } = window
  476. if (devToolsWebContents != null && devToolsWebContents.equal(webContents)) {
  477. return window
  478. }
  479. }
  480. }
  481.  
  482. // Helpers.
  483. Object.assign(BrowserWindow.prototype, {
  484. loadURL (...args) {
  485. return this.webContents.loadURL(...args)
  486. },
  487. getURL (...args) {
  488. return this.webContents.getURL()
  489. },
  490. loadFile (...args) {
  491. return this.webContents.loadFile(...args)
  492. },
  493. reload (...args) {
  494. return this.webContents.reload(...args)
  495. },
  496. send (...args) {
  497. return this.webContents.send(...args)
  498. },
  499. openDevTools (...args) {
  500. return this.webContents.openDevTools(...args)
  501. },
  502. closeDevTools () {
  503. return this.webContents.closeDevTools()
  504. },
  505. isDevToolsOpened () {
  506. return this.webContents.isDevToolsOpened()
  507. },
  508. isDevToolsFocused () {
  509. return this.webContents.isDevToolsFocused()
  510. },
  511. toggleDevTools () {
  512. return this.webContents.toggleDevTools()
  513. },
  514. inspectElement (...args) {
  515. return this.webContents.inspectElement(...args)
  516. },
  517. inspectServiceWorker () {
  518. return this.webContents.inspectServiceWorker()
  519. },
  520. showDefinitionForSelection () {
  521. return this.webContents.showDefinitionForSelection()
  522. },
  523. capturePage (...args) {
  524. return this.webContents.capturePage(...args)
  525. },
  526. setTouchBar (touchBar) {
  527. electron.TouchBar._setOnWindow(touchBar, this)
  528. },
  529. setBackgroundThrottling (allowed) {
  530. this.webContents.setBackgroundThrottling(allowed)
  531. }
  532. })
  533.  
  534. module.exports = BrowserWindow
  535. 'use strict'
  536.  
  537. module.exports = process.atomBinding('content_tracing')
  538. 'use strict'
  539.  
  540. const CrashReporter = require('@electron/internal/common/crash-reporter')
  541. const ipcMain = require('@electron/internal/browser/ipc-main-internal')
  542.  
  543. class CrashReporterMain extends CrashReporter {
  544. sendSync (channel, ...args) {
  545. const event = {}
  546. ipcMain.emit(channel, event, ...args)
  547. return event.returnValue
  548. }
  549. }
  550.  
  551. module.exports = new CrashReporterMain()
  552. 'use strict'
  553.  
  554. const { app, BrowserWindow } = require('electron')
  555. const binding = process.atomBinding('dialog')
  556. const v8Util = process.atomBinding('v8_util')
  557.  
  558. const fileDialogProperties = {
  559. openFile: 1 << 0,
  560. openDirectory: 1 << 1,
  561. multiSelections: 1 << 2,
  562. createDirectory: 1 << 3,
  563. showHiddenFiles: 1 << 4,
  564. promptToCreate: 1 << 5,
  565. noResolveAliases: 1 << 6,
  566. treatPackageAsDirectory: 1 << 7
  567. }
  568.  
  569. const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
  570.  
  571. const messageBoxOptions = {
  572. noLink: 1 << 0
  573. }
  574.  
  575. const parseArgs = function (window, options, callback, ...args) {
  576. if (window != null && window.constructor !== BrowserWindow) {
  577. // Shift.
  578. [callback, options, window] = [options, window, null]
  579. }
  580.  
  581. if ((callback == null) && typeof options === 'function') {
  582. // Shift.
  583. [callback, options] = [options, null]
  584. }
  585.  
  586. // Fallback to using very last argument as the callback function
  587. const lastArgument = args[args.length - 1]
  588. if ((callback == null) && typeof lastArgument === 'function') {
  589. callback = lastArgument
  590. }
  591.  
  592. return [window, options, callback]
  593. }
  594.  
  595. const normalizeAccessKey = (text) => {
  596. if (typeof text !== 'string') return text
  597.  
  598. // macOS does not have access keys so remove single ampersands
  599. // and replace double ampersands with a single ampersand
  600. if (process.platform === 'darwin') {
  601. return text.replace(/&(&?)/g, '$1')
  602. }
  603.  
  604. // Linux uses a single underscore as an access key prefix so escape
  605. // existing single underscores with a second underscore, replace double
  606. // ampersands with a single ampersand, and replace a single ampersand with
  607. // a single underscore
  608. if (process.platform === 'linux') {
  609. return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
  610. if (after === '&') return after
  611. return `_${after}`
  612. })
  613. }
  614.  
  615. return text
  616. }
  617.  
  618. const checkAppInitialized = function () {
  619. if (!app.isReady()) {
  620. throw new Error('dialog module can only be used after app is ready')
  621. }
  622. }
  623.  
  624. module.exports = {
  625. showOpenDialog: function (...args) {
  626. checkAppInitialized()
  627.  
  628. let [window, options, callback] = parseArgs(...args)
  629.  
  630. if (options == null) {
  631. options = {
  632. title: 'Open',
  633. properties: ['openFile']
  634. }
  635. }
  636.  
  637. let { buttonLabel, defaultPath, filters, properties, title, message, securityScopedBookmarks = false } = options
  638.  
  639. if (properties == null) {
  640. properties = ['openFile']
  641. } else if (!Array.isArray(properties)) {
  642. throw new TypeError('Properties must be an array')
  643. }
  644.  
  645. let dialogProperties = 0
  646. for (const prop in fileDialogProperties) {
  647. if (properties.includes(prop)) {
  648. dialogProperties |= fileDialogProperties[prop]
  649. }
  650. }
  651.  
  652. if (title == null) {
  653. title = ''
  654. } else if (typeof title !== 'string') {
  655. throw new TypeError('Title must be a string')
  656. }
  657.  
  658. if (buttonLabel == null) {
  659. buttonLabel = ''
  660. } else if (typeof buttonLabel !== 'string') {
  661. throw new TypeError('Button label must be a string')
  662. }
  663.  
  664. if (defaultPath == null) {
  665. defaultPath = ''
  666. } else if (typeof defaultPath !== 'string') {
  667. throw new TypeError('Default path must be a string')
  668. }
  669.  
  670. if (filters == null) {
  671. filters = []
  672. }
  673.  
  674. if (message == null) {
  675. message = ''
  676. } else if (typeof message !== 'string') {
  677. throw new TypeError('Message must be a string')
  678. }
  679.  
  680. const wrappedCallback = typeof callback === 'function' ? function (success, result, bookmarkData) {
  681. return success ? callback(result, bookmarkData) : callback()
  682. } : null
  683. const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }
  684. settings.properties = dialogProperties
  685. return binding.showOpenDialog(settings, wrappedCallback)
  686. },
  687.  
  688. showSaveDialog: function (...args) {
  689. checkAppInitialized()
  690.  
  691. let [window, options, callback] = parseArgs(...args)
  692.  
  693. if (options == null) {
  694. options = {
  695. title: 'Save'
  696. }
  697. }
  698.  
  699. let { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks = false, nameFieldLabel, showsTagField } = options
  700.  
  701. if (title == null) {
  702. title = ''
  703. } else if (typeof title !== 'string') {
  704. throw new TypeError('Title must be a string')
  705. }
  706.  
  707. if (buttonLabel == null) {
  708. buttonLabel = ''
  709. } else if (typeof buttonLabel !== 'string') {
  710. throw new TypeError('Button label must be a string')
  711. }
  712.  
  713. if (defaultPath == null) {
  714. defaultPath = ''
  715. } else if (typeof defaultPath !== 'string') {
  716. throw new TypeError('Default path must be a string')
  717. }
  718.  
  719. if (filters == null) {
  720. filters = []
  721. }
  722.  
  723. if (message == null) {
  724. message = ''
  725. } else if (typeof message !== 'string') {
  726. throw new TypeError('Message must be a string')
  727. }
  728.  
  729. if (nameFieldLabel == null) {
  730. nameFieldLabel = ''
  731. } else if (typeof nameFieldLabel !== 'string') {
  732. throw new TypeError('Name field label must be a string')
  733. }
  734.  
  735. if (showsTagField == null) {
  736. showsTagField = true
  737. }
  738.  
  739. const wrappedCallback = typeof callback === 'function' ? function (success, result, bookmarkData) {
  740. return success ? callback(result, bookmarkData) : callback()
  741. } : null
  742. const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window }
  743. return binding.showSaveDialog(settings, wrappedCallback)
  744. },
  745.  
  746. showMessageBox: function (...args) {
  747. checkAppInitialized()
  748.  
  749. let [window, options, callback] = parseArgs(...args)
  750.  
  751. if (options == null) {
  752. options = {
  753. type: 'none'
  754. }
  755. }
  756.  
  757. let {
  758. buttons, cancelId, checkboxLabel, checkboxChecked, defaultId, detail,
  759. icon, message, title, type
  760. } = options
  761.  
  762. if (type == null) {
  763. type = 'none'
  764. }
  765.  
  766. const messageBoxType = messageBoxTypes.indexOf(type)
  767. if (messageBoxType === -1) {
  768. throw new TypeError('Invalid message box type')
  769. }
  770.  
  771. if (buttons == null) {
  772. buttons = []
  773. } else if (!Array.isArray(buttons)) {
  774. throw new TypeError('Buttons must be an array')
  775. }
  776.  
  777. if (options.normalizeAccessKeys) {
  778. buttons = buttons.map(normalizeAccessKey)
  779. }
  780.  
  781. if (title == null) {
  782. title = ''
  783. } else if (typeof title !== 'string') {
  784. throw new TypeError('Title must be a string')
  785. }
  786.  
  787. if (message == null) {
  788. message = ''
  789. } else if (typeof message !== 'string') {
  790. throw new TypeError('Message must be a string')
  791. }
  792.  
  793. if (detail == null) {
  794. detail = ''
  795. } else if (typeof detail !== 'string') {
  796. throw new TypeError('Detail must be a string')
  797. }
  798.  
  799. checkboxChecked = !!checkboxChecked
  800.  
  801. if (checkboxLabel == null) {
  802. checkboxLabel = ''
  803. } else if (typeof checkboxLabel !== 'string') {
  804. throw new TypeError('checkboxLabel must be a string')
  805. }
  806.  
  807. if (icon == null) {
  808. icon = null
  809. }
  810.  
  811. if (defaultId == null) {
  812. defaultId = -1
  813. }
  814.  
  815. // Choose a default button to get selected when dialog is cancelled.
  816. if (cancelId == null) {
  817. // If the defaultId is set to 0, ensure the cancel button is a different index (1)
  818. cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0
  819. for (let i = 0; i < buttons.length; i++) {
  820. const text = buttons[i].toLowerCase()
  821. if (text === 'cancel' || text === 'no') {
  822. cancelId = i
  823. break
  824. }
  825. }
  826. }
  827.  
  828. const flags = options.noLink ? messageBoxOptions.noLink : 0
  829. return binding.showMessageBox(messageBoxType, buttons, defaultId, cancelId,
  830. flags, title, message, detail, checkboxLabel,
  831. checkboxChecked, icon, window, callback)
  832. },
  833.  
  834. showErrorBox: function (...args) {
  835. return binding.showErrorBox(...args)
  836. },
  837.  
  838. showCertificateTrustDialog: function (...args) {
  839. const [window, options, callback] = parseArgs(...args)
  840.  
  841. if (options == null || typeof options !== 'object') {
  842. throw new TypeError('options must be an object')
  843. }
  844.  
  845. let { certificate, message } = options
  846. if (certificate == null || typeof certificate !== 'object') {
  847. throw new TypeError('certificate must be an object')
  848. }
  849.  
  850. if (message == null) {
  851. message = ''
  852. } else if (typeof message !== 'string') {
  853. throw new TypeError('message must be a string')
  854. }
  855.  
  856. return binding.showCertificateTrustDialog(window, certificate, message, callback)
  857. }
  858. }
  859.  
  860. // Mark standard asynchronous functions.
  861. v8Util.setHiddenValue(module.exports.showMessageBox, 'asynchronous', true)
  862. v8Util.setHiddenValue(module.exports.showOpenDialog, 'asynchronous', true)
  863. v8Util.setHiddenValue(module.exports.showSaveDialog, 'asynchronous', true)
  864. 'use strict'
  865.  
  866. const common = require('@electron/internal/common/api/exports/electron')
  867. // since browser module list is also used in renderer, keep it separate.
  868. const moduleList = require('@electron/internal/browser/api/module-list')
  869.  
  870. // Import common modules.
  871. common.defineProperties(exports)
  872.  
  873. for (const module of moduleList) {
  874. Object.defineProperty(exports, module.name, {
  875. enumerable: !module.private,
  876. get: common.memoizedGetter(() => require(`@electron/internal/browser/api/${module.file}.js`))
  877. })
  878. }
  879. 'use strict'
  880.  
  881. module.exports = process.atomBinding('global_shortcut').globalShortcut
  882. 'use strict'
  883.  
  884. if (process.platform === 'darwin') {
  885. const { EventEmitter } = require('events')
  886. const { inAppPurchase, InAppPurchase } = process.atomBinding('in_app_purchase')
  887.  
  888. // inAppPurchase is an EventEmitter.
  889. Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype)
  890. EventEmitter.call(inAppPurchase)
  891.  
  892. module.exports = inAppPurchase
  893. } else {
  894. module.exports = {
  895. purchaseProduct: (productID, quantity, callback) => {
  896. throw new Error('The inAppPurchase module can only be used on macOS')
  897. },
  898. canMakePayments: () => false,
  899. getReceiptURL: () => ''
  900. }
  901. }
  902. 'use strict'
  903.  
  904. const { EventEmitter } = require('events')
  905.  
  906. const emitter = new EventEmitter()
  907.  
  908. // Do not throw exception when channel name is "error".
  909. emitter.on('error', () => {})
  910.  
  911. module.exports = emitter
  912. 'use strict'
  913.  
  914. const { app } = require('electron')
  915.  
  916. const roles = {
  917. about: {
  918. get label () {
  919. return process.platform === 'linux' ? 'About' : `About ${app.getName()}`
  920. }
  921. },
  922. close: {
  923. label: process.platform === 'darwin' ? 'Close Window' : 'Close',
  924. accelerator: 'CommandOrControl+W',
  925. windowMethod: 'close'
  926. },
  927. copy: {
  928. label: 'Copy',
  929. accelerator: 'CommandOrControl+C',
  930. webContentsMethod: 'copy',
  931. registerAccelerator: false
  932. },
  933. cut: {
  934. label: 'Cut',
  935. accelerator: 'CommandOrControl+X',
  936. webContentsMethod: 'cut',
  937. registerAccelerator: false
  938. },
  939. delete: {
  940. label: 'Delete',
  941. webContentsMethod: 'delete'
  942. },
  943. forcereload: {
  944. label: 'Force Reload',
  945. accelerator: 'Shift+CmdOrCtrl+R',
  946. nonNativeMacOSRole: true,
  947. windowMethod: (window) => {
  948. window.webContents.reloadIgnoringCache()
  949. }
  950. },
  951. front: {
  952. label: 'Bring All to Front'
  953. },
  954. help: {
  955. label: 'Help'
  956. },
  957. hide: {
  958. get label () {
  959. return `Hide ${app.getName()}`
  960. },
  961. accelerator: 'Command+H'
  962. },
  963. hideothers: {
  964. label: 'Hide Others',
  965. accelerator: 'Command+Alt+H'
  966. },
  967. minimize: {
  968. label: 'Minimize',
  969. accelerator: 'CommandOrControl+M',
  970. windowMethod: 'minimize'
  971. },
  972. paste: {
  973. label: 'Paste',
  974. accelerator: 'CommandOrControl+V',
  975. webContentsMethod: 'paste',
  976. registerAccelerator: false
  977. },
  978. pasteandmatchstyle: {
  979. label: 'Paste and Match Style',
  980. accelerator: 'Shift+CommandOrControl+V',
  981. webContentsMethod: 'pasteAndMatchStyle',
  982. registerAccelerator: false
  983. },
  984. quit: {
  985. get label () {
  986. switch (process.platform) {
  987. case 'darwin': return `Quit ${app.getName()}`
  988. case 'win32': return 'Exit'
  989. default: return 'Quit'
  990. }
  991. },
  992. accelerator: process.platform === 'win32' ? null : 'CommandOrControl+Q',
  993. appMethod: 'quit'
  994. },
  995. redo: {
  996. label: 'Redo',
  997. accelerator: process.platform === 'win32' ? 'Control+Y' : 'Shift+CommandOrControl+Z',
  998. webContentsMethod: 'redo'
  999. },
  1000. reload: {
  1001. label: 'Reload',
  1002. accelerator: 'CmdOrCtrl+R',
  1003. nonNativeMacOSRole: true,
  1004. windowMethod: 'reload'
  1005. },
  1006. resetzoom: {
  1007. label: 'Actual Size',
  1008. accelerator: 'CommandOrControl+0',
  1009. nonNativeMacOSRole: true,
  1010. webContentsMethod: (webContents) => {
  1011. webContents.setZoomLevel(0)
  1012. }
  1013. },
  1014. selectall: {
  1015. label: 'Select All',
  1016. accelerator: 'CommandOrControl+A',
  1017. webContentsMethod: 'selectAll'
  1018. },
  1019. services: {
  1020. label: 'Services'
  1021. },
  1022. recentdocuments: {
  1023. label: 'Open Recent'
  1024. },
  1025. clearrecentdocuments: {
  1026. label: 'Clear Menu'
  1027. },
  1028. startspeaking: {
  1029. label: 'Start Speaking'
  1030. },
  1031. stopspeaking: {
  1032. label: 'Stop Speaking'
  1033. },
  1034. toggledevtools: {
  1035. label: 'Toggle Developer Tools',
  1036. accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
  1037. nonNativeMacOSRole: true,
  1038. windowMethod: 'toggleDevTools'
  1039. },
  1040. togglefullscreen: {
  1041. label: 'Toggle Full Screen',
  1042. accelerator: process.platform === 'darwin' ? 'Control+Command+F' : 'F11',
  1043. windowMethod: (window) => {
  1044. window.setFullScreen(!window.isFullScreen())
  1045. }
  1046. },
  1047. undo: {
  1048. label: 'Undo',
  1049. accelerator: 'CommandOrControl+Z',
  1050. webContentsMethod: 'undo'
  1051. },
  1052. unhide: {
  1053. label: 'Show All'
  1054. },
  1055. window: {
  1056. label: 'Window'
  1057. },
  1058. zoom: {
  1059. label: 'Zoom'
  1060. },
  1061. zoomin: {
  1062. label: 'Zoom In',
  1063. accelerator: 'CommandOrControl+Plus',
  1064. nonNativeMacOSRole: true,
  1065. webContentsMethod: (webContents) => {
  1066. webContents.getZoomLevel((zoomLevel) => {
  1067. webContents.setZoomLevel(zoomLevel + 0.5)
  1068. })
  1069. }
  1070. },
  1071. zoomout: {
  1072. label: 'Zoom Out',
  1073. accelerator: 'CommandOrControl+-',
  1074. nonNativeMacOSRole: true,
  1075. webContentsMethod: (webContents) => {
  1076. webContents.getZoomLevel((zoomLevel) => {
  1077. webContents.setZoomLevel(zoomLevel - 0.5)
  1078. })
  1079. }
  1080. },
  1081. // Edit submenu (should fit both Mac & Windows)
  1082. editmenu: {
  1083. label: 'Edit',
  1084. submenu: [
  1085. {
  1086. role: 'undo'
  1087. },
  1088. {
  1089. role: 'redo'
  1090. },
  1091. {
  1092. type: 'separator'
  1093. },
  1094. {
  1095. role: 'cut'
  1096. },
  1097. {
  1098. role: 'copy'
  1099. },
  1100. {
  1101. role: 'paste'
  1102. },
  1103.  
  1104. process.platform === 'darwin' ? {
  1105. role: 'pasteAndMatchStyle'
  1106. } : null,
  1107.  
  1108. {
  1109. role: 'delete'
  1110. },
  1111.  
  1112. process.platform === 'win32' ? {
  1113. type: 'separator'
  1114. } : null,
  1115.  
  1116. {
  1117. role: 'selectAll'
  1118. }
  1119. ]
  1120. },
  1121.  
  1122. // Window submenu should be used for Mac only
  1123. windowmenu: {
  1124. label: 'Window',
  1125. submenu: [
  1126. {
  1127. role: 'minimize'
  1128. },
  1129. {
  1130. role: 'close'
  1131. },
  1132.  
  1133. process.platform === 'darwin' ? {
  1134. type: 'separator'
  1135. } : null,
  1136.  
  1137. process.platform === 'darwin' ? {
  1138. role: 'front'
  1139. } : null
  1140.  
  1141. ]
  1142. }
  1143. }
  1144.  
  1145. const canExecuteRole = (role) => {
  1146. if (!roles.hasOwnProperty(role)) return false
  1147. if (process.platform !== 'darwin') return true
  1148.  
  1149. // macOS handles all roles natively except for a few
  1150. return roles[role].nonNativeMacOSRole
  1151. }
  1152.  
  1153. exports.getDefaultLabel = (role) => {
  1154. return roles.hasOwnProperty(role) ? roles[role].label : ''
  1155. }
  1156.  
  1157. exports.getDefaultAccelerator = (role) => {
  1158. if (roles.hasOwnProperty(role)) return roles[role].accelerator
  1159. }
  1160.  
  1161. exports.shouldRegisterAccelerator = (role) => {
  1162. const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined
  1163. return hasRoleRegister ? roles[role].registerAccelerator : true
  1164. }
  1165.  
  1166. exports.getDefaultSubmenu = (role) => {
  1167. if (!roles.hasOwnProperty(role)) return
  1168.  
  1169. let { submenu } = roles[role]
  1170.  
  1171. // remove null items from within the submenu
  1172. if (Array.isArray(submenu)) {
  1173. submenu = submenu.filter((item) => item != null)
  1174. }
  1175.  
  1176. return submenu
  1177. }
  1178.  
  1179. exports.execute = (role, focusedWindow, focusedWebContents) => {
  1180. if (!canExecuteRole(role)) return false
  1181.  
  1182. const { appMethod, webContentsMethod, windowMethod } = roles[role]
  1183.  
  1184. if (appMethod) {
  1185. app[appMethod]()
  1186. return true
  1187. }
  1188.  
  1189. if (windowMethod && focusedWindow != null) {
  1190. if (typeof windowMethod === 'function') {
  1191. windowMethod(focusedWindow)
  1192. } else {
  1193. focusedWindow[windowMethod]()
  1194. }
  1195. return true
  1196. }
  1197.  
  1198. if (webContentsMethod && focusedWebContents != null) {
  1199. if (typeof webContentsMethod === 'function') {
  1200. webContentsMethod(focusedWebContents)
  1201. } else {
  1202. focusedWebContents[webContentsMethod]()
  1203. }
  1204. return true
  1205. }
  1206.  
  1207. return false
  1208. }
  1209. 'use strict'
  1210.  
  1211. const roles = require('@electron/internal/browser/api/menu-item-roles')
  1212.  
  1213. let nextCommandId = 0
  1214.  
  1215. const MenuItem = function (options) {
  1216. const { Menu } = require('electron')
  1217.  
  1218. // Preserve extra fields specified by user
  1219. for (const key in options) {
  1220. if (!(key in this)) this[key] = options[key]
  1221. }
  1222. if (typeof this.role === 'string' || this.role instanceof String) {
  1223. this.role = this.role.toLowerCase()
  1224. }
  1225. this.submenu = this.submenu || roles.getDefaultSubmenu(this.role)
  1226. if (this.submenu != null && this.submenu.constructor !== Menu) {
  1227. this.submenu = Menu.buildFromTemplate(this.submenu)
  1228. }
  1229. if (this.type == null && this.submenu != null) {
  1230. this.type = 'submenu'
  1231. }
  1232. if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) {
  1233. throw new Error('Invalid submenu')
  1234. }
  1235.  
  1236. this.overrideReadOnlyProperty('type', 'normal')
  1237. this.overrideReadOnlyProperty('role')
  1238. this.overrideReadOnlyProperty('accelerator')
  1239. this.overrideReadOnlyProperty('icon')
  1240. this.overrideReadOnlyProperty('submenu')
  1241.  
  1242. this.overrideProperty('label', roles.getDefaultLabel(this.role))
  1243. this.overrideProperty('sublabel', '')
  1244. this.overrideProperty('enabled', true)
  1245. this.overrideProperty('visible', true)
  1246. this.overrideProperty('checked', false)
  1247. this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role))
  1248.  
  1249. if (!MenuItem.types.includes(this.type)) {
  1250. throw new Error(`Unknown menu item type: ${this.type}`)
  1251. }
  1252.  
  1253. this.overrideReadOnlyProperty('commandId', ++nextCommandId)
  1254.  
  1255. const click = options.click
  1256. this.click = (event, focusedWindow, focusedWebContents) => {
  1257. // Manually flip the checked flags when clicked.
  1258. if (this.type === 'checkbox' || this.type === 'radio') {
  1259. this.checked = !this.checked
  1260. }
  1261.  
  1262. if (!roles.execute(this.role, focusedWindow, focusedWebContents)) {
  1263. if (typeof click === 'function') {
  1264. click(this, focusedWindow, event)
  1265. } else if (typeof this.selector === 'string' && process.platform === 'darwin') {
  1266. Menu.sendActionToFirstResponder(this.selector)
  1267. }
  1268. }
  1269. }
  1270. }
  1271.  
  1272. MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
  1273.  
  1274. MenuItem.prototype.getDefaultRoleAccelerator = function () {
  1275. return roles.getDefaultAccelerator(this.role)
  1276. }
  1277.  
  1278. MenuItem.prototype.overrideProperty = function (name, defaultValue = null) {
  1279. if (this[name] == null) {
  1280. this[name] = defaultValue
  1281. }
  1282. }
  1283.  
  1284. MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
  1285. this.overrideProperty(name, defaultValue)
  1286. Object.defineProperty(this, name, {
  1287. enumerable: true,
  1288. writable: false,
  1289. value: this[name]
  1290. })
  1291. }
  1292.  
  1293. module.exports = MenuItem
  1294. 'use strict'
  1295.  
  1296. function splitArray (arr, predicate) {
  1297. const result = arr.reduce((multi, item) => {
  1298. const current = multi[multi.length - 1]
  1299. if (predicate(item)) {
  1300. if (current.length > 0) multi.push([])
  1301. } else {
  1302. current.push(item)
  1303. }
  1304. return multi
  1305. }, [[]])
  1306.  
  1307. if (result[result.length - 1].length === 0) {
  1308. return result.slice(0, result.length - 1)
  1309. }
  1310. return result
  1311. }
  1312.  
  1313. function joinArrays (arrays, joinIDs) {
  1314. return arrays.reduce((joined, arr, i) => {
  1315. if (i > 0 && arr.length) {
  1316. if (joinIDs.length > 0) {
  1317. joined.push(joinIDs[0])
  1318. joinIDs.splice(0, 1)
  1319. } else {
  1320. joined.push({ type: 'separator' })
  1321. }
  1322. }
  1323. return joined.concat(arr)
  1324. }, [])
  1325. }
  1326.  
  1327. function pushOntoMultiMap (map, key, value) {
  1328. if (!map.has(key)) {
  1329. map.set(key, [])
  1330. }
  1331. map.get(key).push(value)
  1332. }
  1333.  
  1334. function indexOfGroupContainingID (groups, id, ignoreGroup) {
  1335. return groups.findIndex(
  1336. candidateGroup =>
  1337. candidateGroup !== ignoreGroup &&
  1338. candidateGroup.some(
  1339. candidateItem => candidateItem.id === id
  1340. )
  1341. )
  1342. }
  1343.  
  1344. // Sort nodes topologically using a depth-first approach. Encountered cycles
  1345. // are broken.
  1346. function sortTopologically (originalOrder, edgesById) {
  1347. const sorted = []
  1348. const marked = new Set()
  1349.  
  1350. const visit = (mark) => {
  1351. if (marked.has(mark)) return
  1352. marked.add(mark)
  1353. const edges = edgesById.get(mark)
  1354. if (edges != null) {
  1355. edges.forEach(visit)
  1356. }
  1357. sorted.push(mark)
  1358. }
  1359.  
  1360. originalOrder.forEach(visit)
  1361. return sorted
  1362. }
  1363.  
  1364. function attemptToMergeAGroup (groups) {
  1365. for (let i = 0; i < groups.length; i++) {
  1366. const group = groups[i]
  1367. for (const item of group) {
  1368. const toIDs = [...(item.before || []), ...(item.after || [])]
  1369. for (const id of toIDs) {
  1370. const index = indexOfGroupContainingID(groups, id, group)
  1371. if (index === -1) continue
  1372. const mergeTarget = groups[index]
  1373.  
  1374. mergeTarget.push(...group)
  1375. groups.splice(i, 1)
  1376. return true
  1377. }
  1378. }
  1379. }
  1380. return false
  1381. }
  1382.  
  1383. function mergeGroups (groups) {
  1384. let merged = true
  1385. while (merged) {
  1386. merged = attemptToMergeAGroup(groups)
  1387. }
  1388. return groups
  1389. }
  1390.  
  1391. function sortItemsInGroup (group) {
  1392. const originalOrder = group.map((node, i) => i)
  1393. const edges = new Map()
  1394. const idToIndex = new Map(group.map((item, i) => [item.id, i]))
  1395.  
  1396. group.forEach((item, i) => {
  1397. if (item.before) {
  1398. item.before.forEach(toID => {
  1399. const to = idToIndex.get(toID)
  1400. if (to != null) {
  1401. pushOntoMultiMap(edges, to, i)
  1402. }
  1403. })
  1404. }
  1405. if (item.after) {
  1406. item.after.forEach(toID => {
  1407. const to = idToIndex.get(toID)
  1408. if (to != null) {
  1409. pushOntoMultiMap(edges, i, to)
  1410. }
  1411. })
  1412. }
  1413. })
  1414.  
  1415. const sortedNodes = sortTopologically(originalOrder, edges)
  1416. return sortedNodes.map(i => group[i])
  1417. }
  1418.  
  1419. function findEdgesInGroup (groups, i, edges) {
  1420. const group = groups[i]
  1421. for (const item of group) {
  1422. if (item.beforeGroupContaining) {
  1423. for (const id of item.beforeGroupContaining) {
  1424. const to = indexOfGroupContainingID(groups, id, group)
  1425. if (to !== -1) {
  1426. pushOntoMultiMap(edges, to, i)
  1427. return
  1428. }
  1429. }
  1430. }
  1431. if (item.afterGroupContaining) {
  1432. for (const id of item.afterGroupContaining) {
  1433. const to = indexOfGroupContainingID(groups, id, group)
  1434. if (to !== -1) {
  1435. pushOntoMultiMap(edges, i, to)
  1436. return
  1437. }
  1438. }
  1439. }
  1440. }
  1441. }
  1442.  
  1443. function sortGroups (groups) {
  1444. const originalOrder = groups.map((item, i) => i)
  1445. const edges = new Map()
  1446.  
  1447. for (let i = 0; i < groups.length; i++) {
  1448. findEdgesInGroup(groups, i, edges)
  1449. }
  1450.  
  1451. const sortedGroupIndexes = sortTopologically(originalOrder, edges)
  1452. return sortedGroupIndexes.map(i => groups[i])
  1453. }
  1454.  
  1455. function sortMenuItems (menuItems) {
  1456. const isSeparator = (item) => item.type === 'separator'
  1457. const separators = menuItems.filter(i => i.type === 'separator')
  1458.  
  1459. // Split the items into their implicit groups based upon separators.
  1460. const groups = splitArray(menuItems, isSeparator)
  1461. const mergedGroups = mergeGroups(groups)
  1462. const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup)
  1463. const sortedGroups = sortGroups(mergedGroupsWithSortedItems)
  1464.  
  1465. const joined = joinArrays(sortedGroups, separators)
  1466. return joined
  1467. }
  1468.  
  1469. module.exports = { sortMenuItems }
  1470. 'use strict'
  1471.  
  1472. const { TopLevelWindow, MenuItem, webContents } = require('electron')
  1473. const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils')
  1474. const EventEmitter = require('events').EventEmitter
  1475. const v8Util = process.atomBinding('v8_util')
  1476. const bindings = process.atomBinding('menu')
  1477.  
  1478. const { Menu } = bindings
  1479. let applicationMenu = null
  1480. let groupIdIndex = 0
  1481.  
  1482. Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)
  1483.  
  1484. // Menu Delegate.
  1485. // This object should hold no reference to |Menu| to avoid cyclic reference.
  1486. const delegate = {
  1487. isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : undefined,
  1488. isCommandIdEnabled: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].enabled : undefined,
  1489. isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
  1490. getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
  1491. const command = menu.commandsMap[id]
  1492. if (!command) return
  1493. if (command.accelerator != null) return command.accelerator
  1494. if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
  1495. },
  1496. shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined,
  1497. executeCommand: (menu, event, id) => {
  1498. const command = menu.commandsMap[id]
  1499. if (!command) return
  1500. command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents())
  1501. },
  1502. menuWillShow: (menu) => {
  1503. // Ensure radio groups have at least one menu item seleted
  1504. for (const id in menu.groupsMap) {
  1505. const found = menu.groupsMap[id].find(item => item.checked) || null
  1506. if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true)
  1507. }
  1508. }
  1509. }
  1510.  
  1511. /* Instance Methods */
  1512.  
  1513. Menu.prototype._init = function () {
  1514. this.commandsMap = {}
  1515. this.groupsMap = {}
  1516. this.items = []
  1517. this.delegate = delegate
  1518. }
  1519.  
  1520. Menu.prototype.popup = function (options = {}) {
  1521. if (options == null || typeof options !== 'object') {
  1522. throw new TypeError('Options must be an object')
  1523. }
  1524. let { window, x, y, positioningItem, callback } = options
  1525.  
  1526. // no callback passed
  1527. if (!callback || typeof callback !== 'function') callback = () => {}
  1528.  
  1529. // set defaults
  1530. if (typeof x !== 'number') x = -1
  1531. if (typeof y !== 'number') y = -1
  1532. if (typeof positioningItem !== 'number') positioningItem = -1
  1533.  
  1534. // find which window to use
  1535. const wins = TopLevelWindow.getAllWindows()
  1536. if (!wins || wins.indexOf(window) === -1) {
  1537. window = TopLevelWindow.getFocusedWindow()
  1538. if (!window && wins && wins.length > 0) {
  1539. window = wins[0]
  1540. }
  1541. if (!window) {
  1542. throw new Error(`Cannot open Menu without a TopLevelWindow present`)
  1543. }
  1544. }
  1545.  
  1546. this.popupAt(window, x, y, positioningItem, callback)
  1547. return { browserWindow: window, x, y, position: positioningItem }
  1548. }
  1549.  
  1550. Menu.prototype.closePopup = function (window) {
  1551. if (window instanceof TopLevelWindow) {
  1552. this.closePopupAt(window.id)
  1553. } else {
  1554. // Passing -1 (invalid) would make closePopupAt close the all menu runners
  1555. // belong to this menu.
  1556. this.closePopupAt(-1)
  1557. }
  1558. }
  1559.  
  1560. Menu.prototype.getMenuItemById = function (id) {
  1561. const items = this.items
  1562.  
  1563. let found = items.find(item => item.id === id) || null
  1564. for (let i = 0; !found && i < items.length; i++) {
  1565. if (items[i].submenu) {
  1566. found = items[i].submenu.getMenuItemById(id)
  1567. }
  1568. }
  1569. return found
  1570. }
  1571.  
  1572. Menu.prototype.append = function (item) {
  1573. return this.insert(this.getItemCount(), item)
  1574. }
  1575.  
  1576. Menu.prototype.insert = function (pos, item) {
  1577. if ((item ? item.constructor : void 0) !== MenuItem) {
  1578. throw new TypeError('Invalid item')
  1579. }
  1580.  
  1581. // insert item depending on its type
  1582. insertItemByType.call(this, item, pos)
  1583.  
  1584. // set item properties
  1585. if (item.sublabel) this.setSublabel(pos, item.sublabel)
  1586. if (item.icon) this.setIcon(pos, item.icon)
  1587. if (item.role) this.setRole(pos, item.role)
  1588.  
  1589. // Make menu accessable to items.
  1590. item.overrideReadOnlyProperty('menu', this)
  1591.  
  1592. // Remember the items.
  1593. this.items.splice(pos, 0, item)
  1594. this.commandsMap[item.commandId] = item
  1595. }
  1596.  
  1597. Menu.prototype._callMenuWillShow = function () {
  1598. if (this.delegate) this.delegate.menuWillShow(this)
  1599. this.items.forEach(item => {
  1600. if (item.submenu) item.submenu._callMenuWillShow()
  1601. })
  1602. }
  1603.  
  1604. /* Static Methods */
  1605.  
  1606. Menu.getApplicationMenu = () => applicationMenu
  1607.  
  1608. Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
  1609.  
  1610. // set application menu with a preexisting menu
  1611. Menu.setApplicationMenu = function (menu) {
  1612. if (menu && menu.constructor !== Menu) {
  1613. throw new TypeError('Invalid menu')
  1614. }
  1615.  
  1616. applicationMenu = menu
  1617. if (process.platform === 'darwin') {
  1618. if (!menu) return
  1619. menu._callMenuWillShow()
  1620. bindings.setApplicationMenu(menu)
  1621. } else {
  1622. const windows = TopLevelWindow.getAllWindows()
  1623. return windows.map(w => w.setMenu(menu))
  1624. }
  1625. }
  1626.  
  1627. Menu.buildFromTemplate = function (template) {
  1628. if (!Array.isArray(template)) {
  1629. throw new TypeError('Invalid template for Menu: Menu template must be an array')
  1630. }
  1631. const menu = new Menu()
  1632. if (!areValidTemplateItems(template)) {
  1633. throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type')
  1634. }
  1635. const filtered = removeExtraSeparators(template)
  1636. const sorted = sortTemplate(filtered)
  1637.  
  1638. sorted.forEach((item) => menu.append(new MenuItem(item)))
  1639.  
  1640. return menu
  1641. }
  1642.  
  1643. /* Helper Functions */
  1644.  
  1645. // validate the template against having the wrong attribute
  1646. function areValidTemplateItems (template) {
  1647. return template.every(item =>
  1648. item != null && typeof item === 'object' && (item.hasOwnProperty('label') || item.hasOwnProperty('role') || item.type === 'separator'))
  1649. }
  1650.  
  1651. function sortTemplate (template) {
  1652. const sorted = sortMenuItems(template)
  1653. for (const id in sorted) {
  1654. const item = sorted[id]
  1655. if (Array.isArray(item.submenu)) {
  1656. item.submenu = sortTemplate(item.submenu)
  1657. }
  1658. }
  1659. return sorted
  1660. }
  1661.  
  1662. // Search between separators to find a radio menu item and return its group id
  1663. function generateGroupId (items, pos) {
  1664. if (pos > 0) {
  1665. for (let idx = pos - 1; idx >= 0; idx--) {
  1666. if (items[idx].type === 'radio') return items[idx].groupId
  1667. if (items[idx].type === 'separator') break
  1668. }
  1669. } else if (pos < items.length) {
  1670. for (let idx = pos; idx <= items.length - 1; idx++) {
  1671. if (items[idx].type === 'radio') return items[idx].groupId
  1672. if (items[idx].type === 'separator') break
  1673. }
  1674. }
  1675. groupIdIndex += 1
  1676. return groupIdIndex
  1677. }
  1678.  
  1679. function removeExtraSeparators (items) {
  1680. // fold adjacent separators together
  1681. let ret = items.filter((e, idx, arr) => {
  1682. if (e.visible === false) return true
  1683. return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator'
  1684. })
  1685.  
  1686. // remove edge separators
  1687. ret = ret.filter((e, idx, arr) => {
  1688. if (e.visible === false) return true
  1689. return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1)
  1690. })
  1691.  
  1692. return ret
  1693. }
  1694.  
  1695. function insertItemByType (item, pos) {
  1696. const types = {
  1697. normal: () => this.insertItem(pos, item.commandId, item.label),
  1698. checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),
  1699. separator: () => this.insertSeparator(pos),
  1700. submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
  1701. radio: () => {
  1702. // Grouping radio menu items
  1703. item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
  1704. if (this.groupsMap[item.groupId] == null) {
  1705. this.groupsMap[item.groupId] = []
  1706. }
  1707. this.groupsMap[item.groupId].push(item)
  1708.  
  1709. // Setting a radio menu item should flip other items in the group.
  1710. v8Util.setHiddenValue(item, 'checked', item.checked)
  1711. Object.defineProperty(item, 'checked', {
  1712. enumerable: true,
  1713. get: () => v8Util.getHiddenValue(item, 'checked'),
  1714. set: () => {
  1715. this.groupsMap[item.groupId].forEach(other => {
  1716. if (other !== item) v8Util.setHiddenValue(other, 'checked', false)
  1717. })
  1718. v8Util.setHiddenValue(item, 'checked', true)
  1719. }
  1720. })
  1721. this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
  1722. }
  1723. }
  1724. types[item.type]()
  1725. }
  1726.  
  1727. module.exports = Menu
  1728. 'use strict'
  1729.  
  1730. const features = process.atomBinding('features')
  1731.  
  1732. // Browser side modules, please sort alphabetically.
  1733. module.exports = [
  1734. { name: 'app', file: 'app' },
  1735. { name: 'autoUpdater', file: 'auto-updater' },
  1736. { name: 'BrowserView', file: 'browser-view' },
  1737. { name: 'BrowserWindow', file: 'browser-window' },
  1738. { name: 'contentTracing', file: 'content-tracing' },
  1739. { name: 'crashReporter', file: 'crash-reporter' },
  1740. { name: 'dialog', file: 'dialog' },
  1741. { name: 'globalShortcut', file: 'global-shortcut' },
  1742. { name: 'ipcMain', file: 'ipc-main' },
  1743. { name: 'inAppPurchase', file: 'in-app-purchase' },
  1744. { name: 'Menu', file: 'menu' },
  1745. { name: 'MenuItem', file: 'menu-item' },
  1746. { name: 'net', file: 'net' },
  1747. { name: 'netLog', file: 'net-log' },
  1748. { name: 'Notification', file: 'notification' },
  1749. { name: 'powerMonitor', file: 'power-monitor' },
  1750. { name: 'powerSaveBlocker', file: 'power-save-blocker' },
  1751. { name: 'protocol', file: 'protocol' },
  1752. { name: 'screen', file: 'screen' },
  1753. { name: 'session', file: 'session' },
  1754. { name: 'systemPreferences', file: 'system-preferences' },
  1755. { name: 'TopLevelWindow', file: 'top-level-window' },
  1756. { name: 'TouchBar', file: 'touch-bar' },
  1757. { name: 'Tray', file: 'tray' },
  1758. { name: 'View', file: 'view' },
  1759. { name: 'webContents', file: 'web-contents' },
  1760. { name: 'WebContentsView', file: 'web-contents-view' },
  1761. // The internal modules, invisible unless you know their names.
  1762. { name: 'NavigationController', file: 'navigation-controller', private: true }
  1763. ]
  1764.  
  1765. if (features.isViewApiEnabled()) {
  1766. module.exports.push(
  1767. { name: 'BoxLayout', file: 'views/box-layout' },
  1768. { name: 'Button', file: 'views/button' },
  1769. { name: 'LabelButton', file: 'views/label-button' },
  1770. { name: 'LayoutManager', file: 'views/layout-manager' },
  1771. { name: 'TextField', file: 'views/text-field' }
  1772. )
  1773. }
  1774. 'use strict'
  1775.  
  1776. const ipcMain = require('@electron/internal/browser/ipc-main-internal')
  1777.  
  1778. // The history operation in renderer is redirected to browser.
  1779. ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) {
  1780. event.sender.goBack()
  1781. })
  1782.  
  1783. ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD', function (event) {
  1784. event.sender.goForward()
  1785. })
  1786.  
  1787. ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (event, offset) {
  1788. event.sender.goToOffset(offset)
  1789. })
  1790.  
  1791. ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
  1792. event.returnValue = event.sender.length()
  1793. })
  1794.  
  1795. // JavaScript implementation of Chromium's NavigationController.
  1796. // Instead of relying on Chromium for history control, we compeletely do history
  1797. // control on user land, and only rely on WebContents.loadURL for navigation.
  1798. // This helps us avoid Chromium's various optimizations so we can ensure renderer
  1799. // process is restarted everytime.
  1800. const NavigationController = (function () {
  1801. function NavigationController (webContents) {
  1802. this.webContents = webContents
  1803. this.clearHistory()
  1804.  
  1805. // webContents may have already navigated to a page.
  1806. if (this.webContents._getURL()) {
  1807. this.currentIndex++
  1808. this.history.push(this.webContents._getURL())
  1809. }
  1810. this.webContents.on('navigation-entry-commited', (event, url, inPage, replaceEntry) => {
  1811. if (this.inPageIndex > -1 && !inPage) {
  1812. // Navigated to a new page, clear in-page mark.
  1813. this.inPageIndex = -1
  1814. } else if (this.inPageIndex === -1 && inPage && !replaceEntry) {
  1815. // Started in-page navigations.
  1816. this.inPageIndex = this.currentIndex
  1817. }
  1818. if (this.pendingIndex >= 0) {
  1819. // Go to index.
  1820. this.currentIndex = this.pendingIndex
  1821. this.pendingIndex = -1
  1822. this.history[this.currentIndex] = url
  1823. } else if (replaceEntry) {
  1824. // Non-user initialized navigation.
  1825. this.history[this.currentIndex] = url
  1826. } else {
  1827. // Normal navigation. Clear history.
  1828. this.history = this.history.slice(0, this.currentIndex + 1)
  1829. this.currentIndex++
  1830. this.history.push(url)
  1831. }
  1832. })
  1833. }
  1834.  
  1835. NavigationController.prototype.loadURL = function (url, options) {
  1836. if (options == null) {
  1837. options = {}
  1838. }
  1839. this.pendingIndex = -1
  1840. this.webContents._loadURL(url, options)
  1841. return this.webContents.emit('load-url', url, options)
  1842. }
  1843.  
  1844. NavigationController.prototype.getURL = function () {
  1845. if (this.currentIndex === -1) {
  1846. return ''
  1847. } else {
  1848. return this.history[this.currentIndex]
  1849. }
  1850. }
  1851.  
  1852. NavigationController.prototype.stop = function () {
  1853. this.pendingIndex = -1
  1854. return this.webContents._stop()
  1855. }
  1856.  
  1857. NavigationController.prototype.reload = function () {
  1858. this.pendingIndex = this.currentIndex
  1859. return this.webContents._loadURL(this.getURL(), {})
  1860. }
  1861.  
  1862. NavigationController.prototype.reloadIgnoringCache = function () {
  1863. this.pendingIndex = this.currentIndex
  1864. return this.webContents._loadURL(this.getURL(), {
  1865. extraHeaders: 'pragma: no-cache\n'
  1866. })
  1867. }
  1868.  
  1869. NavigationController.prototype.canGoBack = function () {
  1870. return this.getActiveIndex() > 0
  1871. }
  1872.  
  1873. NavigationController.prototype.canGoForward = function () {
  1874. return this.getActiveIndex() < this.history.length - 1
  1875. }
  1876.  
  1877. NavigationController.prototype.canGoToIndex = function (index) {
  1878. return index >= 0 && index < this.history.length
  1879. }
  1880.  
  1881. NavigationController.prototype.canGoToOffset = function (offset) {
  1882. return this.canGoToIndex(this.currentIndex + offset)
  1883. }
  1884.  
  1885. NavigationController.prototype.clearHistory = function () {
  1886. this.history = []
  1887. this.currentIndex = -1
  1888. this.pendingIndex = -1
  1889. this.inPageIndex = -1
  1890. }
  1891.  
  1892. NavigationController.prototype.goBack = function () {
  1893. if (!this.canGoBack()) {
  1894. return
  1895. }
  1896. this.pendingIndex = this.getActiveIndex() - 1
  1897. if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
  1898. return this.webContents._goBack()
  1899. } else {
  1900. return this.webContents._loadURL(this.history[this.pendingIndex], {})
  1901. }
  1902. }
  1903.  
  1904. NavigationController.prototype.goForward = function () {
  1905. if (!this.canGoForward()) {
  1906. return
  1907. }
  1908. this.pendingIndex = this.getActiveIndex() + 1
  1909. if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
  1910. return this.webContents._goForward()
  1911. } else {
  1912. return this.webContents._loadURL(this.history[this.pendingIndex], {})
  1913. }
  1914. }
  1915.  
  1916. NavigationController.prototype.goToIndex = function (index) {
  1917. if (!this.canGoToIndex(index)) {
  1918. return
  1919. }
  1920. this.pendingIndex = index
  1921. return this.webContents._loadURL(this.history[this.pendingIndex], {})
  1922. }
  1923.  
  1924. NavigationController.prototype.goToOffset = function (offset) {
  1925. if (!this.canGoToOffset(offset)) {
  1926. return
  1927. }
  1928. const pendingIndex = this.currentIndex + offset
  1929. if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
  1930. this.pendingIndex = pendingIndex
  1931. return this.webContents._goToOffset(offset)
  1932. } else {
  1933. return this.goToIndex(pendingIndex)
  1934. }
  1935. }
  1936.  
  1937. NavigationController.prototype.getActiveIndex = function () {
  1938. if (this.pendingIndex === -1) {
  1939. return this.currentIndex
  1940. } else {
  1941. return this.pendingIndex
  1942. }
  1943. }
  1944.  
  1945. NavigationController.prototype.length = function () {
  1946. return this.history.length
  1947. }
  1948.  
  1949. return NavigationController
  1950. })()
  1951.  
  1952. module.exports = NavigationController
  1953. 'use strict'
  1954.  
  1955. // TODO(deepak1556): Deprecate and remove standalone netLog module,
  1956. // it is now a property of sessio module.
  1957. const { app, session } = require('electron')
  1958.  
  1959. // Fallback to default session.
  1960. Object.setPrototypeOf(module.exports, new Proxy({}, {
  1961. get (target, property) {
  1962. if (!app.isReady()) return
  1963.  
  1964. const netLog = session.defaultSession.netLog
  1965. if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return
  1966.  
  1967. // Returning a native function directly would throw error.
  1968. return (...args) => netLog[property](...args)
  1969. },
  1970.  
  1971. ownKeys () {
  1972. if (!app.isReady()) return []
  1973.  
  1974. return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog))
  1975. },
  1976.  
  1977. getOwnPropertyDescriptor (target) {
  1978. return { configurable: true, enumerable: true }
  1979. }
  1980. }))
  1981. 'use strict'
  1982.  
  1983. const url = require('url')
  1984. const { EventEmitter } = require('events')
  1985. const { Readable } = require('stream')
  1986. const { app } = require('electron')
  1987. const { Session } = process.atomBinding('session')
  1988. const { net, Net } = process.atomBinding('net')
  1989. const { URLRequest } = net
  1990.  
  1991. // Net is an EventEmitter.
  1992. Object.setPrototypeOf(Net.prototype, EventEmitter.prototype)
  1993. EventEmitter.call(net)
  1994.  
  1995. Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype)
  1996.  
  1997. const kSupportedProtocols = new Set(['http:', 'https:'])
  1998.  
  1999. class IncomingMessage extends Readable {
  2000. constructor (urlRequest) {
  2001. super()
  2002. this.urlRequest = urlRequest
  2003. this.shouldPush = false
  2004. this.data = []
  2005. this.urlRequest.on('data', (event, chunk) => {
  2006. this._storeInternalData(chunk)
  2007. this._pushInternalData()
  2008. })
  2009. this.urlRequest.on('end', () => {
  2010. this._storeInternalData(null)
  2011. this._pushInternalData()
  2012. })
  2013. }
  2014.  
  2015. get statusCode () {
  2016. return this.urlRequest.statusCode
  2017. }
  2018.  
  2019. get statusMessage () {
  2020. return this.urlRequest.statusMessage
  2021. }
  2022.  
  2023. get headers () {
  2024. return this.urlRequest.rawResponseHeaders
  2025. }
  2026.  
  2027. get httpVersion () {
  2028. return `${this.httpVersionMajor}.${this.httpVersionMinor}`
  2029. }
  2030.  
  2031. get httpVersionMajor () {
  2032. return this.urlRequest.httpVersionMajor
  2033. }
  2034.  
  2035. get httpVersionMinor () {
  2036. return this.urlRequest.httpVersionMinor
  2037. }
  2038.  
  2039. get rawTrailers () {
  2040. throw new Error('HTTP trailers are not supported.')
  2041. }
  2042.  
  2043. get trailers () {
  2044. throw new Error('HTTP trailers are not supported.')
  2045. }
  2046.  
  2047. _storeInternalData (chunk) {
  2048. this.data.push(chunk)
  2049. }
  2050.  
  2051. _pushInternalData () {
  2052. while (this.shouldPush && this.data.length > 0) {
  2053. const chunk = this.data.shift()
  2054. this.shouldPush = this.push(chunk)
  2055. }
  2056. }
  2057.  
  2058. _read () {
  2059. this.shouldPush = true
  2060. this._pushInternalData()
  2061. }
  2062. }
  2063.  
  2064. URLRequest.prototype._emitRequestEvent = function (isAsync, ...rest) {
  2065. if (isAsync) {
  2066. process.nextTick(() => {
  2067. this.clientRequest.emit(...rest)
  2068. })
  2069. } else {
  2070. this.clientRequest.emit(...rest)
  2071. }
  2072. }
  2073.  
  2074. URLRequest.prototype._emitResponseEvent = function (isAsync, ...rest) {
  2075. if (isAsync) {
  2076. process.nextTick(() => {
  2077. this._response.emit(...rest)
  2078. })
  2079. } else {
  2080. this._response.emit(...rest)
  2081. }
  2082. }
  2083.  
  2084. class ClientRequest extends EventEmitter {
  2085. constructor (options, callback) {
  2086. super()
  2087.  
  2088. if (!app.isReady()) {
  2089. throw new Error('net module can only be used after app is ready')
  2090. }
  2091.  
  2092. if (typeof options === 'string') {
  2093. options = url.parse(options)
  2094. } else {
  2095. options = Object.assign({}, options)
  2096. }
  2097.  
  2098. const method = (options.method || 'GET').toUpperCase()
  2099. let urlStr = options.url
  2100.  
  2101. if (!urlStr) {
  2102. const urlObj = {}
  2103. const protocol = options.protocol || 'http:'
  2104. if (!kSupportedProtocols.has(protocol)) {
  2105. throw new Error('Protocol "' + protocol + '" not supported. ')
  2106. }
  2107. urlObj.protocol = protocol
  2108.  
  2109. if (options.host) {
  2110. urlObj.host = options.host
  2111. } else {
  2112. if (options.hostname) {
  2113. urlObj.hostname = options.hostname
  2114. } else {
  2115. urlObj.hostname = 'localhost'
  2116. }
  2117.  
  2118. if (options.port) {
  2119. urlObj.port = options.port
  2120. }
  2121. }
  2122.  
  2123. if (options.path && / /.test(options.path)) {
  2124. // The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
  2125. // with an additional rule for ignoring percentage-escaped characters
  2126. // but that's a) hard to capture in a regular expression that performs
  2127. // well, and b) possibly too restrictive for real-world usage. That's
  2128. // why it only scans for spaces because those are guaranteed to create
  2129. // an invalid request.
  2130. throw new TypeError('Request path contains unescaped characters.')
  2131. }
  2132. const pathObj = url.parse(options.path || '/')
  2133. urlObj.pathname = pathObj.pathname
  2134. urlObj.search = pathObj.search
  2135. urlObj.hash = pathObj.hash
  2136. urlStr = url.format(urlObj)
  2137. }
  2138.  
  2139. const redirectPolicy = options.redirect || 'follow'
  2140. if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
  2141. throw new Error('redirect mode should be one of follow, error or manual')
  2142. }
  2143.  
  2144. const urlRequestOptions = {
  2145. method: method,
  2146. url: urlStr,
  2147. redirect: redirectPolicy
  2148. }
  2149. if (options.session) {
  2150. if (options.session instanceof Session) {
  2151. urlRequestOptions.session = options.session
  2152. } else {
  2153. throw new TypeError('`session` should be an instance of the Session class.')
  2154. }
  2155. } else if (options.partition) {
  2156. if (typeof options.partition === 'string') {
  2157. urlRequestOptions.partition = options.partition
  2158. } else {
  2159. throw new TypeError('`partition` should be an a string.')
  2160. }
  2161. }
  2162.  
  2163. const urlRequest = new URLRequest(urlRequestOptions)
  2164.  
  2165. // Set back and forward links.
  2166. this.urlRequest = urlRequest
  2167. urlRequest.clientRequest = this
  2168.  
  2169. // This is a copy of the extra headers structure held by the native
  2170. // net::URLRequest. The main reason is to keep the getHeader API synchronous
  2171. // after the request starts.
  2172. this.extraHeaders = {}
  2173.  
  2174. if (options.headers) {
  2175. for (const key in options.headers) {
  2176. this.setHeader(key, options.headers[key])
  2177. }
  2178. }
  2179.  
  2180. // Set when the request uses chunked encoding. Can be switched
  2181. // to true only once and never set back to false.
  2182. this.chunkedEncodingEnabled = false
  2183.  
  2184. urlRequest.on('response', () => {
  2185. const response = new IncomingMessage(urlRequest)
  2186. urlRequest._response = response
  2187. this.emit('response', response)
  2188. })
  2189.  
  2190. urlRequest.on('login', (event, authInfo, callback) => {
  2191. this.emit('login', authInfo, (username, password) => {
  2192. // If null or undefined username/password, force to empty string.
  2193. if (username === null || username === undefined) {
  2194. username = ''
  2195. }
  2196. if (typeof username !== 'string') {
  2197. throw new Error('username must be a string')
  2198. }
  2199. if (password === null || password === undefined) {
  2200. password = ''
  2201. }
  2202. if (typeof password !== 'string') {
  2203. throw new Error('password must be a string')
  2204. }
  2205. callback(username, password)
  2206. })
  2207. })
  2208.  
  2209. if (callback) {
  2210. this.once('response', callback)
  2211. }
  2212. }
  2213.  
  2214. get chunkedEncoding () {
  2215. return this.chunkedEncodingEnabled
  2216. }
  2217.  
  2218. set chunkedEncoding (value) {
  2219. if (!this.urlRequest.notStarted) {
  2220. throw new Error('Can\'t set the transfer encoding, headers have been sent.')
  2221. }
  2222. this.chunkedEncodingEnabled = value
  2223. }
  2224.  
  2225. setHeader (name, value) {
  2226. if (typeof name !== 'string') {
  2227. throw new TypeError('`name` should be a string in setHeader(name, value).')
  2228. }
  2229. if (value == null) {
  2230. throw new Error('`value` required in setHeader("' + name + '", value).')
  2231. }
  2232. if (!this.urlRequest.notStarted) {
  2233. throw new Error('Can\'t set headers after they are sent.')
  2234. }
  2235.  
  2236. const key = name.toLowerCase()
  2237. this.extraHeaders[key] = value
  2238. this.urlRequest.setExtraHeader(name, value.toString())
  2239. }
  2240.  
  2241. getHeader (name) {
  2242. if (name == null) {
  2243. throw new Error('`name` is required for getHeader(name).')
  2244. }
  2245.  
  2246. if (!this.extraHeaders) {
  2247. return
  2248. }
  2249.  
  2250. const key = name.toLowerCase()
  2251. return this.extraHeaders[key]
  2252. }
  2253.  
  2254. removeHeader (name) {
  2255. if (name == null) {
  2256. throw new Error('`name` is required for removeHeader(name).')
  2257. }
  2258.  
  2259. if (!this.urlRequest.notStarted) {
  2260. throw new Error('Can\'t remove headers after they are sent.')
  2261. }
  2262.  
  2263. const key = name.toLowerCase()
  2264. delete this.extraHeaders[key]
  2265. this.urlRequest.removeExtraHeader(name)
  2266. }
  2267.  
  2268. _write (chunk, encoding, callback, isLast) {
  2269. const chunkIsString = typeof chunk === 'string'
  2270. const chunkIsBuffer = chunk instanceof Buffer
  2271. if (!chunkIsString && !chunkIsBuffer) {
  2272. throw new TypeError('First argument must be a string or Buffer.')
  2273. }
  2274.  
  2275. if (chunkIsString) {
  2276. // We convert all strings into binary buffers.
  2277. chunk = Buffer.from(chunk, encoding)
  2278. }
  2279.  
  2280. // Since writing to the network is asynchronous, we conservatively
  2281. // assume that request headers are written after delivering the first
  2282. // buffer to the network IO thread.
  2283. if (this.urlRequest.notStarted) {
  2284. this.urlRequest.setChunkedUpload(this.chunkedEncoding)
  2285. }
  2286.  
  2287. // Headers are assumed to be sent on first call to _writeBuffer,
  2288. // i.e. after the first call to write or end.
  2289. const result = this.urlRequest.write(chunk, isLast)
  2290.  
  2291. // The write callback is fired asynchronously to mimic Node.js.
  2292. if (callback) {
  2293. process.nextTick(callback)
  2294. }
  2295.  
  2296. return result
  2297. }
  2298.  
  2299. write (data, encoding, callback) {
  2300. if (this.urlRequest.finished) {
  2301. const error = new Error('Write after end.')
  2302. process.nextTick(writeAfterEndNT, this, error, callback)
  2303. return true
  2304. }
  2305.  
  2306. return this._write(data, encoding, callback, false)
  2307. }
  2308.  
  2309. end (data, encoding, callback) {
  2310. if (this.urlRequest.finished) {
  2311. return false
  2312. }
  2313.  
  2314. if (typeof data === 'function') {
  2315. callback = data
  2316. encoding = null
  2317. data = null
  2318. } else if (typeof encoding === 'function') {
  2319. callback = encoding
  2320. encoding = null
  2321. }
  2322.  
  2323. data = data || ''
  2324.  
  2325. return this._write(data, encoding, callback, true)
  2326. }
  2327.  
  2328. followRedirect () {
  2329. this.urlRequest.followRedirect()
  2330. }
  2331.  
  2332. abort () {
  2333. this.urlRequest.cancel()
  2334. }
  2335.  
  2336. getUploadProgress () {
  2337. return this.urlRequest.getUploadProgress()
  2338. }
  2339. }
  2340.  
  2341. function writeAfterEndNT (self, error, callback) {
  2342. self.emit('error', error)
  2343. if (callback) callback(error)
  2344. }
  2345.  
  2346. Net.prototype.request = function (options, callback) {
  2347. return new ClientRequest(options, callback)
  2348. }
  2349.  
  2350. net.ClientRequest = ClientRequest
  2351.  
  2352. module.exports = net
  2353. 'use strict'
  2354.  
  2355. const { EventEmitter } = require('events')
  2356. const { Notification, isSupported } = process.atomBinding('notification')
  2357.  
  2358. Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype)
  2359.  
  2360. Notification.isSupported = isSupported
  2361.  
  2362. module.exports = Notification
  2363. 'use strict'
  2364.  
  2365. const { EventEmitter } = require('events')
  2366. const { powerMonitor, PowerMonitor } = process.atomBinding('power_monitor')
  2367.  
  2368. // PowerMonitor is an EventEmitter.
  2369. Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)
  2370. EventEmitter.call(powerMonitor)
  2371.  
  2372. // On Linux we need to call blockShutdown() to subscribe to shutdown event.
  2373. if (process.platform === 'linux') {
  2374. powerMonitor.on('newListener', (event) => {
  2375. if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
  2376. powerMonitor.blockShutdown()
  2377. }
  2378. })
  2379.  
  2380. powerMonitor.on('removeListener', (event) => {
  2381. if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
  2382. powerMonitor.unblockShutdown()
  2383. }
  2384. })
  2385. }
  2386.  
  2387. module.exports = powerMonitor
  2388. 'use strict'
  2389.  
  2390. module.exports = process.atomBinding('power_save_blocker').powerSaveBlocker
  2391. 'use strict'
  2392.  
  2393. const { app, session } = require('electron')
  2394.  
  2395. // Global protocol APIs.
  2396. module.exports = process.atomBinding('protocol')
  2397.  
  2398. // Fallback protocol APIs of default session.
  2399. Object.setPrototypeOf(module.exports, new Proxy({}, {
  2400. get (target, property) {
  2401. if (!app.isReady()) return
  2402.  
  2403. const protocol = session.defaultSession.protocol
  2404. if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return
  2405.  
  2406. // Returning a native function directly would throw error.
  2407. return (...args) => protocol[property](...args)
  2408. },
  2409.  
  2410. ownKeys () {
  2411. if (!app.isReady()) return []
  2412.  
  2413. return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.protocol))
  2414. },
  2415.  
  2416. getOwnPropertyDescriptor (target) {
  2417. return { configurable: true, enumerable: true }
  2418. }
  2419. }))
  2420. 'use strict'
  2421.  
  2422. const { EventEmitter } = require('events')
  2423. const { screen, Screen } = process.atomBinding('screen')
  2424.  
  2425. // Screen is an EventEmitter.
  2426. Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype)
  2427. EventEmitter.call(screen)
  2428.  
  2429. module.exports = screen
  2430. 'use strict'
  2431.  
  2432. const { EventEmitter } = require('events')
  2433. const { app } = require('electron')
  2434. const { fromPartition, Session, Cookies } = process.atomBinding('session')
  2435.  
  2436. // Public API.
  2437. Object.defineProperties(exports, {
  2438. defaultSession: {
  2439. enumerable: true,
  2440. get () { return fromPartition('') }
  2441. },
  2442. fromPartition: {
  2443. enumerable: true,
  2444. value: fromPartition
  2445. }
  2446. })
  2447.  
  2448. Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
  2449. Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
  2450.  
  2451. Session.prototype._init = function () {
  2452. app.emit('session-created', this)
  2453. }
  2454. 'use strict'
  2455.  
  2456. const { EventEmitter } = require('events')
  2457. const { systemPreferences, SystemPreferences } = process.atomBinding('system_preferences')
  2458.  
  2459. // SystemPreferences is an EventEmitter.
  2460. Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
  2461. EventEmitter.call(systemPreferences)
  2462.  
  2463. module.exports = systemPreferences
  2464. 'use strict'
  2465.  
  2466. const electron = require('electron')
  2467. const { EventEmitter } = require('events')
  2468. const { TopLevelWindow } = process.atomBinding('top_level_window')
  2469.  
  2470. Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype)
  2471.  
  2472. TopLevelWindow.prototype._init = function () {
  2473. // Avoid recursive require.
  2474. const { app } = electron
  2475.  
  2476. // Simulate the application menu on platforms other than macOS.
  2477. if (process.platform !== 'darwin') {
  2478. const menu = app.getApplicationMenu()
  2479. if (menu) this.setMenu(menu)
  2480. }
  2481. }
  2482.  
  2483. TopLevelWindow.getFocusedWindow = () => {
  2484. return TopLevelWindow.getAllWindows().find((win) => win.isFocused())
  2485. }
  2486.  
  2487. module.exports = TopLevelWindow
  2488. 'use strict'
  2489.  
  2490. const { EventEmitter } = require('events')
  2491.  
  2492. let nextItemID = 1
  2493.  
  2494. class TouchBar extends EventEmitter {
  2495. // Bind a touch bar to a window
  2496. static _setOnWindow (touchBar, window) {
  2497. if (window._touchBar != null) {
  2498. window._touchBar._removeFromWindow(window)
  2499. }
  2500.  
  2501. if (touchBar == null) {
  2502. window._setTouchBarItems([])
  2503. return
  2504. }
  2505.  
  2506. if (Array.isArray(touchBar)) {
  2507. touchBar = new TouchBar(touchBar)
  2508. }
  2509. touchBar._addToWindow(window)
  2510. }
  2511.  
  2512. constructor (options) {
  2513. super()
  2514.  
  2515. if (options == null) {
  2516. throw new Error('Must specify options object as first argument')
  2517. }
  2518.  
  2519. let { items, escapeItem } = options
  2520.  
  2521. // FIXME Support array as first argument, remove in 2.0
  2522. if (Array.isArray(options)) {
  2523. items = options
  2524. escapeItem = null
  2525. }
  2526.  
  2527. if (!Array.isArray(items)) {
  2528. items = []
  2529. }
  2530.  
  2531. this.changeListener = (item) => {
  2532. this.emit('change', item.id, item.type)
  2533. }
  2534.  
  2535. this.windowListeners = {}
  2536. this.items = {}
  2537. this.ordereredItems = []
  2538. this.escapeItem = escapeItem
  2539.  
  2540. const registerItem = (item) => {
  2541. this.items[item.id] = item
  2542. item.on('change', this.changeListener)
  2543. if (item.child instanceof TouchBar) {
  2544. item.child.ordereredItems.forEach(registerItem)
  2545. }
  2546. }
  2547. items.forEach((item) => {
  2548. if (!(item instanceof TouchBarItem)) {
  2549. throw new Error('Each item must be an instance of TouchBarItem')
  2550. }
  2551. this.ordereredItems.push(item)
  2552. registerItem(item)
  2553. })
  2554. }
  2555.  
  2556. set escapeItem (item) {
  2557. if (item != null && !(item instanceof TouchBarItem)) {
  2558. throw new Error('Escape item must be an instance of TouchBarItem')
  2559. }
  2560. if (this.escapeItem != null) {
  2561. this.escapeItem.removeListener('change', this.changeListener)
  2562. }
  2563. this._escapeItem = item
  2564. if (this.escapeItem != null) {
  2565. this.escapeItem.on('change', this.changeListener)
  2566. }
  2567. this.emit('escape-item-change', item)
  2568. }
  2569.  
  2570. get escapeItem () {
  2571. return this._escapeItem
  2572. }
  2573.  
  2574. _addToWindow (window) {
  2575. const { id } = window
  2576.  
  2577. // Already added to window
  2578. if (this.windowListeners.hasOwnProperty(id)) return
  2579.  
  2580. window._touchBar = this
  2581.  
  2582. const changeListener = (itemID) => {
  2583. window._refreshTouchBarItem(itemID)
  2584. }
  2585. this.on('change', changeListener)
  2586.  
  2587. const escapeItemListener = (item) => {
  2588. window._setEscapeTouchBarItem(item != null ? item : {})
  2589. }
  2590. this.on('escape-item-change', escapeItemListener)
  2591.  
  2592. const interactionListener = (event, itemID, details) => {
  2593. let item = this.items[itemID]
  2594. if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
  2595. item = this.escapeItem
  2596. }
  2597. if (item != null && item.onInteraction != null) {
  2598. item.onInteraction(details)
  2599. }
  2600. }
  2601. window.on('-touch-bar-interaction', interactionListener)
  2602.  
  2603. const removeListeners = () => {
  2604. this.removeListener('change', changeListener)
  2605. this.removeListener('escape-item-change', escapeItemListener)
  2606. window.removeListener('-touch-bar-interaction', interactionListener)
  2607. window.removeListener('closed', removeListeners)
  2608. window._touchBar = null
  2609. delete this.windowListeners[id]
  2610. const unregisterItems = (items) => {
  2611. for (const item of items) {
  2612. item.removeListener('change', this.changeListener)
  2613. if (item.child instanceof TouchBar) {
  2614. unregisterItems(item.child.ordereredItems)
  2615. }
  2616. }
  2617. }
  2618. unregisterItems(this.ordereredItems)
  2619. if (this.escapeItem) {
  2620. this.escapeItem.removeListener('change', this.changeListener)
  2621. }
  2622. }
  2623. window.once('closed', removeListeners)
  2624. this.windowListeners[id] = removeListeners
  2625.  
  2626. window._setTouchBarItems(this.ordereredItems)
  2627. escapeItemListener(this.escapeItem)
  2628. }
  2629.  
  2630. _removeFromWindow (window) {
  2631. const removeListeners = this.windowListeners[window.id]
  2632. if (removeListeners != null) removeListeners()
  2633. }
  2634. }
  2635.  
  2636. class TouchBarItem extends EventEmitter {
  2637. constructor () {
  2638. super()
  2639. this._addImmutableProperty('id', `${nextItemID++}`)
  2640. this._parents = []
  2641. }
  2642.  
  2643. _addImmutableProperty (name, value) {
  2644. Object.defineProperty(this, name, {
  2645. get: function () {
  2646. return value
  2647. },
  2648. set: function () {
  2649. throw new Error(`Cannot override property ${name}`)
  2650. },
  2651. enumerable: true,
  2652. configurable: false
  2653. })
  2654. }
  2655.  
  2656. _addLiveProperty (name, initialValue) {
  2657. const privateName = `_${name}`
  2658. this[privateName] = initialValue
  2659. Object.defineProperty(this, name, {
  2660. get: function () {
  2661. return this[privateName]
  2662. },
  2663. set: function (value) {
  2664. this[privateName] = value
  2665. this.emit('change', this)
  2666. },
  2667. enumerable: true
  2668. })
  2669. }
  2670.  
  2671. _addParent (item) {
  2672. const existing = this._parents.some(test => test.id === item.id)
  2673. if (!existing) {
  2674. this._parents.push({
  2675. id: item.id,
  2676. type: item.type
  2677. })
  2678. }
  2679. }
  2680. }
  2681.  
  2682. TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem {
  2683. constructor (config) {
  2684. super()
  2685. if (config == null) config = {}
  2686. this._addImmutableProperty('type', 'button')
  2687. const { click, icon, iconPosition, label, backgroundColor } = config
  2688. this._addLiveProperty('label', label)
  2689. this._addLiveProperty('backgroundColor', backgroundColor)
  2690. this._addLiveProperty('icon', icon)
  2691. this._addLiveProperty('iconPosition', iconPosition)
  2692. if (typeof click === 'function') {
  2693. this._addImmutableProperty('onInteraction', () => {
  2694. config.click()
  2695. })
  2696. }
  2697. }
  2698. }
  2699.  
  2700. TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem {
  2701. constructor (config) {
  2702. super()
  2703. if (config == null) config = {}
  2704. this._addImmutableProperty('type', 'colorpicker')
  2705. const { availableColors, change, selectedColor } = config
  2706. this._addLiveProperty('availableColors', availableColors)
  2707. this._addLiveProperty('selectedColor', selectedColor)
  2708.  
  2709. if (typeof change === 'function') {
  2710. this._addImmutableProperty('onInteraction', (details) => {
  2711. this._selectedColor = details.color
  2712. change(details.color)
  2713. })
  2714. }
  2715. }
  2716. }
  2717.  
  2718. TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem {
  2719. constructor (config) {
  2720. super()
  2721. if (config == null) config = {}
  2722. this._addImmutableProperty('type', 'group')
  2723. const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
  2724. this._addLiveProperty('child', defaultChild)
  2725. this.child.ordereredItems.forEach((item) => item._addParent(this))
  2726. }
  2727. }
  2728.  
  2729. TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem {
  2730. constructor (config) {
  2731. super()
  2732. if (config == null) config = {}
  2733. this._addImmutableProperty('type', 'label')
  2734. this._addLiveProperty('label', config.label)
  2735. this._addLiveProperty('textColor', config.textColor)
  2736. }
  2737. }
  2738.  
  2739. TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem {
  2740. constructor (config) {
  2741. super()
  2742. if (config == null) config = {}
  2743. this._addImmutableProperty('type', 'popover')
  2744. this._addLiveProperty('label', config.label)
  2745. this._addLiveProperty('icon', config.icon)
  2746. this._addLiveProperty('showCloseButton', config.showCloseButton)
  2747. const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
  2748. this._addLiveProperty('child', defaultChild)
  2749. this.child.ordereredItems.forEach((item) => item._addParent(this))
  2750. }
  2751. }
  2752.  
  2753. TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem {
  2754. constructor (config) {
  2755. super()
  2756. if (config == null) config = {}
  2757. this._addImmutableProperty('type', 'slider')
  2758. const { change, label, minValue, maxValue, value } = config
  2759. this._addLiveProperty('label', label)
  2760. this._addLiveProperty('minValue', minValue)
  2761. this._addLiveProperty('maxValue', maxValue)
  2762. this._addLiveProperty('value', value)
  2763.  
  2764. if (typeof change === 'function') {
  2765. this._addImmutableProperty('onInteraction', (details) => {
  2766. this._value = details.value
  2767. change(details.value)
  2768. })
  2769. }
  2770. }
  2771. }
  2772.  
  2773. TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem {
  2774. constructor (config) {
  2775. super()
  2776. if (config == null) config = {}
  2777. this._addImmutableProperty('type', 'spacer')
  2778. this._addImmutableProperty('size', config.size)
  2779. }
  2780. }
  2781.  
  2782. TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem {
  2783. constructor (config) {
  2784. super()
  2785. if (config == null) config = {}
  2786. const { segmentStyle, segments, selectedIndex, change, mode } = config
  2787. this._addImmutableProperty('type', 'segmented_control')
  2788. this._addLiveProperty('segmentStyle', segmentStyle)
  2789. this._addLiveProperty('segments', segments || [])
  2790. this._addLiveProperty('selectedIndex', selectedIndex)
  2791. this._addLiveProperty('mode', mode)
  2792.  
  2793. if (typeof change === 'function') {
  2794. this._addImmutableProperty('onInteraction', (details) => {
  2795. this._selectedIndex = details.selectedIndex
  2796. change(details.selectedIndex, details.isSelected)
  2797. })
  2798. }
  2799. }
  2800. }
  2801.  
  2802. TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem {
  2803. constructor (config) {
  2804. super()
  2805. if (config == null) config = {}
  2806. const { items, selectedStyle, overlayStyle, showArrowButtons, continuous, mode } = config
  2807. let { select, highlight } = config
  2808. this._addImmutableProperty('type', 'scrubber')
  2809. this._addLiveProperty('items', items)
  2810. this._addLiveProperty('selectedStyle', selectedStyle || null)
  2811. this._addLiveProperty('overlayStyle', overlayStyle || null)
  2812. this._addLiveProperty('showArrowButtons', showArrowButtons || false)
  2813. this._addLiveProperty('mode', mode || 'free')
  2814. this._addLiveProperty('continuous', typeof continuous === 'undefined' ? true : continuous)
  2815.  
  2816. if (typeof select === 'function' || typeof highlight === 'function') {
  2817. if (select == null) select = () => {}
  2818. if (highlight == null) highlight = () => {}
  2819. this._addImmutableProperty('onInteraction', (details) => {
  2820. if (details.type === 'select' && typeof select === 'function') {
  2821. select(details.selectedIndex)
  2822. } else if (details.type === 'highlight' && typeof highlight === 'function') {
  2823. highlight(details.highlightedIndex)
  2824. }
  2825. })
  2826. }
  2827. }
  2828. }
  2829.  
  2830. module.exports = TouchBar
  2831. 'use strict'
  2832.  
  2833. const { EventEmitter } = require('events')
  2834. const { Tray } = process.atomBinding('tray')
  2835.  
  2836. Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype)
  2837.  
  2838. module.exports = Tray
  2839. 'use strict'
  2840.  
  2841. const { EventEmitter } = require('events')
  2842. const { View } = process.atomBinding('view')
  2843.  
  2844. Object.setPrototypeOf(View.prototype, EventEmitter.prototype)
  2845.  
  2846. View.prototype._init = function () {
  2847. }
  2848.  
  2849. module.exports = View
  2850. 'use strict'
  2851.  
  2852. const electron = require('electron')
  2853.  
  2854. const { View } = electron
  2855. const { WebContentsView } = process.atomBinding('web_contents_view')
  2856.  
  2857. Object.setPrototypeOf(WebContentsView.prototype, View.prototype)
  2858.  
  2859. WebContentsView.prototype._init = function () {
  2860. // Call parent class's _init.
  2861. View.prototype._init.call(this)
  2862. }
  2863.  
  2864. module.exports = WebContentsView
  2865. 'use strict'
  2866.  
  2867. const features = process.atomBinding('features')
  2868. const { EventEmitter } = require('events')
  2869. const electron = require('electron')
  2870. const path = require('path')
  2871. const url = require('url')
  2872. const v8Util = process.atomBinding('v8_util')
  2873. const { app, ipcMain, session, NavigationController, deprecate } = electron
  2874.  
  2875. const ipcMainInternal = require('@electron/internal/browser/ipc-main-internal')
  2876. const errorUtils = require('@electron/internal/common/error-utils')
  2877.  
  2878. // session is not used here, the purpose is to make sure session is initalized
  2879. // before the webContents module.
  2880. // eslint-disable-next-line
  2881. session
  2882.  
  2883. let nextId = 0
  2884. const getNextId = function () {
  2885. return ++nextId
  2886. }
  2887.  
  2888. // Stock page sizes
  2889. const PDFPageSizes = {
  2890. A5: {
  2891. custom_display_name: 'A5',
  2892. height_microns: 210000,
  2893. name: 'ISO_A5',
  2894. width_microns: 148000
  2895. },
  2896. A4: {
  2897. custom_display_name: 'A4',
  2898. height_microns: 297000,
  2899. name: 'ISO_A4',
  2900. is_default: 'true',
  2901. width_microns: 210000
  2902. },
  2903. A3: {
  2904. custom_display_name: 'A3',
  2905. height_microns: 420000,
  2906. name: 'ISO_A3',
  2907. width_microns: 297000
  2908. },
  2909. Legal: {
  2910. custom_display_name: 'Legal',
  2911. height_microns: 355600,
  2912. name: 'NA_LEGAL',
  2913. width_microns: 215900
  2914. },
  2915. Letter: {
  2916. custom_display_name: 'Letter',
  2917. height_microns: 279400,
  2918. name: 'NA_LETTER',
  2919. width_microns: 215900
  2920. },
  2921. Tabloid: {
  2922. height_microns: 431800,
  2923. name: 'NA_LEDGER',
  2924. width_microns: 279400,
  2925. custom_display_name: 'Tabloid'
  2926. }
  2927. }
  2928.  
  2929. // Default printing setting
  2930. const defaultPrintingSetting = {
  2931. pageRage: [],
  2932. mediaSize: {},
  2933. landscape: false,
  2934. color: 2,
  2935. headerFooterEnabled: false,
  2936. marginsType: 0,
  2937. isFirstRequest: false,
  2938. requestID: getNextId(),
  2939. previewUIID: 0,
  2940. previewModifiable: true,
  2941. printToPDF: true,
  2942. printWithCloudPrint: false,
  2943. printWithPrivet: false,
  2944. printWithExtension: false,
  2945. deviceName: 'Save as PDF',
  2946. generateDraftData: true,
  2947. fitToPageEnabled: false,
  2948. scaleFactor: 1,
  2949. dpiHorizontal: 72,
  2950. dpiVertical: 72,
  2951. rasterizePDF: false,
  2952. duplex: 0,
  2953. copies: 1,
  2954. collate: true,
  2955. shouldPrintBackgrounds: false,
  2956. shouldPrintSelectionOnly: false
  2957. }
  2958.  
  2959. // JavaScript implementations of WebContents.
  2960. const binding = process.atomBinding('web_contents')
  2961. const { WebContents } = binding
  2962.  
  2963. Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype)
  2964. Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype)
  2965.  
  2966. // WebContents::send(channel, args..)
  2967. // WebContents::sendToAll(channel, args..)
  2968. WebContents.prototype.send = function (channel, ...args) {
  2969. if (typeof channel !== 'string') {
  2970. throw new Error('Missing required channel argument')
  2971. }
  2972.  
  2973. const internal = false
  2974. const sendToAll = false
  2975.  
  2976. return this._send(internal, sendToAll, channel, args)
  2977. }
  2978. WebContents.prototype.sendToAll = function (channel, ...args) {
  2979. if (typeof channel !== 'string') {
  2980. throw new Error('Missing required channel argument')
  2981. }
  2982.  
  2983. const internal = false
  2984. const sendToAll = true
  2985.  
  2986. return this._send(internal, sendToAll, channel, args)
  2987. }
  2988.  
  2989. WebContents.prototype._sendInternal = function (channel, ...args) {
  2990. if (typeof channel !== 'string') {
  2991. throw new Error('Missing required channel argument')
  2992. }
  2993.  
  2994. const internal = true
  2995. const sendToAll = false
  2996.  
  2997. return this._send(internal, sendToAll, channel, args)
  2998. }
  2999. WebContents.prototype._sendInternalToAll = function (channel, ...args) {
  3000. if (typeof channel !== 'string') {
  3001. throw new Error('Missing required channel argument')
  3002. }
  3003.  
  3004. const internal = true
  3005. const sendToAll = true
  3006.  
  3007. return this._send(internal, sendToAll, channel, args)
  3008. }
  3009.  
  3010. // Following methods are mapped to webFrame.
  3011. const webFrameMethods = [
  3012. 'insertCSS',
  3013. 'insertText',
  3014. 'setLayoutZoomLevelLimits',
  3015. 'setVisualZoomLevelLimits'
  3016. ]
  3017.  
  3018. const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
  3019. return new Promise((resolve, reject) => {
  3020. ipcMainInternal.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, error, result) {
  3021. if (error == null) {
  3022. if (typeof callback === 'function') callback(result)
  3023. resolve(result)
  3024. } else {
  3025. reject(errorUtils.deserialize(error))
  3026. }
  3027. })
  3028. this._sendInternal('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
  3029. })
  3030. }
  3031.  
  3032. for (const method of webFrameMethods) {
  3033. WebContents.prototype[method] = function (...args) {
  3034. this._sendInternal('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
  3035. }
  3036. }
  3037.  
  3038. // Make sure WebContents::executeJavaScript would run the code only when the
  3039. // WebContents has been loaded.
  3040. WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callback) {
  3041. const requestId = getNextId()
  3042.  
  3043. if (typeof hasUserGesture === 'function') {
  3044. // Shift.
  3045. callback = hasUserGesture
  3046. hasUserGesture = null
  3047. }
  3048.  
  3049. if (hasUserGesture == null) {
  3050. hasUserGesture = false
  3051. }
  3052.  
  3053. if (this.getURL() && !this.isLoadingMainFrame()) {
  3054. return asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
  3055. } else {
  3056. return new Promise((resolve, reject) => {
  3057. this.once('did-stop-loading', () => {
  3058. asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture).then(resolve).catch(reject)
  3059. })
  3060. })
  3061. }
  3062. }
  3063.  
  3064. WebContents.prototype.takeHeapSnapshot = function (filePath) {
  3065. return new Promise((resolve, reject) => {
  3066. const channel = `ELECTRON_TAKE_HEAP_SNAPSHOT_RESULT_${getNextId()}`
  3067. ipcMainInternal.once(channel, (event, success) => {
  3068. if (success) {
  3069. resolve()
  3070. } else {
  3071. reject(new Error('takeHeapSnapshot failed'))
  3072. }
  3073. })
  3074. if (!this._takeHeapSnapshot(filePath, channel)) {
  3075. ipcMainInternal.emit(channel, false)
  3076. }
  3077. })
  3078. }
  3079.  
  3080. // Translate the options of printToPDF.
  3081. WebContents.prototype.printToPDF = function (options, callback) {
  3082. const printingSetting = Object.assign({}, defaultPrintingSetting)
  3083. if (options.landscape) {
  3084. printingSetting.landscape = options.landscape
  3085. }
  3086. if (options.marginsType) {
  3087. printingSetting.marginsType = options.marginsType
  3088. }
  3089. if (options.printSelectionOnly) {
  3090. printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
  3091. }
  3092. if (options.printBackground) {
  3093. printingSetting.shouldPrintBackgrounds = options.printBackground
  3094. }
  3095.  
  3096. if (options.pageSize) {
  3097. const pageSize = options.pageSize
  3098. if (typeof pageSize === 'object') {
  3099. if (!pageSize.height || !pageSize.width) {
  3100. return callback(new Error('Must define height and width for pageSize'))
  3101. }
  3102. // Dimensions in Microns
  3103. // 1 meter = 10^6 microns
  3104. printingSetting.mediaSize = {
  3105. name: 'CUSTOM',
  3106. custom_display_name: 'Custom',
  3107. height_microns: Math.ceil(pageSize.height),
  3108. width_microns: Math.ceil(pageSize.width)
  3109. }
  3110. } else if (PDFPageSizes[pageSize]) {
  3111. printingSetting.mediaSize = PDFPageSizes[pageSize]
  3112. } else {
  3113. return callback(new Error(`Does not support pageSize with ${pageSize}`))
  3114. }
  3115. } else {
  3116. printingSetting.mediaSize = PDFPageSizes['A4']
  3117. }
  3118.  
  3119. // Chromium expects this in a 0-100 range number, not as float
  3120. printingSetting.scaleFactor *= 100
  3121. if (features.isPrintingEnabled()) {
  3122. this._printToPDF(printingSetting, callback)
  3123. } else {
  3124. console.error('Error: Printing feature is disabled.')
  3125. }
  3126. }
  3127.  
  3128. WebContents.prototype.print = function (...args) {
  3129. if (features.isPrintingEnabled()) {
  3130. this._print(...args)
  3131. } else {
  3132. console.error('Error: Printing feature is disabled.')
  3133. }
  3134. }
  3135.  
  3136. WebContents.prototype.getPrinters = function () {
  3137. if (features.isPrintingEnabled()) {
  3138. return this._getPrinters()
  3139. } else {
  3140. console.error('Error: Printing feature is disabled.')
  3141. }
  3142. }
  3143.  
  3144. WebContents.prototype.getZoomLevel = function (callback) {
  3145. if (typeof callback !== 'function') {
  3146. throw new Error('Must pass function as an argument')
  3147. }
  3148. process.nextTick(() => {
  3149. const zoomLevel = this._getZoomLevel()
  3150. callback(zoomLevel)
  3151. })
  3152. }
  3153.  
  3154. WebContents.prototype.loadFile = function (filePath, options = {}) {
  3155. if (typeof filePath !== 'string') {
  3156. throw new Error('Must pass filePath as a string')
  3157. }
  3158. const { query, search, hash } = options
  3159.  
  3160. return this.loadURL(url.format({
  3161. protocol: 'file',
  3162. slashes: true,
  3163. pathname: path.resolve(app.getAppPath(), filePath),
  3164. query,
  3165. search,
  3166. hash
  3167. }))
  3168. }
  3169.  
  3170. WebContents.prototype.getZoomFactor = function (callback) {
  3171. if (typeof callback !== 'function') {
  3172. throw new Error('Must pass function as an argument')
  3173. }
  3174. process.nextTick(() => {
  3175. const zoomFactor = this._getZoomFactor()
  3176. callback(zoomFactor)
  3177. })
  3178. }
  3179.  
  3180. WebContents.prototype.findInPage = function (text, options = {}) {
  3181. // TODO (nitsakh): Remove in 5.0
  3182. if (options.wordStart != null || options.medialCapitalAtWordStart != null) {
  3183. deprecate.log('wordStart and medialCapitalAtWordStart options are deprecated')
  3184. }
  3185. return this._findInPage(text, options)
  3186. }
  3187.  
  3188. const safeProtocols = new Set([
  3189. 'chrome-devtools:',
  3190. 'chrome-extension:'
  3191. ])
  3192.  
  3193. const isWebContentsTrusted = function (contents) {
  3194. const pageURL = contents._getURL()
  3195. const { protocol } = url.parse(pageURL)
  3196. return safeProtocols.has(protocol)
  3197. }
  3198.  
  3199. // Add JavaScript wrappers for WebContents class.
  3200. WebContents.prototype._init = function () {
  3201. // The navigation controller.
  3202. NavigationController.call(this, this)
  3203.  
  3204. // Every remote callback from renderer process would add a listenter to the
  3205. // render-view-deleted event, so ignore the listenters warning.
  3206. this.setMaxListeners(0)
  3207.  
  3208. // Dispatch IPC messages to the ipc module.
  3209. this.on('ipc-message', function (event, [channel, ...args]) {
  3210. ipcMain.emit(channel, event, ...args)
  3211. })
  3212. this.on('ipc-message-sync', function (event, [channel, ...args]) {
  3213. Object.defineProperty(event, 'returnValue', {
  3214. set: function (value) {
  3215. return event.sendReply([value])
  3216. },
  3217. get: function () {}
  3218. })
  3219. ipcMain.emit(channel, event, ...args)
  3220. })
  3221.  
  3222. this.on('ipc-internal-message', function (event, [channel, ...args]) {
  3223. ipcMainInternal.emit(channel, event, ...args)
  3224. })
  3225. this.on('ipc-internal-message-sync', function (event, [channel, ...args]) {
  3226. Object.defineProperty(event, 'returnValue', {
  3227. set: function (value) {
  3228. return event.sendReply([value])
  3229. },
  3230. get: function () {}
  3231. })
  3232. ipcMainInternal.emit(channel, event, ...args)
  3233. })
  3234.  
  3235. // Handle context menu action request from pepper plugin.
  3236. this.on('pepper-context-menu', function (event, params, callback) {
  3237. // Access Menu via electron.Menu to prevent circular require.
  3238. const menu = electron.Menu.buildFromTemplate(params.menu)
  3239. menu.popup({
  3240. window: event.sender.getOwnerBrowserWindow(),
  3241. x: params.x,
  3242. y: params.y,
  3243. callback
  3244. })
  3245. })
  3246.  
  3247. const forwardedEvents = [
  3248. 'remote-require',
  3249. 'remote-get-global',
  3250. 'remote-get-builtin',
  3251. 'remote-get-current-window',
  3252. 'remote-get-current-web-contents',
  3253. 'remote-get-guest-web-contents'
  3254. ]
  3255.  
  3256. for (const eventName of forwardedEvents) {
  3257. this.on(eventName, (event, ...args) => {
  3258. if (!isWebContentsTrusted(event.sender)) {
  3259. app.emit(eventName, event, this, ...args)
  3260. }
  3261. })
  3262. }
  3263.  
  3264. deprecate.event(this, 'did-get-response-details', '-did-get-response-details')
  3265. deprecate.event(this, 'did-get-redirect-request', '-did-get-redirect-request')
  3266.  
  3267. // The devtools requests the webContents to reload.
  3268. this.on('devtools-reload-page', function () {
  3269. this.reload()
  3270. })
  3271.  
  3272. // Handle window.open for BrowserWindow and BrowserView.
  3273. if (['browserView', 'window'].includes(this.getType())) {
  3274. // Make new windows requested by links behave like "window.open"
  3275. this.webContents.on('-new-window', (event, url, frameName, disposition,
  3276. additionalFeatures, postData,
  3277. referrer) => {
  3278. const options = {
  3279. show: true,
  3280. width: 800,
  3281. height: 600
  3282. }
  3283. ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
  3284. event, url, referrer, frameName, disposition,
  3285. options, additionalFeatures, postData)
  3286. })
  3287.  
  3288. this.webContents.on('-web-contents-created', (event, webContents, url,
  3289. frameName) => {
  3290. v8Util.setHiddenValue(webContents, 'url-framename', { url, frameName })
  3291. })
  3292.  
  3293. // Create a new browser window for the native implementation of
  3294. // "window.open", used in sandbox and nativeWindowOpen mode
  3295. this.webContents.on('-add-new-contents', (event, webContents, disposition,
  3296. userGesture, left, top, width,
  3297. height) => {
  3298. const urlFrameName = v8Util.getHiddenValue(webContents, 'url-framename')
  3299. if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
  3300. disposition !== 'background-tab') || !urlFrameName) {
  3301. event.preventDefault()
  3302. return
  3303. }
  3304.  
  3305. if (webContents.getLastWebPreferences().nodeIntegration === true) {
  3306. const message =
  3307. 'Enabling Node.js integration in child windows opened with the ' +
  3308. '"nativeWindowOpen" option will cause memory leaks, please turn off ' +
  3309. 'the "nodeIntegration" option.\\n' +
  3310. 'From 5.x child windows opened with the "nativeWindowOpen" option ' +
  3311. 'will always have Node.js integration disabled.\\n' +
  3312. 'See https://github.com/electron/electron/pull/15076 for more.'
  3313. // console is only available after DOM is created.
  3314. const printWarning = () => this.webContents.executeJavaScript(`console.warn('${message}')`)
  3315. if (this.webContents.isDomReady()) {
  3316. printWarning()
  3317. } else {
  3318. this.webContents.once('dom-ready', printWarning)
  3319. }
  3320. }
  3321.  
  3322. const { url, frameName } = urlFrameName
  3323. v8Util.deleteHiddenValue(webContents, 'url-framename')
  3324. const options = {
  3325. show: true,
  3326. x: left,
  3327. y: top,
  3328. width: width || 800,
  3329. height: height || 600,
  3330. webContents
  3331. }
  3332. const referrer = { url: '', policy: 'default' }
  3333. ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
  3334. event, url, referrer, frameName, disposition, options)
  3335. })
  3336. }
  3337.  
  3338. app.emit('web-contents-created', {}, this)
  3339. }
  3340.  
  3341. // JavaScript wrapper of Debugger.
  3342. const { Debugger } = process.atomBinding('debugger')
  3343.  
  3344. Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)
  3345.  
  3346. // Public APIs.
  3347. module.exports = {
  3348. create (options = {}) {
  3349. return binding.create(options)
  3350. },
  3351.  
  3352. fromId (id) {
  3353. return binding.fromId(id)
  3354. },
  3355.  
  3356. getFocusedWebContents () {
  3357. let focused = null
  3358. for (const contents of binding.getAllWebContents()) {
  3359. if (!contents.isFocused()) continue
  3360. if (focused == null) focused = contents
  3361. // Return webview web contents which may be embedded inside another
  3362. // web contents that is also reporting as focused
  3363. if (contents.getType() === 'webview') return contents
  3364. }
  3365. return focused
  3366. },
  3367.  
  3368. getAllWebContents () {
  3369. return binding.getAllWebContents()
  3370. }
  3371. }
  3372. 'use strict'
  3373.  
  3374. const { app, webContents, BrowserWindow } = require('electron')
  3375. const { getAllWebContents } = process.atomBinding('web_contents')
  3376. const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllWebContents()
  3377. const ipcMain = require('@electron/internal/browser/ipc-main-internal')
  3378.  
  3379. const { Buffer } = require('buffer')
  3380. const fs = require('fs')
  3381. const path = require('path')
  3382. const url = require('url')
  3383.  
  3384. // Mapping between extensionId(hostname) and manifest.
  3385. const manifestMap = {} // extensionId => manifest
  3386. const manifestNameMap = {} // name => manifest
  3387. const devToolsExtensionNames = new Set()
  3388.  
  3389. const generateExtensionIdFromName = function (name) {
  3390. return name.replace(/[\W_]+/g, '-').toLowerCase()
  3391. }
  3392.  
  3393. const isWindowOrWebView = function (webContents) {
  3394. const type = webContents.getType()
  3395. return type === 'window' || type === 'webview'
  3396. }
  3397.  
  3398. // Create or get manifest object from |srcDirectory|.
  3399. const getManifestFromPath = function (srcDirectory) {
  3400. let manifest
  3401. let manifestContent
  3402.  
  3403. try {
  3404. manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
  3405. } catch (readError) {
  3406. console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`)
  3407. console.warn(readError.stack || readError)
  3408. throw readError
  3409. }
  3410.  
  3411. try {
  3412. manifest = JSON.parse(manifestContent)
  3413. } catch (parseError) {
  3414. console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`)
  3415. console.warn(parseError.stack || parseError)
  3416. throw parseError
  3417. }
  3418.  
  3419. if (!manifestNameMap[manifest.name]) {
  3420. const extensionId = generateExtensionIdFromName(manifest.name)
  3421. manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest
  3422. Object.assign(manifest, {
  3423. srcDirectory: srcDirectory,
  3424. extensionId: extensionId,
  3425. // We can not use 'file://' directly because all resources in the extension
  3426. // will be treated as relative to the root in Chrome.
  3427. startPage: url.format({
  3428. protocol: 'chrome-extension',
  3429. slashes: true,
  3430. hostname: extensionId,
  3431. pathname: manifest.devtools_page
  3432. })
  3433. })
  3434. return manifest
  3435. } else if (manifest && manifest.name) {
  3436. console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`)
  3437. return manifest
  3438. }
  3439. }
  3440.  
  3441. // Manage the background pages.
  3442. const backgroundPages = {}
  3443.  
  3444. const startBackgroundPages = function (manifest) {
  3445. if (backgroundPages[manifest.extensionId] || !manifest.background) return
  3446.  
  3447. let html
  3448. let name
  3449. if (manifest.background.page) {
  3450. name = manifest.background.page
  3451. html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page))
  3452. } else {
  3453. name = '_generated_background_page.html'
  3454. const scripts = manifest.background.scripts.map((name) => {
  3455. return `<script src="${name}"></script>`
  3456. }).join('')
  3457. html = Buffer.from(`<html><body>${scripts}</body></html>`)
  3458. }
  3459.  
  3460. const contents = webContents.create({
  3461. partition: 'persist:__chrome_extension',
  3462. isBackgroundPage: true,
  3463. commandLineSwitches: ['--background-page']
  3464. })
  3465. backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name }
  3466. contents.loadURL(url.format({
  3467. protocol: 'chrome-extension',
  3468. slashes: true,
  3469. hostname: manifest.extensionId,
  3470. pathname: name
  3471. }))
  3472. }
  3473.  
  3474. const removeBackgroundPages = function (manifest) {
  3475. if (!backgroundPages[manifest.extensionId]) return
  3476.  
  3477. backgroundPages[manifest.extensionId].webContents.destroy()
  3478. delete backgroundPages[manifest.extensionId]
  3479. }
  3480.  
  3481. const sendToBackgroundPages = function (...args) {
  3482. for (const page of Object.values(backgroundPages)) {
  3483. page.webContents._sendInternalToAll(...args)
  3484. }
  3485. }
  3486.  
  3487. // Dispatch web contents events to Chrome APIs
  3488. const hookWebContentsEvents = function (webContents) {
  3489. const tabId = webContents.id
  3490.  
  3491. sendToBackgroundPages('CHROME_TABS_ONCREATED')
  3492.  
  3493. webContents.on('will-navigate', (event, url) => {
  3494. sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
  3495. frameId: 0,
  3496. parentFrameId: -1,
  3497. processId: webContents.getProcessId(),
  3498. tabId: tabId,
  3499. timeStamp: Date.now(),
  3500. url: url
  3501. })
  3502. })
  3503.  
  3504. webContents.on('did-navigate', (event, url) => {
  3505. sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', {
  3506. frameId: 0,
  3507. parentFrameId: -1,
  3508. processId: webContents.getProcessId(),
  3509. tabId: tabId,
  3510. timeStamp: Date.now(),
  3511. url: url
  3512. })
  3513. })
  3514.  
  3515. webContents.once('destroyed', () => {
  3516. sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId)
  3517. })
  3518. }
  3519.  
  3520. // Handle the chrome.* API messages.
  3521. let nextId = 0
  3522.  
  3523. ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
  3524. const page = backgroundPages[extensionId]
  3525. if (!page) {
  3526. console.error(`Connect to unknown extension ${extensionId}`)
  3527. return
  3528. }
  3529.  
  3530. const portId = ++nextId
  3531. event.returnValue = { tabId: page.webContents.id, portId: portId }
  3532.  
  3533. event.sender.once('render-view-deleted', () => {
  3534. if (page.webContents.isDestroyed()) return
  3535. page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`)
  3536. })
  3537. page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo)
  3538. })
  3539.  
  3540. ipcMain.on('CHROME_I18N_MANIFEST', function (event, extensionId) {
  3541. event.returnValue = manifestMap[extensionId]
  3542. })
  3543.  
  3544. let resultID = 1
  3545. ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message, originResultID) {
  3546. const page = backgroundPages[extensionId]
  3547. if (!page) {
  3548. console.error(`Connect to unknown extension ${extensionId}`)
  3549. return
  3550. }
  3551.  
  3552. page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message, resultID)
  3553. ipcMain.once(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, (event, result) => {
  3554. event.sender._sendInternal(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, result)
  3555. })
  3556. resultID++
  3557. })
  3558.  
  3559. ipcMain.on('CHROME_TABS_SEND_MESSAGE', function (event, tabId, extensionId, isBackgroundPage, message, originResultID) {
  3560. const contents = webContents.fromId(tabId)
  3561. if (!contents) {
  3562. console.error(`Sending message to unknown tab ${tabId}`)
  3563. return
  3564. }
  3565.  
  3566. const senderTabId = isBackgroundPage ? null : event.sender.id
  3567.  
  3568. contents._sendInternalToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message, resultID)
  3569. ipcMain.once(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, (event, result) => {
  3570. event.sender._sendInternal(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, result)
  3571. })
  3572. resultID++
  3573. })
  3574.  
  3575. const isChromeExtension = function (pageURL) {
  3576. const { protocol } = url.parse(pageURL)
  3577. return protocol === 'chrome-extension:'
  3578. }
  3579.  
  3580. ipcMain.on('CHROME_TABS_EXECUTESCRIPT', function (event, requestId, tabId, extensionId, details) {
  3581. const pageURL = event.sender._getURL()
  3582. if (!isChromeExtension(pageURL)) {
  3583. console.error(`Blocked ${pageURL} from calling chrome.tabs.executeScript()`)
  3584. return
  3585. }
  3586.  
  3587. const contents = webContents.fromId(tabId)
  3588. if (!contents) {
  3589. console.error(`Sending message to unknown tab ${tabId}`)
  3590. return
  3591. }
  3592.  
  3593. let code, url
  3594. if (details.file) {
  3595. const manifest = manifestMap[extensionId]
  3596. code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file)))
  3597. url = `chrome-extension://${extensionId}${details.file}`
  3598. } else {
  3599. code = details.code
  3600. url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`
  3601. }
  3602.  
  3603. contents._sendInternal('CHROME_TABS_EXECUTESCRIPT', event.sender.id, requestId, extensionId, url, code)
  3604. })
  3605.  
  3606. // Transfer the content scripts to renderer.
  3607. const contentScripts = {}
  3608.  
  3609. const injectContentScripts = function (manifest) {
  3610. if (contentScripts[manifest.name] || !manifest.content_scripts) return
  3611.  
  3612. const readArrayOfFiles = function (relativePath) {
  3613. return {
  3614. url: `chrome-extension://${manifest.extensionId}/${relativePath}`,
  3615. code: String(fs.readFileSync(path.join(manifest.srcDirectory, relativePath)))
  3616. }
  3617. }
  3618.  
  3619. const contentScriptToEntry = function (script) {
  3620. return {
  3621. matches: script.matches,
  3622. js: script.js ? script.js.map(readArrayOfFiles) : [],
  3623. css: script.css ? script.css.map(readArrayOfFiles) : [],
  3624. runAt: script.run_at || 'document_idle'
  3625. }
  3626. }
  3627.  
  3628. try {
  3629. const entry = {
  3630. extensionId: manifest.extensionId,
  3631. contentScripts: manifest.content_scripts.map(contentScriptToEntry)
  3632. }
  3633. contentScripts[manifest.name] = renderProcessPreferences.addEntry(entry)
  3634. } catch (e) {
  3635. console.error('Failed to read content scripts', e)
  3636. }
  3637. }
  3638.  
  3639. const removeContentScripts = function (manifest) {
  3640. if (!contentScripts[manifest.name]) return
  3641.  
  3642. renderProcessPreferences.removeEntry(contentScripts[manifest.name])
  3643. delete contentScripts[manifest.name]
  3644. }
  3645.  
  3646. // Transfer the |manifest| to a format that can be recognized by the
  3647. // |DevToolsAPI.addExtensions|.
  3648. const manifestToExtensionInfo = function (manifest) {
  3649. return {
  3650. startPage: manifest.startPage,
  3651. srcDirectory: manifest.srcDirectory,
  3652. name: manifest.name,
  3653. exposeExperimentalAPIs: true
  3654. }
  3655. }
  3656.  
  3657. // Load the extensions for the window.
  3658. const loadExtension = function (manifest) {
  3659. startBackgroundPages(manifest)
  3660. injectContentScripts(manifest)
  3661. }
  3662.  
  3663. const loadDevToolsExtensions = function (win, manifests) {
  3664. if (!win.devToolsWebContents) return
  3665.  
  3666. manifests.forEach(loadExtension)
  3667.  
  3668. const extensionInfoArray = manifests.map(manifestToExtensionInfo)
  3669. extensionInfoArray.forEach((extension) => {
  3670. win.devToolsWebContents._grantOriginAccess(extension.startPage)
  3671. })
  3672. win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
  3673. }
  3674.  
  3675. app.on('web-contents-created', function (event, webContents) {
  3676. if (!isWindowOrWebView(webContents)) return
  3677.  
  3678. hookWebContentsEvents(webContents)
  3679. webContents.on('devtools-opened', function () {
  3680. loadDevToolsExtensions(webContents, Object.values(manifestMap))
  3681. })
  3682. })
  3683.  
  3684. // The chrome-extension: can map a extension URL request to real file path.
  3685. const chromeExtensionHandler = function (request, callback) {
  3686. const parsed = url.parse(request.url)
  3687. if (!parsed.hostname || !parsed.path) return callback()
  3688.  
  3689. const manifest = manifestMap[parsed.hostname]
  3690. if (!manifest) return callback()
  3691.  
  3692. const page = backgroundPages[parsed.hostname]
  3693. if (page && parsed.path === `/${page.name}`) {
  3694. // Disabled due to false positive in StandardJS
  3695. // eslint-disable-next-line standard/no-callback-literal
  3696. return callback({
  3697. mimeType: 'text/html',
  3698. data: page.html
  3699. })
  3700. }
  3701.  
  3702. fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) {
  3703. if (err) {
  3704. // Disabled due to false positive in StandardJS
  3705. // eslint-disable-next-line standard/no-callback-literal
  3706. return callback(-6) // FILE_NOT_FOUND
  3707. } else {
  3708. return callback(content)
  3709. }
  3710. })
  3711. }
  3712.  
  3713. app.on('session-created', function (ses) {
  3714. ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
  3715. if (error) {
  3716. console.error(`Unable to register chrome-extension protocol: ${error}`)
  3717. }
  3718. })
  3719. })
  3720.  
  3721. // The persistent path of "DevTools Extensions" preference file.
  3722. let loadedDevToolsExtensionsPath = null
  3723.  
  3724. app.on('will-quit', function () {
  3725. try {
  3726. const loadedDevToolsExtensions = Array.from(devToolsExtensionNames)
  3727. .map(name => manifestNameMap[name].srcDirectory)
  3728. if (loadedDevToolsExtensions.length > 0) {
  3729. try {
  3730. fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath))
  3731. } catch (error) {
  3732. // Ignore error
  3733. }
  3734. fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions))
  3735. } else {
  3736. fs.unlinkSync(loadedDevToolsExtensionsPath)
  3737. }
  3738. } catch (error) {
  3739. // Ignore error
  3740. }
  3741. })
  3742.  
  3743. // We can not use protocol or BrowserWindow until app is ready.
  3744. app.once('ready', function () {
  3745. // The public API to add/remove extensions.
  3746. BrowserWindow.addExtension = function (srcDirectory) {
  3747. const manifest = getManifestFromPath(srcDirectory)
  3748. if (manifest) {
  3749. loadExtension(manifest)
  3750. for (const webContents of getAllWebContents()) {
  3751. if (isWindowOrWebView(webContents)) {
  3752. loadDevToolsExtensions(webContents, [manifest])
  3753. }
  3754. }
  3755. return manifest.name
  3756. }
  3757. }
  3758.  
  3759. BrowserWindow.removeExtension = function (name) {
  3760. const manifest = manifestNameMap[name]
  3761. if (!manifest) return
  3762.  
  3763. removeBackgroundPages(manifest)
  3764. removeContentScripts(manifest)
  3765. delete manifestMap[manifest.extensionId]
  3766. delete manifestNameMap[name]
  3767. }
  3768.  
  3769. BrowserWindow.getExtensions = function () {
  3770. const extensions = {}
  3771. Object.keys(manifestNameMap).forEach(function (name) {
  3772. const manifest = manifestNameMap[name]
  3773. extensions[name] = { name: manifest.name, version: manifest.version }
  3774. })
  3775. return extensions
  3776. }
  3777.  
  3778. BrowserWindow.addDevToolsExtension = function (srcDirectory) {
  3779. const manifestName = BrowserWindow.addExtension(srcDirectory)
  3780. if (manifestName) {
  3781. devToolsExtensionNames.add(manifestName)
  3782. }
  3783. return manifestName
  3784. }
  3785.  
  3786. BrowserWindow.removeDevToolsExtension = function (name) {
  3787. BrowserWindow.removeExtension(name)
  3788. devToolsExtensionNames.delete(name)
  3789. }
  3790.  
  3791. BrowserWindow.getDevToolsExtensions = function () {
  3792. const extensions = BrowserWindow.getExtensions()
  3793. const devExtensions = {}
  3794. Array.from(devToolsExtensionNames).forEach(function (name) {
  3795. if (!extensions[name]) return
  3796. devExtensions[name] = extensions[name]
  3797. })
  3798. return devExtensions
  3799. }
  3800.  
  3801. // Load persisted extensions.
  3802. loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions')
  3803. try {
  3804. const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath))
  3805. if (Array.isArray(loadedDevToolsExtensions)) {
  3806. for (const srcDirectory of loadedDevToolsExtensions) {
  3807. // Start background pages and set content scripts.
  3808. BrowserWindow.addDevToolsExtension(srcDirectory)
  3809. }
  3810. }
  3811. } catch (error) {
  3812. if (process.env.ELECTRON_ENABLE_LOGGING && error.code !== 'ENOENT') {
  3813. console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath)
  3814. console.error(error)
  3815. }
  3816. }
  3817. })
  3818. 'use strict'
  3819.  
  3820. const ipcMain = require('@electron/internal/browser/ipc-main-internal')
  3821. const { desktopCapturer } = process.atomBinding('desktop_capturer')
  3822.  
  3823. const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b)
  3824.  
  3825. // A queue for holding all requests from renderer process.
  3826. let requestsQueue = []
  3827.  
  3828. const electronSources = 'ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES'
  3829. const capturerResult = (id) => `ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`
  3830.  
  3831. ipcMain.on(electronSources, (event, captureWindow, captureScreen, thumbnailSize, id) => {
  3832. const request = {
  3833. id,
  3834. options: {
  3835. captureWindow,
  3836. captureScreen,
  3837. thumbnailSize
  3838. },
  3839. webContents: event.sender
  3840. }
  3841. requestsQueue.push(request)
  3842. if (requestsQueue.length === 1) {
  3843. desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize)
  3844. }
  3845.  
  3846. // If the WebContents is destroyed before receiving result, just remove the
  3847. // reference from requestsQueue to make the module not send the result to it.
  3848. event.sender.once('destroyed', () => {
  3849. request.webContents = null
  3850. })
  3851. })
  3852.  
  3853. desktopCapturer.emit = (event, name, sources) => {
  3854. // Receiving sources result from main process, now send them back to renderer.
  3855. const handledRequest = requestsQueue.shift()
  3856. const handledWebContents = handledRequest.webContents
  3857. const unhandledRequestsQueue = []
  3858.  
  3859. const result = sources.map(source => {
  3860. return {
  3861. id: source.id,
  3862. name: source.name,
  3863. thumbnail: source.thumbnail.toDataURL(),
  3864. display_id: source.display_id
  3865. }
  3866. })
  3867.  
  3868. if (handledWebContents) {
  3869. handledWebContents._sendInternal(capturerResult(handledRequest.id), result)
  3870. }
  3871.  
  3872. // Check the queue to see whether there is another identical request & handle
  3873. requestsQueue.forEach(request => {
  3874. const webContents = request.webContents
  3875. if (deepEqual(handledRequest.options, request.options)) {
  3876. if (webContents) {
  3877. webContents._sendInternal(capturerResult(request.id), result)
  3878. }
  3879. } else {
  3880. unhandledRequestsQueue.push(request)
  3881. }
  3882. })
  3883. requestsQueue = unhandledRequestsQueue
  3884.  
  3885. // If the requestsQueue is not empty, start a new request handling.
  3886. if (requestsQueue.length > 0) {
  3887. const { captureWindow, captureScreen, thumbnailSize } = requestsQueue[0].options
  3888. return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize)
  3889. }
  3890. }
  3891. 'use strict'
  3892.  
  3893. const { webContents } = require('electron')
  3894. const ipcMain = require('@electron/internal/browser/ipc-main-internal')
  3895. const parseFeaturesString = require('@electron/internal/common/parse-features-string')
  3896. const errorUtils = require('@electron/internal/common/error-utils')
  3897. const {
  3898. syncMethods,
  3899. asyncCallbackMethods,
  3900. asyncPromiseMethods
  3901. } = require('@electron/internal/common/web-view-methods')
  3902.  
  3903. // Doesn't exist in early initialization.
  3904. let webViewManager = null
  3905.  
  3906. const supportedWebViewEvents = [
  3907. 'load-commit',
  3908. 'did-attach',
  3909. 'did-finish-load',
  3910. 'did-fail-load',
  3911. 'did-frame-finish-load',
  3912. 'did-start-loading',
  3913. 'did-stop-loading',
  3914. 'dom-ready',
  3915. 'console-message',
  3916. 'context-menu',
  3917. 'devtools-opened',
  3918. 'devtools-closed',
  3919. 'devtools-focused',
  3920. 'new-window',
  3921. 'will-navigate',
  3922. 'did-start-navigation',
  3923. 'did-navigate',
  3924. 'did-frame-navigate',
  3925. 'did-navigate-in-page',
  3926. 'focus-change',
  3927. 'close',
  3928. 'crashed',
  3929. 'gpu-crashed',
  3930. 'plugin-crashed',
  3931. 'destroyed',
  3932. 'page-title-updated',
  3933. 'page-favicon-updated',
  3934. 'enter-html-full-screen',
  3935. 'leave-html-full-screen',
  3936. 'media-started-playing',
  3937. 'media-paused',
  3938. 'found-in-page',
  3939. 'did-change-theme-color',
  3940. 'update-target-url'
  3941. ]
  3942.  
  3943. let nextGuestInstanceId = 0
  3944. const guestInstances = {}
  3945. const embedderElementsMap = {}
  3946.  
  3947. // Generate guestInstanceId.
  3948. const getNextGuestInstanceId = function () {
  3949. return ++nextGuestInstanceId
  3950. }
  3951.  
  3952. // Create a new guest instance.
  3953. const createGuest = function (embedder, params) {
  3954. if (webViewManager == null) {
  3955. webViewManager = process.atomBinding('web_view_manager')
  3956. }
  3957.  
  3958. const guestInstanceId = getNextGuestInstanceId(embedder)
  3959. const guest = webContents.create({
  3960. isGuest: true,
  3961. partition: params.partition,
  3962. embedder: embedder
  3963. })
  3964. guestInstances[guestInstanceId] = {
  3965. guest: guest,
  3966. embedder: embedder
  3967. }
  3968.  
  3969. // Clear the guest from map when it is destroyed.
  3970. //
  3971. // The guest WebContents is usually destroyed in 2 cases:
  3972. // 1. The embedder frame is closed (reloaded or destroyed), and it
  3973. // automatically closes the guest frame.
  3974. // 2. The guest frame is detached dynamically via JS, and it is manually
  3975. // destroyed when the renderer sends the GUEST_VIEW_MANAGER_DESTROY_GUEST
  3976. // message.
  3977. // The second case relies on the libcc patch:
  3978. // https://github.com/electron/libchromiumcontent/pull/676
  3979. // The patch was introduced to work around a bug in Chromium:
  3980. // https://github.com/electron/electron/issues/14211
  3981. // We should revisit the bug to see if we can remove our libcc patch, the
  3982. // patch was introduced in Chrome 66.
  3983. guest.once('destroyed', () => {
  3984. if (guestInstanceId in guestInstances) {
  3985. detachGuest(embedder, guestInstanceId)
  3986. }
  3987. })
  3988.  
  3989. // Init guest web view after attached.
  3990. guest.once('did-attach', function (event) {
  3991. params = this.attachParams
  3992. delete this.attachParams
  3993.  
  3994. const previouslyAttached = this.viewInstanceId != null
  3995. this.viewInstanceId = params.instanceId
  3996.  
  3997. // Only load URL and set size on first attach
  3998. if (previouslyAttached) {
  3999. return
  4000. }
  4001.  
  4002. if (params.src) {
  4003. const opts = {}
  4004. if (params.httpreferrer) {
  4005. opts.httpReferrer = params.httpreferrer
  4006. }
  4007. if (params.useragent) {
  4008. opts.userAgent = params.useragent
  4009. }
  4010. this.loadURL(params.src, opts)
  4011. }
  4012. guest.allowPopups = params.allowpopups
  4013. embedder.emit('did-attach-webview', event, guest)
  4014. })
  4015.  
  4016. const sendToEmbedder = (channel, ...args) => {
  4017. if (!embedder.isDestroyed()) {
  4018. embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args)
  4019. }
  4020. }
  4021.  
  4022. // Dispatch events to embedder.
  4023. const fn = function (event) {
  4024. guest.on(event, function (_, ...args) {
  4025. sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args)
  4026. })
  4027. }
  4028. for (const event of supportedWebViewEvents) {
  4029. fn(event)
  4030. }
  4031.  
  4032. // Dispatch guest's IPC messages to embedder.
  4033. guest.on('ipc-message-host', function (_, [channel, ...args]) {
  4034. sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args)
  4035. })
  4036.  
  4037. // Notify guest of embedder window visibility when it is ready
  4038. // FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed
  4039. guest.on('dom-ready', function () {
  4040. const guestInstance = guestInstances[guestInstanceId]
  4041. if (guestInstance != null && guestInstance.visibilityState != null) {
  4042. guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState)
  4043. }
  4044. })
  4045.  
  4046. // Forward internal web contents event to embedder to handle
  4047. // native window.open setup
  4048. guest.on('-add-new-contents', (...args) => {
  4049. if (guest.getLastWebPreferences().nativeWindowOpen === true) {
  4050. const embedder = getEmbedder(guestInstanceId)
  4051. if (embedder != null) {
  4052. embedder.emit('-add-new-contents', ...args)
  4053. }
  4054. }
  4055. })
  4056. guest.on('-web-contents-created', (...args) => {
  4057. if (guest.getLastWebPreferences().nativeWindowOpen === true) {
  4058. const embedder = getEmbedder(guestInstanceId)
  4059. if (embedder != null) {
  4060. embedder.emit('-web-contents-created', ...args)
  4061. }
  4062. }
  4063. })
  4064.  
  4065. return guestInstanceId
  4066. }
  4067.  
  4068. // Attach the guest to an element of embedder.
  4069. const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
  4070. const embedder = event.sender
  4071. // Destroy the old guest when attaching.
  4072. const key = `${embedder.id}-${elementInstanceId}`
  4073. const oldGuestInstanceId = embedderElementsMap[key]
  4074. if (oldGuestInstanceId != null) {
  4075. // Reattachment to the same guest is just a no-op.
  4076. if (oldGuestInstanceId === guestInstanceId) {
  4077. return
  4078. }
  4079.  
  4080. const oldGuestInstance = guestInstances[oldGuestInstanceId]
  4081. if (oldGuestInstance) {
  4082. oldGuestInstance.guest.destroy()
  4083. }
  4084. }
  4085.  
  4086. const guestInstance = guestInstances[guestInstanceId]
  4087. // If this isn't a valid guest instance then do nothing.
  4088. if (!guestInstance) {
  4089. throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
  4090. }
  4091. const { guest } = guestInstance
  4092. if (guest.hostWebContents !== event.sender) {
  4093. throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`)
  4094. }
  4095.  
  4096. // If this guest is already attached to an element then remove it
  4097. if (guestInstance.elementInstanceId) {
  4098. const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}`
  4099. delete embedderElementsMap[oldKey]
  4100.  
  4101. // Remove guest from embedder if moving across web views
  4102. if (guest.viewInstanceId !== params.instanceId) {
  4103. webViewManager.removeGuest(guestInstance.embedder, guestInstanceId)
  4104. guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`)
  4105. }
  4106. }
  4107.  
  4108. const webPreferences = {
  4109. guestInstanceId: guestInstanceId,
  4110. nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
  4111. enableRemoteModule: params.enableremotemodule,
  4112. plugins: params.plugins,
  4113. zoomFactor: embedder._getZoomFactor(),
  4114. webSecurity: !params.disablewebsecurity,
  4115. enableBlinkFeatures: params.blinkfeatures,
  4116. disableBlinkFeatures: params.disableblinkfeatures
  4117. }
  4118.  
  4119. // parse the 'webpreferences' attribute string, if set
  4120. // this uses the same parsing rules as window.open uses for its features
  4121. if (typeof params.webpreferences === 'string') {
  4122. parseFeaturesString(params.webpreferences, function (key, value) {
  4123. if (value === undefined) {
  4124. // no value was specified, default it to true
  4125. value = true
  4126. }
  4127. webPreferences[key] = value
  4128. })
  4129. }
  4130.  
  4131. if (params.preload) {
  4132. webPreferences.preloadURL = params.preload
  4133. }
  4134.  
  4135. // Return null from native window.open if allowpopups is unset
  4136. if (webPreferences.nativeWindowOpen === true && !params.allowpopups) {
  4137. webPreferences.disablePopups = true
  4138. }
  4139.  
  4140. // Security options that guest will always inherit from embedder
  4141. const inheritedWebPreferences = new Map([
  4142. ['contextIsolation', true],
  4143. ['javascript', false],
  4144. ['nativeWindowOpen', true],
  4145. ['nodeIntegration', false],
  4146. ['enableRemoteModule', false],
  4147. ['sandbox', true]
  4148. ])
  4149.  
  4150. // Inherit certain option values from embedder
  4151. const lastWebPreferences = embedder.getLastWebPreferences()
  4152. for (const [name, value] of inheritedWebPreferences) {
  4153. if (lastWebPreferences[name] === value) {
  4154. webPreferences[name] = value
  4155. }
  4156. }
  4157.  
  4158. embedder.emit('will-attach-webview', event, webPreferences, params)
  4159. if (event.defaultPrevented) {
  4160. if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId
  4161. guest.destroy()
  4162. return
  4163. }
  4164.  
  4165. guest.attachParams = params
  4166. embedderElementsMap[key] = guestInstanceId
  4167.  
  4168. guest.setEmbedder(embedder)
  4169. guestInstance.embedder = embedder
  4170. guestInstance.elementInstanceId = elementInstanceId
  4171.  
  4172. watchEmbedder(embedder)
  4173.  
  4174. webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences)
  4175. guest.attachToIframe(embedder, embedderFrameId)
  4176. }
  4177.  
  4178. // Remove an guest-embedder relationship.
  4179. const detachGuest = function (embedder, guestInstanceId) {
  4180. const guestInstance = guestInstances[guestInstanceId]
  4181. if (embedder !== guestInstance.embedder) {
  4182. return
  4183. }
  4184.  
  4185. webViewManager.removeGuest(embedder, guestInstanceId)
  4186. delete guestInstances[guestInstanceId]
  4187.  
  4188. const key = `${embedder.id}-${guestInstance.elementInstanceId}`
  4189. delete embedderElementsMap[key]
  4190. }
  4191.  
  4192. // Once an embedder has had a guest attached we watch it for destruction to
  4193. // destroy any remaining guests.
  4194. const watchedEmbedders = new Set()
  4195. const watchEmbedder = function (embedder) {
  4196. if (watchedEmbedders.has(embedder)) {
  4197. return
  4198. }
  4199. watchedEmbedders.add(embedder)
  4200.  
  4201. // Forward embedder window visiblity change events to guest
  4202. const onVisibilityChange = function (visibilityState) {
  4203. for (const guestInstanceId in guestInstances) {
  4204. const guestInstance = guestInstances[guestInstanceId]
  4205. guestInstance.visibilityState = visibilityState
  4206. if (guestInstance.embedder === embedder) {
  4207. guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState)
  4208. }
  4209. }
  4210. }
  4211. embedder.on('-window-visibility-change', onVisibilityChange)
  4212.  
  4213. embedder.once('will-destroy', () => {
  4214. // Usually the guestInstances is cleared when guest is destroyed, but it
  4215. // may happen that the embedder gets manually destroyed earlier than guest,
  4216. // and the embedder will be invalid in the usual code path.
  4217. for (const guestInstanceId in guestInstances) {
  4218. const guestInstance = guestInstances[guestInstanceId]
  4219. if (guestInstance.embedder === embedder) {
  4220. detachGuest(embedder, parseInt(guestInstanceId))
  4221. }
  4222. }
  4223. // Clear the listeners.
  4224. embedder.removeListener('-window-visibility-change', onVisibilityChange)
  4225. watchedEmbedders.delete(embedder)
  4226. })
  4227. }
  4228.  
  4229. const isWebViewTagEnabledCache = new WeakMap()
  4230.  
  4231. const isWebViewTagEnabled = function (contents) {
  4232. if (!isWebViewTagEnabledCache.has(contents)) {
  4233. const value = contents.getLastWebPreferences().webviewTag
  4234. isWebViewTagEnabledCache.set(contents, value)
  4235. }
  4236.  
  4237. return isWebViewTagEnabledCache.get(contents)
  4238. }
  4239.  
  4240. const handleMessage = function (channel, handler) {
  4241. ipcMain.on(channel, (event, ...args) => {
  4242. if (isWebViewTagEnabled(event.sender)) {
  4243. handler(event, ...args)
  4244. } else {
  4245. event.returnValue = null
  4246. }
  4247. })
  4248. }
  4249.  
  4250. handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
  4251. event.sender._sendInternal(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
  4252. })
  4253.  
  4254. handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) {
  4255. event.returnValue = createGuest(event.sender, params)
  4256. })
  4257.  
  4258. handleMessage('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) {
  4259. try {
  4260. const guest = getGuestForWebContents(guestInstanceId, event.sender)
  4261. guest.destroy()
  4262. } catch (error) {
  4263. console.error(`Guest destroy failed: ${error}`)
  4264. }
  4265. })
  4266.  
  4267. handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
  4268. try {
  4269. attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
  4270. } catch (error) {
  4271. console.error(`Guest attach failed: ${error}`)
  4272. }
  4273. })
  4274.  
  4275. // this message is sent by the actual <webview>
  4276. ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
  4277. const guest = getGuest(guestInstanceId)
  4278. if (guest === event.sender) {
  4279. event.sender.emit('focus-change', {}, focus, guestInstanceId)
  4280. } else {
  4281. console.error(`focus-change for guestInstanceId: ${guestInstanceId}`)
  4282. }
  4283. })
  4284.  
  4285. handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, requestId, guestInstanceId, method, args, hasCallback) {
  4286. new Promise(resolve => {
  4287. const guest = getGuestForWebContents(guestInstanceId, event.sender)
  4288. if (!asyncCallbackMethods.has(method) && !asyncPromiseMethods.has(method)) {
  4289. throw new Error(`Invalid method: ${method}`)
  4290. }
  4291. if (hasCallback) {
  4292. guest[method](...args, resolve)
  4293. } else {
  4294. resolve(guest[method](...args))
  4295. }
  4296. }).then(result => {
  4297. return [null, result]
  4298. }, error => {
  4299. return [errorUtils.serialize(error)]
  4300. }).then(responseArgs => {
  4301. event.sender._sendInternal(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, ...responseArgs)
  4302. })
  4303. })
  4304.  
  4305. handleMessage('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', function (event, guestInstanceId, method, args) {
  4306. try {
  4307. const guest = getGuestForWebContents(guestInstanceId, event.sender)
  4308. if (!syncMethods.has(method)) {
  4309. throw new Error(`Invalid method: ${method}`)
  4310. }
  4311. event.returnValue = [null, guest[method](...args)]
  4312. } catch (error) {
  4313. event.returnValue = [errorUtils.serialize(error)]
  4314. }
  4315. })
  4316.  
  4317. // Returns WebContents from its guest id hosted in given webContents.
  4318. const getGuestForWebContents = function (guestInstanceId, contents) {
  4319. const guest = getGuest(guestInstanceId)
  4320. if (!guest) {
  4321. throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
  4322. }
  4323. if (guest.hostWebContents !== contents) {
  4324. throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`)
  4325. }
  4326. return guest
  4327. }
  4328.  
  4329. // Returns WebContents from its guest id.
  4330. const getGuest = function (guestInstanceId) {
  4331. const guestInstance = guestInstances[guestInstanceId]
  4332. if (guestInstance != null) return guestInstance.guest
  4333. }
  4334.  
  4335. // Returns the embedder of the guest.
  4336. const getEmbedder = function (guestInstanceId) {
  4337. const guestInstance = guestInstances[guestInstanceId]
  4338. if (guestInstance != null) return guestInstance.embedder
  4339. }
  4340.  
  4341. exports.getGuestForWebContents = getGuestForWebContents
  4342. 'use strict'
  4343.  
  4344. const { BrowserWindow, webContents } = require('electron')
  4345. const { isSameOrigin } = process.atomBinding('v8_util')
  4346. const ipcMain = require('@electron/internal/browser/ipc-main-internal')
  4347. const parseFeaturesString = require('@electron/internal/common/parse-features-string')
  4348.  
  4349. const hasProp = {}.hasOwnProperty
  4350. const frameToGuest = new Map()
  4351.  
  4352. // Security options that child windows will always inherit from parent windows
  4353. const inheritedWebPreferences = new Map([
  4354. ['contextIsolation', true],
  4355. ['javascript', false],
  4356. ['nativeWindowOpen', true],
  4357. ['nodeIntegration', false],
  4358. ['enableRemoteModule', false],
  4359. ['sandbox', true],
  4360. ['webviewTag', false]
  4361. ])
  4362.  
  4363. // Copy attribute of |parent| to |child| if it is not defined in |child|.
  4364. const mergeOptions = function (child, parent, visited) {
  4365. // Check for circular reference.
  4366. if (visited == null) visited = new Set()
  4367. if (visited.has(parent)) return
  4368.  
  4369. visited.add(parent)
  4370. for (const key in parent) {
  4371. if (key === 'isBrowserView') continue
  4372. if (!hasProp.call(parent, key)) continue
  4373. if (key in child && key !== 'webPreferences') continue
  4374.  
  4375. const value = parent[key]
  4376. if (typeof value === 'object') {
  4377. child[key] = mergeOptions(child[key] || {}, value, visited)
  4378. } else {
  4379. child[key] = value
  4380. }
  4381. }
  4382. visited.delete(parent)
  4383.  
  4384. return child
  4385. }
  4386.  
  4387. // Merge |options| with the |embedder|'s window's options.
  4388. const mergeBrowserWindowOptions = function (embedder, options) {
  4389. if (options.webPreferences == null) {
  4390. options.webPreferences = {}
  4391. }
  4392. if (embedder.browserWindowOptions != null) {
  4393. let parentOptions = embedder.browserWindowOptions
  4394.  
  4395. // if parent's visibility is available, that overrides 'show' flag (#12125)
  4396. const win = BrowserWindow.fromWebContents(embedder.webContents)
  4397. if (win != null) {
  4398. parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() }
  4399. }
  4400.  
  4401. // Inherit the original options if it is a BrowserWindow.
  4402. mergeOptions(options, parentOptions)
  4403. } else {
  4404. // Or only inherit webPreferences if it is a webview.
  4405. mergeOptions(options.webPreferences, embedder.getLastWebPreferences())
  4406. }
  4407.  
  4408. // Inherit certain option values from parent window
  4409. const webPreferences = embedder.getLastWebPreferences()
  4410. for (const [name, value] of inheritedWebPreferences) {
  4411. if (webPreferences[name] === value) {
  4412. options.webPreferences[name] = value
  4413. }
  4414. }
  4415.  
  4416. // Sets correct openerId here to give correct options to 'new-window' event handler
  4417. options.webPreferences.openerId = embedder.id
  4418.  
  4419. return options
  4420. }
  4421.  
  4422. // Setup a new guest with |embedder|
  4423. const setupGuest = function (embedder, frameName, guest, options) {
  4424. // When |embedder| is destroyed we should also destroy attached guest, and if
  4425. // guest is closed by user then we should prevent |embedder| from double
  4426. // closing guest.
  4427. const guestId = guest.webContents.id
  4428. const closedByEmbedder = function () {
  4429. guest.removeListener('closed', closedByUser)
  4430. guest.destroy()
  4431. }
  4432. const closedByUser = function () {
  4433. embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
  4434. embedder.removeListener('render-view-deleted', closedByEmbedder)
  4435. }
  4436. embedder.once('render-view-deleted', closedByEmbedder)
  4437. guest.once('closed', closedByUser)
  4438. if (frameName) {
  4439. frameToGuest.set(frameName, guest)
  4440. guest.frameName = frameName
  4441. guest.once('closed', function () {
  4442. frameToGuest.delete(frameName)
  4443. })
  4444. }
  4445. return guestId
  4446. }
  4447.  
  4448. // Create a new guest created by |embedder| with |options|.
  4449. const createGuest = function (embedder, url, referrer, frameName, options, postData) {
  4450. let guest = frameToGuest.get(frameName)
  4451. if (frameName && (guest != null)) {
  4452. guest.loadURL(url)
  4453. return guest.webContents.id
  4454. }
  4455.  
  4456. // Remember the embedder window's id.
  4457. if (options.webPreferences == null) {
  4458. options.webPreferences = {}
  4459. }
  4460.  
  4461. guest = new BrowserWindow(options)
  4462. if (!options.webContents || url !== 'about:blank') {
  4463. // We should not call `loadURL` if the window was constructed from an
  4464. // existing webContents(window.open in a sandboxed renderer) and if the url
  4465. // is not 'about:blank'.
  4466. //
  4467. // Navigating to the url when creating the window from an existing
  4468. // webContents would not be necessary(it will navigate there anyway), but
  4469. // apparently there's a bug that allows the child window to be scripted by
  4470. // the opener, even when the child window is from another origin.
  4471. //
  4472. // That's why the second condition(url !== "about:blank") is required: to
  4473. // force `OverrideSiteInstanceForNavigation` to be called and consequently
  4474. // spawn a new renderer if the new window is targeting a different origin.
  4475. //
  4476. // If the URL is "about:blank", then it is very likely that the opener just
  4477. // wants to synchronously script the popup, for example:
  4478. //
  4479. // let popup = window.open()
  4480. // popup.document.body.write('<h1>hello</h1>')
  4481. //
  4482. // The above code would not work if a navigation to "about:blank" is done
  4483. // here, since the window would be cleared of all changes in the next tick.
  4484. const loadOptions = {
  4485. httpReferrer: referrer
  4486. }
  4487. if (postData != null) {
  4488. loadOptions.postData = postData
  4489. loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'
  4490. if (postData.length > 0) {
  4491. const postDataFront = postData[0].bytes.toString()
  4492. const boundary = /^--.*[^-\r\n]/.exec(postDataFront)
  4493. if (boundary != null) {
  4494. loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`
  4495. }
  4496. }
  4497. }
  4498. guest.loadURL(url, loadOptions)
  4499. }
  4500.  
  4501. return setupGuest(embedder, frameName, guest, options)
  4502. }
  4503.  
  4504. const getGuestWindow = function (guestContents) {
  4505. let guestWindow = BrowserWindow.fromWebContents(guestContents)
  4506. if (guestWindow == null) {
  4507. const hostContents = guestContents.hostWebContents
  4508. if (hostContents != null) {
  4509. guestWindow = BrowserWindow.fromWebContents(hostContents)
  4510. }
  4511. }
  4512. return guestWindow
  4513. }
  4514.  
  4515. // Checks whether |sender| can access the |target|:
  4516. // 1. Check whether |sender| is the parent of |target|.
  4517. // 2. Check whether |sender| has node integration, if so it is allowed to
  4518. // do anything it wants.
  4519. // 3. Check whether the origins match.
  4520. //
  4521. // However it allows a child window without node integration but with same
  4522. // origin to do anything it wants, when its opener window has node integration.
  4523. // The W3C does not have anything on this, but from my understanding of the
  4524. // security model of |window.opener|, this should be fine.
  4525. const canAccessWindow = function (sender, target) {
  4526. return (target.getLastWebPreferences().openerId === sender.id) ||
  4527. (sender.getLastWebPreferences().nodeIntegration === true) ||
  4528. isSameOrigin(sender.getURL(), target.getURL())
  4529. }
  4530.  
  4531. // Routed window.open messages with raw options
  4532. ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
  4533. if (url == null || url === '') url = 'about:blank'
  4534. if (frameName == null) frameName = ''
  4535. if (features == null) features = ''
  4536.  
  4537. const options = {}
  4538.  
  4539. const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
  4540. const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag']
  4541. const disposition = 'new-window'
  4542.  
  4543. // Used to store additional features
  4544. const additionalFeatures = []
  4545.  
  4546. // Parse the features
  4547. parseFeaturesString(features, function (key, value) {
  4548. if (value === undefined) {
  4549. additionalFeatures.push(key)
  4550. } else {
  4551. // Don't allow webPreferences to be set since it must be an object
  4552. // that cannot be directly overridden
  4553. if (key === 'webPreferences') return
  4554.  
  4555. if (webPreferences.includes(key)) {
  4556. if (options.webPreferences == null) {
  4557. options.webPreferences = {}
  4558. }
  4559. options.webPreferences[key] = value
  4560. } else {
  4561. options[key] = value
  4562. }
  4563. }
  4564. })
  4565. if (options.left) {
  4566. if (options.x == null) {
  4567. options.x = options.left
  4568. }
  4569. }
  4570. if (options.top) {
  4571. if (options.y == null) {
  4572. options.y = options.top
  4573. }
  4574. }
  4575. if (options.title == null) {
  4576. options.title = frameName
  4577. }
  4578. if (options.width == null) {
  4579. options.width = 800
  4580. }
  4581. if (options.height == null) {
  4582. options.height = 600
  4583. }
  4584.  
  4585. for (const name of ints) {
  4586. if (options[name] != null) {
  4587. options[name] = parseInt(options[name], 10)
  4588. }
  4589. }
  4590.  
  4591. const referrer = { url: '', policy: 'default' }
  4592. ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
  4593. url, referrer, frameName, disposition, options, additionalFeatures)
  4594. })
  4595.  
  4596. // Routed window.open messages with fully parsed options
  4597. ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer,
  4598. frameName, disposition, options,
  4599. additionalFeatures, postData) {
  4600. options = mergeBrowserWindowOptions(event.sender, options)
  4601. event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer)
  4602. const { newGuest } = event
  4603. if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
  4604. if (newGuest != null) {
  4605. if (options.webContents === newGuest.webContents) {
  4606. // the webContents is not changed, so set defaultPrevented to false to
  4607. // stop the callers of this event from destroying the webContents.
  4608. event.defaultPrevented = false
  4609. }
  4610. event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
  4611. } else {
  4612. event.returnValue = null
  4613. }
  4614. } else {
  4615. event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData)
  4616. }
  4617. })
  4618.  
  4619. ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
  4620. const guestContents = webContents.fromId(guestId)
  4621. if (guestContents == null) return
  4622.  
  4623. if (!canAccessWindow(event.sender, guestContents)) {
  4624. console.error(`Blocked ${event.sender.getURL()} from closing its opener.`)
  4625. return
  4626. }
  4627.  
  4628. const guestWindow = getGuestWindow(guestContents)
  4629. if (guestWindow != null) guestWindow.destroy()
  4630. })
  4631.  
  4632. const windowMethods = new Set([
  4633. 'focus',
  4634. 'blur'
  4635. ])
  4636.  
  4637. ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
  4638. const guestContents = webContents.fromId(guestId)
  4639. if (guestContents == null) {
  4640. event.returnValue = null
  4641. return
  4642. }
  4643.  
  4644. if (!canAccessWindow(event.sender, guestContents) || !windowMethods.has(method)) {
  4645. console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
  4646. event.returnValue = null
  4647. return
  4648. }
  4649.  
  4650. const guestWindow = getGuestWindow(guestContents)
  4651. if (guestWindow != null) {
  4652. event.returnValue = guestWindow[method](...args)
  4653. } else {
  4654. event.returnValue = null
  4655. }
  4656. })
  4657.  
  4658. ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
  4659. if (targetOrigin == null) {
  4660. targetOrigin = '*'
  4661. }
  4662.  
  4663. const guestContents = webContents.fromId(guestId)
  4664. if (guestContents == null) return
  4665.  
  4666. // The W3C does not seem to have word on how postMessage should work when the
  4667. // origins do not match, so we do not do |canAccessWindow| check here since
  4668. // postMessage across origins is useful and not harmful.
  4669. if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
  4670. const sourceId = event.sender.id
  4671. guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
  4672. }
  4673. })
  4674.  
  4675. const webContentsMethods = new Set([
  4676. 'print',
  4677. 'executeJavaScript'
  4678. ])
  4679.  
  4680. ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
  4681. const guestContents = webContents.fromId(guestId)
  4682. if (guestContents == null) return
  4683.  
  4684. if (canAccessWindow(event.sender, guestContents) && webContentsMethods.has(method)) {
  4685. guestContents[method](...args)
  4686. } else {
  4687. console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
  4688. }
  4689. })
  4690.  
  4691. const webContentsSyncMethods = new Set([
  4692. 'getURL',
  4693. 'loadURL'
  4694. ])
  4695.  
  4696. ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
  4697. const guestContents = webContents.fromId(guestId)
  4698. if (guestContents == null) {
  4699. event.returnValue = null
  4700. return
  4701. }
  4702.  
  4703. if (canAccessWindow(event.sender, guestContents) && webContentsSyncMethods.has(method)) {
  4704. event.returnValue = guestContents[method](...args)
  4705. } else {
  4706. console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
  4707. event.returnValue = null
  4708. }
  4709. })
  4710. 'use strict'
  4711.  
  4712. const { Buffer } = require('buffer')
  4713. const fs = require('fs')
  4714. const path = require('path')
  4715. const util = require('util')
  4716. const Module = require('module')
  4717. const v8 = require('v8')
  4718.  
  4719. // We modified the original process.argv to let node.js load the init.js,
  4720. // we need to restore it here.
  4721. process.argv.splice(1, 1)
  4722.  
  4723. // Clear search paths.
  4724. require('../common/reset-search-paths')
  4725.  
  4726. // Import common settings.
  4727. require('@electron/internal/common/init')
  4728.  
  4729. const globalPaths = Module.globalPaths
  4730.  
  4731. // Expose public APIs.
  4732. globalPaths.push(path.join(__dirname, 'api', 'exports'))
  4733.  
  4734. if (process.platform === 'win32') {
  4735. // Redirect node's console to use our own implementations, since node can not
  4736. // handle console output when running as GUI program.
  4737. const consoleLog = function (...args) {
  4738. return process.log(util.format(...args) + '\n')
  4739. }
  4740. const streamWrite = function (chunk, encoding, callback) {
  4741. if (Buffer.isBuffer(chunk)) {
  4742. chunk = chunk.toString(encoding)
  4743. }
  4744. process.log(chunk)
  4745. if (callback) {
  4746. callback()
  4747. }
  4748. return true
  4749. }
  4750. console.log = console.error = console.warn = consoleLog
  4751. process.stdout.write = process.stderr.write = streamWrite
  4752. }
  4753.  
  4754. // Don't quit on fatal error.
  4755. process.on('uncaughtException', function (error) {
  4756. // Do nothing if the user has a custom uncaught exception handler.
  4757. if (process.listeners('uncaughtException').length > 1) {
  4758. return
  4759. }
  4760.  
  4761. // Show error in GUI.
  4762. const dialog = require('electron').dialog
  4763. const stack = error.stack ? error.stack : `${error.name}: ${error.message}`
  4764. const message = 'Uncaught Exception:\n' + stack
  4765. dialog.showErrorBox('A JavaScript error occurred in the main process', message)
  4766. })
  4767.  
  4768. // Emit 'exit' event on quit.
  4769. const { app } = require('electron')
  4770.  
  4771. app.on('quit', function (event, exitCode) {
  4772. process.emit('exit', exitCode)
  4773. })
  4774.  
  4775. if (process.platform === 'win32') {
  4776. // If we are a Squirrel.Windows-installed app, set app user model ID
  4777. // so that users don't have to do this.
  4778. //
  4779. // Squirrel packages are always of the form:
  4780. //
  4781. // PACKAGE-NAME
  4782. // - Update.exe
  4783. // - app-VERSION
  4784. // - OUREXE.exe
  4785. //
  4786. // Squirrel itself will always set the shortcut's App User Model ID to the
  4787. // form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
  4788. // app.setAppUserModelId with a matching identifier so that renderer processes
  4789. // will inherit this value.
  4790. const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe')
  4791.  
  4792. if (fs.existsSync(updateDotExe)) {
  4793. const packageDir = path.dirname(path.resolve(updateDotExe))
  4794. const packageName = path.basename(packageDir).replace(/\s/g, '')
  4795. const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '')
  4796.  
  4797. app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`)
  4798. }
  4799. }
  4800.  
  4801. // Map process.exit to app.exit, which quits gracefully.
  4802. process.exit = app.exit
  4803.  
  4804. // Load the RPC server.
  4805. require('@electron/internal/browser/rpc-server')
  4806.  
  4807. // Load the guest view manager.
  4808. require('@electron/internal/browser/guest-view-manager')
  4809. require('@electron/internal/browser/guest-window-manager')
  4810.  
  4811. // Now we try to load app's package.json.
  4812. let packagePath = null
  4813. let packageJson = null
  4814. const searchPaths = ['app', 'app.asar', 'default_app.asar']
  4815. for (packagePath of searchPaths) {
  4816. try {
  4817. packagePath = path.join(process.resourcesPath, packagePath)
  4818. packageJson = require(path.join(packagePath, 'package.json'))
  4819. break
  4820. } catch (error) {
  4821. continue
  4822. }
  4823. }
  4824.  
  4825. if (packageJson == null) {
  4826. process.nextTick(function () {
  4827. return process.exit(1)
  4828. })
  4829. throw new Error('Unable to find a valid app')
  4830. }
  4831.  
  4832. // Set application's version.
  4833. if (packageJson.version != null) {
  4834. app.setVersion(packageJson.version)
  4835. }
  4836.  
  4837. // Set application's name.
  4838. if (packageJson.productName != null) {
  4839. app.setName(`${packageJson.productName}`.trim())
  4840. } else if (packageJson.name != null) {
  4841. app.setName(`${packageJson.name}`.trim())
  4842. }
  4843.  
  4844. // Set application's desktop name.
  4845. if (packageJson.desktopName != null) {
  4846. app.setDesktopName(packageJson.desktopName)
  4847. } else {
  4848. app.setDesktopName((app.getName()) + '.desktop')
  4849. }
  4850.  
  4851. // Set v8 flags
  4852. if (packageJson.v8Flags != null) {
  4853. v8.setFlagsFromString(packageJson.v8Flags)
  4854. }
  4855.  
  4856. // Set the user path according to application's name.
  4857. app.setPath('userData', path.join(app.getPath('appData'), app.getName()))
  4858. app.setPath('userCache', path.join(app.getPath('cache'), app.getName()))
  4859. app.setAppPath(packagePath)
  4860.  
  4861. // Load the chrome extension support.
  4862. require('@electron/internal/browser/chrome-extension')
  4863.  
  4864. const features = process.atomBinding('features')
  4865. if (features.isDesktopCapturerEnabled()) {
  4866. // Load internal desktop-capturer module.
  4867. require('@electron/internal/browser/desktop-capturer')
  4868. }
  4869.  
  4870. // Load protocol module to ensure it is populated on app ready
  4871. require('@electron/internal/browser/api/protocol')
  4872.  
  4873. // Set main startup script of the app.
  4874. const mainStartupScript = packageJson.main || 'index.js'
  4875.  
  4876. const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME']
  4877.  
  4878. function currentPlatformSupportsAppIndicator () {
  4879. if (process.platform !== 'linux') return false
  4880. const currentDesktop = process.env.XDG_CURRENT_DESKTOP
  4881.  
  4882. if (!currentDesktop) return false
  4883. if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true
  4884. // ubuntu based or derived session (default ubuntu one, communitheme…) supports
  4885. // indicator too.
  4886. if (/ubuntu/ig.test(currentDesktop)) return true
  4887.  
  4888. return false
  4889. }
  4890.  
  4891. // Workaround for electron/electron#5050 and electron/electron#9046
  4892. if (currentPlatformSupportsAppIndicator()) {
  4893. process.env.XDG_CURRENT_DESKTOP = 'Unity'
  4894. }
  4895.  
  4896. // Finally load app's main.js and transfer control to C++.
  4897. Module._load(path.join(packagePath, mainStartupScript), Module, true)
  4898. 'use strict'
  4899.  
  4900. const { EventEmitter } = require('events')
  4901.  
  4902. const emitter = new EventEmitter()
  4903.  
  4904. // Do not throw exception when channel name is "error".
  4905. emitter.on('error', () => {})
  4906.  
  4907. module.exports = emitter
  4908. 'use strict'
  4909.  
  4910. const v8Util = process.atomBinding('v8_util')
  4911.  
  4912. const getOwnerKey = (webContents, contextId) => {
  4913. return `${webContents.id}-${contextId}`
  4914. }
  4915.  
  4916. class ObjectsRegistry {
  4917. constructor () {
  4918. this.nextId = 0
  4919.  
  4920. // Stores all objects by ref-counting.
  4921. // (id) => {object, count}
  4922. this.storage = {}
  4923.  
  4924. // Stores the IDs of objects referenced by WebContents.
  4925. // (ownerKey) => [id]
  4926. this.owners = {}
  4927. }
  4928.  
  4929. // Register a new object and return its assigned ID. If the object is already
  4930. // registered then the already assigned ID would be returned.
  4931. add (webContents, contextId, obj) {
  4932. // Get or assign an ID to the object.
  4933. const id = this.saveToStorage(obj)
  4934.  
  4935. // Add object to the set of referenced objects.
  4936. const ownerKey = getOwnerKey(webContents, contextId)
  4937. let owner = this.owners[ownerKey]
  4938. if (!owner) {
  4939. owner = this.owners[ownerKey] = new Set()
  4940. this.registerDeleteListener(webContents, contextId)
  4941. }
  4942. if (!owner.has(id)) {
  4943. owner.add(id)
  4944. // Increase reference count if not referenced before.
  4945. this.storage[id].count++
  4946. }
  4947. return id
  4948. }
  4949.  
  4950. // Get an object according to its ID.
  4951. get (id) {
  4952. const pointer = this.storage[id]
  4953. if (pointer != null) return pointer.object
  4954. }
  4955.  
  4956. // Dereference an object according to its ID.
  4957. // Note that an object may be double-freed (cleared when page is reloaded, and
  4958. // then garbage collected in old page).
  4959. remove (webContents, contextId, id) {
  4960. const ownerKey = getOwnerKey(webContents, contextId)
  4961. const owner = this.owners[ownerKey]
  4962. if (owner) {
  4963. // Remove the reference in owner.
  4964. owner.delete(id)
  4965. // Dereference from the storage.
  4966. this.dereference(id)
  4967. }
  4968. }
  4969.  
  4970. // Clear all references to objects refrenced by the WebContents.
  4971. clear (webContents, contextId) {
  4972. const ownerKey = getOwnerKey(webContents, contextId)
  4973. const owner = this.owners[ownerKey]
  4974. if (!owner) return
  4975.  
  4976. for (const id of owner) this.dereference(id)
  4977.  
  4978. delete this.owners[ownerKey]
  4979. }
  4980.  
  4981. // Private: Saves the object into storage and assigns an ID for it.
  4982. saveToStorage (object) {
  4983. let id = v8Util.getHiddenValue(object, 'atomId')
  4984. if (!id) {
  4985. id = ++this.nextId
  4986. this.storage[id] = {
  4987. count: 0,
  4988. object: object
  4989. }
  4990. v8Util.setHiddenValue(object, 'atomId', id)
  4991. }
  4992. return id
  4993. }
  4994.  
  4995. // Private: Dereference the object from store.
  4996. dereference (id) {
  4997. const pointer = this.storage[id]
  4998. if (pointer == null) {
  4999. return
  5000. }
  5001. pointer.count -= 1
  5002. if (pointer.count === 0) {
  5003. v8Util.deleteHiddenValue(pointer.object, 'atomId')
  5004. delete this.storage[id]
  5005. }
  5006. }
  5007.  
  5008. // Private: Clear the storage when renderer process is destroyed.
  5009. registerDeleteListener (webContents, contextId) {
  5010. // contextId => ${processHostId}-${contextCount}
  5011. const processHostId = contextId.split('-')[0]
  5012. const listener = (event, deletedProcessHostId) => {
  5013. if (deletedProcessHostId &&
  5014. deletedProcessHostId.toString() === processHostId) {
  5015. webContents.removeListener('render-view-deleted', listener)
  5016. this.clear(webContents, contextId)
  5017. }
  5018. }
  5019. webContents.on('render-view-deleted', listener)
  5020. }
  5021. }
  5022.  
  5023. module.exports = new ObjectsRegistry()
  5024. 'use strict'
  5025.  
  5026. const { spawn } = require('child_process')
  5027. const electron = require('electron')
  5028. const { EventEmitter } = require('events')
  5029. const fs = require('fs')
  5030. const os = require('os')
  5031. const path = require('path')
  5032. const v8Util = process.atomBinding('v8_util')
  5033. const eventBinding = process.atomBinding('event')
  5034.  
  5035. const { isPromise } = electron
  5036.  
  5037. const ipcMain = require('@electron/internal/browser/ipc-main-internal')
  5038. const objectsRegistry = require('@electron/internal/browser/objects-registry')
  5039. const guestViewManager = require('@electron/internal/browser/guest-view-manager')
  5040. const bufferUtils = require('@electron/internal/common/buffer-utils')
  5041. const errorUtils = require('@electron/internal/common/error-utils')
  5042.  
  5043. const hasProp = {}.hasOwnProperty
  5044.  
  5045. // The internal properties of Function.
  5046. const FUNCTION_PROPERTIES = [
  5047. 'length', 'name', 'arguments', 'caller', 'prototype'
  5048. ]
  5049.  
  5050. // The remote functions in renderer processes.
  5051. // id => Function
  5052. const rendererFunctions = v8Util.createDoubleIDWeakMap()
  5053.  
  5054. // Return the description of object's members:
  5055. const getObjectMembers = function (object) {
  5056. let names = Object.getOwnPropertyNames(object)
  5057. // For Function, we should not override following properties even though they
  5058. // are "own" properties.
  5059. if (typeof object === 'function') {
  5060. names = names.filter((name) => {
  5061. return !FUNCTION_PROPERTIES.includes(name)
  5062. })
  5063. }
  5064. // Map properties to descriptors.
  5065. return names.map((name) => {
  5066. const descriptor = Object.getOwnPropertyDescriptor(object, name)
  5067. const member = { name, enumerable: descriptor.enumerable, writable: false }
  5068. if (descriptor.get === undefined && typeof object[name] === 'function') {
  5069. member.type = 'method'
  5070. } else {
  5071. if (descriptor.set || descriptor.writable) member.writable = true
  5072. member.type = 'get'
  5073. }
  5074. return member
  5075. })
  5076. }
  5077.  
  5078. // Return the description of object's prototype.
  5079. const getObjectPrototype = function (object) {
  5080. const proto = Object.getPrototypeOf(object)
  5081. if (proto === null || proto === Object.prototype) return null
  5082. return {
  5083. members: getObjectMembers(proto),
  5084. proto: getObjectPrototype(proto)
  5085. }
  5086. }
  5087.  
  5088. // Convert a real value into meta data.
  5089. const valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
  5090. // Determine the type of value.
  5091. const meta = { type: typeof value }
  5092. if (meta.type === 'object') {
  5093. // Recognize certain types of objects.
  5094. if (value === null) {
  5095. meta.type = 'value'
  5096. } else if (bufferUtils.isBuffer(value)) {
  5097. meta.type = 'buffer'
  5098. } else if (Array.isArray(value)) {
  5099. meta.type = 'array'
  5100. } else if (value instanceof Error) {
  5101. meta.type = 'error'
  5102. } else if (value instanceof Date) {
  5103. meta.type = 'date'
  5104. } else if (isPromise(value)) {
  5105. meta.type = 'promise'
  5106. } else if (hasProp.call(value, 'callee') && value.length != null) {
  5107. // Treat the arguments object as array.
  5108. meta.type = 'array'
  5109. } else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
  5110. // Treat simple objects as value.
  5111. meta.type = 'value'
  5112. }
  5113. }
  5114.  
  5115. // Fill the meta object according to value's type.
  5116. if (meta.type === 'array') {
  5117. meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
  5118. } else if (meta.type === 'object' || meta.type === 'function') {
  5119. meta.name = value.constructor ? value.constructor.name : ''
  5120.  
  5121. // Reference the original value if it's an object, because when it's
  5122. // passed to renderer we would assume the renderer keeps a reference of
  5123. // it.
  5124. meta.id = objectsRegistry.add(sender, contextId, value)
  5125. meta.members = getObjectMembers(value)
  5126. meta.proto = getObjectPrototype(value)
  5127. } else if (meta.type === 'buffer') {
  5128. meta.value = bufferUtils.bufferToMeta(value)
  5129. } else if (meta.type === 'promise') {
  5130. // Add default handler to prevent unhandled rejections in main process
  5131. // Instead they should appear in the renderer process
  5132. value.then(function () {}, function () {})
  5133.  
  5134. meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
  5135. value.then(onFulfilled, onRejected)
  5136. })
  5137. } else if (meta.type === 'error') {
  5138. meta.members = plainObjectToMeta(value)
  5139.  
  5140. // Error.name is not part of own properties.
  5141. meta.members.push({
  5142. name: 'name',
  5143. value: value.name
  5144. })
  5145. } else if (meta.type === 'date') {
  5146. meta.value = value.getTime()
  5147. } else {
  5148. meta.type = 'value'
  5149. meta.value = value
  5150. }
  5151. return meta
  5152. }
  5153.  
  5154. // Convert object to meta by value.
  5155. const plainObjectToMeta = function (obj) {
  5156. return Object.getOwnPropertyNames(obj).map(function (name) {
  5157. return {
  5158. name: name,
  5159. value: obj[name]
  5160. }
  5161. })
  5162. }
  5163.  
  5164. // Convert Error into meta data.
  5165. const exceptionToMeta = function (sender, contextId, error) {
  5166. return {
  5167. type: 'exception',
  5168. value: errorUtils.serialize(error)
  5169. }
  5170. }
  5171.  
  5172. const throwRPCError = function (message) {
  5173. const error = new Error(message)
  5174. error.code = 'EBADRPC'
  5175. error.errno = -72
  5176. throw error
  5177. }
  5178.  
  5179. const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => {
  5180. const location = v8Util.getHiddenValue(callIntoRenderer, 'location')
  5181. let message = `Attempting to call a function in a renderer window that has been closed or released.` +
  5182. `\nFunction provided here: ${location}`
  5183.  
  5184. if (sender instanceof EventEmitter) {
  5185. const remoteEvents = sender.eventNames().filter((eventName) => {
  5186. return sender.listeners(eventName).includes(callIntoRenderer)
  5187. })
  5188.  
  5189. if (remoteEvents.length > 0) {
  5190. message += `\nRemote event names: ${remoteEvents.join(', ')}`
  5191. remoteEvents.forEach((eventName) => {
  5192. sender.removeListener(eventName, callIntoRenderer)
  5193. })
  5194. }
  5195. }
  5196.  
  5197. console.warn(message)
  5198. }
  5199.  
  5200. // Convert array of meta data from renderer into array of real values.
  5201. const unwrapArgs = function (sender, contextId, args) {
  5202. const metaToValue = function (meta) {
  5203. switch (meta.type) {
  5204. case 'value':
  5205. return meta.value
  5206. case 'remote-object':
  5207. return objectsRegistry.get(meta.id)
  5208. case 'array':
  5209. return unwrapArgs(sender, contextId, meta.value)
  5210. case 'buffer':
  5211. return bufferUtils.metaToBuffer(meta.value)
  5212. case 'date':
  5213. return new Date(meta.value)
  5214. case 'promise':
  5215. return Promise.resolve({
  5216. then: metaToValue(meta.then)
  5217. })
  5218. case 'object': {
  5219. const ret = {}
  5220. Object.defineProperty(ret.constructor, 'name', { value: meta.name })
  5221.  
  5222. for (const { name, value } of meta.members) {
  5223. ret[name] = metaToValue(value)
  5224. }
  5225. return ret
  5226. }
  5227. case 'function-with-return-value':
  5228. const returnValue = metaToValue(meta.value)
  5229. return function () {
  5230. return returnValue
  5231. }
  5232. case 'function': {
  5233. // Merge contextId and meta.id, since meta.id can be the same in
  5234. // different webContents.
  5235. const objectId = [contextId, meta.id]
  5236.  
  5237. // Cache the callbacks in renderer.
  5238. if (rendererFunctions.has(objectId)) {
  5239. return rendererFunctions.get(objectId)
  5240. }
  5241.  
  5242. const callIntoRenderer = function (...args) {
  5243. if (!sender.isDestroyed()) {
  5244. sender._sendInternal('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
  5245. } else {
  5246. removeRemoteListenersAndLogWarning(this, callIntoRenderer)
  5247. }
  5248. }
  5249. v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location)
  5250. Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
  5251.  
  5252. v8Util.setRemoteCallbackFreer(callIntoRenderer, contextId, meta.id, sender)
  5253. rendererFunctions.set(objectId, callIntoRenderer)
  5254. return callIntoRenderer
  5255. }
  5256. default:
  5257. throw new TypeError(`Unknown type: ${meta.type}`)
  5258. }
  5259. }
  5260. return args.map(metaToValue)
  5261. }
  5262.  
  5263. // Call a function and send reply asynchronously if it's a an asynchronous
  5264. // style function and the caller didn't pass a callback.
  5265. const callFunction = function (event, contextId, func, caller, args) {
  5266. const funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
  5267. const funcPassedCallback = typeof args[args.length - 1] === 'function'
  5268. try {
  5269. if (funcMarkedAsync && !funcPassedCallback) {
  5270. args.push(function (ret) {
  5271. event.returnValue = valueToMeta(event.sender, contextId, ret, true)
  5272. })
  5273. func.apply(caller, args)
  5274. } else {
  5275. const ret = func.apply(caller, args)
  5276. return valueToMeta(event.sender, contextId, ret, true)
  5277. }
  5278. } catch (error) {
  5279. // Catch functions thrown further down in function invocation and wrap
  5280. // them with the function name so it's easier to trace things like
  5281. // `Error processing argument -1.`
  5282. const funcName = func.name || 'anonymous'
  5283. const err = new Error(`Could not call remote function '${funcName}'. Check that the function signature is correct. Underlying error: ${error.message}`)
  5284. err.cause = error
  5285. throw err
  5286. }
  5287. }
  5288.  
  5289. const handleRemoteCommand = function (channel, handler) {
  5290. ipcMain.on(channel, (event, contextId, ...args) => {
  5291. let returnValue
  5292. if (!event.sender._isRemoteModuleEnabled()) {
  5293. event.returnValue = null
  5294. return
  5295. }
  5296.  
  5297. try {
  5298. returnValue = handler(event, contextId, ...args)
  5299. } catch (error) {
  5300. returnValue = exceptionToMeta(event.sender, contextId, error)
  5301. }
  5302.  
  5303. if (returnValue !== undefined) {
  5304. event.returnValue = returnValue
  5305. }
  5306. })
  5307. }
  5308.  
  5309. handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) {
  5310. const objectId = [passedContextId, id]
  5311. if (!rendererFunctions.has(objectId)) {
  5312. // Do nothing if the error has already been reported before.
  5313. return
  5314. }
  5315. removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId))
  5316. })
  5317.  
  5318. handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName) {
  5319. const customEvent = eventBinding.createWithSender(event.sender)
  5320. event.sender.emit('remote-require', customEvent, moduleName)
  5321.  
  5322. if (customEvent.returnValue === undefined) {
  5323. if (customEvent.defaultPrevented) {
  5324. throw new Error(`Blocked remote.require('${moduleName}')`)
  5325. } else {
  5326. customEvent.returnValue = process.mainModule.require(moduleName)
  5327. }
  5328. }
  5329.  
  5330. return valueToMeta(event.sender, contextId, customEvent.returnValue)
  5331. })
  5332.  
  5333. handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) {
  5334. const customEvent = eventBinding.createWithSender(event.sender)
  5335. event.sender.emit('remote-get-builtin', customEvent, moduleName)
  5336.  
  5337. if (customEvent.returnValue === undefined) {
  5338. if (customEvent.defaultPrevented) {
  5339. throw new Error(`Blocked remote.getBuiltin('${moduleName}')`)
  5340. } else {
  5341. customEvent.returnValue = electron[moduleName]
  5342. }
  5343. }
  5344.  
  5345. return valueToMeta(event.sender, contextId, customEvent.returnValue)
  5346. })
  5347.  
  5348. handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) {
  5349. const customEvent = eventBinding.createWithSender(event.sender)
  5350. event.sender.emit('remote-get-global', customEvent, globalName)
  5351.  
  5352. if (customEvent.returnValue === undefined) {
  5353. if (customEvent.defaultPrevented) {
  5354. throw new Error(`Blocked remote.getGlobal('${globalName}')`)
  5355. } else {
  5356. customEvent.returnValue = global[globalName]
  5357. }
  5358. }
  5359.  
  5360. return valueToMeta(event.sender, contextId, customEvent.returnValue)
  5361. })
  5362.  
  5363. handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
  5364. const customEvent = eventBinding.createWithSender(event.sender)
  5365. event.sender.emit('remote-get-current-window', customEvent)
  5366.  
  5367. if (customEvent.returnValue === undefined) {
  5368. if (customEvent.defaultPrevented) {
  5369. throw new Error('Blocked remote.getCurrentWindow()')
  5370. } else {
  5371. customEvent.returnValue = event.sender.getOwnerBrowserWindow()
  5372. }
  5373. }
  5374.  
  5375. return valueToMeta(event.sender, contextId, customEvent.returnValue)
  5376. })
  5377.  
  5378. handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
  5379. const customEvent = eventBinding.createWithSender(event.sender)
  5380. event.sender.emit('remote-get-current-web-contents', customEvent)
  5381.  
  5382. if (customEvent.returnValue === undefined) {
  5383. if (customEvent.defaultPrevented) {
  5384. throw new Error('Blocked remote.getCurrentWebContents()')
  5385. } else {
  5386. customEvent.returnValue = event.sender
  5387. }
  5388. }
  5389.  
  5390. return valueToMeta(event.sender, contextId, customEvent.returnValue)
  5391. })
  5392.  
  5393. handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
  5394. args = unwrapArgs(event.sender, contextId, args)
  5395. const constructor = objectsRegistry.get(id)
  5396.  
  5397. if (constructor == null) {
  5398. throwRPCError(`Cannot call constructor on missing remote object ${id}`)
  5399. }
  5400.  
  5401. return valueToMeta(event.sender, contextId, new constructor(...args))
  5402. })
  5403.  
  5404. handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
  5405. args = unwrapArgs(event.sender, contextId, args)
  5406. const func = objectsRegistry.get(id)
  5407.  
  5408. if (func == null) {
  5409. throwRPCError(`Cannot call function on missing remote object ${id}`)
  5410. }
  5411.  
  5412. return callFunction(event, contextId, func, global, args)
  5413. })
  5414.  
  5415. handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
  5416. args = unwrapArgs(event.sender, contextId, args)
  5417. const object = objectsRegistry.get(id)
  5418.  
  5419. if (object == null) {
  5420. throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
  5421. }
  5422.  
  5423. return valueToMeta(event.sender, contextId, new object[method](...args))
  5424. })
  5425.  
  5426. handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
  5427. args = unwrapArgs(event.sender, contextId, args)
  5428. const obj = objectsRegistry.get(id)
  5429.  
  5430. if (obj == null) {
  5431. throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
  5432. }
  5433.  
  5434. return callFunction(event, contextId, obj[method], obj, args)
  5435. })
  5436.  
  5437. handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
  5438. args = unwrapArgs(event.sender, contextId, args)
  5439. const obj = objectsRegistry.get(id)
  5440.  
  5441. if (obj == null) {
  5442. throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`)
  5443. }
  5444.  
  5445. obj[name] = args[0]
  5446. return null
  5447. })
  5448.  
  5449. handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
  5450. const obj = objectsRegistry.get(id)
  5451.  
  5452. if (obj == null) {
  5453. throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
  5454. }
  5455.  
  5456. return valueToMeta(event.sender, contextId, obj[name])
  5457. })
  5458.  
  5459. handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id) {
  5460. objectsRegistry.remove(event.sender, contextId, id)
  5461. })
  5462.  
  5463. handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
  5464. objectsRegistry.clear(event.sender, contextId)
  5465. return null
  5466. })
  5467.  
  5468. handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
  5469. const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender)
  5470.  
  5471. const customEvent = eventBinding.createWithSender(event.sender)
  5472. event.sender.emit('remote-get-guest-web-contents', customEvent, guest)
  5473.  
  5474. if (customEvent.returnValue === undefined) {
  5475. if (customEvent.defaultPrevented) {
  5476. throw new Error(`Blocked remote.getGuestForWebContents()`)
  5477. } else {
  5478. customEvent.returnValue = guest
  5479. }
  5480. }
  5481.  
  5482. return valueToMeta(event.sender, contextId, customEvent.returnValue)
  5483. })
  5484.  
  5485. // Implements window.close()
  5486. ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
  5487. const window = event.sender.getOwnerBrowserWindow()
  5488. if (window) {
  5489. window.close()
  5490. }
  5491. event.returnValue = null
  5492. })
  5493.  
  5494. const getTempDirectory = function () {
  5495. try {
  5496. return electron.app.getPath('temp')
  5497. } catch (error) {
  5498. return os.tmpdir()
  5499. }
  5500. }
  5501.  
  5502. const crashReporterInit = function (options) {
  5503. const productName = options.productName || electron.app.getName()
  5504. const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`)
  5505. let crashServicePid
  5506.  
  5507. if (process.platform === 'win32') {
  5508. const env = {
  5509. ELECTRON_INTERNAL_CRASH_SERVICE: 1
  5510. }
  5511. const args = [
  5512. '--reporter-url=' + options.submitURL,
  5513. '--application-name=' + productName,
  5514. '--crashes-directory=' + crashesDirectory,
  5515. '--v=1'
  5516. ]
  5517.  
  5518. const crashServiceProcess = spawn(process.helperExecPath, args, {
  5519. env,
  5520. detached: true
  5521. })
  5522.  
  5523. crashServicePid = crashServiceProcess.pid
  5524. }
  5525.  
  5526. return {
  5527. productName,
  5528. crashesDirectory,
  5529. crashServicePid,
  5530. appVersion: electron.app.getVersion()
  5531. }
  5532. }
  5533.  
  5534. const setReturnValue = function (event, getValue) {
  5535. try {
  5536. event.returnValue = [null, getValue()]
  5537. } catch (error) {
  5538. event.returnValue = [errorUtils.serialize(error)]
  5539. }
  5540. }
  5541.  
  5542. ipcMain.on('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
  5543. setReturnValue(event, () => crashReporterInit(options))
  5544. })
  5545.  
  5546. ipcMain.on('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
  5547. setReturnValue(event, () => event.sender.getLastWebPreferences())
  5548. })
  5549.  
  5550. ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_READ_FIND_TEXT', function (event) {
  5551. setReturnValue(event, () => electron.clipboard.readFindText())
  5552. })
  5553.  
  5554. ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', function (event, text) {
  5555. setReturnValue(event, () => electron.clipboard.writeFindText(text))
  5556. })
  5557.  
  5558. ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
  5559. const preloadPath = event.sender._getPreloadPath()
  5560. let preloadSrc = null
  5561. let preloadError = null
  5562. if (preloadPath) {
  5563. try {
  5564. preloadSrc = fs.readFileSync(preloadPath).toString()
  5565. } catch (err) {
  5566. preloadError = { stack: err ? err.stack : (new Error(`Failed to load "${preloadPath}"`)).stack }
  5567. }
  5568. }
  5569. event.returnValue = {
  5570. preloadSrc,
  5571. preloadError,
  5572. isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
  5573. process: {
  5574. arch: process.arch,
  5575. platform: process.platform,
  5576. env: process.env,
  5577. version: process.version,
  5578. versions: process.versions
  5579. }
  5580. }
  5581. })
  5582. 'use strict'
  5583.  
  5584. if (process.platform === 'linux' && process.type === 'renderer') {
  5585. // On Linux we could not access clipboard in renderer process.
  5586. const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
  5587. module.exports = getRemoteForUsage('clipboard').clipboard
  5588. } else {
  5589. const clipboard = process.atomBinding('clipboard')
  5590.  
  5591. // Read/write to find pasteboard over IPC since only main process is notified
  5592. // of changes
  5593. if (process.platform === 'darwin' && process.type === 'renderer') {
  5594. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  5595. const errorUtils = require('@electron/internal/common/error-utils')
  5596.  
  5597. const invoke = function (command, ...args) {
  5598. const [ error, result ] = ipcRenderer.sendSync(command, ...args)
  5599.  
  5600. if (error) {
  5601. throw errorUtils.deserialize(error)
  5602. } else {
  5603. return result
  5604. }
  5605. }
  5606.  
  5607. clipboard.readFindText = (...args) => invoke('ELECTRON_BROWSER_CLIPBOARD_READ_FIND_TEXT', ...args)
  5608. clipboard.writeFindText = (...args) => invoke('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', ...args)
  5609. }
  5610.  
  5611. module.exports = clipboard
  5612. }
  5613. 'use strict'
  5614.  
  5615. let deprecationHandler = null
  5616.  
  5617. function warnOnce (oldName, newName) {
  5618. let warned = false
  5619. const msg = newName
  5620. ? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.`
  5621. : `'${oldName}' is deprecated and will be removed.`
  5622. return () => {
  5623. if (!warned && !process.noDeprecation) {
  5624. warned = true
  5625. deprecate.log(msg)
  5626. }
  5627. }
  5628. }
  5629.  
  5630. const deprecate = {
  5631. setHandler: (handler) => { deprecationHandler = handler },
  5632. getHandler: () => deprecationHandler,
  5633. warn: (oldName, newName) => {
  5634. return deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`)
  5635. },
  5636. log: (message) => {
  5637. if (typeof deprecationHandler === 'function') {
  5638. deprecationHandler(message)
  5639. } else if (process.throwDeprecation) {
  5640. throw new Error(message)
  5641. } else if (process.traceDeprecation) {
  5642. return console.trace(message)
  5643. } else {
  5644. return console.warn(`(electron) ${message}`)
  5645. }
  5646. },
  5647.  
  5648. event: (emitter, oldName, newName) => {
  5649. const warn = newName.startsWith('-') /* internal event */
  5650. ? warnOnce(`${oldName} event`)
  5651. : warnOnce(`${oldName} event`, `${newName} event`)
  5652. return emitter.on(newName, function (...args) {
  5653. if (this.listenerCount(oldName) !== 0) {
  5654. warn()
  5655. this.emit(oldName, ...args)
  5656. }
  5657. })
  5658. },
  5659.  
  5660. removeProperty: (o, removedName) => {
  5661. // if the property's already been removed, warn about it
  5662. if (!(removedName in o)) {
  5663. deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`)
  5664. }
  5665.  
  5666. // wrap the deprecated property in an accessor to warn
  5667. const warn = warnOnce(removedName)
  5668. let val = o[removedName]
  5669. return Object.defineProperty(o, removedName, {
  5670. configurable: true,
  5671. get: () => {
  5672. warn()
  5673. return val
  5674. },
  5675. set: newVal => {
  5676. warn()
  5677. val = newVal
  5678. }
  5679. })
  5680. },
  5681.  
  5682. renameProperty: (o, oldName, newName) => {
  5683. const warn = warnOnce(oldName, newName)
  5684.  
  5685. // if the new property isn't there yet,
  5686. // inject it and warn about it
  5687. if ((oldName in o) && !(newName in o)) {
  5688. warn()
  5689. o[newName] = o[oldName]
  5690. }
  5691.  
  5692. // wrap the deprecated property in an accessor to warn
  5693. // and redirect to the new property
  5694. return Object.defineProperty(o, oldName, {
  5695. get: () => {
  5696. warn()
  5697. return o[newName]
  5698. },
  5699. set: value => {
  5700. warn()
  5701. o[newName] = value
  5702. }
  5703. })
  5704. }
  5705. }
  5706.  
  5707. module.exports = deprecate
  5708. 'use strict'
  5709.  
  5710. const deprecate = require('electron').deprecate
  5711.  
  5712. exports.setHandler = function (deprecationHandler) {
  5713. deprecate.setHandler(deprecationHandler)
  5714. }
  5715.  
  5716. exports.getHandler = function () {
  5717. return deprecate.getHandler()
  5718. }
  5719. 'use strict'
  5720.  
  5721. const moduleList = require('@electron/internal/common/api/module-list')
  5722.  
  5723. exports.memoizedGetter = (getter) => {
  5724. /*
  5725. * It's ok to leak this value as it would be leaked by the global
  5726. * node module cache anyway at `Module._cache`. This memoization
  5727. * is dramatically faster than relying on nodes module cache however
  5728. */
  5729. let memoizedValue = null
  5730.  
  5731. return () => {
  5732. if (memoizedValue === null) {
  5733. memoizedValue = getter()
  5734. }
  5735. return memoizedValue
  5736. }
  5737. }
  5738.  
  5739. // Attaches properties to |targetExports|.
  5740. exports.defineProperties = function (targetExports) {
  5741. const descriptors = {}
  5742. for (const module of moduleList) {
  5743. descriptors[module.name] = {
  5744. enumerable: !module.private,
  5745. get: exports.memoizedGetter(() => require(`@electron/internal/common/api/${module.file}`))
  5746. }
  5747. }
  5748. return Object.defineProperties(targetExports, descriptors)
  5749. }
  5750. 'use strict'
  5751.  
  5752. module.exports = function isPromise (val) {
  5753. return (
  5754. val &&
  5755. val.then &&
  5756. val.then instanceof Function &&
  5757. val.constructor &&
  5758. val.constructor.reject &&
  5759. val.constructor.reject instanceof Function &&
  5760. val.constructor.resolve &&
  5761. val.constructor.resolve instanceof Function
  5762. )
  5763. }
  5764. 'use strict'
  5765.  
  5766. // Common modules, please sort alphabetically
  5767. module.exports = [
  5768. { name: 'clipboard', file: 'clipboard' },
  5769. { name: 'nativeImage', file: 'native-image' },
  5770. { name: 'shell', file: 'shell' },
  5771. // The internal modules, invisible unless you know their names.
  5772. { name: 'deprecate', file: 'deprecate', private: true },
  5773. { name: 'deprecations', file: 'deprecations', private: true },
  5774. { name: 'isPromise', file: 'is-promise', private: true }
  5775. ]
  5776. 'use strict'
  5777.  
  5778. module.exports = process.atomBinding('native_image')
  5779. 'use strict'
  5780.  
  5781. module.exports = process.atomBinding('shell')
  5782. 'use strict'
  5783.  
  5784. module.exports = function atomBindingSetup (binding, processType) {
  5785. return function atomBinding (name) {
  5786. try {
  5787. return binding(`atom_${processType}_${name}`)
  5788. } catch (error) {
  5789. if (/No such module/.test(error.message)) {
  5790. return binding(`atom_common_${name}`)
  5791. } else {
  5792. throw error
  5793. }
  5794. }
  5795. }
  5796. }
  5797. 'use strict'
  5798.  
  5799. // Note: Don't use destructuring assignment for `Buffer`, or we'll hit a
  5800. // browserify bug that makes the statement invalid, throwing an error in
  5801. // sandboxed renderer.
  5802. const Buffer = require('buffer').Buffer
  5803.  
  5804. const typedArrays = {
  5805. Buffer,
  5806. ArrayBuffer,
  5807. Int8Array,
  5808. Uint8Array,
  5809. Uint8ClampedArray,
  5810. Int16Array,
  5811. Uint16Array,
  5812. Int32Array,
  5813. Uint32Array,
  5814. Float32Array,
  5815. Float64Array
  5816. }
  5817.  
  5818. function getType (value) {
  5819. for (const type of Object.keys(typedArrays)) {
  5820. if (value instanceof typedArrays[type]) {
  5821. return type
  5822. }
  5823. }
  5824. return null
  5825. }
  5826.  
  5827. function getBuffer (value) {
  5828. if (value instanceof Buffer) {
  5829. return value
  5830. } else if (value instanceof ArrayBuffer) {
  5831. return Buffer.from(value)
  5832. } else {
  5833. return Buffer.from(value.buffer, value.byteOffset, value.byteLength)
  5834. }
  5835. }
  5836.  
  5837. exports.isBuffer = function (value) {
  5838. return ArrayBuffer.isView(value) || value instanceof ArrayBuffer
  5839. }
  5840.  
  5841. exports.bufferToMeta = function (value) {
  5842. return {
  5843. type: getType(value),
  5844. data: getBuffer(value),
  5845. length: value.length
  5846. }
  5847. }
  5848.  
  5849. exports.metaToBuffer = function (value) {
  5850. const constructor = typedArrays[value.type]
  5851. const data = getBuffer(value.data)
  5852.  
  5853. if (constructor === Buffer) {
  5854. return data
  5855. } else if (constructor === ArrayBuffer) {
  5856. return data.buffer
  5857. } else if (constructor) {
  5858. return new constructor(data.buffer, data.byteOffset, value.length)
  5859. } else {
  5860. return data
  5861. }
  5862. }
  5863. 'use strict'
  5864.  
  5865. const binding = process.atomBinding('crash_reporter')
  5866.  
  5867. const errorUtils = require('@electron/internal/common/error-utils')
  5868.  
  5869. class CrashReporter {
  5870. contructor () {
  5871. this.productName = null
  5872. this.crashesDirectory = null
  5873. }
  5874.  
  5875. sendSync (channel, ...args) {
  5876. throw new Error('Not implemented')
  5877. }
  5878.  
  5879. invoke (command, ...args) {
  5880. const [ error, result ] = this.sendSync(command, ...args)
  5881.  
  5882. if (error) {
  5883. throw errorUtils.deserialize(error)
  5884. }
  5885.  
  5886. return result
  5887. }
  5888.  
  5889. start (options) {
  5890. if (options == null) options = {}
  5891.  
  5892. let {
  5893. productName,
  5894. companyName,
  5895. extra,
  5896. ignoreSystemCrashHandler,
  5897. submitURL,
  5898. uploadToServer
  5899. } = options
  5900.  
  5901. if (uploadToServer == null) {
  5902. uploadToServer = true
  5903. }
  5904.  
  5905. if (ignoreSystemCrashHandler == null) {
  5906. ignoreSystemCrashHandler = false
  5907. }
  5908.  
  5909. if (companyName == null) {
  5910. throw new Error('companyName is a required option to crashReporter.start')
  5911. }
  5912. if (submitURL == null) {
  5913. throw new Error('submitURL is a required option to crashReporter.start')
  5914. }
  5915.  
  5916. const ret = this.invoke('ELECTRON_CRASH_REPORTER_INIT', {
  5917. submitURL,
  5918. productName
  5919. })
  5920.  
  5921. this.productName = ret.productName
  5922. this.crashesDirectory = ret.crashesDirectory
  5923. this.crashServicePid = ret.crashServicePid
  5924.  
  5925. if (extra == null) extra = {}
  5926. if (extra._productName == null) extra._productName = ret.productName
  5927. if (extra._companyName == null) extra._companyName = companyName
  5928. if (extra._version == null) extra._version = ret.appVersion
  5929.  
  5930. binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra)
  5931. }
  5932.  
  5933. getLastCrashReport () {
  5934. const reports = this.getUploadedReports()
  5935. .sort((a, b) => {
  5936. const ats = (a && a.date) ? new Date(a.date).getTime() : 0
  5937. const bts = (b && b.date) ? new Date(b.date).getTime() : 0
  5938. return bts - ats
  5939. })
  5940.  
  5941. return (reports.length > 0) ? reports[0] : null
  5942. }
  5943.  
  5944. getUploadedReports () {
  5945. return binding.getUploadedReports(this.getCrashesDirectory())
  5946. }
  5947.  
  5948. getCrashesDirectory () {
  5949. return this.crashesDirectory
  5950. }
  5951.  
  5952. getProductName () {
  5953. return this.productName
  5954. }
  5955.  
  5956. getUploadToServer () {
  5957. if (process.type === 'browser') {
  5958. return binding.getUploadToServer()
  5959. } else {
  5960. throw new Error('getUploadToServer can only be called from the main process')
  5961. }
  5962. }
  5963.  
  5964. setUploadToServer (uploadToServer) {
  5965. if (process.type === 'browser') {
  5966. return binding.setUploadToServer(uploadToServer)
  5967. } else {
  5968. throw new Error('setUploadToServer can only be called from the main process')
  5969. }
  5970. }
  5971.  
  5972. addExtraParameter (key, value) {
  5973. binding.addExtraParameter(key, value)
  5974. }
  5975.  
  5976. removeExtraParameter (key) {
  5977. binding.removeExtraParameter(key)
  5978. }
  5979.  
  5980. getParameters (key, value) {
  5981. return binding.getParameters()
  5982. }
  5983. }
  5984.  
  5985. module.exports = CrashReporter
  5986. 'use strict'
  5987.  
  5988. const constructors = new Map([
  5989. [Error.name, Error],
  5990. [EvalError.name, EvalError],
  5991. [RangeError.name, RangeError],
  5992. [ReferenceError.name, ReferenceError],
  5993. [SyntaxError.name, SyntaxError],
  5994. [TypeError.name, TypeError],
  5995. [URIError.name, URIError]
  5996. ])
  5997.  
  5998. exports.deserialize = function (error) {
  5999. if (error && error.__ELECTRON_SERIALIZED_ERROR__ && constructors.has(error.name)) {
  6000. const constructor = constructors.get(error.name)
  6001. const deserializedError = new constructor(error.message)
  6002. deserializedError.stack = error.stack
  6003. deserializedError.from = error.from
  6004. deserializedError.cause = exports.deserialize(error.cause)
  6005. return deserializedError
  6006. }
  6007. return error
  6008. }
  6009.  
  6010. exports.serialize = function (error) {
  6011. if (error instanceof Error) {
  6012. // Errors get lost, because: JSON.stringify(new Error('Message')) === {}
  6013. // Take the serializable properties and construct a generic object
  6014. return {
  6015. message: error.message,
  6016. stack: error.stack,
  6017. name: error.name,
  6018. from: process.type,
  6019. cause: exports.serialize(error.cause),
  6020. __ELECTRON_SERIALIZED_ERROR__: true
  6021. }
  6022. }
  6023. return error
  6024. }
  6025. 'use strict'
  6026.  
  6027. const timers = require('timers')
  6028. const util = require('util')
  6029.  
  6030. process.atomBinding = require('@electron/internal/common/atom-binding-setup')(process.binding, process.type)
  6031.  
  6032. // setImmediate and process.nextTick makes use of uv_check and uv_prepare to
  6033. // run the callbacks, however since we only run uv loop on requests, the
  6034. // callbacks wouldn't be called until something else activated the uv loop,
  6035. // which would delay the callbacks for arbitrary long time. So we should
  6036. // initiatively activate the uv loop once setImmediate and process.nextTick is
  6037. // called.
  6038. const wrapWithActivateUvLoop = function (func) {
  6039. return wrap(func, function (func) {
  6040. return function () {
  6041. process.activateUvLoop()
  6042. return func.apply(this, arguments)
  6043. }
  6044. })
  6045. }
  6046.  
  6047. function wrap (func, wrapper) {
  6048. const wrapped = wrapper(func)
  6049. if (func[util.promisify.custom]) {
  6050. wrapped[util.promisify.custom] = wrapper(func[util.promisify.custom])
  6051. }
  6052. return wrapped
  6053. }
  6054.  
  6055. process.nextTick = wrapWithActivateUvLoop(process.nextTick)
  6056.  
  6057. global.setImmediate = wrapWithActivateUvLoop(timers.setImmediate)
  6058. global.clearImmediate = timers.clearImmediate
  6059.  
  6060. if (process.type === 'browser') {
  6061. // setTimeout needs to update the polling timeout of the event loop, when
  6062. // called under Chromium's event loop the node's event loop won't get a chance
  6063. // to update the timeout, so we have to force the node's event loop to
  6064. // recalculate the timeout in browser process.
  6065. global.setTimeout = wrapWithActivateUvLoop(timers.setTimeout)
  6066. global.setInterval = wrapWithActivateUvLoop(timers.setInterval)
  6067. }
  6068.  
  6069. if (process.platform === 'win32') {
  6070. // Always returns EOF for stdin stream.
  6071. const { Readable } = require('stream')
  6072. const stdin = new Readable()
  6073. stdin.push(null)
  6074. process.__defineGetter__('stdin', function () {
  6075. return stdin
  6076. })
  6077. }
  6078. 'use strict'
  6079.  
  6080. // parses a feature string that has the format used in window.open()
  6081. // - `features` input string
  6082. // - `emit` function(key, value) - called for each parsed KV
  6083. module.exports = function parseFeaturesString (features, emit) {
  6084. features = `${features}`
  6085. // split the string by ','
  6086. features.split(/,\s*/).forEach((feature) => {
  6087. // expected form is either a key by itself or a key/value pair in the form of
  6088. // 'key=value'
  6089. let [key, value] = feature.split(/\s*=/)
  6090. if (!key) return
  6091.  
  6092. // interpret the value as a boolean, if possible
  6093. value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value
  6094.  
  6095. // emit the parsed pair
  6096. emit(key, value)
  6097. })
  6098. }
  6099. 'use strict'
  6100.  
  6101. const path = require('path')
  6102. const Module = require('module')
  6103.  
  6104. // Clear Node's global search paths.
  6105. Module.globalPaths.length = 0
  6106.  
  6107. // Clear current and parent(init.js)'s search paths.
  6108. module.paths = []
  6109. module.parent.paths = []
  6110.  
  6111. // Prevent Node from adding paths outside this app to search paths.
  6112. const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep
  6113. const originalNodeModulePaths = Module._nodeModulePaths
  6114. Module._nodeModulePaths = function (from) {
  6115. const paths = originalNodeModulePaths(from)
  6116. const fromPath = path.resolve(from) + path.sep
  6117. // If "from" is outside the app then we do nothing.
  6118. if (fromPath.startsWith(resourcesPathWithTrailingSlash)) {
  6119. return paths.filter(function (candidate) {
  6120. return candidate.startsWith(resourcesPathWithTrailingSlash)
  6121. })
  6122. } else {
  6123. return paths
  6124. }
  6125. }
  6126.  
  6127. const BASE_INTERNAL_PATH = path.resolve(__dirname, '..')
  6128. const INTERNAL_MODULE_PREFIX = '@electron/internal/'
  6129.  
  6130. // Patch Module._resolveFilename to always require the Electron API when
  6131. // require('electron') is done.
  6132. const electronPath = path.join(__dirname, '..', process.type, 'api', 'exports', 'electron.js')
  6133. const originalResolveFilename = Module._resolveFilename
  6134. Module._resolveFilename = function (request, parent, isMain) {
  6135. if (request === 'electron') {
  6136. return electronPath
  6137. } else if (request.startsWith(INTERNAL_MODULE_PREFIX) && request.length > INTERNAL_MODULE_PREFIX.length) {
  6138. const slicedRequest = request.slice(INTERNAL_MODULE_PREFIX.length)
  6139. return path.resolve(BASE_INTERNAL_PATH, `${slicedRequest}${slicedRequest.endsWith('.js') ? '' : '.js'}`)
  6140. } else {
  6141. return originalResolveFilename(request, parent, isMain)
  6142. }
  6143. }
  6144. 'use strict'
  6145.  
  6146. // Public-facing API methods.
  6147. exports.syncMethods = new Set([
  6148. 'getURL',
  6149. 'loadURL',
  6150. 'getTitle',
  6151. 'isLoading',
  6152. 'isLoadingMainFrame',
  6153. 'isWaitingForResponse',
  6154. 'stop',
  6155. 'reload',
  6156. 'reloadIgnoringCache',
  6157. 'canGoBack',
  6158. 'canGoForward',
  6159. 'canGoToOffset',
  6160. 'clearHistory',
  6161. 'goBack',
  6162. 'goForward',
  6163. 'goToIndex',
  6164. 'goToOffset',
  6165. 'isCrashed',
  6166. 'setUserAgent',
  6167. 'getUserAgent',
  6168. 'openDevTools',
  6169. 'closeDevTools',
  6170. 'isDevToolsOpened',
  6171. 'isDevToolsFocused',
  6172. 'inspectElement',
  6173. 'setAudioMuted',
  6174. 'isAudioMuted',
  6175. 'isCurrentlyAudible',
  6176. 'undo',
  6177. 'redo',
  6178. 'cut',
  6179. 'copy',
  6180. 'paste',
  6181. 'pasteAndMatchStyle',
  6182. 'delete',
  6183. 'selectAll',
  6184. 'unselect',
  6185. 'replace',
  6186. 'replaceMisspelling',
  6187. 'findInPage',
  6188. 'stopFindInPage',
  6189. 'downloadURL',
  6190. 'inspectServiceWorker',
  6191. 'showDefinitionForSelection',
  6192. 'setZoomFactor',
  6193. 'setZoomLevel',
  6194. 'sendImeEvent'
  6195. ])
  6196.  
  6197. exports.asyncCallbackMethods = new Set([
  6198. 'insertCSS',
  6199. 'insertText',
  6200. 'send',
  6201. 'sendInputEvent',
  6202. 'setLayoutZoomLevelLimits',
  6203. 'setVisualZoomLevelLimits',
  6204. 'getZoomFactor',
  6205. 'getZoomLevel',
  6206. 'print',
  6207. 'printToPDF'
  6208. ])
  6209.  
  6210. exports.asyncPromiseMethods = new Set([
  6211. 'capturePage',
  6212. 'executeJavaScript'
  6213. ])
  6214. 'use strict'
  6215.  
  6216. const CrashReporter = require('@electron/internal/common/crash-reporter')
  6217. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  6218.  
  6219. class CrashReporterRenderer extends CrashReporter {
  6220. sendSync (channel, ...args) {
  6221. return ipcRenderer.sendSync(channel, ...args)
  6222. }
  6223. }
  6224.  
  6225. module.exports = new CrashReporterRenderer()
  6226. 'use strict'
  6227.  
  6228. const { nativeImage } = require('electron')
  6229. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  6230.  
  6231. const includes = [].includes
  6232. let currentId = 0
  6233.  
  6234. const incrementId = () => {
  6235. currentId += 1
  6236. return currentId
  6237. }
  6238.  
  6239. // |options.types| can't be empty and must be an array
  6240. function isValid (options) {
  6241. const types = options ? options.types : undefined
  6242. return Array.isArray(types)
  6243. }
  6244.  
  6245. exports.getSources = function (options, callback) {
  6246. if (!isValid(options)) return callback(new Error('Invalid options'))
  6247. const captureWindow = includes.call(options.types, 'window')
  6248. const captureScreen = includes.call(options.types, 'screen')
  6249.  
  6250. if (options.thumbnailSize == null) {
  6251. options.thumbnailSize = {
  6252. width: 150,
  6253. height: 150
  6254. }
  6255. }
  6256.  
  6257. const id = incrementId()
  6258. ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id)
  6259. return ipcRenderer.once(`ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`, (event, sources) => {
  6260. callback(null, (() => {
  6261. const results = []
  6262. sources.forEach(source => {
  6263. results.push({
  6264. id: source.id,
  6265. name: source.name,
  6266. thumbnail: nativeImage.createFromDataURL(source.thumbnail),
  6267. display_id: source.display_id
  6268. })
  6269. })
  6270. return results
  6271. })())
  6272. })
  6273. }
  6274. 'use strict'
  6275.  
  6276. const common = require('@electron/internal/common/api/exports/electron')
  6277. const moduleList = require('@electron/internal/renderer/api/module-list')
  6278.  
  6279. // Import common modules.
  6280. common.defineProperties(exports)
  6281.  
  6282. for (const {
  6283. name,
  6284. file,
  6285. enabled = true,
  6286. private: isPrivate = false
  6287. } of moduleList) {
  6288. if (!enabled) {
  6289. continue
  6290. }
  6291.  
  6292. Object.defineProperty(exports, name, {
  6293. enumerable: !isPrivate,
  6294. get: common.memoizedGetter(() => require(`@electron/internal/renderer/api/${file}`))
  6295. })
  6296. }
  6297. 'use strict'
  6298.  
  6299. const binding = process.atomBinding('ipc')
  6300. const v8Util = process.atomBinding('v8_util')
  6301.  
  6302. // Created by init.js.
  6303. const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
  6304. const internal = false
  6305.  
  6306. ipcRenderer.send = function (...args) {
  6307. return binding.send('ipc-message', args)
  6308. }
  6309.  
  6310. ipcRenderer.sendSync = function (...args) {
  6311. return binding.sendSync('ipc-message-sync', args)[0]
  6312. }
  6313.  
  6314. ipcRenderer.sendToHost = function (...args) {
  6315. return binding.send('ipc-message-host', args)
  6316. }
  6317.  
  6318. ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
  6319. return binding.sendTo(internal, false, webContentsId, channel, args)
  6320. }
  6321.  
  6322. ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
  6323. return binding.sendTo(internal, true, webContentsId, channel, args)
  6324. }
  6325.  
  6326. module.exports = ipcRenderer
  6327. 'use strict'
  6328.  
  6329. const features = process.atomBinding('features')
  6330. const v8Util = process.atomBinding('v8_util')
  6331.  
  6332. const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule')
  6333.  
  6334. // Renderer side modules, please sort alphabetically.
  6335. // A module is `enabled` if there is no explicit condition defined.
  6336. module.exports = [
  6337. { name: 'crashReporter', file: 'crash-reporter', enabled: true },
  6338. {
  6339. name: 'desktopCapturer',
  6340. file: 'desktop-capturer',
  6341. enabled: features.isDesktopCapturerEnabled()
  6342. },
  6343. { name: 'ipcRenderer', file: 'ipc-renderer' },
  6344. { name: 'remote', file: 'remote', enabled: enableRemoteModule },
  6345. { name: 'screen', file: 'screen' },
  6346. { name: 'webFrame', file: 'web-frame' }
  6347. ]
  6348. 'use strict'
  6349.  
  6350. const v8Util = process.atomBinding('v8_util')
  6351. const { isPromise } = require('electron')
  6352. const resolvePromise = Promise.resolve.bind(Promise)
  6353.  
  6354. const CallbacksRegistry = require('@electron/internal/renderer/callbacks-registry')
  6355. const bufferUtils = require('@electron/internal/common/buffer-utils')
  6356. const errorUtils = require('@electron/internal/common/error-utils')
  6357. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  6358.  
  6359. const callbacksRegistry = new CallbacksRegistry()
  6360. const remoteObjectCache = v8Util.createIDWeakMap()
  6361.  
  6362. // An unique ID that can represent current context.
  6363. const contextId = v8Util.getHiddenValue(global, 'contextId')
  6364.  
  6365. // Notify the main process when current context is going to be released.
  6366. // Note that when the renderer process is destroyed, the message may not be
  6367. // sent, we also listen to the "render-view-deleted" event in the main process
  6368. // to guard that situation.
  6369. process.on('exit', () => {
  6370. const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
  6371. ipcRenderer.sendSync(command, contextId)
  6372. })
  6373.  
  6374. // Convert the arguments object into an array of meta data.
  6375. function wrapArgs (args, visited = new Set()) {
  6376. const valueToMeta = (value) => {
  6377. // Check for circular reference.
  6378. if (visited.has(value)) {
  6379. return {
  6380. type: 'value',
  6381. value: null
  6382. }
  6383. }
  6384.  
  6385. if (Array.isArray(value)) {
  6386. visited.add(value)
  6387. const meta = {
  6388. type: 'array',
  6389. value: wrapArgs(value, visited)
  6390. }
  6391. visited.delete(value)
  6392. return meta
  6393. } else if (bufferUtils.isBuffer(value)) {
  6394. return {
  6395. type: 'buffer',
  6396. value: bufferUtils.bufferToMeta(value)
  6397. }
  6398. } else if (value instanceof Date) {
  6399. return {
  6400. type: 'date',
  6401. value: value.getTime()
  6402. }
  6403. } else if ((value != null) && typeof value === 'object') {
  6404. if (isPromise(value)) {
  6405. return {
  6406. type: 'promise',
  6407. then: valueToMeta(function (onFulfilled, onRejected) {
  6408. value.then(onFulfilled, onRejected)
  6409. })
  6410. }
  6411. } else if (v8Util.getHiddenValue(value, 'atomId')) {
  6412. return {
  6413. type: 'remote-object',
  6414. id: v8Util.getHiddenValue(value, 'atomId')
  6415. }
  6416. }
  6417.  
  6418. const meta = {
  6419. type: 'object',
  6420. name: value.constructor ? value.constructor.name : '',
  6421. members: []
  6422. }
  6423. visited.add(value)
  6424. for (const prop in value) {
  6425. meta.members.push({
  6426. name: prop,
  6427. value: valueToMeta(value[prop])
  6428. })
  6429. }
  6430. visited.delete(value)
  6431. return meta
  6432. } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
  6433. return {
  6434. type: 'function-with-return-value',
  6435. value: valueToMeta(value())
  6436. }
  6437. } else if (typeof value === 'function') {
  6438. return {
  6439. type: 'function',
  6440. id: callbacksRegistry.add(value),
  6441. location: v8Util.getHiddenValue(value, 'location'),
  6442. length: value.length
  6443. }
  6444. } else {
  6445. return {
  6446. type: 'value',
  6447. value: value
  6448. }
  6449. }
  6450. }
  6451. return args.map(valueToMeta)
  6452. }
  6453.  
  6454. // Populate object's members from descriptors.
  6455. // The |ref| will be kept referenced by |members|.
  6456. // This matches |getObjectMemebers| in rpc-server.
  6457. function setObjectMembers (ref, object, metaId, members) {
  6458. if (!Array.isArray(members)) return
  6459.  
  6460. for (const member of members) {
  6461. if (object.hasOwnProperty(member.name)) continue
  6462.  
  6463. const descriptor = { enumerable: member.enumerable }
  6464. if (member.type === 'method') {
  6465. const remoteMemberFunction = function (...args) {
  6466. let command
  6467. if (this && this.constructor === remoteMemberFunction) {
  6468. command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'
  6469. } else {
  6470. command = 'ELECTRON_BROWSER_MEMBER_CALL'
  6471. }
  6472. const ret = ipcRenderer.sendSync(command, contextId, metaId, member.name, wrapArgs(args))
  6473. return metaToValue(ret)
  6474. }
  6475.  
  6476. let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)
  6477.  
  6478. descriptor.get = () => {
  6479. descriptorFunction.ref = ref // The member should reference its object.
  6480. return descriptorFunction
  6481. }
  6482. // Enable monkey-patch the method
  6483. descriptor.set = (value) => {
  6484. descriptorFunction = value
  6485. return value
  6486. }
  6487. descriptor.configurable = true
  6488. } else if (member.type === 'get') {
  6489. descriptor.get = () => {
  6490. const command = 'ELECTRON_BROWSER_MEMBER_GET'
  6491. const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name)
  6492. return metaToValue(meta)
  6493. }
  6494.  
  6495. if (member.writable) {
  6496. descriptor.set = (value) => {
  6497. const args = wrapArgs([value])
  6498. const command = 'ELECTRON_BROWSER_MEMBER_SET'
  6499. const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name, args)
  6500. if (meta != null) metaToValue(meta)
  6501. return value
  6502. }
  6503. }
  6504. }
  6505.  
  6506. Object.defineProperty(object, member.name, descriptor)
  6507. }
  6508. }
  6509.  
  6510. // Populate object's prototype from descriptor.
  6511. // This matches |getObjectPrototype| in rpc-server.
  6512. function setObjectPrototype (ref, object, metaId, descriptor) {
  6513. if (descriptor === null) return
  6514. const proto = {}
  6515. setObjectMembers(ref, proto, metaId, descriptor.members)
  6516. setObjectPrototype(ref, proto, metaId, descriptor.proto)
  6517. Object.setPrototypeOf(object, proto)
  6518. }
  6519.  
  6520. // Wrap function in Proxy for accessing remote properties
  6521. function proxyFunctionProperties (remoteMemberFunction, metaId, name) {
  6522. let loaded = false
  6523.  
  6524. // Lazily load function properties
  6525. const loadRemoteProperties = () => {
  6526. if (loaded) return
  6527. loaded = true
  6528. const command = 'ELECTRON_BROWSER_MEMBER_GET'
  6529. const meta = ipcRenderer.sendSync(command, contextId, metaId, name)
  6530. setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
  6531. }
  6532.  
  6533. return new Proxy(remoteMemberFunction, {
  6534. set: (target, property, value, receiver) => {
  6535. if (property !== 'ref') loadRemoteProperties()
  6536. target[property] = value
  6537. return true
  6538. },
  6539. get: (target, property, receiver) => {
  6540. if (!target.hasOwnProperty(property)) loadRemoteProperties()
  6541. const value = target[property]
  6542. if (property === 'toString' && typeof value === 'function') {
  6543. return value.bind(target)
  6544. }
  6545. return value
  6546. },
  6547. ownKeys: (target) => {
  6548. loadRemoteProperties()
  6549. return Object.getOwnPropertyNames(target)
  6550. },
  6551. getOwnPropertyDescriptor: (target, property) => {
  6552. const descriptor = Object.getOwnPropertyDescriptor(target, property)
  6553. if (descriptor) return descriptor
  6554. loadRemoteProperties()
  6555. return Object.getOwnPropertyDescriptor(target, property)
  6556. }
  6557. })
  6558. }
  6559.  
  6560. // Convert meta data from browser into real value.
  6561. function metaToValue (meta) {
  6562. const types = {
  6563. value: () => meta.value,
  6564. array: () => meta.members.map((member) => metaToValue(member)),
  6565. buffer: () => bufferUtils.metaToBuffer(meta.value),
  6566. promise: () => resolvePromise({ then: metaToValue(meta.then) }),
  6567. error: () => metaToPlainObject(meta),
  6568. date: () => new Date(meta.value),
  6569. exception: () => { throw errorUtils.deserialize(meta.value) }
  6570. }
  6571.  
  6572. if (meta.type in types) {
  6573. return types[meta.type]()
  6574. } else {
  6575. let ret
  6576. if (remoteObjectCache.has(meta.id)) {
  6577. return remoteObjectCache.get(meta.id)
  6578. }
  6579.  
  6580. // A shadow class to represent the remote function object.
  6581. if (meta.type === 'function') {
  6582. const remoteFunction = function (...args) {
  6583. let command
  6584. if (this && this.constructor === remoteFunction) {
  6585. command = 'ELECTRON_BROWSER_CONSTRUCTOR'
  6586. } else {
  6587. command = 'ELECTRON_BROWSER_FUNCTION_CALL'
  6588. }
  6589. const obj = ipcRenderer.sendSync(command, contextId, meta.id, wrapArgs(args))
  6590. return metaToValue(obj)
  6591. }
  6592. ret = remoteFunction
  6593. } else {
  6594. ret = {}
  6595. }
  6596.  
  6597. setObjectMembers(ret, ret, meta.id, meta.members)
  6598. setObjectPrototype(ret, ret, meta.id, meta.proto)
  6599. Object.defineProperty(ret.constructor, 'name', { value: meta.name })
  6600.  
  6601. // Track delegate obj's lifetime & tell browser to clean up when object is GCed.
  6602. v8Util.setRemoteObjectFreer(ret, contextId, meta.id)
  6603. v8Util.setHiddenValue(ret, 'atomId', meta.id)
  6604. remoteObjectCache.set(meta.id, ret)
  6605. return ret
  6606. }
  6607. }
  6608.  
  6609. // Construct a plain object from the meta.
  6610. function metaToPlainObject (meta) {
  6611. const obj = (() => meta.type === 'error' ? new Error() : {})()
  6612. for (let i = 0; i < meta.members.length; i++) {
  6613. const { name, value } = meta.members[i]
  6614. obj[name] = value
  6615. }
  6616. return obj
  6617. }
  6618.  
  6619. function handleMessage (channel, handler) {
  6620. ipcRenderer.on(channel, (event, passedContextId, id, ...args) => {
  6621. if (passedContextId === contextId) {
  6622. handler(id, ...args)
  6623. } else {
  6624. // Message sent to an un-exist context, notify the error to main process.
  6625. ipcRenderer.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id)
  6626. }
  6627. })
  6628. }
  6629.  
  6630. // Browser calls a callback in renderer.
  6631. handleMessage('ELECTRON_RENDERER_CALLBACK', (id, args) => {
  6632. callbacksRegistry.apply(id, metaToValue(args))
  6633. })
  6634.  
  6635. // A callback in browser is released.
  6636. handleMessage('ELECTRON_RENDERER_RELEASE_CALLBACK', (id) => {
  6637. callbacksRegistry.remove(id)
  6638. })
  6639.  
  6640. exports.require = (module) => {
  6641. const command = 'ELECTRON_BROWSER_REQUIRE'
  6642. const meta = ipcRenderer.sendSync(command, contextId, module)
  6643. return metaToValue(meta)
  6644. }
  6645.  
  6646. // Alias to remote.require('electron').xxx.
  6647. exports.getBuiltin = (module) => {
  6648. const command = 'ELECTRON_BROWSER_GET_BUILTIN'
  6649. const meta = ipcRenderer.sendSync(command, contextId, module)
  6650. return metaToValue(meta)
  6651. }
  6652.  
  6653. exports.getCurrentWindow = () => {
  6654. const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
  6655. const meta = ipcRenderer.sendSync(command, contextId)
  6656. return metaToValue(meta)
  6657. }
  6658.  
  6659. // Get current WebContents object.
  6660. exports.getCurrentWebContents = () => {
  6661. return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', contextId))
  6662. }
  6663.  
  6664. // Get a global object in browser.
  6665. exports.getGlobal = (name) => {
  6666. const command = 'ELECTRON_BROWSER_GLOBAL'
  6667. const meta = ipcRenderer.sendSync(command, contextId, name)
  6668. return metaToValue(meta)
  6669. }
  6670.  
  6671. // Get the process object in browser.
  6672. exports.__defineGetter__('process', () => exports.getGlobal('process'))
  6673.  
  6674. // Create a function that will return the specified value when called in browser.
  6675. exports.createFunctionWithReturnValue = (returnValue) => {
  6676. const func = () => returnValue
  6677. v8Util.setHiddenValue(func, 'returnValue', true)
  6678. return func
  6679. }
  6680.  
  6681. // Get the guest WebContents from guestInstanceId.
  6682. exports.getGuestWebContents = (guestInstanceId) => {
  6683. const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'
  6684. const meta = ipcRenderer.sendSync(command, contextId, guestInstanceId)
  6685. return metaToValue(meta)
  6686. }
  6687.  
  6688. const addBuiltinProperty = (name) => {
  6689. Object.defineProperty(exports, name, {
  6690. get: () => exports.getBuiltin(name)
  6691. })
  6692. }
  6693.  
  6694. const browserModules =
  6695. require('@electron/internal/common/api/module-list').concat(
  6696. require('@electron/internal/browser/api/module-list'))
  6697.  
  6698. // And add a helper receiver for each one.
  6699. browserModules
  6700. .filter((m) => !m.private)
  6701. .map((m) => m.name)
  6702. .forEach(addBuiltinProperty)
  6703. 'use strict'
  6704.  
  6705. const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
  6706. module.exports = getRemoteForUsage('screen').screen
  6707. 'use strict'
  6708.  
  6709. const { EventEmitter } = require('events')
  6710. const binding = process.atomBinding('web_frame')
  6711.  
  6712. class WebFrame extends EventEmitter {
  6713. constructor (context) {
  6714. super()
  6715.  
  6716. this.context = context
  6717. // Lots of webview would subscribe to webFrame's events.
  6718. this.setMaxListeners(0)
  6719. }
  6720.  
  6721. findFrameByRoutingId (...args) {
  6722. return getWebFrame(binding._findFrameByRoutingId(this.context, ...args))
  6723. }
  6724.  
  6725. getFrameForSelector (...args) {
  6726. return getWebFrame(binding._getFrameForSelector(this.context, ...args))
  6727. }
  6728.  
  6729. findFrameByName (...args) {
  6730. return getWebFrame(binding._findFrameByName(this.context, ...args))
  6731. }
  6732.  
  6733. get opener () {
  6734. return getWebFrame(binding._getOpener(this.context))
  6735. }
  6736.  
  6737. get parent () {
  6738. return getWebFrame(binding._getParent(this.context))
  6739. }
  6740.  
  6741. get top () {
  6742. return getWebFrame(binding._getTop(this.context))
  6743. }
  6744.  
  6745. get firstChild () {
  6746. return getWebFrame(binding._getFirstChild(this.context))
  6747. }
  6748.  
  6749. get nextSibling () {
  6750. return getWebFrame(binding._getNextSibling(this.context))
  6751. }
  6752.  
  6753. get routingId () {
  6754. return binding._getRoutingId(this.context)
  6755. }
  6756. }
  6757.  
  6758. // Populate the methods.
  6759. for (const name in binding) {
  6760. if (!name.startsWith('_')) { // some methods are manully populated above
  6761. WebFrame.prototype[name] = function (...args) {
  6762. return binding[name](this.context, ...args)
  6763. }
  6764. }
  6765. }
  6766.  
  6767. // Helper to return WebFrame or null depending on context.
  6768. // TODO(zcbenz): Consider returning same WebFrame for the same context.
  6769. function getWebFrame (context) {
  6770. return context ? new WebFrame(context) : null
  6771. }
  6772.  
  6773. module.exports = new WebFrame(window)
  6774. 'use strict'
  6775.  
  6776. const v8Util = process.atomBinding('v8_util')
  6777.  
  6778. class CallbacksRegistry {
  6779. constructor () {
  6780. this.nextId = 0
  6781. this.callbacks = {}
  6782. }
  6783.  
  6784. add (callback) {
  6785. // The callback is already added.
  6786. let id = v8Util.getHiddenValue(callback, 'callbackId')
  6787. if (id != null) return id
  6788.  
  6789. id = this.nextId += 1
  6790.  
  6791. // Capture the location of the function and put it in the ID string,
  6792. // so that release errors can be tracked down easily.
  6793. const regexp = /at (.*)/gi
  6794. const stackString = (new Error()).stack
  6795.  
  6796. let filenameAndLine
  6797. let match
  6798.  
  6799. while ((match = regexp.exec(stackString)) !== null) {
  6800. const location = match[1]
  6801. if (location.includes('(native)')) continue
  6802. if (location.includes('(<anonymous>)')) continue
  6803. if (location.includes('electron.asar')) continue
  6804.  
  6805. const ref = /([^/^)]*)\)?$/gi.exec(location)
  6806. filenameAndLine = ref[1]
  6807. break
  6808. }
  6809. this.callbacks[id] = callback
  6810. v8Util.setHiddenValue(callback, 'callbackId', id)
  6811. v8Util.setHiddenValue(callback, 'location', filenameAndLine)
  6812. return id
  6813. }
  6814.  
  6815. get (id) {
  6816. return this.callbacks[id] || function () {}
  6817. }
  6818.  
  6819. apply (id, ...args) {
  6820. return this.get(id).apply(global, ...args)
  6821. }
  6822.  
  6823. remove (id) {
  6824. const callback = this.callbacks[id]
  6825. if (callback) {
  6826. v8Util.deleteHiddenValue(callback, 'callbackId')
  6827. delete this.callbacks[id]
  6828. }
  6829. }
  6830. }
  6831.  
  6832. module.exports = CallbacksRegistry
  6833. 'use strict'
  6834.  
  6835. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  6836. const Event = require('@electron/internal/renderer/extensions/event')
  6837. const url = require('url')
  6838.  
  6839. class Tab {
  6840. constructor (tabId) {
  6841. this.id = tabId
  6842. }
  6843. }
  6844.  
  6845. class MessageSender {
  6846. constructor (tabId, extensionId) {
  6847. this.tab = tabId ? new Tab(tabId) : null
  6848. this.id = extensionId
  6849. this.url = `chrome-extension://${extensionId}`
  6850. }
  6851. }
  6852.  
  6853. class Port {
  6854. constructor (tabId, portId, extensionId, name) {
  6855. this.tabId = tabId
  6856. this.portId = portId
  6857. this.disconnected = false
  6858.  
  6859. this.name = name
  6860. this.onDisconnect = new Event()
  6861. this.onMessage = new Event()
  6862. this.sender = new MessageSender(tabId, extensionId)
  6863.  
  6864. ipcRenderer.once(`CHROME_PORT_DISCONNECT_${portId}`, () => {
  6865. this._onDisconnect()
  6866. })
  6867. ipcRenderer.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (event, message) => {
  6868. const sendResponse = function () { console.error('sendResponse is not implemented') }
  6869. this.onMessage.emit(message, this.sender, sendResponse)
  6870. })
  6871. }
  6872.  
  6873. disconnect () {
  6874. if (this.disconnected) return
  6875.  
  6876. ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`)
  6877. this._onDisconnect()
  6878. }
  6879.  
  6880. postMessage (message) {
  6881. ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, message)
  6882. }
  6883.  
  6884. _onDisconnect () {
  6885. this.disconnected = true
  6886. ipcRenderer.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`)
  6887. this.onDisconnect.emit()
  6888. }
  6889. }
  6890.  
  6891. // Inject chrome API to the |context|
  6892. exports.injectTo = function (extensionId, isBackgroundPage, context) {
  6893. const chrome = context.chrome = context.chrome || {}
  6894. let originResultID = 1
  6895.  
  6896. ipcRenderer.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (event, tabId, portId, connectInfo) => {
  6897. chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name))
  6898. })
  6899.  
  6900. ipcRenderer.on(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (event, tabId, message, resultID) => {
  6901. chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), (messageResult) => {
  6902. ipcRenderer.send(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, messageResult)
  6903. })
  6904. })
  6905.  
  6906. ipcRenderer.on('CHROME_TABS_ONCREATED', (event, tabId) => {
  6907. chrome.tabs.onCreated.emit(new Tab(tabId))
  6908. })
  6909.  
  6910. ipcRenderer.on('CHROME_TABS_ONREMOVED', (event, tabId) => {
  6911. chrome.tabs.onRemoved.emit(tabId)
  6912. })
  6913.  
  6914. chrome.runtime = {
  6915. id: extensionId,
  6916.  
  6917. getURL: function (path) {
  6918. return url.format({
  6919. protocol: 'chrome-extension',
  6920. slashes: true,
  6921. hostname: extensionId,
  6922. pathname: path
  6923. })
  6924. },
  6925.  
  6926. connect (...args) {
  6927. if (isBackgroundPage) {
  6928. console.error('chrome.runtime.connect is not supported in background page')
  6929. return
  6930. }
  6931.  
  6932. // Parse the optional args.
  6933. let targetExtensionId = extensionId
  6934. let connectInfo = { name: '' }
  6935. if (args.length === 1) {
  6936. connectInfo = args[0]
  6937. } else if (args.length === 2) {
  6938. [targetExtensionId, connectInfo] = args
  6939. }
  6940.  
  6941. const { tabId, portId } = ipcRenderer.sendSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo)
  6942. return new Port(tabId, portId, extensionId, connectInfo.name)
  6943. },
  6944.  
  6945. sendMessage (...args) {
  6946. if (isBackgroundPage) {
  6947. console.error('chrome.runtime.sendMessage is not supported in background page')
  6948. return
  6949. }
  6950.  
  6951. // Parse the optional args.
  6952. let targetExtensionId = extensionId
  6953. let message
  6954. if (args.length === 1) {
  6955. message = args[0]
  6956. } else if (args.length === 2) {
  6957. // A case of not provide extension-id: (message, responseCallback)
  6958. if (typeof args[1] === 'function') {
  6959. ipcRenderer.on(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, (event, result) => args[1](result))
  6960. message = args[0]
  6961. } else {
  6962. [targetExtensionId, message] = args
  6963. }
  6964. } else {
  6965. console.error('options is not supported')
  6966. ipcRenderer.on(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, (event, result) => args[2](result))
  6967. }
  6968.  
  6969. ipcRenderer.send('CHROME_RUNTIME_SENDMESSAGE', targetExtensionId, message, originResultID)
  6970. originResultID++
  6971. },
  6972.  
  6973. onConnect: new Event(),
  6974. onMessage: new Event(),
  6975. onInstalled: new Event()
  6976. }
  6977.  
  6978. chrome.tabs = {
  6979. executeScript (tabId, details, resultCallback) {
  6980. if (resultCallback) {
  6981. ipcRenderer.once(`CHROME_TABS_EXECUTESCRIPT_RESULT_${originResultID}`, (event, result) => resultCallback([result]))
  6982. }
  6983. ipcRenderer.send('CHROME_TABS_EXECUTESCRIPT', originResultID, tabId, extensionId, details)
  6984. originResultID++
  6985. },
  6986.  
  6987. sendMessage (tabId, message, options, responseCallback) {
  6988. if (responseCallback) {
  6989. ipcRenderer.on(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, (event, result) => responseCallback(result))
  6990. }
  6991. ipcRenderer.send('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, isBackgroundPage, message, originResultID)
  6992. originResultID++
  6993. },
  6994.  
  6995. onUpdated: new Event(),
  6996. onCreated: new Event(),
  6997. onRemoved: new Event()
  6998. }
  6999.  
  7000. chrome.extension = {
  7001. getURL: chrome.runtime.getURL,
  7002. connect: chrome.runtime.connect,
  7003. onConnect: chrome.runtime.onConnect,
  7004. sendMessage: chrome.runtime.sendMessage,
  7005. onMessage: chrome.runtime.onMessage
  7006. }
  7007.  
  7008. chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId)
  7009.  
  7010. chrome.pageAction = {
  7011. show () {},
  7012. hide () {},
  7013. setTitle () {},
  7014. getTitle () {},
  7015. setIcon () {},
  7016. setPopup () {},
  7017. getPopup () {}
  7018. }
  7019.  
  7020. chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId)
  7021. chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup()
  7022. }
  7023. 'use strict'
  7024.  
  7025. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  7026. const { runInThisContext } = require('vm')
  7027.  
  7028. // Check whether pattern matches.
  7029. // https://developer.chrome.com/extensions/match_patterns
  7030. const matchesPattern = function (pattern) {
  7031. if (pattern === '<all_urls>') return true
  7032. const regexp = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`)
  7033. const url = `${location.protocol}//${location.host}${location.pathname}`
  7034. return url.match(regexp)
  7035. }
  7036.  
  7037. // Run the code with chrome API integrated.
  7038. const runContentScript = function (extensionId, url, code) {
  7039. const context = {}
  7040. require('@electron/internal/renderer/chrome-api').injectTo(extensionId, false, context)
  7041. const wrapper = `((chrome) => {\n ${code}\n })`
  7042. const compiledWrapper = runInThisContext(wrapper, {
  7043. filename: url,
  7044. lineOffset: 1,
  7045. displayErrors: true
  7046. })
  7047. return compiledWrapper.call(this, context.chrome)
  7048. }
  7049.  
  7050. const runAllContentScript = function (scripts, extensionId) {
  7051. for (const { url, code } of scripts) {
  7052. runContentScript.call(window, extensionId, url, code)
  7053. }
  7054. }
  7055.  
  7056. const runStylesheet = function (url, code) {
  7057. const wrapper = `((code) => {
  7058. function init() {
  7059. const styleElement = document.createElement('style');
  7060. styleElement.textContent = code;
  7061. document.head.append(styleElement);
  7062. }
  7063. document.addEventListener('DOMContentLoaded', init);
  7064. })`
  7065. const compiledWrapper = runInThisContext(wrapper, {
  7066. filename: url,
  7067. lineOffset: 1,
  7068. displayErrors: true
  7069. })
  7070. return compiledWrapper.call(this, code)
  7071. }
  7072.  
  7073. const runAllStylesheet = function (css) {
  7074. for (const { url, code } of css) {
  7075. runStylesheet.call(window, url, code)
  7076. }
  7077. }
  7078.  
  7079. // Run injected scripts.
  7080. // https://developer.chrome.com/extensions/content_scripts
  7081. const injectContentScript = function (extensionId, script) {
  7082. if (!script.matches.some(matchesPattern)) return
  7083.  
  7084. if (script.js) {
  7085. const fire = runAllContentScript.bind(window, script.js, extensionId)
  7086. if (script.runAt === 'document_start') {
  7087. process.once('document-start', fire)
  7088. } else if (script.runAt === 'document_end') {
  7089. process.once('document-end', fire)
  7090. } else {
  7091. document.addEventListener('DOMContentLoaded', fire)
  7092. }
  7093. }
  7094.  
  7095. if (script.css) {
  7096. const fire = runAllStylesheet.bind(window, script.css)
  7097. if (script.runAt === 'document_start') {
  7098. process.once('document-start', fire)
  7099. } else if (script.runAt === 'document_end') {
  7100. process.once('document-end', fire)
  7101. } else {
  7102. document.addEventListener('DOMContentLoaded', fire)
  7103. }
  7104. }
  7105. }
  7106.  
  7107. // Handle the request of chrome.tabs.executeJavaScript.
  7108. ipcRenderer.on('CHROME_TABS_EXECUTESCRIPT', function (event, senderWebContentsId, requestId, extensionId, url, code) {
  7109. const result = runContentScript.call(window, extensionId, url, code)
  7110. ipcRenderer.sendToAll(senderWebContentsId, `CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, result)
  7111. })
  7112.  
  7113. // Read the renderer process preferences.
  7114. const preferences = process.getRenderProcessPreferences()
  7115. if (preferences) {
  7116. for (const pref of preferences) {
  7117. if (pref.contentScripts) {
  7118. for (const script of pref.contentScripts) {
  7119. injectContentScript(pref.extensionId, script)
  7120. }
  7121. }
  7122. }
  7123. }
  7124. 'use strict'
  7125.  
  7126. class Event {
  7127. constructor () {
  7128. this.listeners = []
  7129. }
  7130.  
  7131. addListener (callback) {
  7132. this.listeners.push(callback)
  7133. }
  7134.  
  7135. removeListener (callback) {
  7136. const index = this.listeners.indexOf(callback)
  7137. if (index !== -1) {
  7138. this.listeners.splice(index, 1)
  7139. }
  7140. }
  7141.  
  7142. emit (...args) {
  7143. for (const listener of this.listeners) {
  7144. listener(...args)
  7145. }
  7146. }
  7147. }
  7148.  
  7149. module.exports = Event
  7150. 'use strict'
  7151.  
  7152. // Implementation of chrome.i18n.getMessage
  7153. // https://developer.chrome.com/extensions/i18n#method-getMessage
  7154. //
  7155. // Does not implement predefined messages:
  7156. // https://developer.chrome.com/extensions/i18n#overview-predefined
  7157.  
  7158. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  7159. const fs = require('fs')
  7160. const path = require('path')
  7161.  
  7162. let metadata
  7163.  
  7164. const getExtensionMetadata = (extensionId) => {
  7165. if (!metadata) {
  7166. metadata = ipcRenderer.sendSync('CHROME_I18N_MANIFEST', extensionId)
  7167. }
  7168. return metadata
  7169. }
  7170.  
  7171. const getMessagesPath = (extensionId, language) => {
  7172. const metadata = getExtensionMetadata(extensionId)
  7173. const localesDirectory = path.join(metadata.srcDirectory, '_locales')
  7174. try {
  7175. const filename = path.join(localesDirectory, language, 'messages.json')
  7176. fs.accessSync(filename, fs.constants.R_OK)
  7177. return filename
  7178. } catch (err) {
  7179. const defaultLocale = metadata.default_locale || 'en'
  7180. return path.join(localesDirectory, defaultLocale, 'messages.json')
  7181. }
  7182. }
  7183.  
  7184. const getMessages = (extensionId, language) => {
  7185. try {
  7186. const messagesPath = getMessagesPath(extensionId, language)
  7187. return JSON.parse(fs.readFileSync(messagesPath)) || {}
  7188. } catch (error) {
  7189. return {}
  7190. }
  7191. }
  7192.  
  7193. const getLanguage = () => {
  7194. return navigator.language.replace(/-.*$/, '').toLowerCase()
  7195. }
  7196.  
  7197. const replaceNumberedSubstitutions = (message, substitutions) => {
  7198. return message.replace(/\$(\d+)/, (_, number) => {
  7199. const index = parseInt(number, 10) - 1
  7200. return substitutions[index] || ''
  7201. })
  7202. }
  7203.  
  7204. const replacePlaceholders = (message, placeholders, substitutions) => {
  7205. if (typeof substitutions === 'string') {
  7206. substitutions = [substitutions]
  7207. }
  7208. if (!Array.isArray(substitutions)) {
  7209. substitutions = []
  7210. }
  7211.  
  7212. if (placeholders) {
  7213. Object.keys(placeholders).forEach((name) => {
  7214. let { content } = placeholders[name]
  7215. content = replaceNumberedSubstitutions(content, substitutions)
  7216. message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content)
  7217. })
  7218. }
  7219.  
  7220. return replaceNumberedSubstitutions(message, substitutions)
  7221. }
  7222.  
  7223. const getMessage = (extensionId, messageName, substitutions) => {
  7224. const messages = getMessages(extensionId, getLanguage())
  7225. if (messages.hasOwnProperty(messageName)) {
  7226. const { message, placeholders } = messages[messageName]
  7227. return replacePlaceholders(message, placeholders, substitutions)
  7228. }
  7229. }
  7230.  
  7231. exports.setup = (extensionId) => {
  7232. return {
  7233. getMessage (messageName, substitutions) {
  7234. return getMessage(extensionId, messageName, substitutions)
  7235. }
  7236. }
  7237. }
  7238. 'use strict'
  7239.  
  7240. const fs = require('fs')
  7241. const path = require('path')
  7242. const { remote } = require('electron')
  7243. const { app } = remote
  7244.  
  7245. const getChromeStoragePath = (storageType, extensionId) => {
  7246. return path.join(
  7247. app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`)
  7248. }
  7249.  
  7250. const mkdirp = (dir, callback) => {
  7251. fs.mkdir(dir, (error) => {
  7252. if (error && error.code === 'ENOENT') {
  7253. mkdirp(path.dirname(dir), (error) => {
  7254. if (!error) {
  7255. mkdirp(dir, callback)
  7256. }
  7257. })
  7258. } else if (error && error.code === 'EEXIST') {
  7259. callback(null)
  7260. } else {
  7261. callback(error)
  7262. }
  7263. })
  7264. }
  7265.  
  7266. const readChromeStorageFile = (storageType, extensionId, cb) => {
  7267. const filePath = getChromeStoragePath(storageType, extensionId)
  7268. fs.readFile(filePath, 'utf8', (err, data) => {
  7269. if (err && err.code === 'ENOENT') {
  7270. return cb(null, null)
  7271. }
  7272. cb(err, data)
  7273. })
  7274. }
  7275.  
  7276. const writeChromeStorageFile = (storageType, extensionId, data, cb) => {
  7277. const filePath = getChromeStoragePath(storageType, extensionId)
  7278.  
  7279. mkdirp(path.dirname(filePath), err => {
  7280. if (err) { /* we just ignore the errors of mkdir or mkdirp */ }
  7281. fs.writeFile(filePath, data, cb)
  7282. })
  7283. }
  7284.  
  7285. const getStorage = (storageType, extensionId, cb) => {
  7286. readChromeStorageFile(storageType, extensionId, (err, data) => {
  7287. if (err) throw err
  7288. if (!cb) throw new TypeError('No callback provided')
  7289.  
  7290. if (data !== null) {
  7291. cb(JSON.parse(data))
  7292. } else {
  7293. // Disabled due to false positive in StandardJS
  7294. // eslint-disable-next-line standard/no-callback-literal
  7295. cb({})
  7296. }
  7297. })
  7298. }
  7299.  
  7300. const setStorage = (storageType, extensionId, storage, cb) => {
  7301. const json = JSON.stringify(storage)
  7302. writeChromeStorageFile(storageType, extensionId, json, err => {
  7303. if (err) throw err
  7304. if (cb) cb()
  7305. })
  7306. }
  7307.  
  7308. const getStorageManager = (storageType, extensionId) => {
  7309. return {
  7310. get (keys, callback) {
  7311. getStorage(storageType, extensionId, storage => {
  7312. if (keys == null) return callback(storage)
  7313.  
  7314. let defaults = {}
  7315. switch (typeof keys) {
  7316. case 'string':
  7317. keys = [keys]
  7318. break
  7319. case 'object':
  7320. if (!Array.isArray(keys)) {
  7321. defaults = keys
  7322. keys = Object.keys(keys)
  7323. }
  7324. break
  7325. }
  7326.  
  7327. // Disabled due to false positive in StandardJS
  7328. // eslint-disable-next-line standard/no-callback-literal
  7329. if (keys.length === 0) return callback({})
  7330.  
  7331. const items = {}
  7332. keys.forEach(function (key) {
  7333. let value = storage[key]
  7334. if (value == null) value = defaults[key]
  7335. items[key] = value
  7336. })
  7337. callback(items)
  7338. })
  7339. },
  7340.  
  7341. set (items, callback) {
  7342. getStorage(storageType, extensionId, storage => {
  7343. Object.keys(items).forEach(function (name) {
  7344. storage[name] = items[name]
  7345. })
  7346.  
  7347. setStorage(storageType, extensionId, storage, callback)
  7348. })
  7349. },
  7350.  
  7351. remove (keys, callback) {
  7352. getStorage(storageType, extensionId, storage => {
  7353. if (!Array.isArray(keys)) {
  7354. keys = [keys]
  7355. }
  7356. keys.forEach(function (key) {
  7357. delete storage[key]
  7358. })
  7359.  
  7360. setStorage(storageType, extensionId, storage, callback)
  7361. })
  7362. },
  7363.  
  7364. clear (callback) {
  7365. setStorage(storageType, extensionId, {}, callback)
  7366. }
  7367. }
  7368. }
  7369.  
  7370. module.exports = {
  7371. setup: extensionId => ({
  7372. sync: getStorageManager('sync', extensionId),
  7373. local: getStorageManager('local', extensionId)
  7374. })
  7375. }
  7376. 'use strict'
  7377.  
  7378. const Event = require('@electron/internal/renderer/extensions/event')
  7379. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  7380.  
  7381. class WebNavigation {
  7382. constructor () {
  7383. this.onBeforeNavigate = new Event()
  7384. this.onCompleted = new Event()
  7385.  
  7386. ipcRenderer.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event, details) => {
  7387. this.onBeforeNavigate.emit(details)
  7388. })
  7389.  
  7390. ipcRenderer.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event, details) => {
  7391. this.onCompleted.emit(details)
  7392. })
  7393. }
  7394. }
  7395.  
  7396. exports.setup = () => {
  7397. return new WebNavigation()
  7398. }
  7399. 'use strict'
  7400.  
  7401. const { EventEmitter } = require('events')
  7402. const path = require('path')
  7403. const Module = require('module')
  7404.  
  7405. // We modified the original process.argv to let node.js load the
  7406. // init.js, we need to restore it here.
  7407. process.argv.splice(1, 1)
  7408.  
  7409. // Clear search paths.
  7410. require('../common/reset-search-paths')
  7411.  
  7412. // Import common settings.
  7413. require('@electron/internal/common/init')
  7414.  
  7415. const globalPaths = Module.globalPaths
  7416.  
  7417. // Expose public APIs.
  7418. globalPaths.push(path.join(__dirname, 'api', 'exports'))
  7419.  
  7420. // The global variable will be used by ipc for event dispatching
  7421. const v8Util = process.atomBinding('v8_util')
  7422.  
  7423. v8Util.setHiddenValue(global, 'ipc', new EventEmitter())
  7424. v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter())
  7425.  
  7426. // Use electron module after everything is ready.
  7427. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  7428.  
  7429. require('@electron/internal/renderer/web-frame-init')()
  7430.  
  7431. // Process command line arguments.
  7432. let nodeIntegration = false
  7433. let webviewTag = false
  7434. let preloadScript = null
  7435. let preloadScripts = []
  7436. let isBackgroundPage = false
  7437. let appPath = null
  7438. for (const arg of process.argv) {
  7439. if (arg.indexOf('--guest-instance-id=') === 0) {
  7440. // This is a guest web view.
  7441. process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1))
  7442. } else if (arg.indexOf('--opener-id=') === 0) {
  7443. // This is a guest BrowserWindow.
  7444. process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1))
  7445. } else if (arg.indexOf('--node-integration=') === 0) {
  7446. nodeIntegration = arg.substr(arg.indexOf('=') + 1) === 'true'
  7447. } else if (arg.indexOf('--preload=') === 0) {
  7448. preloadScript = arg.substr(arg.indexOf('=') + 1)
  7449. } else if (arg === '--background-page') {
  7450. isBackgroundPage = true
  7451. } else if (arg.indexOf('--app-path=') === 0) {
  7452. appPath = arg.substr(arg.indexOf('=') + 1)
  7453. } else if (arg.indexOf('--webview-tag=') === 0) {
  7454. webviewTag = arg.substr(arg.indexOf('=') + 1) === 'true'
  7455. } else if (arg.indexOf('--preload-scripts') === 0) {
  7456. preloadScripts = arg.substr(arg.indexOf('=') + 1).split(path.delimiter)
  7457. }
  7458. }
  7459.  
  7460. // The webContents preload script is loaded after the session preload scripts.
  7461. if (preloadScript) {
  7462. preloadScripts.push(preloadScript)
  7463. }
  7464.  
  7465. if (window.location.protocol === 'chrome-devtools:') {
  7466. // Override some inspector APIs.
  7467. require('@electron/internal/renderer/inspector')
  7468. nodeIntegration = false
  7469. } else if (window.location.protocol === 'chrome-extension:') {
  7470. // Add implementations of chrome API.
  7471. require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
  7472. nodeIntegration = false
  7473. } else if (window.location.protocol === 'chrome:') {
  7474. // Disable node integration for chrome UI scheme.
  7475. nodeIntegration = false
  7476. } else {
  7477. // Override default web functions.
  7478. require('@electron/internal/renderer/override')
  7479.  
  7480. // Inject content scripts.
  7481. require('@electron/internal/renderer/content-scripts-injector')
  7482.  
  7483. // Load webview tag implementation.
  7484. if (webviewTag && process.guestInstanceId == null) {
  7485. const { setupWebView } = require('@electron/internal/renderer/web-view/web-view')
  7486. if (process.argv.includes('--context-isolation')) {
  7487. v8Util.setHiddenValue(window, 'setup-webview', setupWebView)
  7488. } else {
  7489. setupWebView(window)
  7490. }
  7491. }
  7492. }
  7493.  
  7494. if (nodeIntegration) {
  7495. // Export node bindings to global.
  7496. global.require = require
  7497. global.module = module
  7498.  
  7499. // Set the __filename to the path of html file if it is file: protocol.
  7500. if (window.location.protocol === 'file:') {
  7501. const location = window.location
  7502. let pathname = location.pathname
  7503.  
  7504. if (process.platform === 'win32') {
  7505. if (pathname[0] === '/') pathname = pathname.substr(1)
  7506.  
  7507. const isWindowsNetworkSharePath = location.hostname.length > 0 && globalPaths[0].startsWith('\\')
  7508. if (isWindowsNetworkSharePath) {
  7509. pathname = `//${location.host}/${pathname}`
  7510. }
  7511. }
  7512.  
  7513. global.__filename = path.normalize(decodeURIComponent(pathname))
  7514. global.__dirname = path.dirname(global.__filename)
  7515.  
  7516. // Set module's filename so relative require can work as expected.
  7517. module.filename = global.__filename
  7518.  
  7519. // Also search for module under the html file.
  7520. module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
  7521. } else {
  7522. global.__filename = __filename
  7523. global.__dirname = __dirname
  7524.  
  7525. if (appPath) {
  7526. // Search for module under the app directory
  7527. module.paths = module.paths.concat(Module._nodeModulePaths(appPath))
  7528. }
  7529. }
  7530.  
  7531. // Redirect window.onerror to uncaughtException.
  7532. window.onerror = function (message, filename, lineno, colno, error) {
  7533. if (global.process.listeners('uncaughtException').length > 0) {
  7534. global.process.emit('uncaughtException', error)
  7535. return true
  7536. } else {
  7537. return false
  7538. }
  7539. }
  7540. } else {
  7541. // Delete Node's symbols after the Environment has been loaded.
  7542. process.once('loaded', function () {
  7543. delete global.process
  7544. delete global.Buffer
  7545. delete global.setImmediate
  7546. delete global.clearImmediate
  7547. delete global.global
  7548. })
  7549. }
  7550.  
  7551. // Load the preload scripts.
  7552. for (const preloadScript of preloadScripts) {
  7553. try {
  7554. require(preloadScript)
  7555. } catch (error) {
  7556. console.error('Unable to load preload script: ' + preloadScript)
  7557. console.error(error.stack || error.message)
  7558. }
  7559. }
  7560.  
  7561. // Warn about security issues
  7562. require('@electron/internal/renderer/security-warnings')(nodeIntegration)
  7563.  
  7564. // Report focus/blur events of webview to browser.
  7565. // Note that while Chromium content APIs have observer for focus/blur, they
  7566. // unfortunately do not work for webview.
  7567. if (process.guestInstanceId) {
  7568. window.addEventListener('focus', () => {
  7569. ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, process.guestInstanceId)
  7570. })
  7571. window.addEventListener('blur', () => {
  7572. ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, process.guestInstanceId)
  7573. })
  7574. }
  7575. 'use strict'
  7576.  
  7577. window.onload = function () {
  7578. // Use menu API to show context menu.
  7579. window.InspectorFrontendHost.showContextMenuAtPoint = createMenu
  7580.  
  7581. // correct for Chromium returning undefined for filesystem
  7582. window.Persistence.FileSystemWorkspaceBinding.completeURL = completeURL
  7583.  
  7584. // Use dialog API to override file chooser dialog.
  7585. window.UI.createFileSelectorElement = createFileSelectorElement
  7586. }
  7587.  
  7588. // Extra / is needed as a result of MacOS requiring absolute paths
  7589. function completeURL (project, path) {
  7590. project = 'file:///'
  7591. return `${project}${path}`
  7592. }
  7593.  
  7594. window.confirm = function (message, title) {
  7595. const { dialog } = require('electron').remote
  7596. if (title == null) {
  7597. title = ''
  7598. }
  7599. return !dialog.showMessageBox({
  7600. message: message,
  7601. title: title,
  7602. buttons: ['OK', 'Cancel'],
  7603. cancelId: 1
  7604. })
  7605. }
  7606.  
  7607. const convertToMenuTemplate = function (items) {
  7608. return items.map(function (item) {
  7609. const transformed = item.type === 'subMenu' ? {
  7610. type: 'submenu',
  7611. label: item.label,
  7612. enabled: item.enabled,
  7613. submenu: convertToMenuTemplate(item.subItems)
  7614. } : item.type === 'separator' ? {
  7615. type: 'separator'
  7616. } : item.type === 'checkbox' ? {
  7617. type: 'checkbox',
  7618. label: item.label,
  7619. enabled: item.enabled,
  7620. checked: item.checked
  7621. } : {
  7622. type: 'normal',
  7623. label: item.label,
  7624. enabled: item.enabled
  7625. }
  7626.  
  7627. if (item.id != null) {
  7628. transformed.click = function () {
  7629. window.DevToolsAPI.contextMenuItemSelected(item.id)
  7630. return window.DevToolsAPI.contextMenuCleared()
  7631. }
  7632. }
  7633.  
  7634. return transformed
  7635. })
  7636. }
  7637.  
  7638. const createMenu = function (x, y, items) {
  7639. const { remote } = require('electron')
  7640. const { Menu } = remote
  7641.  
  7642. let template = convertToMenuTemplate(items)
  7643. if (useEditMenuItems(x, y, template)) {
  7644. template = getEditMenuItems()
  7645. }
  7646. const menu = Menu.buildFromTemplate(template)
  7647.  
  7648. // The menu is expected to show asynchronously.
  7649. setTimeout(function () {
  7650. menu.popup({ window: remote.getCurrentWindow() })
  7651. })
  7652. }
  7653.  
  7654. const useEditMenuItems = function (x, y, items) {
  7655. return items.length === 0 && document.elementsFromPoint(x, y).some(function (element) {
  7656. return element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA' || element.isContentEditable
  7657. })
  7658. }
  7659.  
  7660. const getEditMenuItems = function () {
  7661. return [
  7662. {
  7663. role: 'undo'
  7664. },
  7665. {
  7666. role: 'redo'
  7667. },
  7668. {
  7669. type: 'separator'
  7670. },
  7671. {
  7672. role: 'cut'
  7673. },
  7674. {
  7675. role: 'copy'
  7676. },
  7677. {
  7678. role: 'paste'
  7679. },
  7680. {
  7681. role: 'pasteAndMatchStyle'
  7682. },
  7683. {
  7684. role: 'delete'
  7685. },
  7686. {
  7687. role: 'selectAll'
  7688. }
  7689. ]
  7690. }
  7691.  
  7692. const showFileChooserDialog = function (callback) {
  7693. const { dialog } = require('electron').remote
  7694. const files = dialog.showOpenDialog({})
  7695. if (files != null) {
  7696. callback(pathToHtml5FileObject(files[0]))
  7697. }
  7698. }
  7699.  
  7700. const pathToHtml5FileObject = function (path) {
  7701. const fs = require('fs')
  7702. const blob = new Blob([fs.readFileSync(path)])
  7703. blob.name = path
  7704. return blob
  7705. }
  7706.  
  7707. const createFileSelectorElement = function (callback) {
  7708. const fileSelectorElement = document.createElement('span')
  7709. fileSelectorElement.style.display = 'none'
  7710. fileSelectorElement.click = showFileChooserDialog.bind(this, callback)
  7711. return fileSelectorElement
  7712. }
  7713. 'use strict'
  7714.  
  7715. const binding = process.atomBinding('ipc')
  7716. const v8Util = process.atomBinding('v8_util')
  7717.  
  7718. // Created by init.js.
  7719. const ipcRenderer = v8Util.getHiddenValue(global, 'ipc-internal')
  7720. const internal = true
  7721.  
  7722. ipcRenderer.send = function (...args) {
  7723. return binding.send('ipc-internal-message', args)
  7724. }
  7725.  
  7726. ipcRenderer.sendSync = function (...args) {
  7727. return binding.sendSync('ipc-internal-message-sync', args)[0]
  7728. }
  7729.  
  7730. ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
  7731. return binding.sendTo(internal, false, webContentsId, channel, args)
  7732. }
  7733.  
  7734. ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
  7735. return binding.sendTo(internal, true, webContentsId, channel, args)
  7736. }
  7737.  
  7738. module.exports = ipcRenderer
  7739. 'use strict'
  7740.  
  7741. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  7742.  
  7743. const v8Util = process.atomBinding('v8_util')
  7744.  
  7745. const { guestInstanceId, openerId } = process
  7746. const hiddenPage = process.argv.includes('--hidden-page')
  7747. const usesNativeWindowOpen = process.argv.includes('--native-window-open')
  7748. const contextIsolation = process.argv.includes('--context-isolation')
  7749.  
  7750. // Pass the arguments to isolatedWorld.
  7751. if (contextIsolation) {
  7752. const isolatedWorldArgs = { ipcRenderer, guestInstanceId, hiddenPage, openerId, usesNativeWindowOpen }
  7753. v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs)
  7754. }
  7755.  
  7756. require('@electron/internal/renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen)
  7757. 'use strict'
  7758.  
  7759. const { remote } = require('electron')
  7760.  
  7761. exports.getRemoteForUsage = function (usage) {
  7762. if (!remote) {
  7763. throw new Error(`${usage} requires remote, which is not enabled`)
  7764. }
  7765. return remote
  7766. }
  7767. 'use strict'
  7768.  
  7769. let shouldLog = null
  7770.  
  7771. /**
  7772. * This method checks if a security message should be logged.
  7773. * It does so by determining whether we're running as Electron,
  7774. * which indicates that a developer is currently looking at the
  7775. * app.
  7776. *
  7777. * @returns {boolean} - Should we log?
  7778. */
  7779. const shouldLogSecurityWarnings = function () {
  7780. if (shouldLog !== null) {
  7781. return shouldLog
  7782. }
  7783.  
  7784. const { platform, execPath, env } = process
  7785.  
  7786. switch (platform) {
  7787. case 'darwin':
  7788. shouldLog = execPath.endsWith('MacOS/Electron') ||
  7789. execPath.includes('Electron.app/Contents/Frameworks/')
  7790. break
  7791. case 'freebsd':
  7792. case 'linux':
  7793. shouldLog = execPath.endsWith('/electron')
  7794. break
  7795. case 'win32':
  7796. shouldLog = execPath.endsWith('\\electron.exe')
  7797. break
  7798. default:
  7799. shouldLog = false
  7800. }
  7801.  
  7802. if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) ||
  7803. (window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) {
  7804. shouldLog = false
  7805. }
  7806.  
  7807. if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) ||
  7808. (window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) {
  7809. shouldLog = true
  7810. }
  7811.  
  7812. return shouldLog
  7813. }
  7814.  
  7815. /**
  7816. * Checks if the current window is remote.
  7817. *
  7818. * @returns {boolean} - Is this a remote protocol?
  7819. */
  7820. const getIsRemoteProtocol = function () {
  7821. if (window && window.location && window.location.protocol) {
  7822. return /^(http|ftp)s?/gi.test(window.location.protocol)
  7823. }
  7824. }
  7825.  
  7826. /**
  7827. * Tries to determine whether a CSP without `unsafe-eval` is set.
  7828. *
  7829. * @returns {boolean} Is a CSP with `unsafe-eval` set?
  7830. */
  7831. const isUnsafeEvalEnabled = function () {
  7832. const { webFrame } = require('electron')
  7833.  
  7834. return new Promise((resolve) => {
  7835. webFrame.executeJavaScript(`(${(() => {
  7836. try {
  7837. new Function('') // eslint-disable-line no-new,no-new-func
  7838. } catch (err) {
  7839. return false
  7840. }
  7841. return true
  7842. }).toString()})()`, resolve)
  7843. })
  7844. }
  7845.  
  7846. const moreInformation = `\nFor more information and help, consult
  7847. https://electronjs.org/docs/tutorial/security.\n This warning will not show up
  7848. once the app is packaged.`
  7849.  
  7850. /**
  7851. * #1 Only load secure content
  7852. *
  7853. * Checks the loaded resources on the current page and logs a
  7854. * message about all resources loaded over HTTP or FTP.
  7855. */
  7856. const warnAboutInsecureResources = function () {
  7857. if (!window || !window.performance || !window.performance.getEntriesByType) {
  7858. return
  7859. }
  7860.  
  7861. const resources = window.performance
  7862. .getEntriesByType('resource')
  7863. .filter(({ name }) => /^(http|ftp):/gi.test(name || ''))
  7864. .map(({ name }) => `- ${name}`)
  7865. .join('\n')
  7866.  
  7867. if (!resources || resources.length === 0) {
  7868. return
  7869. }
  7870.  
  7871. const warning = `This renderer process loads resources using insecure
  7872. protocols.This exposes users of this app to unnecessary security risks.
  7873. Consider loading the following resources over HTTPS or FTPS. \n ${resources}
  7874. \n ${moreInformation}`
  7875.  
  7876. console.warn('%cElectron Security Warning (Insecure Resources)',
  7877. 'font-weight: bold;', warning)
  7878. }
  7879.  
  7880. /**
  7881. * #2 on the checklist: Disable the Node.js integration in all renderers that
  7882. * display remote content
  7883. *
  7884. * Logs a warning message about Node integration.
  7885. */
  7886. const warnAboutNodeWithRemoteContent = function (nodeIntegration) {
  7887. if (!nodeIntegration) return
  7888.  
  7889. if (getIsRemoteProtocol()) {
  7890. const warning = `This renderer process has Node.js integration enabled
  7891. and attempted to load remote content from '${window.location}'. This
  7892. exposes users of this app to severe security risks.\n ${moreInformation}`
  7893.  
  7894. console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)',
  7895. 'font-weight: bold;', warning)
  7896. }
  7897. }
  7898.  
  7899. // Currently missing since it has ramifications and is still experimental:
  7900. // #3 Enable context isolation in all renderers that display remote content
  7901. //
  7902. // Currently missing since we can't easily programmatically check for those cases:
  7903. // #4 Use ses.setPermissionRequestHandler() in all sessions that load remote content
  7904.  
  7905. /**
  7906. * #5 on the checklist: Do not disable websecurity
  7907. *
  7908. * Logs a warning message about disabled webSecurity.
  7909. */
  7910. const warnAboutDisabledWebSecurity = function (webPreferences) {
  7911. if (!webPreferences || webPreferences.webSecurity !== false) return
  7912.  
  7913. const warning = `This renderer process has "webSecurity" disabled. This
  7914. exposes users of this app to severe security risks.\n ${moreInformation}`
  7915.  
  7916. console.warn('%cElectron Security Warning (Disabled webSecurity)',
  7917. 'font-weight: bold;', warning)
  7918. }
  7919.  
  7920. /**
  7921. * #6 on the checklist: Define a Content-Security-Policy and use restrictive
  7922. * rules (i.e. script-src 'self')
  7923. *
  7924. * #7 on the checklist: Disable eval
  7925. *
  7926. * Logs a warning message about unset or insecure CSP
  7927. */
  7928. const warnAboutInsecureCSP = function () {
  7929. isUnsafeEvalEnabled().then((enabled) => {
  7930. if (!enabled) return
  7931.  
  7932. const warning = `This renderer process has either no Content Security
  7933. Policy set or a policy with "unsafe-eval" enabled. This exposes users of
  7934. this app to unnecessary security risks.\n ${moreInformation}`
  7935.  
  7936. console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
  7937. 'font-weight: bold;', warning)
  7938. })
  7939. }
  7940.  
  7941. /**
  7942. * #8 on the checklist: Do not set allowRunningInsecureContent to true
  7943. *
  7944. * Logs a warning message about disabled webSecurity.
  7945. */
  7946. const warnAboutInsecureContentAllowed = function (webPreferences) {
  7947. if (!webPreferences || !webPreferences.allowRunningInsecureContent) return
  7948.  
  7949. const warning = `This renderer process has "allowRunningInsecureContent"
  7950. enabled. This exposes users of this app to severe security risks.\n
  7951. ${moreInformation}`
  7952.  
  7953. console.warn('%cElectron Security Warning (allowRunningInsecureContent)',
  7954. 'font-weight: bold;', warning)
  7955. }
  7956.  
  7957. /**
  7958. * #9 on the checklist: Do not enable experimental features
  7959. *
  7960. * Logs a warning message about experimental features.
  7961. */
  7962. const warnAboutExperimentalFeatures = function (webPreferences) {
  7963. if (!webPreferences || (!webPreferences.experimentalFeatures)) {
  7964. return
  7965. }
  7966.  
  7967. const warning = `This renderer process has "experimentalFeatures" enabled.
  7968. This exposes users of this app to some security risk. If you do not need
  7969. this feature, you should disable it.\n ${moreInformation}`
  7970.  
  7971. console.warn('%cElectron Security Warning (experimentalFeatures)',
  7972. 'font-weight: bold;', warning)
  7973. }
  7974.  
  7975. /**
  7976. * #10 on the checklist: Do not use enableBlinkFeatures
  7977. *
  7978. * Logs a warning message about enableBlinkFeatures
  7979. */
  7980. const warnAboutEnableBlinkFeatures = function (webPreferences) {
  7981. if (webPreferences === null ||
  7982. !webPreferences.hasOwnProperty('enableBlinkFeatures') ||
  7983. webPreferences.enableBlinkFeatures.length === 0) {
  7984. return
  7985. }
  7986.  
  7987. const warning = `This renderer process has additional "enableBlinkFeatures"
  7988. enabled. This exposes users of this app to some security risk. If you do not
  7989. need this feature, you should disable it.\n ${moreInformation}`
  7990.  
  7991. console.warn('%cElectron Security Warning (enableBlinkFeatures)',
  7992. 'font-weight: bold;', warning)
  7993. }
  7994.  
  7995. /**
  7996. * #11 on the checklist: Do Not Use allowpopups
  7997. *
  7998. * Logs a warning message about allowed popups
  7999. */
  8000. const warnAboutAllowedPopups = function () {
  8001. if (document && document.querySelectorAll) {
  8002. const domElements = document.querySelectorAll('[allowpopups]')
  8003.  
  8004. if (!domElements || domElements.length === 0) {
  8005. return
  8006. }
  8007.  
  8008. const warning = `A <webview> has "allowpopups" set to true. This exposes
  8009. users of this app to some security risk, since popups are just
  8010. BrowserWindows. If you do not need this feature, you should disable it.\n
  8011. ${moreInformation}`
  8012.  
  8013. console.warn('%cElectron Security Warning (allowpopups)',
  8014. 'font-weight: bold;', warning)
  8015. }
  8016. }
  8017.  
  8018. const warnAboutNodeIntegrationDefault = function (webPreferences) {
  8019. if (webPreferences && webPreferences.nodeIntegration && !webPreferences.nodeIntegrationWasExplicitlyEnabled) {
  8020. const warning = `This window has node integration enabled by default. In ` +
  8021. `Electron 5.0.0, node integration will be disabled by default. To prepare ` +
  8022. `for this change, set {nodeIntegration: true} in the webPreferences for ` +
  8023. `this window, or ensure that this window does not rely on node integration ` +
  8024. `and set {nodeIntegration: false}.`
  8025. console.warn('%cElectron Deprecation Warning (nodeIntegration default change)', 'font-weight: bold;', warning)
  8026. }
  8027. }
  8028.  
  8029. const warnAboutContextIsolationDefault = function (webPreferences) {
  8030. if (webPreferences && webPreferences.preload && !webPreferences.contextIsolation && !webPreferences.contextIsolationWasExplicitlyDisabled) {
  8031. const url = 'https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content'
  8032. const warning = `This window has context isolation disabled by default. In ` +
  8033. `Electron 5.0.0, context isolation will be enabled by default. To prepare ` +
  8034. `for this change, set {contextIsolation: false} in the webPreferences for ` +
  8035. `this window, or ensure that this window does not rely on context ` +
  8036. `isolation being disabled, and set {contextIsolation: true}.\n\n` +
  8037. `For more information, see ${url}`
  8038. console.warn('%cElectron Deprecation Warning (contextIsolation default change)', 'font-weight: bold;', warning)
  8039. }
  8040. }
  8041.  
  8042. const warnAboutDeprecatedWebviewTagDefault = function (webPreferences) {
  8043. if (!webPreferences) {
  8044. return
  8045. }
  8046. if (webPreferences.webviewTagWasExplicitlyEnabled) {
  8047. return
  8048. }
  8049. if (!document || !document.getElementsByTagName) {
  8050. return
  8051. }
  8052. const webviews = document.getElementsByTagName('webview')
  8053. if (webviews && webviews.length > 0) {
  8054. const url = 'https://github.com/electron/electron/blob/master/docs/api/breaking-changes.md#new-browserwindow-webpreferences-'
  8055. const warning = `This window has the <webview> tag enabled by default. In ` +
  8056. `Electron 5.0.0, <webview> tags will be disabled by default. To prepare ` +
  8057. `for this change, set {webviewTag: true} in the webPreferences for ` +
  8058. `this window.\n\n` +
  8059. `For more information, see ${url}`
  8060.  
  8061. console.warn('%cElectron Deprecation Warning (webviewTag default change)',
  8062. 'font-weight: bold;', warning)
  8063. }
  8064. }
  8065.  
  8066. // Currently missing since we can't easily programmatically check for it:
  8067. // #12WebViews: Verify the options and params of all `<webview>` tags
  8068.  
  8069. const logSecurityWarnings = function (webPreferences, nodeIntegration) {
  8070. warnAboutNodeWithRemoteContent(nodeIntegration)
  8071. warnAboutDisabledWebSecurity(webPreferences)
  8072. warnAboutInsecureResources()
  8073. warnAboutInsecureContentAllowed(webPreferences)
  8074. warnAboutExperimentalFeatures(webPreferences)
  8075. warnAboutEnableBlinkFeatures(webPreferences)
  8076. warnAboutInsecureCSP()
  8077. warnAboutAllowedPopups()
  8078. warnAboutNodeIntegrationDefault(webPreferences)
  8079. warnAboutContextIsolationDefault(webPreferences)
  8080. warnAboutDeprecatedWebviewTagDefault(webPreferences)
  8081. }
  8082.  
  8083. const getWebPreferences = function () {
  8084. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  8085. const errorUtils = require('@electron/internal/common/error-utils')
  8086.  
  8087. const [ error, result ] = ipcRenderer.sendSync('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES')
  8088.  
  8089. if (error) {
  8090. console.warn(`getLastWebPreferences() failed: ${errorUtils.deserialize(error)}`)
  8091. return null
  8092. } else {
  8093. return result
  8094. }
  8095. }
  8096.  
  8097. module.exports = function (nodeIntegration) {
  8098. const loadHandler = function () {
  8099. if (shouldLogSecurityWarnings()) {
  8100. const webPreferences = getWebPreferences()
  8101. logSecurityWarnings(webPreferences, nodeIntegration)
  8102. }
  8103. }
  8104. window.addEventListener('load', loadHandler, { once: true })
  8105. }
  8106. 'use strict'
  8107.  
  8108. const { webFrame } = require('electron')
  8109. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  8110. const errorUtils = require('@electron/internal/common/error-utils')
  8111.  
  8112. module.exports = () => {
  8113. // Call webFrame method
  8114. ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
  8115. webFrame[method](...args)
  8116. })
  8117.  
  8118. ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  8119. new Promise(resolve =>
  8120. webFrame[method](...args, resolve)
  8121. ).then(result => {
  8122. return [null, result]
  8123. }, error => {
  8124. return [errorUtils.serialize(error)]
  8125. }).then(responseArgs => {
  8126. event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, ...responseArgs)
  8127. })
  8128. })
  8129. }
  8130. 'use strict'
  8131.  
  8132. const { webFrame } = require('electron')
  8133. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  8134.  
  8135. let requestId = 0
  8136.  
  8137. const WEB_VIEW_EVENTS = {
  8138. 'load-commit': ['url', 'isMainFrame'],
  8139. 'did-attach': [],
  8140. 'did-finish-load': [],
  8141. 'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
  8142. 'did-frame-finish-load': ['isMainFrame', 'frameProcessId', 'frameRoutingId'],
  8143. 'did-start-loading': [],
  8144. 'did-stop-loading': [],
  8145. 'dom-ready': [],
  8146. 'console-message': ['level', 'message', 'line', 'sourceId'],
  8147. 'context-menu': ['params'],
  8148. 'devtools-opened': [],
  8149. 'devtools-closed': [],
  8150. 'devtools-focused': [],
  8151. 'new-window': ['url', 'frameName', 'disposition', 'options'],
  8152. 'will-navigate': ['url'],
  8153. 'did-start-navigation': ['url', 'isInPlace', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
  8154. 'did-navigate': ['url', 'httpResponseCode', 'httpStatusText'],
  8155. 'did-frame-navigate': ['url', 'httpResponseCode', 'httpStatusText', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
  8156. 'did-navigate-in-page': ['url', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
  8157. 'focus-change': ['focus', 'guestInstanceId'],
  8158. 'close': [],
  8159. 'crashed': [],
  8160. 'gpu-crashed': [],
  8161. 'plugin-crashed': ['name', 'version'],
  8162. 'destroyed': [],
  8163. 'page-title-updated': ['title', 'explicitSet'],
  8164. 'page-favicon-updated': ['favicons'],
  8165. 'enter-html-full-screen': [],
  8166. 'leave-html-full-screen': [],
  8167. 'media-started-playing': [],
  8168. 'media-paused': [],
  8169. 'found-in-page': ['result'],
  8170. 'did-change-theme-color': ['themeColor'],
  8171. 'update-target-url': ['url']
  8172. }
  8173.  
  8174. const DEPRECATED_EVENTS = {
  8175. 'page-title-updated': 'page-title-set'
  8176. }
  8177.  
  8178. const dispatchEvent = function (webView, eventName, eventKey, ...args) {
  8179. if (DEPRECATED_EVENTS[eventName] != null) {
  8180. dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args)
  8181. }
  8182. const domEvent = new Event(eventName)
  8183. WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => {
  8184. domEvent[prop] = args[index]
  8185. })
  8186. webView.dispatchEvent(domEvent)
  8187. if (eventName === 'load-commit') {
  8188. webView.onLoadCommit(domEvent)
  8189. } else if (eventName === 'focus-change') {
  8190. webView.onFocusChange(domEvent)
  8191. }
  8192. }
  8193.  
  8194. module.exports = {
  8195. registerEvents: function (webView, viewInstanceId) {
  8196. ipcRenderer.on(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () {
  8197. webView.guestInstanceId = undefined
  8198. webView.reset()
  8199. const domEvent = new Event('destroyed')
  8200. webView.dispatchEvent(domEvent)
  8201. })
  8202.  
  8203. ipcRenderer.on(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) {
  8204. dispatchEvent(webView, eventName, eventName, ...args)
  8205. })
  8206.  
  8207. ipcRenderer.on(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) {
  8208. const domEvent = new Event('ipc-message')
  8209. domEvent.channel = channel
  8210. domEvent.args = args
  8211. webView.dispatchEvent(domEvent)
  8212. })
  8213. },
  8214. deregisterEvents: function (viewInstanceId) {
  8215. ipcRenderer.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`)
  8216. ipcRenderer.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`)
  8217. ipcRenderer.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`)
  8218. },
  8219. createGuest: function (params, callback) {
  8220. requestId++
  8221. ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId)
  8222. ipcRenderer.once(`ELECTRON_RESPONSE_${requestId}`, callback)
  8223. },
  8224. createGuestSync: function (params) {
  8225. return ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', params)
  8226. },
  8227. destroyGuest: function (guestInstanceId) {
  8228. ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId)
  8229. },
  8230. attachGuest: function (elementInstanceId, guestInstanceId, params, contentWindow) {
  8231. const embedderFrameId = webFrame.getWebFrameId(contentWindow)
  8232. if (embedderFrameId < 0) { // this error should not happen.
  8233. throw new Error('Invalid embedder frame')
  8234. }
  8235. ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params)
  8236. }
  8237. }
  8238. 'use strict'
  8239.  
  8240. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  8241. const { WebViewImpl } = require('@electron/internal/renderer/web-view/web-view')
  8242. const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
  8243. const errorUtils = require('@electron/internal/common/error-utils')
  8244.  
  8245. // Helper function to resolve url set in attribute.
  8246. const a = document.createElement('a')
  8247.  
  8248. const resolveURL = function (url) {
  8249. if (url === '') return ''
  8250. a.href = url
  8251. return a.href
  8252. }
  8253.  
  8254. // Attribute objects.
  8255. // Default implementation of a WebView attribute.
  8256. class WebViewAttribute {
  8257. constructor (name, webViewImpl) {
  8258. this.name = name
  8259. this.value = webViewImpl.webviewNode[name] || ''
  8260. this.webViewImpl = webViewImpl
  8261. this.ignoreMutation = false
  8262. this.defineProperty()
  8263. }
  8264.  
  8265. // Retrieves and returns the attribute's value.
  8266. getValue () {
  8267. return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value
  8268. }
  8269.  
  8270. // Sets the attribute's value.
  8271. setValue (value) {
  8272. this.webViewImpl.webviewNode.setAttribute(this.name, value || '')
  8273. }
  8274.  
  8275. // Changes the attribute's value without triggering its mutation handler.
  8276. setValueIgnoreMutation (value) {
  8277. this.ignoreMutation = true
  8278. this.setValue(value)
  8279. this.ignoreMutation = false
  8280. }
  8281.  
  8282. // Defines this attribute as a property on the webview node.
  8283. defineProperty () {
  8284. return Object.defineProperty(this.webViewImpl.webviewNode, this.name, {
  8285. get: () => {
  8286. return this.getValue()
  8287. },
  8288. set: (value) => {
  8289. return this.setValue(value)
  8290. },
  8291. enumerable: true
  8292. })
  8293. }
  8294.  
  8295. // Called when the attribute's value changes.
  8296. handleMutation () {}
  8297. }
  8298.  
  8299. // An attribute that is treated as a Boolean.
  8300. class BooleanAttribute extends WebViewAttribute {
  8301. getValue () {
  8302. return this.webViewImpl.webviewNode.hasAttribute(this.name)
  8303. }
  8304.  
  8305. setValue (value) {
  8306. if (value) {
  8307. this.webViewImpl.webviewNode.setAttribute(this.name, '')
  8308. } else {
  8309. this.webViewImpl.webviewNode.removeAttribute(this.name)
  8310. }
  8311. }
  8312. }
  8313.  
  8314. // Attribute representing the state of the storage partition.
  8315. class PartitionAttribute extends WebViewAttribute {
  8316. constructor (webViewImpl) {
  8317. super(webViewConstants.ATTRIBUTE_PARTITION, webViewImpl)
  8318. this.validPartitionId = true
  8319. }
  8320.  
  8321. handleMutation (oldValue, newValue) {
  8322. newValue = newValue || ''
  8323.  
  8324. // The partition cannot change if the webview has already navigated.
  8325. if (!this.webViewImpl.beforeFirstNavigation) {
  8326. console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED)
  8327. this.setValueIgnoreMutation(oldValue)
  8328. return
  8329. }
  8330. if (newValue === 'persist:') {
  8331. this.validPartitionId = false
  8332. console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE)
  8333. }
  8334. }
  8335. }
  8336.  
  8337. // Attribute that handles the location and navigation of the webview.
  8338. class SrcAttribute extends WebViewAttribute {
  8339. constructor (webViewImpl) {
  8340. super(webViewConstants.ATTRIBUTE_SRC, webViewImpl)
  8341. this.setupMutationObserver()
  8342. }
  8343.  
  8344. getValue () {
  8345. if (this.webViewImpl.webviewNode.hasAttribute(this.name)) {
  8346. return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name))
  8347. } else {
  8348. return this.value
  8349. }
  8350. }
  8351.  
  8352. setValueIgnoreMutation (value) {
  8353. super.setValueIgnoreMutation(value)
  8354.  
  8355. // takeRecords() is needed to clear queued up src mutations. Without it, it
  8356. // is possible for this change to get picked up asyncronously by src's
  8357. // mutation observer |observer|, and then get handled even though we do not
  8358. // want to handle this mutation.
  8359. this.observer.takeRecords()
  8360. }
  8361.  
  8362. handleMutation (oldValue, newValue) {
  8363. // Once we have navigated, we don't allow clearing the src attribute.
  8364. // Once <webview> enters a navigated state, it cannot return to a
  8365. // placeholder state.
  8366. if (!newValue && oldValue) {
  8367. // src attribute changes normally initiate a navigation. We suppress
  8368. // the next src attribute handler call to avoid reloading the page
  8369. // on every guest-initiated navigation.
  8370. this.setValueIgnoreMutation(oldValue)
  8371. return
  8372. }
  8373. this.parse()
  8374. }
  8375.  
  8376. // The purpose of this mutation observer is to catch assignment to the src
  8377. // attribute without any changes to its value. This is useful in the case
  8378. // where the webview guest has crashed and navigating to the same address
  8379. // spawns off a new process.
  8380. setupMutationObserver () {
  8381. this.observer = new MutationObserver((mutations) => {
  8382. for (const mutation of mutations) {
  8383. const { oldValue } = mutation
  8384. const newValue = this.getValue()
  8385. if (oldValue !== newValue) {
  8386. return
  8387. }
  8388. this.handleMutation(oldValue, newValue)
  8389. }
  8390. })
  8391. const params = {
  8392. attributes: true,
  8393. attributeOldValue: true,
  8394. attributeFilter: [this.name]
  8395. }
  8396. this.observer.observe(this.webViewImpl.webviewNode, params)
  8397. }
  8398.  
  8399. parse () {
  8400. if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) {
  8401. return
  8402. }
  8403. if (this.webViewImpl.guestInstanceId == null) {
  8404. if (this.webViewImpl.beforeFirstNavigation) {
  8405. this.webViewImpl.beforeFirstNavigation = false
  8406. this.webViewImpl.createGuest()
  8407. }
  8408. return
  8409. }
  8410.  
  8411. // Navigate to |this.src|.
  8412. const opts = {}
  8413. const httpreferrer = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
  8414. if (httpreferrer) {
  8415. opts.httpReferrer = httpreferrer
  8416. }
  8417. const useragent = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_USERAGENT].getValue()
  8418. if (useragent) {
  8419. opts.userAgent = useragent
  8420. }
  8421.  
  8422. const guestInstanceId = this.webViewImpl.guestInstanceId
  8423. const method = 'loadURL'
  8424. const args = [this.getValue(), opts]
  8425.  
  8426. const [error] = ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', guestInstanceId, method, args)
  8427. if (error) {
  8428. throw errorUtils.deserialize(error)
  8429. }
  8430. }
  8431. }
  8432.  
  8433. // Attribute specifies HTTP referrer.
  8434. class HttpReferrerAttribute extends WebViewAttribute {
  8435. constructor (webViewImpl) {
  8436. super(webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl)
  8437. }
  8438. }
  8439.  
  8440. // Attribute specifies user agent
  8441. class UserAgentAttribute extends WebViewAttribute {
  8442. constructor (webViewImpl) {
  8443. super(webViewConstants.ATTRIBUTE_USERAGENT, webViewImpl)
  8444. }
  8445. }
  8446.  
  8447. // Attribute that set preload script.
  8448. class PreloadAttribute extends WebViewAttribute {
  8449. constructor (webViewImpl) {
  8450. super(webViewConstants.ATTRIBUTE_PRELOAD, webViewImpl)
  8451. }
  8452.  
  8453. getValue () {
  8454. if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) {
  8455. return this.value
  8456. }
  8457. let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name))
  8458. const protocol = preload.substr(0, 5)
  8459. if (protocol !== 'file:') {
  8460. console.error(webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE)
  8461. preload = ''
  8462. }
  8463. return preload
  8464. }
  8465. }
  8466.  
  8467. // Attribute that specifies the blink features to be enabled.
  8468. class BlinkFeaturesAttribute extends WebViewAttribute {
  8469. constructor (webViewImpl) {
  8470. super(webViewConstants.ATTRIBUTE_BLINKFEATURES, webViewImpl)
  8471. }
  8472. }
  8473.  
  8474. // Attribute that specifies the blink features to be disabled.
  8475. class DisableBlinkFeaturesAttribute extends WebViewAttribute {
  8476. constructor (webViewImpl) {
  8477. super(webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl)
  8478. }
  8479. }
  8480.  
  8481. // Attribute that specifies the web preferences to be enabled.
  8482. class WebPreferencesAttribute extends WebViewAttribute {
  8483. constructor (webViewImpl) {
  8484. super(webViewConstants.ATTRIBUTE_WEBPREFERENCES, webViewImpl)
  8485. }
  8486. }
  8487.  
  8488. class EnableRemoteModuleAttribute extends WebViewAttribute {
  8489. constructor (webViewImpl) {
  8490. super(webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl)
  8491. }
  8492.  
  8493. getValue () {
  8494. return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false'
  8495. }
  8496.  
  8497. setValue (value) {
  8498. this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false')
  8499. }
  8500. }
  8501.  
  8502. // Sets up all of the webview attributes.
  8503. WebViewImpl.prototype.setupWebViewAttributes = function () {
  8504. this.attributes = {}
  8505. this.attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this)
  8506. this.attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this)
  8507. this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
  8508. this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this)
  8509. this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this)
  8510. this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this)
  8511. this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this)
  8512. this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
  8513. this.attributes[webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this)
  8514. this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
  8515. this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this)
  8516. this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this)
  8517. this.attributes[webViewConstants.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this)
  8518. }
  8519. 'use strict'
  8520.  
  8521. module.exports = {
  8522. // Attributes.
  8523. ATTRIBUTE_NAME: 'name',
  8524. ATTRIBUTE_PARTITION: 'partition',
  8525. ATTRIBUTE_SRC: 'src',
  8526. ATTRIBUTE_HTTPREFERRER: 'httpreferrer',
  8527. ATTRIBUTE_NODEINTEGRATION: 'nodeintegration',
  8528. ATTRIBUTE_ENABLEREMOTEMODULE: 'enableremotemodule',
  8529. ATTRIBUTE_PLUGINS: 'plugins',
  8530. ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity',
  8531. ATTRIBUTE_ALLOWPOPUPS: 'allowpopups',
  8532. ATTRIBUTE_PRELOAD: 'preload',
  8533. ATTRIBUTE_USERAGENT: 'useragent',
  8534. ATTRIBUTE_BLINKFEATURES: 'blinkfeatures',
  8535. ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures',
  8536. ATTRIBUTE_WEBPREFERENCES: 'webpreferences',
  8537.  
  8538. // Internal attribute.
  8539. ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',
  8540.  
  8541. // Error messages.
  8542. ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.',
  8543. ERROR_MSG_CANNOT_INJECT_SCRIPT: '<webview>: ' + 'Script cannot be injected into content until the page has loaded.',
  8544. ERROR_MSG_INVALID_PARTITION_ATTRIBUTE: 'Invalid partition attribute.',
  8545. ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE: 'Only "file:" protocol is supported in "preload" attribute.'
  8546. }
  8547. 'use strict'
  8548.  
  8549. const { webFrame } = require('electron')
  8550.  
  8551. const v8Util = process.atomBinding('v8_util')
  8552. const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
  8553. const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal')
  8554. const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
  8555. const errorUtils = require('@electron/internal/common/error-utils')
  8556. const {
  8557. syncMethods,
  8558. asyncCallbackMethods,
  8559. asyncPromiseMethods
  8560. } = require('@electron/internal/common/web-view-methods')
  8561.  
  8562. // ID generator.
  8563. let nextId = 0
  8564.  
  8565. const getNextId = function () {
  8566. return ++nextId
  8567. }
  8568.  
  8569. // Represents the internal state of the WebView node.
  8570. class WebViewImpl {
  8571. constructor (webviewNode) {
  8572. this.webviewNode = webviewNode
  8573. v8Util.setHiddenValue(this.webviewNode, 'internal', this)
  8574. this.elementAttached = false
  8575. this.beforeFirstNavigation = true
  8576. this.hasFocus = false
  8577.  
  8578. // on* Event handlers.
  8579. this.on = {}
  8580.  
  8581. // Create internal iframe element.
  8582. this.internalElement = this.createInternalElement()
  8583. const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' })
  8584. shadowRoot.innerHTML = '<!DOCTYPE html><style type="text/css">:host { display: flex; }</style>'
  8585. this.setupWebViewAttributes()
  8586. this.viewInstanceId = getNextId()
  8587. shadowRoot.appendChild(this.internalElement)
  8588.  
  8589. // Provide access to contentWindow.
  8590. Object.defineProperty(this.webviewNode, 'contentWindow', {
  8591. get: () => {
  8592. return this.internalElement.contentWindow
  8593. },
  8594. enumerable: true
  8595. })
  8596. }
  8597.  
  8598. createInternalElement () {
  8599. const iframeElement = document.createElement('iframe')
  8600. iframeElement.style.flex = '1 1 auto'
  8601. iframeElement.style.width = '100%'
  8602. iframeElement.style.border = '0'
  8603. v8Util.setHiddenValue(iframeElement, 'internal', this)
  8604. return iframeElement
  8605. }
  8606.  
  8607. // Resets some state upon reattaching <webview> element to the DOM.
  8608. reset () {
  8609. // If guestInstanceId is defined then the <webview> has navigated and has
  8610. // already picked up a partition ID. Thus, we need to reset the initialization
  8611. // state. However, it may be the case that beforeFirstNavigation is false BUT
  8612. // guestInstanceId has yet to be initialized. This means that we have not
  8613. // heard back from createGuest yet. We will not reset the flag in this case so
  8614. // that we don't end up allocating a second guest.
  8615. if (this.guestInstanceId) {
  8616. guestViewInternal.destroyGuest(this.guestInstanceId)
  8617. this.guestInstanceId = void 0
  8618. }
  8619.  
  8620. this.beforeFirstNavigation = true
  8621. this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true
  8622.  
  8623. // Since attachment swaps a local frame for a remote frame, we need our
  8624. // internal iframe element to be local again before we can reattach.
  8625. const newFrame = this.createInternalElement()
  8626. const oldFrame = this.internalElement
  8627. this.internalElement = newFrame
  8628. oldFrame.parentNode.replaceChild(newFrame, oldFrame)
  8629. }
  8630.  
  8631. // Sets the <webview>.request property.
  8632. setRequestPropertyOnWebViewNode (request) {
  8633. Object.defineProperty(this.webviewNode, 'request', {
  8634. value: request,
  8635. enumerable: true
  8636. })
  8637. }
  8638.  
  8639. // This observer monitors mutations to attributes of the <webview> and
  8640. // updates the BrowserPlugin properties accordingly. In turn, updating
  8641. // a BrowserPlugin property will update the corresponding BrowserPlugin
  8642. // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
  8643. // details.
  8644. handleWebviewAttributeMutation (attributeName, oldValue, newValue) {
  8645. if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) {
  8646. return
  8647. }
  8648.  
  8649. // Let the changed attribute handle its own mutation
  8650. this.attributes[attributeName].handleMutation(oldValue, newValue)
  8651. }
  8652.  
  8653. onElementResize () {
  8654. const resizeEvent = new Event('resize')
  8655. resizeEvent.newWidth = this.webviewNode.clientWidth
  8656. resizeEvent.newHeight = this.webviewNode.clientHeight
  8657. this.dispatchEvent(resizeEvent)
  8658. }
  8659.  
  8660. createGuest () {
  8661. return guestViewInternal.createGuest(this.buildParams(), (event, guestInstanceId) => {
  8662. this.attachGuestInstance(guestInstanceId)
  8663. })
  8664. }
  8665.  
  8666. createGuestSync () {
  8667. this.beforeFirstNavigation = false
  8668. this.attachGuestInstance(guestViewInternal.createGuestSync(this.buildParams()))
  8669. }
  8670.  
  8671. dispatchEvent (webViewEvent) {
  8672. this.webviewNode.dispatchEvent(webViewEvent)
  8673. }
  8674.  
  8675. // Adds an 'on<event>' property on the webview, which can be used to set/unset
  8676. // an event handler.
  8677. setupEventProperty (eventName) {
  8678. const propertyName = `on${eventName.toLowerCase()}`
  8679. return Object.defineProperty(this.webviewNode, propertyName, {
  8680. get: () => {
  8681. return this.on[propertyName]
  8682. },
  8683. set: (value) => {
  8684. if (this.on[propertyName]) {
  8685. this.webviewNode.removeEventListener(eventName, this.on[propertyName])
  8686. }
  8687. this.on[propertyName] = value
  8688. if (value) {
  8689. return this.webviewNode.addEventListener(eventName, value)
  8690. }
  8691. },
  8692. enumerable: true
  8693. })
  8694. }
  8695.  
  8696. // Updates state upon loadcommit.
  8697. onLoadCommit (webViewEvent) {
  8698. const oldValue = this.webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC)
  8699. const newValue = webViewEvent.url
  8700. if (webViewEvent.isMainFrame && (oldValue !== newValue)) {
  8701. // Touching the src attribute triggers a navigation. To avoid
  8702. // triggering a page reload on every guest-initiated navigation,
  8703. // we do not handle this mutation.
  8704. this.attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue)
  8705. }
  8706. }
  8707.  
  8708. // Emits focus/blur events.
  8709. onFocusChange () {
  8710. const hasFocus = document.activeElement === this.webviewNode
  8711. if (hasFocus !== this.hasFocus) {
  8712. this.hasFocus = hasFocus
  8713. this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur'))
  8714. }
  8715. }
  8716.  
  8717. onAttach (storagePartitionId) {
  8718. return this.attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue(storagePartitionId)
  8719. }
  8720.  
  8721. buildParams () {
  8722. const params = {
  8723. instanceId: this.viewInstanceId,
  8724. userAgentOverride: this.userAgentOverride
  8725. }
  8726. for (const attributeName in this.attributes) {
  8727. if (this.attributes.hasOwnProperty(attributeName)) {
  8728. params[attributeName] = this.attributes[attributeName].getValue()
  8729. }
  8730. }
  8731. return params
  8732. }
  8733.  
  8734. attachGuestInstance (guestInstanceId) {
  8735. if (!this.elementAttached) {
  8736. // The element could be detached before we got response from browser.
  8737. return
  8738. }
  8739. this.internalInstanceId = getNextId()
  8740. this.guestInstanceId = guestInstanceId
  8741. guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams(), this.internalElement.contentWindow)
  8742. // ResizeObserver is a browser global not recognized by "standard".
  8743. /* globals ResizeObserver */
  8744. // TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not
  8745. // even documented.
  8746. this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this)).observe(this.internalElement)
  8747. }
  8748. }
  8749.  
  8750. // Registers <webview> custom element.
  8751. const registerWebViewElement = (window) => {
  8752. const proto = Object.create(window.HTMLObjectElement.prototype)
  8753. proto.createdCallback = function () {
  8754. return new WebViewImpl(this)
  8755. }
  8756. proto.attributeChangedCallback = function (name, oldValue, newValue) {
  8757. const internal = v8Util.getHiddenValue(this, 'internal')
  8758. if (internal) {
  8759. internal.handleWebviewAttributeMutation(name, oldValue, newValue)
  8760. }
  8761. }
  8762. proto.detachedCallback = function () {
  8763. const internal = v8Util.getHiddenValue(this, 'internal')
  8764. if (!internal) {
  8765. return
  8766. }
  8767. guestViewInternal.deregisterEvents(internal.viewInstanceId)
  8768. internal.elementAttached = false
  8769. this.internalInstanceId = 0
  8770. internal.reset()
  8771. }
  8772. proto.attachedCallback = function () {
  8773. const internal = v8Util.getHiddenValue(this, 'internal')
  8774. if (!internal) {
  8775. return
  8776. }
  8777. if (!internal.elementAttached) {
  8778. guestViewInternal.registerEvents(internal, internal.viewInstanceId)
  8779. internal.elementAttached = true
  8780. internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse()
  8781. }
  8782. }
  8783.  
  8784. const getGuestInstanceId = function (self) {
  8785. const internal = v8Util.getHiddenValue(self, 'internal')
  8786. if (!internal.guestInstanceId) {
  8787. throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.')
  8788. }
  8789. return internal.guestInstanceId
  8790. }
  8791.  
  8792. // Forward proto.foo* method calls to WebViewImpl.foo*.
  8793. const createBlockHandler = function (method) {
  8794. return function (...args) {
  8795. const [error, result] = ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', getGuestInstanceId(this), method, args)
  8796. if (error) {
  8797. throw errorUtils.deserialize(error)
  8798. } else {
  8799. return result
  8800. }
  8801. }
  8802. }
  8803. for (const method of syncMethods) {
  8804. proto[method] = createBlockHandler(method)
  8805. }
  8806.  
  8807. const createNonBlockHandler = function (method) {
  8808. return function (...args) {
  8809. const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
  8810. const requestId = getNextId()
  8811. ipcRenderer.once(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, function (event, error, result) {
  8812. if (error == null) {
  8813. if (callback) callback(result)
  8814. } else {
  8815. throw errorUtils.deserialize(error)
  8816. }
  8817. })
  8818. ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
  8819. }
  8820. }
  8821.  
  8822. for (const method of asyncCallbackMethods) {
  8823. proto[method] = createNonBlockHandler(method)
  8824. }
  8825.  
  8826. const createPromiseHandler = function (method) {
  8827. return function (...args) {
  8828. return new Promise((resolve, reject) => {
  8829. const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
  8830. const requestId = getNextId()
  8831.  
  8832. ipcRenderer.once(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, function (event, error, result) {
  8833. if (error == null) {
  8834. if (callback) {
  8835. callback(result)
  8836. }
  8837. resolve(result)
  8838. } else {
  8839. reject(errorUtils.deserialize(error))
  8840. }
  8841. })
  8842. ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
  8843. })
  8844. }
  8845. }
  8846.  
  8847. for (const method of asyncPromiseMethods) {
  8848. proto[method] = createPromiseHandler(method)
  8849. }
  8850.  
  8851. // WebContents associated with this webview.
  8852. proto.getWebContents = function () {
  8853. const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
  8854. const remote = getRemoteForUsage('getWebContents()')
  8855. const internal = v8Util.getHiddenValue(this, 'internal')
  8856. if (!internal.guestInstanceId) {
  8857. internal.createGuestSync()
  8858. }
  8859. return remote.getGuestWebContents(internal.guestInstanceId)
  8860. }
  8861.  
  8862. // Focusing the webview should move page focus to the underlying iframe.
  8863. proto.focus = function () {
  8864. this.contentWindow.focus()
  8865. }
  8866.  
  8867. window.WebView = webFrame.registerEmbedderCustomElement(window, 'webview', {
  8868. prototype: proto
  8869. })
  8870.  
  8871. // Delete the callbacks so developers cannot call them and produce unexpected
  8872. // behavior.
  8873. delete proto.createdCallback
  8874. delete proto.attachedCallback
  8875. delete proto.detachedCallback
  8876. delete proto.attributeChangedCallback
  8877. }
  8878.  
  8879. const setupWebView = (window) => {
  8880. require('@electron/internal/renderer/web-view/web-view-attributes')
  8881.  
  8882. const useCapture = true
  8883. window.addEventListener('readystatechange', function listener (event) {
  8884. if (document.readyState === 'loading') {
  8885. return
  8886. }
  8887. registerWebViewElement(window)
  8888. window.removeEventListener(event.type, listener, useCapture)
  8889. }, useCapture)
  8890. }
  8891.  
  8892. module.exports = { setupWebView, WebViewImpl }
  8893. 'use strict'
  8894.  
  8895. // This file should have no requires since it is used by the isolated context
  8896. // preload bundle. Instead arguments should be passed in for everything it
  8897. // needs.
  8898.  
  8899. // This file implements the following APIs:
  8900. // - window.alert()
  8901. // - window.confirm()
  8902. // - window.history.back()
  8903. // - window.history.forward()
  8904. // - window.history.go()
  8905. // - window.history.length
  8906. // - window.open()
  8907. // - window.opener.blur()
  8908. // - window.opener.close()
  8909. // - window.opener.eval()
  8910. // - window.opener.focus()
  8911. // - window.opener.location
  8912. // - window.opener.print()
  8913. // - window.opener.postMessage()
  8914. // - window.prompt()
  8915. // - document.hidden
  8916. // - document.visibilityState
  8917.  
  8918. const { defineProperty } = Object
  8919.  
  8920. // Helper function to resolve relative url.
  8921. const a = window.top.document.createElement('a')
  8922. const resolveURL = function (url) {
  8923. a.href = url
  8924. return a.href
  8925. }
  8926.  
  8927. // Use this method to ensure values expected as strings in the main process
  8928. // are convertible to strings in the renderer process. This ensures exceptions
  8929. // converting values to strings are thrown in this process.
  8930. const toString = (value) => {
  8931. return value != null ? `${value}` : value
  8932. }
  8933.  
  8934. const windowProxies = {}
  8935.  
  8936. const getOrCreateProxy = (ipcRenderer, guestId) => {
  8937. let proxy = windowProxies[guestId]
  8938. if (proxy == null) {
  8939. proxy = new BrowserWindowProxy(ipcRenderer, guestId)
  8940. windowProxies[guestId] = proxy
  8941. }
  8942. return proxy
  8943. }
  8944.  
  8945. const removeProxy = (guestId) => {
  8946. delete windowProxies[guestId]
  8947. }
  8948.  
  8949. function BrowserWindowProxy (ipcRenderer, guestId) {
  8950. this.closed = false
  8951.  
  8952. defineProperty(this, 'location', {
  8953. get: function () {
  8954. return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'getURL')
  8955. },
  8956. set: function (url) {
  8957. url = resolveURL(url)
  8958. return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'loadURL', url)
  8959. }
  8960. })
  8961.  
  8962. ipcRenderer.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => {
  8963. removeProxy(guestId)
  8964. this.closed = true
  8965. })
  8966.  
  8967. this.close = () => {
  8968. ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', guestId)
  8969. }
  8970.  
  8971. this.focus = () => {
  8972. ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', guestId, 'focus')
  8973. }
  8974.  
  8975. this.blur = () => {
  8976. ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', guestId, 'blur')
  8977. }
  8978.  
  8979. this.print = () => {
  8980. ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', guestId, 'print')
  8981. }
  8982.  
  8983. this.postMessage = (message, targetOrigin) => {
  8984. ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', guestId, message, toString(targetOrigin), window.location.origin)
  8985. }
  8986.  
  8987. this.eval = (...args) => {
  8988. ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', guestId, 'executeJavaScript', ...args)
  8989. }
  8990. }
  8991.  
  8992. module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen) => {
  8993. if (guestInstanceId == null) {
  8994. // Override default window.close.
  8995. window.close = function () {
  8996. ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE')
  8997. }
  8998. }
  8999.  
  9000. if (!usesNativeWindowOpen) {
  9001. // Make the browser window or guest view emit "new-window" event.
  9002. window.open = function (url, frameName, features) {
  9003. if (url != null && url !== '') {
  9004. url = resolveURL(url)
  9005. }
  9006. const guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features))
  9007. if (guestId != null) {
  9008. return getOrCreateProxy(ipcRenderer, guestId)
  9009. } else {
  9010. return null
  9011. }
  9012. }
  9013.  
  9014. if (openerId != null) {
  9015. window.opener = getOrCreateProxy(ipcRenderer, openerId)
  9016. }
  9017. }
  9018.  
  9019. // But we do not support prompt().
  9020. window.prompt = function () {
  9021. throw new Error('prompt() is and will not be supported.')
  9022. }
  9023.  
  9024. ipcRenderer.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (event, sourceId, message, sourceOrigin) {
  9025. // Manually dispatch event instead of using postMessage because we also need to
  9026. // set event.source.
  9027. event = document.createEvent('Event')
  9028. event.initEvent('message', false, false)
  9029. event.data = message
  9030. event.origin = sourceOrigin
  9031. event.source = getOrCreateProxy(ipcRenderer, sourceId)
  9032. window.dispatchEvent(event)
  9033. })
  9034.  
  9035. window.history.back = function () {
  9036. ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK')
  9037. }
  9038.  
  9039. window.history.forward = function () {
  9040. ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD')
  9041. }
  9042.  
  9043. window.history.go = function (offset) {
  9044. ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset)
  9045. }
  9046.  
  9047. defineProperty(window.history, 'length', {
  9048. get: function () {
  9049. return ipcRenderer.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH')
  9050. }
  9051. })
  9052.  
  9053. if (guestInstanceId != null) {
  9054. // Webview `document.visibilityState` tracks window visibility (and ignores
  9055. // the actual <webview> element visibility) for backwards compatibility.
  9056. // See discussion in #9178.
  9057. //
  9058. // Note that this results in duplicate visibilitychange events (since
  9059. // Chromium also fires them) and potentially incorrect visibility change.
  9060. // We should reconsider this decision for Electron 2.0.
  9061. let cachedVisibilityState = hiddenPage ? 'hidden' : 'visible'
  9062.  
  9063. // Subscribe to visibilityState changes.
  9064. ipcRenderer.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (event, visibilityState) {
  9065. if (cachedVisibilityState !== visibilityState) {
  9066. cachedVisibilityState = visibilityState
  9067. document.dispatchEvent(new Event('visibilitychange'))
  9068. }
  9069. })
  9070.  
  9071. // Make document.hidden and document.visibilityState return the correct value.
  9072. defineProperty(document, 'hidden', {
  9073. get: function () {
  9074. return cachedVisibilityState !== 'visible'
  9075. }
  9076. })
  9077.  
  9078. defineProperty(document, 'visibilityState', {
  9079. get: function () {
  9080. return cachedVisibilityState
  9081. }
  9082. })
  9083. }
  9084. }
  9085. 'use strict'
  9086.  
  9087. const path = require('path')
  9088. const Module = require('module')
  9089.  
  9090. // We modified the original process.argv to let node.js load the
  9091. // init.js, we need to restore it here.
  9092. process.argv.splice(1, 1)
  9093.  
  9094. // Clear search paths.
  9095. require('../common/reset-search-paths')
  9096.  
  9097. // Import common settings.
  9098. require('@electron/internal/common/init')
  9099.  
  9100. // Expose public APIs.
  9101. Module.globalPaths.push(path.join(__dirname, 'api', 'exports'))
  9102.  
  9103. // Export node bindings to global.
  9104. global.require = require
  9105. global.module = module
  9106.  
  9107. // Set the __filename to the path of html file if it is file: protocol.
  9108. if (self.location.protocol === 'file:') {
  9109. const pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.pathname
  9110. global.__filename = path.normalize(decodeURIComponent(pathname))
  9111. global.__dirname = path.dirname(global.__filename)
  9112.  
  9113. // Set module's filename so relative require can work as expected.
  9114. module.filename = global.__filename
  9115.  
  9116. // Also search for module under the html file.
  9117. module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
  9118. } else {
  9119. global.__filename = __filename
  9120. global.__dirname = __dirname
  9121. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement