Compare commits

...

332 Commits

Author SHA1 Message Date
Electron Bot
663d389925 Bump v8.2.3 2020-04-16 14:16:08 -07:00
Samuel Attard
b8e3477092 Merge pull request from GHSA-h9jc-284h-533g 2020-04-16 14:10:18 -07:00
trop[bot]
035c784b6c docs: explain the swipe event on macOS (#23133)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-04-16 10:55:37 -07:00
Electron Bot
8faf8a3535 Bump v8.2.2 2020-04-13 11:31:46 -07:00
trop[bot]
331125d35a fix: don't assign NSAlert to window which is not visible (#23088)
* fix: don't assign NSAlert to window which is not visible

Without this change it's possible to create message box which can't
be dismissed on mac.

* fixup! fix: don't assign NSAlert to window which is not visible

* fixup! fix: don't assign NSAlert to window which is not visible

Co-authored-by: Cezary Kulakowski <cezary@openfin.co>
2020-04-13 11:28:01 -07:00
trop[bot]
7316ebde79 fix: reset node env earlier during shutdown (#23069)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-04-13 11:25:37 -07:00
Jeremy Apthorp
c3340ad21c chore: cherry-pick 85f708fa7ab8 from chromium (#23047)
* chore: cherry-pick 85f708fa7ab8 from chromium

* additionally backport 3626b1f19e
2020-04-13 09:17:18 -04:00
trop[bot]
1e84816c96 fix: persist maximizable state when toggling fullscreen (#23020)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-04-10 09:40:29 -07:00
Jeremy Apthorp
58c6ea5de8 ci: auto-3way patches and detect changes (#23032) 2020-04-08 15:31:00 -07:00
trop[bot]
34be80602c fix: nullptr check when closing windows (#23022) 2020-04-07 22:10:41 -07:00
Shelley Vohr
ce40be475b fix: webframe crashes for removed render frame (#22975)
* fix: webframe crashes for removed render frame

* Make errors more descriptive
2020-04-07 21:54:56 -07:00
trop[bot]
5f7bd36119 build: set merge=union for .patches (#22992)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-04-07 09:55:24 -07:00
Electron Bot
0f19193afd Bump v8.2.1 2020-04-06 14:12:11 -07:00
Samuel Attard
06b3b0ea15 Revert "Bump v8.2.1"
This reverts commit 32f973b010.
2020-04-06 12:43:27 -07:00
Electron Bot
32f973b010 Bump v8.2.1 2020-04-06 12:17:38 -07:00
Samuel Attard
ad2c2a055b Revert "Bump v8.2.1"
This reverts commit d70b1c68fe.
2020-04-06 12:16:21 -07:00
Electron Bot
3069ef3827 chore: bump chromium in DEPS to 80.0.3987.163 (#22931) 2020-04-03 09:20:45 -07:00
Electron Bot
d70b1c68fe Bump v8.2.1 2020-04-02 11:16:28 -07:00
Samuel Attard
3ba4a35c9c fix: propagate preferred color scheme to the renderer (#22896) (#22901)
* fix: propagate preferred color scheme to the renderer (#22896)

* fix: do not crash if the window is closed syncronously with a nativeTheme change

* fix: propogate preferred color scheme to the renderer and keep it up to date

* chore: update native theme source patch for linux
2020-04-01 22:12:14 -07:00
trop[bot]
2c4c1a3dd9 fix: screen module should still be creatable if the first create is before the ready event (#22913)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-04-01 11:37:15 -07:00
John Kleinschmidt
a4d8974556 fix: revive offscreen rendering support (#22431)
* fix: revive offscreen rendering support (#22160)

(cherry picked from commit 36f982aee2)

* Fixup compile issues

* skip offscreen tests on 32-bit linux
2020-03-31 17:32:52 -04:00
Electron Bot
b7d4759c19 chore: bump chromium in DEPS to 80.0.3987.162 (#22905) 2020-03-31 17:32:30 -04:00
trop[bot]
d6ecc65df3 fix: dialog fails to show after modal close (#22889)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-30 16:29:31 -04:00
Electron Bot
98b67cdb69 chore: bump chromium in DEPS to 80.0.3987.159 (#22847) 2020-03-30 15:58:17 -04:00
John Kleinschmidt
eb37822e03 ci: Add goma to older branches (#22784)
* ci: use goma for windows and linux builds (#21868)

* ci: use goma for windows and linux builds

(cherry picked from commit dc2fcff01c)

* ci:  enable goma for all testing builds (#21992) (#22203)

(cherry picked from commit e7982623ec)
(cherry picked from commit 0e9727e8d5)
2020-03-25 15:28:15 -04:00
trop[bot]
8751f485c5 fix: workaround for hang when preventDefault-ing nativeWindowOpen (#22749)
* fix: enable workaround for nativeWindowOpen hang

* add test

* test: ensure window doesn't leak into other test

* update to use new webcontents delegate methods

Co-authored-by: Andy Locascio <andy@slack-corp.com>
2020-03-25 10:40:26 +09:00
Electron Bot
bfef6a54f0 Bump v8.2.0 2020-03-24 08:54:06 -07:00
Electron Bot
5fd403b571 Revert "Bump v8.2.0"
This reverts commit abe8c7168a.
2020-03-23 20:25:40 -07:00
Electron Bot
abe8c7168a Bump v8.2.0 2020-03-23 19:40:00 -07:00
Samuel Attard
5c32d18248 Revert "fix: better window hierarchy checks"
This reverts commit 7ba879711a.
2020-03-23 19:35:54 -07:00
Samuel Attard
c1da7b5238 Revert "Bump v8.2.0"
This reverts commit 5b75a4a4be.
2020-03-23 19:35:44 -07:00
Electron Bot
5b75a4a4be Bump v8.2.0 2020-03-23 14:47:36 -07:00
Electron Bot
57e36afc7e Revert "Bump v8.2.0"
This reverts commit a4ea27de36.
2020-03-23 14:46:14 -07:00
Electron Bot
a4ea27de36 Bump v8.2.0 2020-03-23 14:20:03 -07:00
Samuel Attard
7ba879711a fix: better window hierarchy checks 2020-03-23 14:12:48 -07:00
Samuel Attard
f057b0e494 feat: add support for net requests to use the session cookie store (#22807)
* chore: refactor all the net specs to be async with better error handling (#22731)

* chore: fix net specs when rerunning locally (#22745)

* feat: add support for net requests to use the session cookie store (#22704)

* fix: allow net requests to use Same-Site cookies (#22788)

* build: fix merge conflict

* Update extensions-spec.ts

* Update extensions-spec.ts
2020-03-23 13:38:46 -07:00
Samuel Attard
fa01a20f9b build: enable JS semicolons (#22786) 2020-03-23 09:18:39 -07:00
trop[bot]
579f7a1899 fix: prevent crash in ListValue v8 converter when conversion fails (#22758)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-03-19 10:48:26 -04:00
Electron Bot
920a82a4de chore: bump chromium in DEPS to 80.0.3987.158 (#22742) 2020-03-19 10:36:17 -04:00
trop[bot]
b501c40bd6 build: auto-generate the codesigning cert used for macOS CI testing runs (#22763)
* build: auto-generate the codesigning cert used for macOS CI testing runs

* build: give the cert ALL the trust values

* chore: also import public key

* idek

Co-authored-by: Samuel Attard <sattard@slack-corp.com>
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-03-19 16:34:47 +09:00
trop[bot]
672aa1c67f test: no need to loadURL in menu test (#22766)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-03-19 16:34:08 +09:00
Сковорода Никита Андреевич
b305784aa3 feat: add disableDialogs option to WebPreferences (#22665)
Allows to disable dialogs completely in a similar way of how safeDialogs option can be used. Overrides safeDialogs option.
2020-03-18 20:01:55 -04:00
trop[bot]
c4efeef92c fix: remove bad usages of for-in and guard against it (#22728)
* fix: remove bad usages of for-in and guard against it

* Apply suggestions from code review

Co-Authored-By: Samuel Maddock <samuel.maddock@gmail.com>

* Apply suggestions from code review

Co-Authored-By: Jeremy Apthorp <jeremya@chromium.org>

* Update remote.js

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
Co-authored-by: Samuel Maddock <samuel.maddock@gmail.com>
Co-authored-by: Jeremy Apthorp <jeremya@chromium.org>
2020-03-18 09:38:11 +09:00
Shelley Vohr
96b41d0930 fix: crash on invalid zoomFactor (#22709) 2020-03-18 09:32:19 +09:00
Electron Bot
1f652f08f8 chore: bump chromium in DEPS to 80.0.3987.156 (#22718) 2020-03-17 13:09:13 -04:00
Electron Bot
d12c157242 chore: bump chromium to 80.0.3987.148 (8-x-y) (#22680) 2020-03-16 23:19:00 -07:00
Electron Bot
68873fbadb chore: bump chromium in DEPS to 80.0.3987.144 (#22669) 2020-03-12 13:42:41 -04:00
Erick Zhao
526d748b7e fix: guard against duplicate TouchBarItem IDs (#22645) 2020-03-12 10:48:26 +09:00
Electron Bot
54109cb04d chore: bump chromium in DEPS to 80.0.3987.143 (#22648) 2020-03-11 17:20:28 -04:00
trop[bot]
542f30b54b test: test setPath for errors thrown (#22638)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-11 16:02:02 +09:00
Electron Bot
07ea340314 chore: bump chromium in DEPS to 80.0.3987.142 (#22635) 2020-03-10 21:51:34 -04:00
trop[bot]
524d6a5d79 chore: don't delete nightly tag after draft (#22623)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-10 17:04:11 +09:00
Alexey Kuzmin
a0431e1265 build: fix build without built-in spellchecker (#22608) 2020-03-10 16:59:51 +09:00
Electron Bot
647299fa9f Bump v8.1.1 2020-03-09 16:26:54 -07:00
trop[bot]
ee5d52440b build: upload sentry src bundles to symbol S3 bucket (#22619)
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
2020-03-09 16:24:37 -07:00
Electron Bot
d4a79bc4c0 chore: bump chromium to 80.0.3987.141 (8-x-y) (#22593)
* chore: bump chromium in DEPS to 80.0.3987.139

* chore: bump chromium in DEPS to 80.0.3987.140

* chore: bump chromium in DEPS to 80.0.3987.141
2020-03-09 10:55:54 -07:00
Electron Bot
e37f69c467 chore: bump chromium in DEPS to 80.0.3987.138 (#22583) 2020-03-06 12:17:59 -05:00
Erick Zhao
df6faec72e fix: allow persistent media salts (#22386) (#22569) 2020-03-05 19:39:23 -08:00
Electron Bot
691ddb3f67 Bump v8.1.0 2020-03-05 17:09:17 -08:00
Samuel Attard
b4e1b840cd feat: programmatically modify traffic light positioning (#22533) (#22564)
* setter

* getter

* specs and docs

* fixup

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-03-05 17:07:51 -08:00
Samuel Attard
6f12b67582 feat: add events for spellcheck dictionary downloads (#22449) (#22558) 2020-03-05 17:07:44 -08:00
Samuel Attard
33f5fa3c56 feat: allow http-parser NODE_OPTION in packaged apps (#21694) (#22539) 2020-03-05 17:07:37 -08:00
Samuel Attard
6f88108132 fix: disable contextBridge object identity caching (#21803) (#22557)
* fix: disable contextBridge object identity caching (#21803)

* fix: disable contextBridge object identity caching

* cleanup

* chore: make non-const references raw pointers

* fix: zero-param constructors are not explicit

* refactor: use base::LinkedList

Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>

* Update electron_api_context_bridge.h

* Update electron_api_context_bridge.cc

Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-03-05 17:02:54 -08:00
Samuel Attard
05a7f5f492 docs: clean up dark mode related docs (#22563)
Backport of #22489

Electron 8 uses macOS 10.14 SDK, so we can remove paragraphs from the
systemPreferences API docs.
2020-03-05 14:02:58 -08:00
Samuel Attard
b878a5ea4c fix: reposition traffic lights on theme change (#22559)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-03-05 14:02:19 -08:00
Jeremy Apthorp
8756ff0349 chore: bump chromium to 80.0.3987.137 (8-x-y) (#22548) 2020-03-05 13:11:01 -08:00
Cheng Zhao
20c5a3b9e1 fix: destroy node platform after destroying wrappers (#22537)
Co-authored-by: Cheng Zhao <zcbenz@electronjs.org>
2020-03-05 13:07:58 +09:00
Samuel Attard
ddb62e8d7c fix: do not reposition traffic lights when fullscreened (#22508)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-03-04 16:52:25 -08:00
Electron Bot
077e35c576 chore: bump chromium in DEPS to 80.0.3987.136 (#22529) 2020-03-04 12:34:07 -05:00
trop[bot]
eb63062889 fix: properly forward properties to webview (#22510)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-04 16:22:27 +09:00
trop[bot]
cac6db76a0 fix: bail early if no printers on the network (#22518)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-04 16:19:05 +09:00
Electron Bot
9257c315ac chore: bump chromium in DEPS to 80.0.3987.135 (#22505) 2020-03-03 16:21:22 -08:00
Cheng Zhao
031480c3db fix: add patch to fix os_metrics_mac with 10.15 SDK (#22496) 2020-03-03 15:51:26 +09:00
trop[bot]
9f9d3e98bf docs: add documentation on case insensitive dictionary hosting (#22486)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-03-03 15:10:09 +09:00
trop[bot]
ae639186e3 fix: check WebContents in OnGetDefaultPrinter (#22475)
Co-authored-by: Cheng Zhao <zcbenz@electronjs.org>
2020-03-03 15:05:21 +09:00
Shelley Vohr
6e4690574d fix: prevent potential modal window close segfault (#22481) 2020-03-03 15:04:59 +09:00
trop[bot]
c83dc5be92 fix: Add ContentsView to AXChildren (#22470)
Co-authored-by: Felix Rieseberg <felix@felixrieseberg.com>
2020-03-02 22:02:20 +00:00
Electron Bot
63cb360df0 Bump v8.0.3 2020-03-02 11:30:38 -08:00
Samuel Attard
b311235165 Revert "Bump v8.0.3"
This reverts commit 6cd2623a87.
2020-03-02 11:26:00 -08:00
Electron Bot
6cd2623a87 Bump v8.0.3 2020-03-02 11:15:24 -08:00
Electron Bot
9aa81de83d Revert "Bump v8.0.3"
This reverts commit 0f246b6be7.
2020-03-02 11:10:57 -08:00
Electron Bot
0f246b6be7 Bump v8.0.3 2020-03-02 11:08:50 -08:00
Samuel Attard
5be76fd4ea Revert "Bump v8.0.3"
This reverts commit 7fe2cad354.
2020-03-02 11:03:36 -08:00
Samuel Attard
a315283f88 chore: update g_swizzle_imk_input_session for new SDK 2020-03-02 11:03:28 -08:00
Electron Bot
b741cbf3d7 chore: bump chromium to 80.0.3987.134 (8-x-y) (#22463) 2020-03-02 11:02:17 -08:00
loc
031c0d6951 fix: port CL that fixes ARIA tree impl for macOS (#22423) 2020-03-02 18:25:37 +00:00
Electron Bot
3639a82915 chore: bump chromium to 80.0.3987.132 (8-x-y) (#22441)
* chore: bump chromium in DEPS to 80.0.3987.129

* chore: bump chromium in DEPS to 80.0.3987.132
2020-02-29 13:37:12 -08:00
Electron Bot
7fe2cad354 Bump v8.0.3 2020-02-28 16:02:00 -08:00
trop[bot]
de2e12343e fix: dictionaries download path should be in userdata (#22448)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-28 15:58:33 -08:00
John Kleinschmidt
69598ae5c6 build: try using newer version of xcode/macOS sdk (#22104) 2020-02-28 15:54:30 -08:00
Samuel Attard
0f0a7e6b1e Revert "Bump v8.0.3"
This reverts commit a0dcb55d6c.
2020-02-28 15:48:57 -08:00
Electron Bot
a0dcb55d6c Bump v8.0.3 2020-02-27 20:30:36 -08:00
Samuel Attard
ee35482d97 Revert "fix: backport v8 patch for type inference issue (#22434)"
This reverts commit cfe350a534.
2020-02-27 16:44:49 -08:00
Electron Bot
8317553cd4 Revert "Bump v8.0.3"
This reverts commit ebf98e8af4.
2020-02-27 16:37:02 -08:00
Electron Bot
ebf98e8af4 Bump v8.0.3 2020-02-27 16:06:52 -08:00
trop[bot]
cfe350a534 fix: backport v8 patch for type inference issue (#22434)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-27 15:52:48 -08:00
Jeremy Apthorp
854dcb3247 chore: bump chromium to 80.0.3987.128 (#22419) 2020-02-27 14:42:39 -08:00
trop[bot]
882ce295ee docs: improve documentation on spellchecker download URL (#22403)
* docs: improve documentation on spellchecker download URL

* Update session.md

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-26 23:09:40 -08:00
trop[bot]
1ac31e18b7 test: disable clipboard tests for WOA (#22388)
Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-02-26 16:47:03 +09:00
Electron Bot
957717e483 Bump v8.0.2 2020-02-25 17:25:00 -08:00
trop[bot]
d1ccfea882 fix: add patch to set the base download URL rather than override it completely (#22384)
* fix: add patch to set the base download URL rather than override it completely

* test

* test

* test

* test

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-25 17:23:37 -08:00
trop[bot]
232ca04edd fix: pass safeDialogs preference properly (#22377)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-02-25 20:42:53 +00:00
trop[bot]
2888e46b7a chore: allow custom node-spec-runner options (#22332)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-02-25 11:09:51 +09:00
Jeremy Apthorp
4464a04f35 fix: emit will-navigate for sandboxed contents (#22188) (#22328) 2020-02-24 11:43:35 -08:00
trop[bot]
37baff1e31 fix: typo in crash reporter constructor (#22344)
Co-authored-by: Syed Umair <31096792+Syed-Umair@users.noreply.github.com>
2020-02-24 17:52:37 +09:00
Jeremy Apthorp
dde19b0583 fix: revert {Atom => Electron}Application rename (#22206) (#22326) 2020-02-24 12:29:35 +09:00
trop[bot]
e2b9cd7b7c docs: clean up protocol docs (#22309)
* docs: clean up protocol docs

* Fix capitalization

Co-authored-by: Mark Lee <malept@users.noreply.github.com>
2020-02-24 12:28:37 +09:00
Cheng Zhao
ecd398f8bd fix: disable remote layer APIs in MAS build (8-x-y) (#22298)
* fix: add patch to disable remote layer APIs

* fix: use --disable-gpu-memory-buffer-compositor-resources for MAS build
2020-02-24 12:09:12 +09:00
trop[bot]
21f544392b chore: remove libcc from release not generator (#22295)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-02-20 10:19:28 -05:00
John Kleinschmidt
5c93682a89 build: only strip binaries on linux (#22282) 2020-02-19 14:32:32 -05:00
trop[bot]
1db64c9e51 doc: remove accidental deprecation (#22265)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-02-19 13:44:36 +09:00
Samuel Attard
3a747eddbb fix: add patch to route mouse event navigations through the WebContentsDelegate (#22205) 2020-02-18 15:22:26 -08:00
John Kleinschmidt
51504aee64 build: preserve timestamps when stripping files (#22094) (#22257)
* build: preserve timestamps when stripping files

Resolves an issue where the binaries in mksnapshot.zip were not getting stripped.

Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
(cherry picked from commit 5e49aafe55)
2020-02-18 13:56:10 -05:00
trop[bot]
360e8a2eb8 fix: don't include breakpad_symbols dir in dsym.zip (#22218)
Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-02-18 12:00:16 -05:00
Electron Bot
4447a63f20 Bump v8.0.1 2020-02-14 06:02:51 -08:00
trop[bot]
ceb3b0cf54 fix: no-arg console.log is undefined (#22172)
Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-02-14 15:32:13 +09:00
Shelley Vohr
89607e647d fix: crash on custom printing margins (#22186) 2020-02-13 21:03:23 +00:00
Shelley Vohr
c8e2a6261e refactor: use NSVisualEffectMaterial* constants directly (#22149) 2020-02-12 12:01:38 -05:00
trop[bot]
e9814e016b fix RTL bug when used with traffic light repositioning (#22162)
Co-authored-by: tonyfwoo <55114329+tonyfwoo@users.noreply.github.com>
2020-02-12 16:26:34 +00:00
Cheng Zhao
dc9654c03c fix: make webRequest work with WebSocket (#22134) 2020-02-12 11:44:39 +09:00
trop[bot]
fce3426675 build: fix spellchecker deps (#22154)
Co-authored-by: Alexey Kuzmin <alex.s.kuzmin@gmail.com>
2020-02-11 23:47:55 +00:00
Jeremy Apthorp
637cf8a02b fix: don't crash on invalid certs (#22124)
* test: add test for app.on('certificate-error') event (#21978)

* types

* fix: don't crash on invalid certs (#21976)
2020-02-11 23:46:40 +00:00
Alexey Kuzmin
daab432fb6 ci: strip mksnapshot binaries on Linux (#22145)
Related to #21086.
2020-02-11 13:13:10 -05:00
trop[bot]
50dac1d908 ci: fix build failure on doc only changes (#22088)
* ci: fix build failure on doc only changes

* ci: fix doc-only check when CI fires on branch before PR is created

Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-02-10 17:10:07 +09:00
trop[bot]
d4f915e428 fix: flash plugin (#22110)
* fix: flash plugin

Fixes https://github.com/electron/electron/issues/20744

* cleanup

* fix linting issue

Co-authored-by: t57ser <seve@live.at>
2020-02-10 10:50:26 +09:00
trop[bot]
72bccd1305 fix: use a WeakPtr so we do not UAF the store in FunctionLifetimeMonitor (#22114)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-10 10:46:54 +09:00
Shelley Vohr
9439388b7b chore: wrap cb with default invocation (#22036) 2020-02-05 18:24:41 +00:00
Jeremy Apthorp
b0fee4b067 ci: fix ELECTRON_OUT_DIR (#21994) (#22019) 2020-02-05 10:30:47 +09:00
Jeremy Apthorp
7c33fc0c5c chore: rename atom -> electron (#21987) 2020-02-04 13:50:17 -08:00
Shelley Vohr
f263a3f755 fix: default printer if none is provided (#21956) (#22011) 2020-02-04 11:59:45 -05:00
Electron Bot
1af3a71fdb Bump v8.0.0 2020-02-03 13:17:36 -08:00
trop[bot]
9f2371fc4e fix: bind spellchecker receivers correctly in the renderer (#22015)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-03 11:48:01 -08:00
trop[bot]
f658c1aeb4 fix: use the new MediaPlayPause key listener for internal chrome logic (#21998)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-03 10:28:32 -08:00
Samuel Attard
c196121467 Revert "Bump v8.0.0-beta.10"
This reverts commit a326408bce.
2020-02-03 10:24:51 -08:00
Electron Bot
133ac9a323 chore: bump chromium to 80.0.3987.86 (8-x-y) (#22000)
* chore: bump chromium in DEPS to 80.0.3987.84

* chore: bump chromium in DEPS to 80.0.3987.85

* chore: bump chromium in DEPS to 80.0.3987.86
2020-02-03 11:20:30 -05:00
Electron Bot
a326408bce Bump v8.0.0-beta.10 2020-01-31 15:19:09 -08:00
Samuel Attard
2085111c43 Revert "Bump v8.0.0-beta.10"
This reverts commit 7ae8d54265.
2020-01-31 15:17:13 -08:00
Electron Bot
7ae8d54265 Bump v8.0.0-beta.10 2020-01-31 13:39:41 -08:00
Samuel Attard
c5574c8667 feat: custom positioning for traffic light buttons (#21990)
* feat: custom positioning for traffic light buttons (#21781)

* feat: custom positioning for traffic light buttons

* remove NSLog and unnecessary call-site in IsVisible()

* no longer need to check if entering fullscreen

* change API to take a point object

Co-authored-by: tonyfwoo <55114329+tonyfwoo@users.noreply.github.com>

* chore: add safety checks to RepositionTrafficLights

Co-authored-by: Tony <TonyWuu@users.noreply.github.com>
Co-authored-by: tonyfwoo <55114329+tonyfwoo@users.noreply.github.com>
2020-01-31 13:35:47 -08:00
trop[bot]
6cf8abc3b3 fix: return path from netLog.stopLogging (#21989)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-01-31 11:22:14 -08:00
LuoJinghua
46a4864b08 feat: Exposing methods required by capturing a hidden webContents (#21895) 2020-01-31 11:21:42 -08:00
Electron Bot
7956b45323 chore: bump chromium in DEPS to 80.0.3987.82 (#21984) 2020-01-31 11:50:10 -05:00
trop[bot]
63bcbd4ff5 fix: prevent print crash on bad deviceName (#21982) 2020-01-31 16:26:19 +00:00
Electron Bot
3b5ca91a40 chore: bump chromium to 80.0.3987.79 (8-x-y) (#21953)
* chore: bump chromium in DEPS to 80.0.3987.78

* Update patches

* chore: bump chromium in DEPS to 80.0.3987.79

Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-01-30 15:55:47 -05:00
Electron Bot
80282ba972 Bump v8.0.0-beta.9 2020-01-29 18:23:35 -08:00
Erick Zhao
7ddb3dd184 fix: add executable to ChromeDriver's rpath for electron 8+ (#21966) 2020-01-29 18:14:12 -08:00
trop[bot]
0cee5cc1c1 fix: ensure web_contents is not nullptr in UpdateDraggableRegions (#21965)
* fix: ensure web_contents is not nullptr in UpdateDraggableRegions

This is a speculative fix for a crash in `UpdateDraggableRegions` that we've noticed

* Update atom_api_browser_window_mac.mm

* Update atom_api_browser_window_mac.mm

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-01-29 17:43:55 -08:00
Jeremy Apthorp
048d770a0e fix: show module name in deprecation warning for context-aware modules (#21958) 2020-01-29 17:29:06 -08:00
Robo
b3fdb242f0 fix: compilation of native modules on windows with older msvc versions (#21950) (#21960) 2020-01-29 17:13:07 -08:00
Electron Bot
38d908ee6a Bump v8.0.0-beta.8 2020-01-29 11:22:54 -08:00
Alexey Kuzmin
416b850dd2 chore: fix linter errors in .mm files (#21933) 2020-01-29 17:04:14 +00:00
Erick Zhao
714e068cba refactor: try just using regular [Sync] for MessageSync (#20797) (#21948)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-01-29 07:11:32 -05:00
trop[bot]
0a0689587a chore: update build_bring_back_node_with_ltcg_configuration.patch (#21947)
* chore: update build_bring_back_node_with_ltcg_configuration.patch

set default value for node_with_ltcg=true

* fix: move ltcg definition to Release configuration

Co-authored-by: Robo <hop2deep@gmail.com>
2020-01-28 22:42:53 -08:00
trop[bot]
83c4633b21 fix: use powerMonitor.on() only after app is ready (#21942) 2020-01-28 22:13:19 +00:00
Electron Bot
a33063c8a7 chore: bump chromium in DEPS to 80.0.3987.75 (#21932) 2020-01-28 15:15:07 -05:00
trop[bot]
c478d45713 fix: ensure tray icon is the proper size on linux (#21936) 2020-01-28 18:15:22 +00:00
trop[bot]
74686a9cb3 fix: About Panel credits should be dark mode aware (#21926)
* fix: about panel credits should be dark mode aware

* use textColor for automatic adaptability

Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-01-28 17:05:04 +09:00
Shelley Vohr
473a90f99f fix: window.print() only working once (#21908) 2020-01-28 02:11:05 +00:00
trop[bot]
15e9f22fda docs: clean up context bridge API docs (#21917) 2020-01-28 02:01:44 +00:00
Electron Bot
ee44155f7a chore: bump chromium to 80.0.3987.74 (8-x-y) (#21886) 2020-01-27 23:08:46 +00:00
Shelley Vohr
bf64b34475 fix: recursive printing crash (#21905) 2020-01-27 21:18:02 +00:00
trop[bot]
26dbd2218e fix: menu not updating with Tray.setContextMenu (#21902)
Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-01-27 15:48:02 +09:00
trop[bot]
38e46abf0b Update browser-window.md (#21901)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-01-27 14:56:03 +09:00
Electron Bot
67f7bf4a23 chore: bump chromium to 80.0.3987.69 (8-x-y) (#21863)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-01-23 10:38:06 -08:00
trop[bot]
02f040c765 docs: added info on bookmark return values for securityScopedBookmarks (#21874) 2020-01-22 18:37:32 -08:00
trop[bot]
9b841e4594 fix: Windows checkboxChecked edge case (#21861) 2020-01-22 18:36:03 -08:00
trop[bot]
a40957f2bc fix: some websites using WebComponents V0 not loading (#21866)
Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-01-23 09:38:21 +09:00
trop[bot]
a6121f527c fix: call SetCanActivate in setFocusable (#21854)
Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-01-22 14:45:27 +09:00
Cheng Zhao
71a31d553f fix: crash when doing redirect navigation with webRequest listener (8-x-y) (#21841)
* fix: pass navigation_ui_data to proxying factory

* fix: clone response instead of move in redirect
2020-01-22 10:31:19 +09:00
Electron Bot
1a309fd55f chore: bump chromium in DEPS to 80.0.3987.64 (#21848) 2020-01-21 11:41:13 -08:00
Electron Bot
73527e54c5 chore: bump chromium to 80.0.3987.63 (8-x-y) (#21832) 2020-01-20 19:09:53 +00:00
trop[bot]
2ef0827767 fix: crash when restoring minimized hidden window (#21821)
Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-01-20 15:43:32 +09:00
Electron Bot
76bcab07e6 chore: bump chromium in DEPS to 80.0.3987.61 (#21826) 2020-01-18 17:45:23 +00:00
Electron Bot
5a5b6abd2c chore: bump chromium in DEPS to 80.0.3987.60 (#21817) 2020-01-17 10:27:43 -08:00
Electron Bot
4ce5a48076 Bump v8.0.0-beta.7 2020-01-16 09:55:43 -08:00
Electron Bot
7b61e6044a chore: bump chromium in DEPS to 80.0.3987.59 (#21800) 2020-01-16 12:08:28 -05:00
trop[bot]
048f06c7f5 feat: add session.addWordToSpellCheckerDictionary to allow custom words in the dictionary (#21297)
* feat: add session.addWordToSpellCheckerDictionary to allow custom words in the dictionary

* Update session.md

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-01-16 09:47:58 -05:00
trop[bot]
06868c938f fix: printToPDF failing to generate PDF (#21797)
Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-01-16 09:39:26 -05:00
Electron Bot
22e8fc6379 chore: bump chromium in DEPS to 80.0.3987.58 (#21789) 2020-01-15 19:04:45 -05:00
trop[bot]
427c139eff fix: pass full response headers in net module (#21769)
* fix: pass full response headers in net module

* chore: put helper classes in annoymouse namespace

* fix: use hasOwnProperty to test key

* chore: shorter class name

Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-01-15 13:38:09 +09:00
Electron Bot
6a881520ee chore: bump chromium in DEPS to 80.0.3987.55 (#21765) 2020-01-14 06:56:22 -08:00
trop[bot]
5a2c451c0b fix: stream protocols not completing (#21759)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-01-14 16:09:46 +09:00
Robo
f47fbccf1a Fix memory leak in generator functions (#21760)
Backports https://chromium-review.googlesource.com/c/v8/v8/+/1967317
2020-01-14 16:08:41 +09:00
Electron Bot
eb419946ad Bump v8.0.0-beta.6 2020-01-13 10:25:18 -08:00
Electron Bot
1b204a4369 chore: bump chromium to 80.0.3987.51 (8-x-y) (#21738)
* chore: bump chromium in DEPS to 80.0.3987.49

* chore: bump chromium in DEPS to 80.0.3987.50

* chore: bump chromium in DEPS to 80.0.3987.51
2020-01-13 13:13:02 -05:00
Cheng Zhao
ee2de310f5 fix: disable private macOS APIs in MAS build except for CAContext/CALayerHost (8-x-y) (#21574)
* fix: add patch to disable remote accessibility APIs

* fix: add patch to disable private window frame APIs
2020-01-13 09:06:13 -05:00
trop[bot]
e81aa83a14 fix: don't fallback to OpenFolderViaShell (#21748)
Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-01-13 14:56:19 +09:00
trop[bot]
96cae44f32 fix: prefer occluded rather than unloading layout info (#21751)
Co-authored-by: loc <andy@slack-corp.com>
2020-01-13 14:55:05 +09:00
Milan Burda
757e7a91a9 fix: load window-setup in sandboxed renderer (#21416) (#21431) 2020-01-13 11:04:10 +09:00
trop[bot]
6faef05095 docs: responseHeaders should be Record<string, string[]> (#21743)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2020-01-13 09:51:59 +09:00
Shelley Vohr
ee65a190fa chore: deprecate visibleOnFullScreen option (#21732) 2020-01-13 09:48:16 +09:00
trop[bot]
da45f0341a fix: avoid contextBridge double free on garbage collection (#21735)
* fix: reset next/prev pointers for life-monitored nodes

* fix: don't double-delete nodes in a linked list

Co-authored-by: loc <andy@slack-corp.com>
2020-01-10 16:56:40 -08:00
Electron Bot
c6517c0b7f chore: bump chromium in DEPS to 80.0.3987.48 (#21728) 2020-01-10 11:47:38 -08:00
trop[bot]
2c92573978 fix: don't unnecessarily copy draggable regions (#21722) 2020-01-10 09:09:26 -08:00
trop[bot]
fa7326af64 fix: Notification crash in before-quit (#21720) 2020-01-10 09:08:24 -08:00
Electron Bot
aca96553fc chore: bump chromium to 80.0.3987.47 (8-x-y) (#21651)
* chore: bump chromium in DEPS to 80.0.3987.32

* chore: bump chromium in DEPS to 80.0.3987.33

* chore: bump chromium in DEPS to 80.0.3987.34

* chore: bump chromium in DEPS to 80.0.3987.36

* chore: bump chromium in DEPS to 80.0.3987.37

* chore: bump chromium in DEPS to 80.0.3987.38

* chore: bump chromium in DEPS to 80.0.3987.39

* chore: bump chromium in DEPS to 80.0.3987.40

* chore: bump chromium in DEPS to 80.0.3987.43

* chore: bump chromium in DEPS to 80.0.3987.47
2020-01-09 08:22:19 -08:00
trop[bot]
37592cdaee refactor: throw error for getLastCrashReport if crashReporter not started (#21685) 2020-01-07 09:15:50 -05:00
trop[bot]
64e48ad0e6 fix: SimpleURLLoaderWrapper redirects (#21566) (#21644) 2020-01-01 09:31:38 +09:00
trop[bot]
68566583f0 fix: highlight defaulted button correctly (#21653) 2019-12-31 08:42:07 -08:00
Electron Bot
6a56aa2240 chore: bump chromium to 80.0.3987.31 (8-x-y) (#21557)
* chore: bump chromium in DEPS to 80.0.3987.16

* chore: bump chromium in DEPS to 80.0.3987.18

* chore: bump chromium in DEPS to 80.0.3987.20

* chore: bump chromium in DEPS to 80.0.3987.21

* chore: bump chromium in DEPS to 80.0.3987.22

* chore: bump chromium in DEPS to 80.0.3987.23

* chore: bump chromium in DEPS to 80.0.3987.24

* chore: bump chromium in DEPS to 80.0.3987.25

* chore: bump chromium in DEPS to 80.0.3987.26

* chore: bump chromium in DEPS to 80.0.3987.27

* chore: bump chromium in DEPS to 80.0.3987.28

* chore: bump chromium in DEPS to 80.0.3987.29

* chore: bump chromium in DEPS to 80.0.3987.30

* chore: bump chromium in DEPS to 80.0.3987.31
2019-12-30 08:53:24 -08:00
trop[bot]
7e8c1108e0 fix: set enable_negotiate_port to false in allowNTLMCredentialsForDomains (#21580)
* fix: set enable_negotiate_port to false in allowNTLMCredentialsForDomains

* read commandline switch

Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2019-12-19 19:26:46 -08:00
trop[bot]
f5ce5f8218 build: free up macos disk space on Mac publish (#21570) 2019-12-18 16:40:09 -08:00
Electron Bot
28e44b31ca Bump very far --> this is electron bot fixing the release 2019-12-18 14:04:34 -08:00
Samuel Attard
606ca98df0 build: handle origin/X branch formats 2019-12-18 14:02:38 -08:00
Electron Bot
acd2eabdfd Bump v8.0.0-beta.5 2019-12-17 16:05:21 -08:00
Shelley Vohr
07972c2892 Revert "Bump v8.0.0-beta.6"
This reverts commit 70079d1c8a.
2019-12-17 15:47:28 -08:00
Electron Bot
70079d1c8a Bump v8.0.0-beta.6 2019-12-17 15:16:32 -08:00
trop[bot]
4def69876f fix: enforce parent-child relationship in custom postMessage() handler (#21512) 2019-12-17 15:08:36 -08:00
John Kleinschmidt
61a2c7bd16 Revert "Bump v8.0.0-beta.5"
This reverts commit 6c1eb46f68.
2019-12-17 13:52:56 -08:00
Shelley Vohr
f53262d58f fix: MediaKey globalShortcuts not working on macOS (#21547) 2019-12-17 13:36:47 -08:00
trop[bot]
8c15619905 docs: update webContents.printToPDF() example for promisified API (#21549) 2019-12-16 21:29:03 -08:00
trop[bot]
4a66bed930 refactor: export internalWindowOpen from guest-window-manager (#21530) 2019-12-16 13:55:17 -08:00
trop[bot]
8dabb3fe33 docs: update installation docs to reflect latest @electron/get changes (#21540) 2019-12-16 13:53:43 -08:00
Electron Bot
3f88934025 chore: bump chromium to 80.0.3987.14 (8-x-y) (#21520)
* chore: bump chromium in DEPS to 80.0.3987.12

* chore: bump chromium in DEPS to 80.0.3987.13

* chore: bump chromium in DEPS to 80.0.3987.14
2019-12-16 12:43:29 -05:00
trop[bot]
296dcc3405 fix: avoid contextBridge crash when RenderFrame address is reused (#21513)
* fix: avoid contextBridge crash when RenderFrame address is reused

Co-Authored-By: Jeremy Apthorp <nornagon@nornagon.net>

* make routing_id_ const
2019-12-16 11:01:20 +09:00
trop[bot]
5f6de7053a fix: quit after Chromium is fully started (#21507)
* fix: quit when chromium is fully started

* test: remove hacks on app.quit

* chore: RunUntilIdle is unnecessary
2019-12-16 09:45:45 +09:00
Electron Bot
6c1eb46f68 Bump v8.0.0-beta.5 2019-12-14 08:10:53 -08:00
Electron Bot
05cde0d945 chore: bump chromium to 80.0.3987.6 (8-x-y) (#21061) 2019-12-13 12:13:29 -08:00
trop[bot]
696c94c08a fix: sourcemaps not loading with network service (#21493)
Backports https://chromium-review.googlesource.com/c/chromium/src/+/1525270
Backports https://chromium-review.googlesource.com/c/chromium/src/+/1852212
2019-12-12 18:55:44 -08:00
trop[bot]
4284691cb3 fix: avoid Electron.dsym files in the main app bundle (#21484)
* ci: CHECK_DIST_MANIFEST in release builds

* fix: skip Electron.dSYM on macOS app zip
2019-12-11 13:16:56 -08:00
trop[bot]
2ab56802cb fix: restore POST forms that open a new window with target=_blank (#21474)
* fix: restore parts of original ResourceRequestBody V8 conversion

Restore some of the original conversion logic in order to fix target=_blank post form submissions.

* test: add test for POST form submission
2019-12-11 19:36:01 +09:00
Cheng Zhao
353d12b231 fix: name and expirationDate should be optional when setting cookie (#21454) (#21477)
* fix: correctly set cookie date

* fix: name is not required for setting cookie

* test: clear cookie after each cookie test

* test: should test session property

* chore: style fixes
2019-12-11 19:34:23 +09:00
trop[bot]
cbd734faf9 fix: restore accessibility window title on macOS (#21467)
Electron's `AtomNSWindow` implements `accessibilityAttributeValue` to
provide various accessibility info to the OS, including window titles.

Chromium 75 changed to Apple's newer accessibility API for window titles
in the super class that `AtomNSWindow` inherits from. macOS still
supports both the old and new style APIs, but it will prefer the new
style if it is implemented.  This means the Electron window title is
being ignored because the newer API at the Chromium level has taken
precedence.

By implementing the newer accessibility API in `AtomNSWindow`, this
restores correct accessibility window titles in macOS Electron apps.

This is a regression has been present since Electron 6.0.0 (the first
release including the Chromium change above).
2019-12-10 18:45:18 -08:00
trop[bot]
cf282c177f fix: window menu should handle keys correctly (#21452) 2019-12-10 10:08:15 -08:00
trop[bot]
e8c8afb1b4 fix: Fix compositor recycling when creating new BrowserView (#21399)
In #20829, we fixed compositor recycling when switching between
BrowserViews, but it turns out that there is one additional case that we
need to handle. When we create a completely new BrowserView instance, it
starts of as visible (even when it hasn't been added to the window),
which means that it will need its own compositor instead of using the
recycled compositor.

To fix this, lets make BrowserViews hidden by default until they're
added to the window. See also #19988. This is a potentially breaking
change given that the initial value of `document.visibilityState` will
now be `hidden`, but given the experimental status of BrowserViews, I
think this is a fine change to make. The old behavior can be restored
with `webPreferences: { show: true }`.

Notes: Fix compositor recycling when creating new BrowserView
2019-12-10 10:05:10 +09:00
trop[bot]
dd0d06448c fix: hiding window menu should work on startup (#21443)
* fix: menu visibility should not be overwritten on startup

* fix: removing menu for window without global menubar

* test: setMenu tests are not for mac
2019-12-10 09:29:00 +09:00
trop[bot]
3527fdc291 chore: workflows and pipeline state were split in the circle API (#21444) 2019-12-09 11:32:55 -08:00
trop[bot]
a6b9d68897 fix: fix ClientRequest.getUploadProgress (#21424) 2019-12-09 11:18:39 -08:00
trop[bot]
e1e7ec737f fix: pass noLink correctly on Windows (#21405) 2019-12-08 10:39:35 -08:00
trop[bot]
4fd03f59f4 docs: fix return type of getPrinters (#21421) 2019-12-08 10:38:17 -08:00
trop[bot]
e64e0f00ca fix: do not use messages after move (#21420) 2019-12-06 14:41:29 -08:00
trop[bot]
1bfead2f6b fix: ensure persistence store still exists when GC runs (#21418)
Fix a bad access crash that happens when a render frame is deleted (window closed) and garbage collection runs afterward.
2019-12-06 12:35:32 -08:00
Jacob
c79f1ee720 fix: prevent silent failure when DOM storage quota exceeded (#20899) (#21380)
* test: update DOM storage quota limits test

* fix: update dom_storage_limits.patch (fixes #13465)

The previous version of this patch did not include
changes required to circumvent the quota enforcement
performed by StorageAreaImpl. Consequently when
the quota was exceeded, things still "appeared to
work" at first but then would later fail silently.
That is, the cache would be updated but the backing
store would not.

This could be fixed by disabling the code below
(from `content/browser/dom_storage/storage_area_impl.cc`)
```
  // Only check quota if the size is increasing, this allows
  // shrinking changes to pre-existing maps that are over budget.
  if (new_item_size > old_item_size && new_storage_used > max_size_) {
    if (map_state_ == MapState::LOADED_KEYS_ONLY) {
      receivers_.ReportBadMessage(
          "The quota in browser cannot exceed when there is only one "
          "renderer.");
    } else {
      std::move(callback).Run(false);
    }
    return;
  }
```

However, since this seems to have some unintended side-effects
(see updated notes in dom_storage_limits.patch) it seems
more prudent to simply increase the quota to a larger
yet still reasonable size rather than attempt to circumvent
the storage quota altogether.
2019-12-04 12:27:43 -08:00
trop[bot]
e18c369e4a fix: deprecate setLayoutZoomLevelLimits (#21360) 2019-12-04 08:46:15 -08:00
trop[bot]
1953ab3bf2 chore: remove unused shell/common/crash_reporter/win/crash_service.cc (#21376) 2019-12-04 16:40:40 +09:00
Electron Bot
d998bf9fed Bump v8.0.0-beta.4 2019-12-03 16:37:39 -08:00
Electron Bot
40eff3a778 Revert "Bump v8.0.0-beta.4"
This reverts commit 9dd089fc56.
2019-12-03 16:17:17 -08:00
Electron Bot
9dd089fc56 Bump v8.0.0-beta.4 2019-12-03 16:11:22 -08:00
Electron Bot
5e2bd0e55f Revert "Bump v8.0.0-beta.4"
This reverts commit 95b8be4cc4.
2019-12-03 13:31:30 -08:00
Electron Bot
95b8be4cc4 Bump v8.0.0-beta.4 2019-12-03 13:28:04 -08:00
Jeremy Apthorp
d48f99fd6e ci: generate debug symbols on Linux (#21278)
* ci: generate debug symbols on Linux (#18676)

* kick ci
2019-12-03 11:03:51 -05:00
trop[bot]
3a9b934cc5 fix: backgroundThrottling rwh assignment (#21358)
* fix: backgroundThrottling rwh assignment

* fix: disable DOM timer throttling

* chore: fix typo
2019-12-02 15:42:44 -08:00
Electron Bot
768f372675 Revert "Bump v8.0.0-beta.4"
This reverts commit 9e3f0d5190.
2019-12-02 13:55:49 -08:00
Electron Bot
9e3f0d5190 Bump v8.0.0-beta.4 2019-12-02 13:53:37 -08:00
Electron Bot
5a297f409f Revert "Bump v8.0.0-beta.4"
This reverts commit 57335cea69.
2019-12-02 12:32:55 -08:00
Electron Bot
57335cea69 Bump v8.0.0-beta.4 2019-12-02 12:30:44 -08:00
trop[bot]
3331f51571 fix: ensure no node globals passively leak when nodeIntegration is disabled (#21355) 2019-12-02 12:27:51 -08:00
Robo
f122268ca8 fix: focus with OOPIF embedded inside <webview> (#21343)
Backports https://chromium-review.googlesource.com/c/chromium/src/+/1922650
2019-12-02 10:26:35 -08:00
Robo
bfc817fd28 build: fix building with enable_builtin_spellchecker = false (#21334) (#21341) 2019-11-30 16:22:50 -08:00
trop[bot]
2bd83d0e89 fix: correctly plumb checkboxChecked on win (#21312)
* fix: correctly plumb checkboxChecked on win

* address final style comment
2019-11-29 17:23:43 +09:00
Jeremy Apthorp
f29f3418ed refactor: rewrite the net module to simplify state tracking (#21303)
* refactor: rewrite the net module to simplify state tracking (#21244)

* fix build

* Update atom_api_net.cc
2019-11-29 17:22:45 +09:00
trop[bot]
c87a0077dd build: disable strip_absolute_paths_from_debug_symbols on debug.gn (#21316) 2019-11-29 17:22:17 +09:00
Jeremy Apthorp
23c1dcea46 fix: restore --ignore-connections-limit functionality (#21286) (#21298) 2019-11-27 16:41:44 +09:00
Samuel Attard
41e64d2469 chore: add deprecation warning for the default of allowRendererProcessReuse (#21287)
* chore: add deprecation warning for the default of allowRendererProcessReuse

* Update web-contents.js
2019-11-26 15:48:35 -08:00
Jeremy Apthorp
5fdc24d3bf fix: record cpu_profiler data for main process (#21187) (#21276)
* fix: record cpu_profiler data for main process

* kick ci
2019-11-26 13:20:15 -08:00
Samuel Attard
64dc86c8c2 fix: allow reading body from non-2xx responses in net.request (#21055) (#21285)
* fix(urlrequest): allow non-2xx repsponse results

- closes #21046

* test(net): add test cases to verify non-2xx body

* test(session): update spec to match clientrequest behavior

* test(net): update test cases to match clientrequest behavior

* spec: clean up async net spec
2019-11-26 13:10:07 -08:00
Jeremy Apthorp
9d1ec6b0eb fix: implement 'login' event for net.ClientRequest (#21133)
* fix: implement 'login' event for net.ClientRequest (#21096)

* lint

* more lint

* whoops forgot patch

* fix compile

* fix ts

* i swear to god i already fixed this

* ugh

* asfdsafd

* disambiguate callback converter (i hope)

* Update atom_api_url_request_ns.cc

* use gin, not mate
2019-11-26 11:32:28 -08:00
trop[bot]
8919480ebc fix: reloadIgnoringCache() should ignore the cache (#21283) 2019-11-25 12:51:02 -08:00
trop[bot]
bb9e68beee fix: add missing early return (#21282) 2019-11-25 12:50:52 -08:00
trop[bot]
88c1f2caf7 feat: expose executeJavaScriptInIsolatedWorld on webContents (#21267)
* feat: expose executeJavaScriptInIsolatedWorld on webContents

* Apply suggestions from code review

Co-Authored-By: loc <andy@slack-corp.com>
2019-11-22 20:44:39 -08:00
Shelley Vohr
88375be2b2 fix: conversion of NativeImage from path (#21241) 2019-11-22 10:26:13 -08:00
trop[bot]
2aa69505f9 docs: remove string literal type from window events (#21245) 2019-11-21 14:50:02 -08:00
Electron Bot
e78fe7c8da Bump v8.0.0-beta.3 2019-11-20 11:24:28 -08:00
trop[bot]
7a7b944c74 build: update release build endpoint from /jobs to /job (#21233) 2019-11-20 14:23:13 -05:00
Electron Bot
09f5a2b741 Revert "Bump v8.0.0-beta.3"
This reverts commit cbf50eabd9.
2019-11-20 11:16:26 -08:00
Electron Bot
cbf50eabd9 Bump v8.0.0-beta.3 2019-11-20 11:14:52 -08:00
trop[bot]
90a74139c1 build: delete unneeded files when running a release (#21230)
* build: delete unneeded files when running a release

Needed to free up disk space on MacOS.

* Delete all the .git directories

* Update comment

* Run gn gen after deleting .git dirs
2019-11-20 14:04:25 -05:00
Cheng Zhao
3a6cc1b786 fix: menu should not be garbage-collected when popuping (8-x-y) (#21224)
* fix: retain menu when popuping

* test: menu should not be garbage-collected when popuping
2019-11-20 11:19:21 -05:00
trop[bot]
f5fde13b14 spec: skip flaky <webview>.capturePage() test on Windows (#21212) 2019-11-20 11:00:52 -05:00
John Kleinschmidt
c6d429d533 Revert "Bump v8.0.0-beta.3"
This reverts commit 269f4ba2bb.
2019-11-19 16:53:29 -05:00
Charles Kerr
73df925241 docs: document webkitdirectory breaking change (#21209) 2019-11-19 15:31:06 -05:00
Electron Bot
269f4ba2bb Bump v8.0.0-beta.3 2019-11-19 11:41:06 -08:00
Electron Bot
9b304beb54 Revert "Bump v8.0.0-beta.3"
This reverts commit 5ffad09e91.
2019-11-19 11:34:24 -08:00
Electron Bot
5ffad09e91 Bump v8.0.0-beta.3 2019-11-19 11:32:01 -08:00
John Kleinschmidt
b694315cd3 Revert "Bump v8.0.0-beta.3" (#21207)
This reverts commit 7fe2f25341.
2019-11-19 14:21:25 -05:00
trop[bot]
8566315902 fix: allow chromium to handle WM_NCCALCSIZE for frameless windows (#21201) 2019-11-19 14:18:50 -05:00
trop[bot]
cd94ab9de3 build: use python3 to download external binaries (#21202)
* build: use python3 to download external binaries

* Update config.py
2019-11-19 08:40:51 -08:00
trop[bot]
09ebadaf5b build: use symbol_level 1 for 32bit linux releases (#21204)
* build: use symbol_level 1 for 32bit linux releases

* Add comment
2019-11-19 08:14:34 -08:00
trop[bot]
990189ab35 fix: stream protocols sometimes flake out (#21180) 2019-11-19 06:31:07 -08:00
trop[bot]
049e536c5f docs: fix isMactemplateImage type definition (#21182) 2019-11-18 21:04:01 -08:00
Electron Bot
7fe2f25341 Bump v8.0.0-beta.3 2019-11-18 10:41:07 -08:00
Charles Kerr
c79809ddb9 build: missing include in windows release builds (#21132)
* fix: add missing `#include <algorithm>` as needed

Manual backport of #21045

* fix: add missing `#include <algorithm>` as needed

Manual backport of #21045

* chore: add patch to include missing `#include <memory>`

* chore: add another `#include <memory>` needed

* chore: regenerate patches w/correct breakpad root

* chore: regenerate breakpad include failure patches

* refactor: use --keep-cr in the git am patch script

We need something like this to patch files that have crlf endings. See
https://stackoverflow.com/questions/6289001/git-am-format-patch-control-format-of-line-endings

* chore: regenerate node patches

The udpated crlf support in git-{import,export}-patches caused a new
warning when applying patches from `electron/patches/nodes`, so refresh
the patches.

* chore: no need to regenerate node patches

* chore: silence whitespace warnings

* chore: fix FTBFS from stl features used but not included

* fixup! refactor: use --keep-cr in the git am patch script
2019-11-18 10:39:59 -08:00
Robo
b3edf86914 fix: incorrect size of windows on differently scaled monitors (#21139)
* Revert "fix: handle WM_GETMINMAXINFO instead of letting chromium do it (#19928)"

This reverts commit 27ce6a9cd3.

* fix: don't reset the width and height when correcting window placement
2019-11-15 09:28:47 -08:00
John Kleinschmidt
7044122f5d chore: Reset version for 8-x-y (#21134)
* Revert "Bump v8.0.0-beta.4"

This reverts commit 2560776888.

* Revert "Bump v8.0.0-beta.3"

This reverts commit d5d5fef931.
2019-11-14 16:34:57 -08:00
Jeremy Apthorp
b7bcce9576 fix: implement login event for WebContents (#21098)
* fix: implement login event for WebContents

* fix gin header path

* use mate

* correct path to native_mate/dictionary.h

* use BindRepeating bc apparently no BindOnce converter...?
2019-11-14 16:18:28 -08:00
Electron Bot
2560776888 Bump v8.0.0-beta.4 2019-11-14 10:20:46 -08:00
Electron Bot
d5d5fef931 Bump v8.0.0-beta.3 2019-11-14 09:50:16 -08:00
Cheng Zhao
6b158872fc fix: webRequest should be able to modify CORS headers (#21099) (#21122) 2019-11-14 12:45:25 -05:00
Andrew MacDonald
a4ef2d4356 feat: add app.getApplicationNameForProtocol API (#20399) (#21117)
* Add GetApplicationNameForProtocol.

* Fix Windows implementation.

* Fix up test.

* Add documentation.

* Implement for real on Linux using xdg-mime.

Also ensure we allow blocking calls here to avoid errant DCHECKing.

* Improve docs for Linux.

* Clean up tests.

* Add a note about not relying on the precise format.

* Update docs/api/app.md

Co-Authored-By: Shelley Vohr <codebytere@github.com>

* Remove needless `done()`s from tests.

* Use vector list initialization.

* Add a simple test for isDefaultProtocolClient.

* Remove unneeded include and skip a test on Linux CI.

* We no longer differentiate between CI and non-CI test runs.
2019-11-14 12:38:54 -05:00
Milan Burda
14cc902ad8 feat: add 'screen' to systemPreferences.getMediaAccessStatus() (#21116) 2019-11-14 15:15:24 +00:00
Milan Burda
a62a367b9f fix: NativeImage serialization of <webview>.capturePage() result (#21103)
* refactor: add Error to isSerializableObject() (#20886)

* fix: NativeImage serialization of <webview>.capturePage() result (#20825)
2019-11-14 10:36:22 +00:00
Shelley Vohr
8d67f16512 fix: refactor printing for mojo (#21059) 2019-11-13 17:43:38 +00:00
loc
59cb78e9aa fix: allow iframe-initiated HTML fullscreen to exit while in macOS fullscreen (8-x-y) (#21042)
* fix: explicitly resize the contents when exiting html fullscreen while in OS fullscreen

* test: ensure HTML fullscreen toggles while in OS fullscreen
2019-11-13 14:33:31 +09:00
Samuel Attard
032552df57 build: depend on chromium version not SHA (#20839) 2019-11-08 16:35:10 -08:00
trop[bot]
1913926ebc feat: deprecate <webview>.getWebContents() (#21039) 2019-11-07 15:31:54 -05:00
trop[bot]
dc979388ba fix: check for validity of guest webcontents (#21035) 2019-11-07 13:37:14 -05:00
trop[bot]
bcabc25b93 fix: Fix broken globalShortcuts.registerAll() on non-macOS platforms (#20984)
This was a regression in #16125, which unintentionally put
`GlobalShortcutListener::RegisterAccelerator` into a
`#if defined(OS_MACOSX)` block.

Notes: Fix broken `globalShortcut.registerAll()` on Windows and Linux
2019-11-06 23:01:51 -08:00
trop[bot]
ab6d22c958 docs: fix win.setIcon ts type (#20981)
* docs: fix win.setIcon ts type

* test: update smoke tests
2019-11-06 16:25:26 -08:00
trop[bot]
806925ee79 fix: proper i18n of recentDocuments item (#20956) 2019-11-06 13:57:42 -08:00
Samuel Attard
a46b50fc7b fix: correctly emplace optional values in the value converter (#20985)
* fix: correctly emplace optional values in the value converter

* chore: replace optional with nullopt when the conversion failed
2019-11-06 10:34:29 -08:00
trop[bot]
517a5915d7 chore: emit the document-start and document-end events in a sandboxed renderer (#20992) 2019-11-05 19:25:10 -08:00
trop[bot]
1de38af8cc chore: upgrade ts generator for better type safety (#20976)
* chore: upgrade ts generator for better type safety

* spec: fix tests
2019-11-05 15:20:08 -08:00
trop[bot]
66a95db3df docs: update installation instructions for proxies (#20979) 2019-11-05 13:45:14 -08:00
trop[bot]
7b779c6e6a fix: cannot access nativeTheme via electron.remote (#20953) 2019-11-04 16:00:28 -08:00
trop[bot]
429dfd7054 fix: don't export __esModule = true by electron.ts (#20952) 2019-11-04 16:00:03 -08:00
Samuel Attard
03f7a85cfb fix: capture the promise global to avoid userland mutation (#20925) (#20946) 2019-11-04 15:58:28 -08:00
trop[bot]
2d2a753dd9 fix: don't copy tray image when it's set (#20936) 2019-11-04 14:17:18 -08:00
trop[bot]
028e3889f0 fix: use Unicode version of ShellExecute() in OpenExternalOnWorkerThread() (#20906) 2019-11-01 18:25:29 -07:00
trop[bot]
47eb123649 build: lengthen wait times and retries for CircleCI releases (#20894)
* build: lengthen wait times and retries for CircleCI releases

* Review suggestions

* build: allow CircleCI timeout and retry to be set via env variables (#20896)

* build: allow circleci timeout and retry to be set via env variables

* check for more statuses and run indefinitely

(cherry picked from commit 4240017cb6)
2019-11-01 14:17:24 -04:00
trop[bot]
45ab098079 build: enable sccache on windows (#20898)
* build: enable sccache on windows

* chore: temporarily disable the docs only check

* build: fix escaping in sccache path on windows

* Update appveyor.yml

* Update appveyor.yml

* Use sccache settings from CI

* Use Azure enabled sccache for Windows
2019-11-01 11:46:10 -04:00
Electron Bot
6cfc05ded2 Bump v8.0.0-beta.2 2019-10-31 14:37:20 -07:00
Samuel Attard
40e0e8e499 feat: enable builtin spellchecker (#20897)
* feat: enable builtin spellchecker (#20692)

* chore: add code required to use chromes spellchecker

* chore: fix linting

* chore: manifests needs buildflags now

* chore: add dictionarySuggestions to the context menu event when the spellchecker is active

* chore: enable by default for windows builds

* chore: add patch to remove incognito usage in the spellchecker

* chore: add dependencies on spellcheck common and flags

* chore: conditionally include spell check panel impl

* chore: fix deps for spellcheck feature flags

* chore: add patch for electron resources

* chore: add dependency on //components/language/core/browser

* chore: patches to make hunspell work on windows

* build: collect hunspell dictionaries into a zip file and publish

* chore: clean up patches

* chore: add docs and set spell checker url method

* chore: fix error handling

* chore: fix hash logic

* build: update hunspell filename generator

* fix: default spellchecker list to the current system locale if we can

* docs: document the language getter

* chore: patch IDS_ resources for linux builds

* feat: add spellcheck webpref flag to disable the builtin spellchecker

* chore: fix docs typo

* chore: clean up spellchecker impl as per feedback

* remove unneeded deps

* chore: disable spellcheck by default in web prefs
2019-10-31 14:35:38 -07:00
trop[bot]
beff8b8b51 docs: clean up performance checklist formatting (#20888)
* docs: fix list formatting in performance checklist

* docs: remove unused link ref
2019-10-31 09:52:36 -07:00
trop[bot]
311723396a fix: swapped labels on open/save gtkdialog (#20883) 2019-10-31 08:42:37 -07:00
trop[bot]
d39d75321a build: do not try to run non existent VSTS release builds (#20878) 2019-10-31 10:53:44 -04:00
Birunthan Mohanathas
55201d7db6 fix: Disable compositor recycling only for attached views (8-x-y) (#20847)
Backport of #20829

Notes: Fix flicker when switching between `BrowserView`s
2019-10-30 16:52:33 -04:00
trop[bot]
ac46d5b16e fix: devtools extensions not loading (#20842) 2019-10-30 09:16:32 -07:00
Milan Burda
a1fb069624 fix: pass frameId to v8Util.setRemoteCallbackFreer() (#20732) (#20813) 2019-10-30 14:35:18 +09:00
trop[bot]
3aa33dd220 fix: do not DCHECK production-necessary methods (#20836) 2019-10-29 14:49:12 -07:00
trop[bot]
9b74d0d54a fix: properly generate requestID in webContents.printToPDF() (#20802) 2019-10-29 15:39:41 +09:00
trop[bot]
018fc2ca46 fix: deprecation warnings in Electron code (#20805) 2019-10-29 15:38:25 +09:00
trop[bot]
e286b78df7 docs: the ipc main listener being removed can have args (#20807) 2019-10-29 15:34:44 +09:00
Shelley Vohr
f77bd19a70 fix: prevent menu gc during popup (#20785) 2019-10-29 13:21:08 +09:00
trop[bot]
cff63d32a0 chore: fix formatting of a few Python files (#20778) 2019-10-28 15:36:53 -04:00
trop[bot]
a8e7696674 ci: skip build on doc only changes (#20717)
* ci: skip build on doc only changes

* Try using exit codes on doc-only-change

* Fixup

* Fixup circleci doc-only check

* Update appveyor.yml

Co-Authored-By: Samuel Attard <sattard@slack-corp.com>

* Properly detect doc only change on Windows

* Flip exit code per review

* build: fix doc only change when there isn't a PR (#20749)

* build: fix doc only change when there isn't a PR

Fixes issue where CI was mistakenly marking a PR as a doc only change because the CI was kicked off before the PR was created.

(cherry picked from commit 73da4b7215)
2019-10-28 11:14:50 -04:00
trop[bot]
5fc689dc4e docs: fix process.getSystemVersion() type (#20767) 2019-10-27 11:21:00 -07:00
Electron Bot
895bdc0ee0 Bump v8.0.0-beta.1 2019-10-24 12:05:38 -07:00
Electron Bot
c99f1d317e Revert "Bump v8.0.0-beta.1"
This reverts commit 315e3e325d.
2019-10-23 16:07:43 -07:00
Electron Bot
315e3e325d Bump v8.0.0-beta.1 2019-10-23 16:04:33 -07:00
Samuel Attard
8acce4279b Revert "Bump v8.0.0-beta.1"
This reverts commit c8943cdc3c.
2019-10-23 16:03:04 -07:00
Electron Bot
c8943cdc3c Bump v8.0.0-beta.1 2019-10-23 13:15:01 -07:00
trop[bot]
f74f009648 build: handle -x-y format for getCurrentBranch (#20711) 2019-10-23 13:03:50 -07:00
927 changed files with 26221 additions and 20108 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,11 @@
"browser": true "browser": true
}, },
"rules": { "rules": {
"semi": ["error", "always"],
"no-var": "error", "no-var": "error",
"no-unused-vars": 0, "no-unused-vars": 0,
"no-global-assign": 0, "no-global-assign": 0,
"guard-for-in": 2,
"@typescript-eslint/no-unused-vars": ["error", { "@typescript-eslint/no-unused-vars": ["error", {
"vars": "all", "vars": "all",
"args": "after-used", "args": "after-used",

1
.gitattributes vendored
View File

@@ -1,3 +1,4 @@
# `git apply` and friends don't understand CRLF, even on windows. Force those # `git apply` and friends don't understand CRLF, even on windows. Force those
# files to be checked out with LF endings even if core.autocrlf is true. # files to be checked out with LF endings even if core.autocrlf is true.
*.patch text eol=lf *.patch text eol=lf
patches/**/.patches merge=union

105
BUILD.gn
View File

@@ -1,6 +1,7 @@
import("//build/config/locales.gni") import("//build/config/locales.gni")
import("//build/config/ui.gni") import("//build/config/ui.gni")
import("//build/config/win/manifest.gni") import("//build/config/win/manifest.gni")
import("//components/spellcheck/spellcheck_build_features.gni")
import("//content/public/app/mac_helpers.gni") import("//content/public/app/mac_helpers.gni")
import("//pdf/features.gni") import("//pdf/features.gni")
import("//printing/buildflags/buildflags.gni") import("//printing/buildflags/buildflags.gni")
@@ -21,6 +22,7 @@ import("buildflags/buildflags.gni")
import("electron_paks.gni") import("electron_paks.gni")
import("filenames.auto.gni") import("filenames.auto.gni")
import("filenames.gni") import("filenames.gni")
import("filenames.hunspell.gni")
if (is_mac) { if (is_mac) {
import("//build/config/mac/rules.gni") import("//build/config/mac/rules.gni")
@@ -147,7 +149,7 @@ webpack_build("electron_content_script_bundle") {
out_file = "$target_gen_dir/js2c/content_script_bundle.js" out_file = "$target_gen_dir/js2c/content_script_bundle.js"
} }
copy("atom_js2c_copy") { copy("electron_js2c_copy") {
sources = [ sources = [
"lib/common/asar.js", "lib/common/asar.js",
"lib/common/asar_init.js", "lib/common/asar_init.js",
@@ -157,12 +159,12 @@ copy("atom_js2c_copy") {
] ]
} }
action("atom_js2c") { action("electron_js2c") {
deps = [ deps = [
":atom_js2c_copy",
":electron_browser_bundle", ":electron_browser_bundle",
":electron_content_script_bundle", ":electron_content_script_bundle",
":electron_isolated_renderer_bundle", ":electron_isolated_renderer_bundle",
":electron_js2c_copy",
":electron_renderer_bundle", ":electron_renderer_bundle",
":electron_sandboxed_renderer_bundle", ":electron_sandboxed_renderer_bundle",
":electron_worker_bundle", ":electron_worker_bundle",
@@ -184,7 +186,7 @@ action("atom_js2c") {
inputs = sources + [ "//third_party/electron_node/tools/js2c.py" ] inputs = sources + [ "//third_party/electron_node/tools/js2c.py" ]
outputs = [ outputs = [
"$root_gen_dir/atom_natives.cc", "$root_gen_dir/electron_natives.cc",
] ]
script = "tools/js2c.py" script = "tools/js2c.py"
@@ -345,12 +347,13 @@ source_set("electron_lib") {
] ]
deps = [ deps = [
":atom_js2c", ":electron_js2c",
":electron_version_header", ":electron_version_header",
":manifests", ":manifests",
":resources", ":resources",
"buildflags", "buildflags",
"chromium_src:chrome", "chromium_src:chrome",
"chromium_src:chrome_spellchecker",
"native_mate", "native_mate",
"shell/common/api:mojo", "shell/common/api:mojo",
"//base:base_static", "//base:base_static",
@@ -358,12 +361,13 @@ source_set("electron_lib") {
"//chrome/app/resources:platform_locale_settings", "//chrome/app/resources:platform_locale_settings",
"//chrome/services/printing/public/mojom", "//chrome/services/printing/public/mojom",
"//components/certificate_transparency", "//components/certificate_transparency",
"//components/language/core/browser",
"//components/net_log", "//components/net_log",
"//components/network_hints/common", "//components/network_hints/browser",
"//components/network_hints/common:mojo_bindings",
"//components/network_hints/renderer", "//components/network_hints/renderer",
"//components/network_session_configurator/common", "//components/network_session_configurator/common",
"//components/prefs", "//components/prefs",
"//components/spellcheck/renderer",
"//components/viz/host", "//components/viz/host",
"//components/viz/service", "//components/viz/service",
"//content/public/browser", "//content/public/browser",
@@ -379,12 +383,10 @@ source_set("electron_lib") {
"//media/mojo/mojom", "//media/mojo/mojom",
"//net:extras", "//net:extras",
"//net:net_resources", "//net:net_resources",
"//net:net_with_v8",
"//ppapi/host", "//ppapi/host",
"//ppapi/proxy", "//ppapi/proxy",
"//ppapi/shared_impl", "//ppapi/shared_impl",
"//printing/buildflags", "//printing/buildflags",
"//services/audio/public/mojom:constants",
"//services/device/public/cpp/geolocation", "//services/device/public/cpp/geolocation",
"//services/device/public/mojom", "//services/device/public/mojom",
"//services/proxy_resolver:lib", "//services/proxy_resolver:lib",
@@ -396,7 +398,7 @@ source_set("electron_lib") {
"//third_party/electron_node:node_lib", "//third_party/electron_node:node_lib",
"//third_party/leveldatabase", "//third_party/leveldatabase",
"//third_party/libyuv", "//third_party/libyuv",
"//third_party/webrtc_overrides:init_webrtc", "//third_party/webrtc_overrides:webrtc_component",
"//third_party/widevine/cdm:headers", "//third_party/widevine/cdm:headers",
"//ui/base/idle", "//ui/base/idle",
"//ui/events:dom_keycode_converter", "//ui/events:dom_keycode_converter",
@@ -502,7 +504,7 @@ source_set("electron_lib") {
"shell/browser/ui/views/autofill_popup_view.h", "shell/browser/ui/views/autofill_popup_view.h",
] ]
if (is_mas_build) { if (is_mas_build) {
sources += [ "shell/browser/api/atom_api_app_mas.mm" ] sources += [ "shell/browser/api/electron_api_app_mas.mm" ]
sources -= [ sources -= [
"shell/browser/auto_updater_mac.mm", "shell/browser/auto_updater_mac.mm",
"shell/common/crash_reporter/crash_reporter_mac.h", "shell/common/crash_reporter/crash_reporter_mac.h",
@@ -546,6 +548,10 @@ source_set("electron_lib") {
] ]
sources += filenames.lib_sources_nss sources += filenames.lib_sources_nss
sources += [
"shell/browser/ui/gtk_util.cc",
"shell/browser/ui/gtk_util.h",
]
} }
if (is_win) { if (is_win) {
libs += [ "dwmapi.lib" ] libs += [ "dwmapi.lib" ]
@@ -621,27 +627,27 @@ source_set("electron_lib") {
deps += [ "//third_party/webrtc/modules/desktop_capture" ] deps += [ "//third_party/webrtc/modules/desktop_capture" ]
} }
sources += [ sources += [
"shell/browser/api/atom_api_desktop_capturer.cc", "shell/browser/api/electron_api_desktop_capturer.cc",
"shell/browser/api/atom_api_desktop_capturer.h", "shell/browser/api/electron_api_desktop_capturer.h",
] ]
} }
if (enable_view_api) { if (enable_view_api) {
sources += [ sources += [
"shell/browser/api/views/atom_api_box_layout.cc", "shell/browser/api/views/electron_api_box_layout.cc",
"shell/browser/api/views/atom_api_box_layout.h", "shell/browser/api/views/electron_api_box_layout.h",
"shell/browser/api/views/atom_api_button.cc", "shell/browser/api/views/electron_api_button.cc",
"shell/browser/api/views/atom_api_button.h", "shell/browser/api/views/electron_api_button.h",
"shell/browser/api/views/atom_api_label_button.cc", "shell/browser/api/views/electron_api_label_button.cc",
"shell/browser/api/views/atom_api_label_button.h", "shell/browser/api/views/electron_api_label_button.h",
"shell/browser/api/views/atom_api_layout_manager.cc", "shell/browser/api/views/electron_api_layout_manager.cc",
"shell/browser/api/views/atom_api_layout_manager.h", "shell/browser/api/views/electron_api_layout_manager.h",
"shell/browser/api/views/atom_api_md_text_button.cc", "shell/browser/api/views/electron_api_md_text_button.cc",
"shell/browser/api/views/atom_api_md_text_button.h", "shell/browser/api/views/electron_api_md_text_button.h",
"shell/browser/api/views/atom_api_resize_area.cc", "shell/browser/api/views/electron_api_resize_area.cc",
"shell/browser/api/views/atom_api_resize_area.h", "shell/browser/api/views/electron_api_resize_area.h",
"shell/browser/api/views/atom_api_text_field.cc", "shell/browser/api/views/electron_api_text_field.cc",
"shell/browser/api/views/atom_api_text_field.h", "shell/browser/api/views/electron_api_text_field.h",
] ]
} }
@@ -705,7 +711,6 @@ if (is_mac) {
public_deps += [ "//third_party/icu:icudata" ] public_deps += [ "//third_party/icu:icudata" ]
} }
if (v8_use_external_startup_data) { if (v8_use_external_startup_data) {
sources += [ "$root_out_dir/natives_blob.bin" ]
public_deps += [ "//v8" ] public_deps += [ "//v8" ]
if (use_v8_context_snapshot) { if (use_v8_context_snapshot) {
sources += [ "$root_out_dir/v8_context_snapshot.bin" ] sources += [ "$root_out_dir/v8_context_snapshot.bin" ]
@@ -824,6 +829,7 @@ if (is_mac) {
include_dirs = [ "." ] include_dirs = [ "." ]
sources = filenames.framework_sources sources = filenames.framework_sources
libs = []
if (enable_osr) { if (enable_osr) {
libs += [ "IOSurface.framework" ] libs += [ "IOSurface.framework" ]
@@ -857,7 +863,7 @@ if (is_mac) {
} }
defines = [ "HELPER_EXECUTABLE" ] defines = [ "HELPER_EXECUTABLE" ]
sources = filenames.app_sources sources = filenames.app_sources
sources += [ "shell/common/atom_constants.cc" ] sources += [ "shell/common/electron_constants.cc" ]
include_dirs = [ "." ] include_dirs = [ "." ]
info_plist = "shell/renderer/resources/mac/Info.plist" info_plist = "shell/renderer/resources/mac/Info.plist"
extra_substitutions = extra_substitutions =
@@ -978,7 +984,7 @@ if (is_mac) {
mac_app_bundle("electron_app") { mac_app_bundle("electron_app") {
output_name = electron_product_name output_name = electron_product_name
sources = filenames.app_sources sources = filenames.app_sources
sources += [ "shell/common/atom_constants.cc" ] sources += [ "shell/common/electron_constants.cc" ]
include_dirs = [ "." ] include_dirs = [ "." ]
deps = [ deps = [
":electron_app_framework_bundle_data", ":electron_app_framework_bundle_data",
@@ -1265,9 +1271,14 @@ template("dist_zip") {
"outputs", "outputs",
"testonly", "testonly",
]) ])
flatten = false
if (defined(invoker.flatten)) {
flatten = invoker.flatten
}
args = rebase_path(outputs + [ _runtime_deps_file ], root_build_dir) + [ args = rebase_path(outputs + [ _runtime_deps_file ], root_build_dir) + [
target_cpu, target_cpu,
target_os, target_os,
"$flatten",
] ]
} }
} }
@@ -1342,17 +1353,41 @@ dist_zip("electron_chromedriver_zip") {
] ]
} }
mksnapshot_deps = [
":licenses",
"//tools/v8_context_snapshot:v8_context_snapshot_generator",
"//v8:mksnapshot($v8_snapshot_toolchain)",
]
group("electron_mksnapshot") {
public_deps = mksnapshot_deps
}
dist_zip("electron_mksnapshot_zip") { dist_zip("electron_mksnapshot_zip") {
data_deps = [ data_deps = mksnapshot_deps
"//v8:mksnapshot($v8_snapshot_toolchain)",
"//tools/v8_context_snapshot:v8_context_snapshot_generator",
":licenses",
]
outputs = [ outputs = [
"$root_build_dir/mksnapshot.zip", "$root_build_dir/mksnapshot.zip",
] ]
} }
copy("hunspell_dictionaries") {
sources = hunspell_dictionaries + hunspell_licenses
outputs = [
"$target_gen_dir/electron_hunspell/{{source_file_part}}",
]
}
dist_zip("hunspell_dictionaries_zip") {
data_deps = [
":hunspell_dictionaries",
]
flatten = true
outputs = [
"$root_build_dir/hunspell_dictionaries.zip",
]
}
group("electron") { group("electron") {
public_deps = [ public_deps = [
":electron_app", ":electron_app",

6
DEPS
View File

@@ -11,7 +11,7 @@ gclient_gn_args = [
vars = { vars = {
'chromium_version': 'chromium_version':
'c3a0220e7bde049d599a8332b9b2785b0178be74', '80.0.3987.163',
'node_version': 'node_version':
'v12.13.0', 'v12.13.0',
'nan_version': 'nan_version':
@@ -114,7 +114,7 @@ hooks = [
'pattern': 'src/electron/script/update-external-binaries.py', 'pattern': 'src/electron/script/update-external-binaries.py',
'condition': 'download_external_binaries', 'condition': 'download_external_binaries',
'action': [ 'action': [
'python', 'python3',
'src/electron/script/update-external-binaries.py', 'src/electron/script/update-external-binaries.py',
], ],
}, },
@@ -152,3 +152,5 @@ hooks = [
recursedeps = [ recursedeps = [
'src', 'src',
] ]
# Touch DEPS to bust cache

View File

@@ -1 +1 @@
8.0.0-nightly.20191023 8.2.3

View File

@@ -29,7 +29,7 @@
version: 1.0.{build} version: 1.0.{build}
build_cloud: libcc-20 build_cloud: libcc-20
image: vs2017-15.9-10.0.18362 image: vs2019-16.3-10.0.18362
environment: environment:
GIT_CACHE_PATH: C:\Users\electron\libcc_cache GIT_CACHE_PATH: C:\Users\electron\libcc_cache
ELECTRON_OUT_DIR: Default ELECTRON_OUT_DIR: Default
@@ -50,6 +50,12 @@ build_script:
- ps: >- - ps: >-
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) { if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild
} else {
node script/yarn.js install --frozen-lockfile
if ($(node script/doc-only-change.js --prNumber=$env:APPVEYOR_PULL_REQUEST_NUMBER --prBranch=$env:APPVEYOR_REPO_BRANCH;$LASTEXITCODE -eq 0)) {
Write-warning "Skipping build for doc only change"; Exit-AppveyorBuild
}
} }
- echo "Building $env:GN_CONFIG build" - echo "Building $env:GN_CONFIG build"
- git config --global core.longpaths true - git config --global core.longpaths true
@@ -70,23 +76,49 @@ build_script:
--unmanaged --unmanaged
%GCLIENT_EXTRA_ARGS% %GCLIENT_EXTRA_ARGS%
"https://github.com/electron/electron" "https://github.com/electron/electron"
- gclient sync --with_branch_heads --with_tags --reset - gclient sync --with_branch_heads --with_tags --ignore_locks --break_repo_locks
- ps: >-
if ($env:SAVE_GCLIENT_SRC -eq 'true') {
# archive current source for future use
# only run on x64/woa to avoid contention saving
if ($(7z a $zipfile src -xr!android_webview -xr!electron -xr'!*\.git' -xr!third_party\WebKit\LayoutTests! -xr!third_party\blink\web_tests -xr!third_party\blink\perf_tests -slp -t7z -mmt=30;$LASTEXITCODE -ne 0)) {
Write-warning "Could not save source to shared drive; continuing anyway"
}
}
- ps: >-
if ($env:GN_CONFIG -ne 'release') {
if (Test-Path 'env:RAW_GOMA_AUTH') {
$env:GOMA_OAUTH2_CONFIG_FILE = "$pwd\.goma_oauth2_config"
$env:RAW_GOMA_AUTH | Set-Content $env:GOMA_OAUTH2_CONFIG_FILE
}
git clone https://github.com/electron/build-tools.git
cd build-tools
npm install
mkdir third_party
node -e "require('./src/utils/goma.js').downloadAndPrepare()"
$env:GN_GOMA_FILE = node -e "console.log(require('./src/utils/goma.js').gnFilePath)"
$env:LOCAL_GOMA_DIR = node -e "console.log(require('./src/utils/goma.js').dir)"
cd ..
.\src\electron\script\start-goma.ps1 -gomaDir $env:LOCAL_GOMA_DIR
}
- cd src - cd src
- ps: $env:BUILD_CONFIG_PATH="//electron/build/args/%GN_CONFIG%.gn" - set BUILD_CONFIG_PATH=//electron/build/args/%GN_CONFIG%.gn
- gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") %GN_EXTRA_ARGS%" - if DEFINED GN_GOMA_FILE (gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") import(\"%GN_GOMA_FILE%\") %GN_EXTRA_ARGS% ") else (gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") %GN_EXTRA_ARGS% cc_wrapper=\"%SCCACHE_PATH%\"")
- gn check out/Default //electron:electron_lib - gn check out/Default //electron:electron_lib
- gn check out/Default //electron:electron_app - gn check out/Default //electron:electron_app
- gn check out/Default //electron:manifests - gn check out/Default //electron:manifests
- gn check out/Default //electron/shell/common/api:mojo - gn check out/Default //electron/shell/common/api:mojo
- ninja -C out/Default electron:electron_app - if DEFINED GN_GOMA_FILE (ninja -j 300 -C out/Default electron:electron_app) else (ninja -C out/Default electron:electron_app)
- if "%GN_CONFIG%"=="testing" ( python C:\Users\electron\depot_tools\post_build_ninja_summary.py -C out\Default ) - if "%GN_CONFIG%"=="testing" ( python C:\Users\electron\depot_tools\post_build_ninja_summary.py -C out\Default )
- gn gen out/ffmpeg "--args=import(\"//electron/build/args/ffmpeg.gn\") %GN_EXTRA_ARGS%" - gn gen out/ffmpeg "--args=import(\"//electron/build/args/ffmpeg.gn\") %GN_EXTRA_ARGS%"
- ninja -C out/ffmpeg electron:electron_ffmpeg_zip - ninja -C out/ffmpeg electron:electron_ffmpeg_zip
- ninja -C out/Default electron:electron_dist_zip - ninja -C out/Default electron:electron_dist_zip
- ninja -C out/Default shell_browser_ui_unittests - ninja -C out/Default shell_browser_ui_unittests
- ninja -C out/Default electron:electron_mksnapshot_zip - ninja -C out/Default electron:electron_mksnapshot_zip
- ninja -C out/Default electron:hunspell_dictionaries_zip
- ninja -C out/Default electron:electron_chromedriver_zip - ninja -C out/Default electron:electron_chromedriver_zip
- ninja -C out/Default third_party/electron_node:headers - ninja -C out/Default third_party/electron_node:headers
- if "%GN_CONFIG%"=="testing" ( python %LOCAL_GOMA_DIR%\goma_ctl.py stat )
- appveyor PushArtifact out/Default/dist.zip - appveyor PushArtifact out/Default/dist.zip
- appveyor PushArtifact out/Default/shell_browser_ui_unittests.exe - appveyor PushArtifact out/Default/shell_browser_ui_unittests.exe
- appveyor PushArtifact out/Default/chromedriver.zip - appveyor PushArtifact out/Default/chromedriver.zip
@@ -94,6 +126,7 @@ build_script:
- 7z a node_headers.zip out\Default\gen\node_headers - 7z a node_headers.zip out\Default\gen\node_headers
- appveyor PushArtifact node_headers.zip - appveyor PushArtifact node_headers.zip
- appveyor PushArtifact out/Default/mksnapshot.zip - appveyor PushArtifact out/Default/mksnapshot.zip
- appveyor PushArtifact out/Default/hunspell_dictionaries.zip
- appveyor PushArtifact out/Default/electron.lib - appveyor PushArtifact out/Default/electron.lib
- ps: >- - ps: >-
if ($env:GN_CONFIG -eq 'release') { if ($env:GN_CONFIG -eq 'release') {

View File

@@ -1,5 +1,4 @@
is_electron_build = true is_electron_build = true
use_jumbo_build = true
root_extra_deps = [ "//electron" ] root_extra_deps = [ "//electron" ]
# Registry of NMVs --> https://github.com/nodejs/node/blob/master/doc/abi_version_registry.json # Registry of NMVs --> https://github.com/nodejs/node/blob/master/doc/abi_version_registry.json
@@ -9,6 +8,12 @@ v8_promise_internal_field_count = 1
v8_typed_array_max_size_in_heap = 0 v8_typed_array_max_size_in_heap = 0
v8_embedder_string = "-electron.0" v8_embedder_string = "-electron.0"
# TODO: this breaks native modules. See e.g. https://www.github.com/nodejs/node/pull/30463
# We can probably enable this as long as we make sure node native modules
# also build with the relevant #defines (V8_COMPRESS_POINTERS etc.)
v8_enable_pointer_compression = false
v8_enable_31bit_smis_on_64bit_arch = false
# TODO: this breaks mksnapshot # TODO: this breaks mksnapshot
v8_enable_snapshot_native_code_counters = false v8_enable_snapshot_native_code_counters = false
@@ -18,12 +23,8 @@ ffmpeg_branding = "Chrome"
enable_basic_printing = true enable_basic_printing = true
angle_enable_vulkan_validation_layers = false angle_enable_vulkan_validation_layers = false
dawn_enable_vulkan_validation_layers = false
is_cfi = false is_cfi = false
# TODO: Remove this and update CI to contain 10.14 SDK once enable_osr = true
# crbug.com/986701 is fixed.
mac_sdk_min = "10.13"
# TODO: disabled due to crashes. re-enable.
enable_osr = false

View File

@@ -5,4 +5,3 @@ is_debug = false
is_component_build = false is_component_build = false
is_component_ffmpeg = false is_component_ffmpeg = false
symbol_level = 1 symbol_level = 1
use_jumbo_build = true

View File

@@ -1,9 +1,50 @@
import("//build/config/mac/mac_sdk.gni") import("//build/config/mac/mac_sdk.gni")
# This is imported from /ios becuase this functionality was moved # Template to compile .xib and .storyboard files.
# after Chromium stopped using xib files for macOS menu functionality # (copied from src/build/config/ios/rules.gni)
# See https://chromium-review.googlesource.com/c/chromium/src/+/1648695 #
import("//build/config/ios/rules.gni") # Arguments
#
# sources:
# list of string, sources to compile
#
# ibtool_flags:
# (optional) list of string, additional flags to pass to the ibtool
template("compile_ib_files") {
action_foreach(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
assert(defined(invoker.sources),
"sources must be specified for $target_name")
assert(defined(invoker.output_extension),
"output_extension must be specified for $target_name")
ibtool_flags = []
if (defined(invoker.ibtool_flags)) {
ibtool_flags = invoker.ibtool_flags
}
_output_extension = invoker.output_extension
script = "//build/config/ios/compile_ib_files.py"
sources = invoker.sources
outputs = [
"$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension",
]
args = [
"--input",
"{{source}}",
"--output",
rebase_path(
"$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension",
root_build_dir),
]
args += ibtool_flags
}
}
# Template is copied here from Chromium but was removed in # Template is copied here from Chromium but was removed in
# https://chromium-review.googlesource.com/c/chromium/src/+/1637981 # https://chromium-review.googlesource.com/c/chromium/src/+/1637981

View File

@@ -74,7 +74,10 @@ module.exports = ({
global: ['@electron/internal/renderer/webpack-provider', '_global'], global: ['@electron/internal/renderer/webpack-provider', '_global'],
Buffer: ['@electron/internal/renderer/webpack-provider', 'Buffer'], Buffer: ['@electron/internal/renderer/webpack-provider', 'Buffer'],
}) })
] : []) ] : []),
new webpack.ProvidePlugin({
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise'],
}),
] ]
}) })
} }

View File

@@ -16,6 +16,10 @@ PATHS_TO_SKIP = [
'./libVkICD_mock_', #Skipping because these are outputs that we don't need './libVkICD_mock_', #Skipping because these are outputs that we don't need
'./VkICD_mock_', #Skipping because these are outputs that we don't need './VkICD_mock_', #Skipping because these are outputs that we don't need
# Skipping because its an output of create_bundle from //build/config/mac/rules.gni
# that we don't need
'Electron.dSYM',
# //chrome/browser:resources depends on this via # //chrome/browser:resources depends on this via
# //chrome/browser/resources/ssl/ssl_error_assistant, but we don't need to # //chrome/browser/resources/ssl/ssl_error_assistant, but we don't need to
# ship it. # ship it.
@@ -46,19 +50,19 @@ def execute(argv):
raise e raise e
def main(argv): def main(argv):
dist_zip, runtime_deps, target_cpu, target_os = argv dist_zip, runtime_deps, target_cpu, target_os, flatten_val = argv
should_flatten = flatten_val == "true"
dist_files = set() dist_files = set()
with open(runtime_deps) as f: with open(runtime_deps) as f:
for dep in f.readlines(): for dep in f.readlines():
dep = dep.strip() dep = dep.strip()
dist_files.add(dep) if not skip_path(dep, dist_zip, target_cpu):
if sys.platform == 'darwin': dist_files.add(dep)
if sys.platform == 'darwin' and not should_flatten:
execute(['zip', '-r', '-y', dist_zip] + list(dist_files)) execute(['zip', '-r', '-y', dist_zip] + list(dist_files))
else: else:
with zipfile.ZipFile(dist_zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as z: with zipfile.ZipFile(dist_zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as z:
for dep in dist_files: for dep in dist_files:
if skip_path(dep, dist_zip, target_cpu):
continue
if os.path.isdir(dep): if os.path.isdir(dep):
for root, dirs, files in os.walk(dep): for root, dirs, files in os.walk(dep):
for file in files: for file in files:
@@ -67,7 +71,7 @@ def main(argv):
basename = os.path.basename(dep) basename = os.path.basename(dep)
dirname = os.path.dirname(dep) dirname = os.path.dirname(dep)
arcname = os.path.join(dirname, 'chrome-sandbox') if basename == 'chrome_sandbox' else dep arcname = os.path.join(dirname, 'chrome-sandbox') if basename == 'chrome_sandbox' else dep
z.write(dep, arcname) z.write(dep, os.path.basename(arcname) if should_flatten else arcname)
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main(sys.argv[1:])) sys.exit(main(sys.argv[1:]))

View File

@@ -19,6 +19,7 @@ buildflag_header("buildflags") {
"ENABLE_TTS=$enable_tts", "ENABLE_TTS=$enable_tts",
"ENABLE_COLOR_CHOOSER=$enable_color_chooser", "ENABLE_COLOR_CHOOSER=$enable_color_chooser",
"ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions", "ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions",
"ENABLE_BUILTIN_SPELLCHECKER=$enable_builtin_spellchecker",
"ENABLE_PICTURE_IN_PICTURE=$enable_picture_in_picture", "ENABLE_PICTURE_IN_PICTURE=$enable_picture_in_picture",
"OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider", "OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider",
] ]

View File

@@ -33,4 +33,7 @@ declare_args() {
# Enable Chrome extensions support. # Enable Chrome extensions support.
enable_electron_extensions = false enable_electron_extensions = false
# Enable Spellchecker support
enable_builtin_spellchecker = true
} }

View File

@@ -3,6 +3,7 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//build/config/ui.gni") import("//build/config/ui.gni")
import("//components/spellcheck/spellcheck_build_features.gni")
import("//electron/buildflags/buildflags.gni") import("//electron/buildflags/buildflags.gni")
import("//printing/buildflags/buildflags.gni") import("//printing/buildflags/buildflags.gni")
import("//third_party/widevine/cdm/widevine.gni") import("//third_party/widevine/cdm/widevine.gni")
@@ -31,6 +32,8 @@ static_library("chrome") {
"//chrome/browser/icon_loader_win.cc", "//chrome/browser/icon_loader_win.cc",
"//chrome/browser/icon_manager.cc", "//chrome/browser/icon_manager.cc",
"//chrome/browser/icon_manager.h", "//chrome/browser/icon_manager.h",
"//chrome/browser/media/webrtc/system_media_capture_permissions_mac.h",
"//chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm",
"//chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc",
"//chrome/browser/net/chrome_mojo_proxy_resolver_factory.h", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.h",
"//chrome/browser/net/proxy_config_monitor.cc", "//chrome/browser/net/proxy_config_monitor.cc",
@@ -39,6 +42,8 @@ static_library("chrome") {
"//chrome/browser/net/proxy_service_factory.h", "//chrome/browser/net/proxy_service_factory.h",
"//chrome/browser/predictors/preconnect_manager.cc", "//chrome/browser/predictors/preconnect_manager.cc",
"//chrome/browser/predictors/preconnect_manager.h", "//chrome/browser/predictors/preconnect_manager.h",
"//chrome/browser/predictors/predictors_features.cc",
"//chrome/browser/predictors/predictors_features.h",
"//chrome/browser/predictors/proxy_lookup_client_impl.cc", "//chrome/browser/predictors/proxy_lookup_client_impl.cc",
"//chrome/browser/predictors/proxy_lookup_client_impl.h", "//chrome/browser/predictors/proxy_lookup_client_impl.h",
"//chrome/browser/predictors/resolve_host_client_impl.cc", "//chrome/browser/predictors/resolve_host_client_impl.cc",
@@ -225,3 +230,58 @@ static_library("chrome") {
] ]
} }
} }
# This source set is just so we don't have to depend on all of //chrome/browser
# You may have to add new files here during the upgrade if //chrome/browser/spellchecker
# gets more files
source_set("chrome_spellchecker") {
sources = []
deps = []
libs = []
if (enable_builtin_spellchecker) {
sources += [
"//chrome/browser/spellchecker/spell_check_host_chrome_impl.cc",
"//chrome/browser/spellchecker/spell_check_host_chrome_impl.h",
"//chrome/browser/spellchecker/spellcheck_custom_dictionary.cc",
"//chrome/browser/spellchecker/spellcheck_custom_dictionary.h",
"//chrome/browser/spellchecker/spellcheck_factory.cc",
"//chrome/browser/spellchecker/spellcheck_factory.h",
"//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc",
"//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h",
"//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc",
"//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.h",
"//chrome/browser/spellchecker/spellcheck_language_policy_handler.cc",
"//chrome/browser/spellchecker/spellcheck_language_policy_handler.h",
"//chrome/browser/spellchecker/spellcheck_service.cc",
"//chrome/browser/spellchecker/spellcheck_service.h",
]
if (has_spellcheck_panel) {
sources += [
"//chrome/browser/spellchecker/spell_check_panel_host_impl.cc",
"//chrome/browser/spellchecker/spell_check_panel_host_impl.h",
]
}
if (use_browser_spellchecker) {
sources += [
"//chrome/browser/spellchecker/spelling_request.cc",
"//chrome/browser/spellchecker/spelling_request.h",
]
}
deps += [
"//base:base_static",
"//components/language/core/browser",
"//components/spellcheck:buildflags",
"//components/sync",
]
}
public_deps = [
"//components/spellcheck/browser",
"//components/spellcheck/common",
"//components/spellcheck/renderer",
]
}

View File

@@ -37,10 +37,8 @@ net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
// public and private slot. // public and private slot.
// Redirect any slot usage to this persistent slot on Linux. // Redirect any slot usage to this persistent slot on Linux.
g_nss_cert_database = new net::NSSCertDatabase( g_nss_cert_database = new net::NSSCertDatabase(
crypto::ScopedPK11Slot( crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* public slot */,
crypto::GetPersistentNSSKeySlot()) /* public slot */, crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* private slot */);
crypto::ScopedPK11Slot(
crypto::GetPersistentNSSKeySlot()) /* private slot */);
} }
return g_nss_cert_database; return g_nss_cert_database;
} }

View File

@@ -56,7 +56,7 @@
#include <stddef.h> #include <stddef.h>
#include "shell/browser/browser.h" #include "shell/browser/browser.h"
#include "shell/common/atom_command_line.h" #include "shell/common/electron_command_line.h"
#include "base/base_paths.h" #include "base/base_paths.h"
#include "base/bind.h" #include "base/bind.h"
@@ -826,7 +826,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
return PROCESS_NONE; return PROCESS_NONE;
to_send.append(current_dir.value()); to_send.append(current_dir.value());
const std::vector<std::string>& argv = electron::AtomCommandLine::argv(); const std::vector<std::string>& argv = electron::ElectronCommandLine::argv();
for (std::vector<std::string>::const_iterator it = argv.begin(); for (std::vector<std::string>::const_iterator it = argv.begin();
it != argv.end(); ++it) { it != argv.end(); ++it) {
to_send.push_back(kTokenDelimiter); to_send.push_back(kTokenDelimiter);

View File

@@ -1,48 +1,50 @@
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron' import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron';
import * as path from 'path' import * as path from 'path';
let mainWindow: BrowserWindow | null = null let mainWindow: BrowserWindow | null = null;
app.allowRendererProcessReuse = true;
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
app.quit() app.quit();
}) });
function decorateURL (url: string) { function decorateURL (url: string) {
// safely add `?utm_source=default_app // safely add `?utm_source=default_app
const parsedUrl = new URL(url) const parsedUrl = new URL(url);
parsedUrl.searchParams.append('utm_source', 'default_app') parsedUrl.searchParams.append('utm_source', 'default_app');
return parsedUrl.toString() return parsedUrl.toString();
} }
// Find the shortest path to the electron binary // Find the shortest path to the electron binary
const absoluteElectronPath = process.execPath const absoluteElectronPath = process.execPath;
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath) const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath);
const electronPath = absoluteElectronPath.length < relativeElectronPath.length const electronPath = absoluteElectronPath.length < relativeElectronPath.length
? absoluteElectronPath ? absoluteElectronPath
: relativeElectronPath : relativeElectronPath;
const indexPath = path.resolve(app.getAppPath(), 'index.html') const indexPath = path.resolve(app.getAppPath(), 'index.html');
function isTrustedSender (webContents: Electron.WebContents) { function isTrustedSender (webContents: Electron.WebContents) {
if (webContents !== (mainWindow && mainWindow.webContents)) { if (webContents !== (mainWindow && mainWindow.webContents)) {
return false return false;
} }
const parsedUrl = new URL(webContents.getURL()) const parsedUrl = new URL(webContents.getURL());
const urlPath = process.platform === 'win32' const urlPath = process.platform === 'win32'
// Strip the prefixed "/" that occurs on windows // Strip the prefixed "/" that occurs on windows
? path.resolve(parsedUrl.pathname.substr(1)) ? path.resolve(parsedUrl.pathname.substr(1))
: parsedUrl.pathname : parsedUrl.pathname;
return parsedUrl.protocol === 'file:' && urlPath === indexPath return parsedUrl.protocol === 'file:' && urlPath === indexPath;
} }
ipcMain.handle('bootstrap', (event) => { ipcMain.handle('bootstrap', (event) => {
return isTrustedSender(event.sender) ? electronPath : null return isTrustedSender(event.sender) ? electronPath : null;
}) });
async function createWindow () { async function createWindow () {
await app.whenReady() await app.whenReady();
const options: Electron.BrowserWindowConstructorOptions = { const options: Electron.BrowserWindowConstructorOptions = {
width: 900, width: 900,
@@ -57,46 +59,46 @@ async function createWindow () {
}, },
useContentSize: true, useContentSize: true,
show: false show: false
} };
if (process.platform === 'linux') { if (process.platform === 'linux') {
options.icon = path.join(__dirname, 'icon.png') options.icon = path.join(__dirname, 'icon.png');
} }
mainWindow = new BrowserWindow(options) mainWindow = new BrowserWindow(options);
mainWindow.on('ready-to-show', () => mainWindow!.show()) mainWindow.on('ready-to-show', () => mainWindow!.show());
mainWindow.webContents.on('new-window', (event, url) => { mainWindow.webContents.on('new-window', (event, url) => {
event.preventDefault() event.preventDefault();
shell.openExternal(decorateURL(url)) shell.openExternal(decorateURL(url));
}) });
mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, done) => { mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, done) => {
const parsedUrl = new URL(webContents.getURL()) const parsedUrl = new URL(webContents.getURL());
const options: Electron.MessageBoxOptions = { const options: Electron.MessageBoxOptions = {
title: 'Permission Request', title: 'Permission Request',
message: `Allow '${parsedUrl.origin}' to access '${permission}'?`, message: `Allow '${parsedUrl.origin}' to access '${permission}'?`,
buttons: ['OK', 'Cancel'], buttons: ['OK', 'Cancel'],
cancelId: 1 cancelId: 1
} };
dialog.showMessageBox(mainWindow!, options).then(({ response }) => { dialog.showMessageBox(mainWindow!, options).then(({ response }) => {
done(response === 0) done(response === 0);
}) });
}) });
return mainWindow return mainWindow;
} }
export const loadURL = async (appUrl: string) => { export const loadURL = async (appUrl: string) => {
mainWindow = await createWindow() mainWindow = await createWindow();
mainWindow.loadURL(appUrl) mainWindow.loadURL(appUrl);
mainWindow.focus() mainWindow.focus();
} };
export const loadFile = async (appPath: string) => { export const loadFile = async (appPath: string) => {
mainWindow = await createWindow() mainWindow = await createWindow();
mainWindow.loadFile(appPath) mainWindow.loadFile(appPath);
mainWindow.focus() mainWindow.focus();
} };

View File

@@ -1,8 +1,8 @@
import { app, dialog } from 'electron' import { app, dialog } from 'electron';
import * as fs from 'fs' import * as fs from 'fs';
import * as path from 'path' import * as path from 'path';
import * as url from 'url' import * as url from 'url';
type DefaultAppOptions = { type DefaultAppOptions = {
file: null | string; file: null | string;
@@ -14,10 +14,10 @@ type DefaultAppOptions = {
modules: string[]; modules: string[];
} }
const Module = require('module') const Module = require('module');
// Parse command line options. // Parse command line options.
const argv = process.argv.slice(1) const argv = process.argv.slice(1);
const option: DefaultAppOptions = { const option: DefaultAppOptions = {
file: null, file: null,
@@ -27,50 +27,50 @@ const option: DefaultAppOptions = {
interactive: false, interactive: false,
abi: false, abi: false,
modules: [] modules: []
} };
let nextArgIsRequire = false let nextArgIsRequire = false;
for (const arg of argv) { for (const arg of argv) {
if (nextArgIsRequire) { if (nextArgIsRequire) {
option.modules.push(arg) option.modules.push(arg);
nextArgIsRequire = false nextArgIsRequire = false;
continue continue;
} else if (arg === '--version' || arg === '-v') { } else if (arg === '--version' || arg === '-v') {
option.version = true option.version = true;
break break;
} else if (arg.match(/^--app=/)) { } else if (arg.match(/^--app=/)) {
option.file = arg.split('=')[1] option.file = arg.split('=')[1];
break break;
} else if (arg === '--interactive' || arg === '-i' || arg === '-repl') { } else if (arg === '--interactive' || arg === '-i' || arg === '-repl') {
option.interactive = true option.interactive = true;
} else if (arg === '--test-type=webdriver') { } else if (arg === '--test-type=webdriver') {
option.webdriver = true option.webdriver = true;
} else if (arg === '--require' || arg === '-r') { } else if (arg === '--require' || arg === '-r') {
nextArgIsRequire = true nextArgIsRequire = true;
continue continue;
} else if (arg === '--abi' || arg === '-a') { } else if (arg === '--abi' || arg === '-a') {
option.abi = true option.abi = true;
continue continue;
} else if (arg === '--no-help') { } else if (arg === '--no-help') {
option.noHelp = true option.noHelp = true;
continue continue;
} else if (arg[0] === '-') { } else if (arg[0] === '-') {
continue continue;
} else { } else {
option.file = arg option.file = arg;
break break;
} }
} }
if (nextArgIsRequire) { if (nextArgIsRequire) {
console.error('Invalid Usage: --require [file]\n\n"file" is required') console.error('Invalid Usage: --require [file]\n\n"file" is required');
process.exit(1) process.exit(1);
} }
// Set up preload modules // Set up preload modules
if (option.modules.length > 0) { if (option.modules.length > 0) {
Module._preloadModules(option.modules) Module._preloadModules(option.modules);
} }
function loadApplicationPackage (packagePath: string) { function loadApplicationPackage (packagePath: string) {
@@ -79,102 +79,102 @@ function loadApplicationPackage (packagePath: string) {
configurable: false, configurable: false,
enumerable: true, enumerable: true,
value: true value: true
}) });
try { try {
// Override app name and version. // Override app name and version.
packagePath = path.resolve(packagePath) packagePath = path.resolve(packagePath);
const packageJsonPath = path.join(packagePath, 'package.json') const packageJsonPath = path.join(packagePath, 'package.json');
let appPath let appPath;
if (fs.existsSync(packageJsonPath)) { if (fs.existsSync(packageJsonPath)) {
let packageJson let packageJson;
try { try {
packageJson = require(packageJsonPath) packageJson = require(packageJsonPath);
} catch (e) { } catch (e) {
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`) showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`);
return return;
} }
if (packageJson.version) { if (packageJson.version) {
app.setVersion(packageJson.version) app.setVersion(packageJson.version);
} }
if (packageJson.productName) { if (packageJson.productName) {
app.name = packageJson.productName app.name = packageJson.productName;
} else if (packageJson.name) { } else if (packageJson.name) {
app.name = packageJson.name app.name = packageJson.name;
} }
appPath = packagePath appPath = packagePath;
} }
try { try {
const filePath = Module._resolveFilename(packagePath, module, true) const filePath = Module._resolveFilename(packagePath, module, true);
app._setDefaultAppPaths(appPath || path.dirname(filePath)) app._setDefaultAppPaths(appPath || path.dirname(filePath));
} catch (e) { } catch (e) {
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`) showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`);
return return;
} }
// Run the app. // Run the app.
Module._load(packagePath, module, true) Module._load(packagePath, module, true);
} catch (e) { } catch (e) {
console.error('App threw an error during load') console.error('App threw an error during load');
console.error(e.stack || e) console.error(e.stack || e);
throw e throw e;
} }
} }
function showErrorMessage (message: string) { function showErrorMessage (message: string) {
app.focus() app.focus();
dialog.showErrorBox('Error launching app', message) dialog.showErrorBox('Error launching app', message);
process.exit(1) process.exit(1);
} }
async function loadApplicationByURL (appUrl: string) { async function loadApplicationByURL (appUrl: string) {
const { loadURL } = await import('./default_app') const { loadURL } = await import('./default_app');
loadURL(appUrl) loadURL(appUrl);
} }
async function loadApplicationByFile (appPath: string) { async function loadApplicationByFile (appPath: string) {
const { loadFile } = await import('./default_app') const { loadFile } = await import('./default_app');
loadFile(appPath) loadFile(appPath);
} }
function startRepl () { function startRepl () {
if (process.platform === 'win32') { if (process.platform === 'win32') {
console.error('Electron REPL not currently supported on Windows') console.error('Electron REPL not currently supported on Windows');
process.exit(1) process.exit(1);
} }
// prevent quitting // prevent quitting
app.on('window-all-closed', () => {}) app.on('window-all-closed', () => {});
const repl = require('repl') const repl = require('repl');
repl.start('> ').on('exit', () => { repl.start('> ').on('exit', () => {
process.exit(0) process.exit(0);
}) });
} }
// Start the specified app if there is one specified in command line, otherwise // Start the specified app if there is one specified in command line, otherwise
// start the default app. // start the default app.
if (option.file && !option.webdriver) { if (option.file && !option.webdriver) {
const file = option.file const file = option.file;
const protocol = url.parse(file).protocol const protocol = url.parse(file).protocol;
const extension = path.extname(file) const extension = path.extname(file);
if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') { if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') {
loadApplicationByURL(file) loadApplicationByURL(file);
} else if (extension === '.html' || extension === '.htm') { } else if (extension === '.html' || extension === '.htm') {
loadApplicationByFile(path.resolve(file)) loadApplicationByFile(path.resolve(file));
} else { } else {
loadApplicationPackage(file) loadApplicationPackage(file);
} }
} else if (option.version) { } else if (option.version) {
console.log('v' + process.versions.electron) console.log('v' + process.versions.electron);
process.exit(0) process.exit(0);
} else if (option.abi) { } else if (option.abi) {
console.log(process.versions.modules) console.log(process.versions.modules);
process.exit(0) process.exit(0);
} else if (option.interactive) { } else if (option.interactive) {
startRepl() startRepl();
} else { } else {
if (!option.noHelp) { if (!option.noHelp) {
const welcomeMessage = ` const welcomeMessage = `
@@ -192,10 +192,10 @@ Options:
-i, --interactive Open a REPL to the main process. -i, --interactive Open a REPL to the main process.
-r, --require Module to preload (option can be repeated). -r, --require Module to preload (option can be repeated).
-v, --version Print the version. -v, --version Print the version.
-a, --abi Print the Node ABI version.` -a, --abi Print the Node ABI version.`;
console.log(welcomeMessage) console.log(welcomeMessage);
} }
loadApplicationByFile('index.html') loadApplicationByFile('index.html');
} }

View File

@@ -1,53 +1,53 @@
import { ipcRenderer, contextBridge } from 'electron' import { ipcRenderer, contextBridge } from 'electron';
async function getOcticonSvg (name: string) { async function getOcticonSvg (name: string) {
try { try {
const response = await fetch(`octicon/${name}.svg`) const response = await fetch(`octicon/${name}.svg`);
const div = document.createElement('div') const div = document.createElement('div');
div.innerHTML = await response.text() div.innerHTML = await response.text();
return div return div;
} catch { } catch {
return null return null;
} }
} }
async function loadSVG (element: HTMLSpanElement) { async function loadSVG (element: HTMLSpanElement) {
for (const cssClass of element.classList) { for (const cssClass of element.classList) {
if (cssClass.startsWith('octicon-')) { if (cssClass.startsWith('octicon-')) {
const icon = await getOcticonSvg(cssClass.substr(8)) const icon = await getOcticonSvg(cssClass.substr(8));
if (icon) { if (icon) {
for (const elemClass of element.classList) { for (const elemClass of element.classList) {
icon.classList.add(elemClass) icon.classList.add(elemClass);
} }
element.before(icon) element.before(icon);
element.remove() element.remove();
break break;
} }
} }
} }
} }
async function initialize () { async function initialize () {
const electronPath = await ipcRenderer.invoke('bootstrap') const electronPath = await ipcRenderer.invoke('bootstrap');
function replaceText (selector: string, text: string) { function replaceText (selector: string, text: string) {
const element = document.querySelector<HTMLElement>(selector) const element = document.querySelector<HTMLElement>(selector);
if (element) { if (element) {
element.innerText = text element.innerText = text;
} }
} }
replaceText('.electron-version', `Electron v${process.versions.electron}`) replaceText('.electron-version', `Electron v${process.versions.electron}`);
replaceText('.chrome-version', `Chromium v${process.versions.chrome}`) replaceText('.chrome-version', `Chromium v${process.versions.chrome}`);
replaceText('.node-version', `Node v${process.versions.node}`) replaceText('.node-version', `Node v${process.versions.node}`);
replaceText('.v8-version', `v8 v${process.versions.v8}`) replaceText('.v8-version', `v8 v${process.versions.v8}`);
replaceText('.command-example', `${electronPath} path-to-app`) replaceText('.command-example', `${electronPath} path-to-app`);
for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) { for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) {
loadSVG(element) loadSVG(element);
} }
} }
contextBridge.exposeInMainWorld('electronDefaultApp', { contextBridge.exposeInMainWorld('electronDefaultApp', {
initialize initialize
}) });

View File

@@ -314,10 +314,8 @@ Returns:
* `event` Event * `event` Event
* `webContents` [WebContents](web-contents.md) * `webContents` [WebContents](web-contents.md)
* `request` Object * `authenticationResponseDetails` Object
* `method` String
* `url` URL * `url` URL
* `referrer` URL
* `authInfo` Object * `authInfo` Object
* `isProxy` Boolean * `isProxy` Boolean
* `scheme` String * `scheme` String
@@ -325,8 +323,8 @@ Returns:
* `port` Integer * `port` Integer
* `realm` String * `realm` String
* `callback` Function * `callback` Function
* `username` String * `username` String (optional)
* `password` String * `password` String (optional)
Emitted when `webContents` wants to do basic auth. Emitted when `webContents` wants to do basic auth.
@@ -337,12 +335,16 @@ should prevent the default behavior with `event.preventDefault()` and call
```javascript ```javascript
const { app } = require('electron') const { app } = require('electron')
app.on('login', (event, webContents, request, authInfo, callback) => { app.on('login', (event, webContents, details, authInfo, callback) => {
event.preventDefault() event.preventDefault()
callback('username', 'secret') callback('username', 'secret')
}) })
``` ```
If `callback` is called without a username or password, the authentication
request will be cancelled and the authentication error will be returned to the
page.
### Event: 'gpu-info-update' ### Event: 'gpu-info-update'
Emitted whenever there is a GPU info update. Emitted whenever there is a GPU info update.
@@ -711,34 +713,34 @@ Clears the recent documents list.
### `app.setAsDefaultProtocolClient(protocol[, path, args])` ### `app.setAsDefaultProtocolClient(protocol[, path, args])`
* `protocol` String - The name of your protocol, without `://`. If you want your * `protocol` String - The name of your protocol, without `://`. For example,
app to handle `electron://` links, call this method with `electron` as the if you want your app to handle `electron://` links, call this method with
parameter. `electron` as the parameter.
* `path` String (optional) _Windows_ - Defaults to `process.execPath` * `path` String (optional) _Windows_ - The path to the Electron executable.
* `args` String[] (optional) _Windows_ - Defaults to an empty array Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Arguments passed to the executable.
Defaults to an empty array
Returns `Boolean` - Whether the call succeeded. Returns `Boolean` - Whether the call succeeded.
This method sets the current executable as the default handler for a protocol Sets the current executable as the default handler for a protocol (aka URI
(aka URI scheme). It allows you to integrate your app deeper into the operating scheme). It allows you to integrate your app deeper into the operating system.
system. Once registered, all links with `your-protocol://` will be opened with Once registered, all links with `your-protocol://` will be opened with the
the current executable. The whole link, including protocol, will be passed to current executable. The whole link, including protocol, will be passed to your
your application as a parameter. application as a parameter.
On Windows, you can provide optional parameters path, the path to your executable,
and args, an array of arguments to be passed to your executable when it launches.
**Note:** On macOS, you can only register protocols that have been added to **Note:** On macOS, you can only register protocols that have been added to
your app's `info.plist`, which can not be modified at runtime. You can however your app's `info.plist`, which cannot be modified at runtime. However, you can
change the file with a simple text editor or script during build time. change the file during build time via [Electron Forge][electron-forge],
Please refer to [Apple's documentation][CFBundleURLTypes] for details. [Electron Packager][electron-packager], or by editing `info.plist` with a text
editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details.
**Note:** In a Windows Store environment (when packaged as an `appx`) this API **Note:** In a Windows Store environment (when packaged as an `appx`) this API
will return `true` for all calls but the registry key it sets won't be accessible will return `true` for all calls but the registry key it sets won't be accessible
by other applications. In order to register your Windows Store application by other applications. In order to register your Windows Store application
as a default protocol handler you must [declare the protocol in your manifest](https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol). as a default protocol handler you must [declare the protocol in your manifest](https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol).
The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. The API uses the Windows Registry and `LSSetDefaultHandlerForURLScheme` internally.
### `app.removeAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_ ### `app.removeAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_
@@ -757,10 +759,8 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler.
* `path` String (optional) _Windows_ - Defaults to `process.execPath` * `path` String (optional) _Windows_ - Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Defaults to an empty array * `args` String[] (optional) _Windows_ - Defaults to an empty array
Returns `Boolean` Returns `Boolean` - Whether the current executable is the default handler for a
protocol (aka URI scheme).
This method checks if the current executable is the default handler for a protocol
(aka URI scheme). If so, it will return true. Otherwise, it will return false.
**Note:** On macOS, you can use this method to check if the app has been **Note:** On macOS, you can use this method to check if the app has been
registered as the default protocol handler for a protocol. You can also verify registered as the default protocol handler for a protocol. You can also verify
@@ -768,7 +768,22 @@ this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the
macOS machine. Please refer to macOS machine. Please refer to
[Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details. [Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details.
The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. The API uses the Windows Registry and `LSCopyDefaultHandlerForURLScheme` internally.
### `app.getApplicationNameForProtocol(url)`
* `url` String - a URL with the protocol name to check. Unlike the other
methods in this family, this accepts an entire URL, including `://` at a
minimum (e.g. `https://`).
Returns `String` - Name of the application handling the protocol, or an empty
string if there is no handler. For instance, if Electron is the default
handler of the URL, this could be `Electron` on Windows and Mac. However,
don't rely on the precise format which is not guaranteed to remain unchanged.
Expect a different format on Linux, possibly with a `.desktop` suffix.
This method returns the application name of the default handler for the protocol
(aka URI scheme) of a URL.
### `app.setUserTasks(tasks)` _Windows_ ### `app.setUserTasks(tasks)` _Windows_
@@ -1186,8 +1201,9 @@ Show the app's about panel options. These options can be overridden with `app.se
* `website` String (optional) _Linux_ - The app's website. * `website` String (optional) _Linux_ - The app's website.
* `iconPath` String (optional) _Linux_ _Windows_ - Path to the app's icon. On Linux, will be shown as 64x64 pixels while retaining aspect ratio. * `iconPath` String (optional) _Linux_ _Windows_ - Path to the app's icon. On Linux, will be shown as 64x64 pixels while retaining aspect ratio.
Set the about panel options. This will override the values defined in the app's Set the about panel options. This will override the values defined in the app's `.plist` file on MacOS. See the [Apple docs][about-panel-options] for more details. On Linux, values must be set in order to be shown; there are no defaults.
`.plist` file on MacOS. See the [Apple docs][about-panel-options] for more details. On Linux, values must be set in order to be shown; there are no defaults.
If you do not set `credits` but still wish to surface them in your app, AppKit will look for a file named "Credits.html", "Credits.rtf", and "Credits.rtfd", in that order, in the bundle returned by the NSBundle class method main. The first file found is used, and if none is found, the info area is left blank. See Apple [documentation](https://developer.apple.com/documentation/appkit/nsaboutpaneloptioncredits?language=objc) for more information.
### `app.isEmojiPanelSupported()` ### `app.isEmojiPanelSupported()`
@@ -1308,6 +1324,8 @@ A `Boolean` property that returns `true` if the app is packaged, `false` otherw
[dock-menu]:https://developer.apple.com/macos/human-interface-guidelines/menus/dock-menus/ [dock-menu]:https://developer.apple.com/macos/human-interface-guidelines/menus/dock-menus/
[tasks]:https://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [tasks]:https://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks
[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx
[electron-forge]: https://www.electronforge.io/
[electron-packager]: https://github.com/electron/electron-packager
[CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115 [CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115
[LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme [LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme
[handoff]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html [handoff]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html

View File

@@ -59,6 +59,55 @@ these kinds of objects will throw a 'could not be cloned' error.
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm [SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
### `<webview>.getWebContents()`
This API is implemented using the `remote` module, which has both performance
and security implications. Therefore its usage should be explicit.
```js
// Deprecated
webview.getWebContents()
// Replace with
const { remote } = require('electron')
remote.webContents.fromId(webview.getWebContentsId())
```
However, it is recommended to avoid using the `remote` module altogether.
```js
// main
const { ipcMain, webContents } = require('electron')
const getGuestForWebContents = function (webContentsId, contents) {
const guest = webContents.fromId(webContentsId)
if (!guest) {
throw new Error(`Invalid webContentsId: ${webContentsId}`)
}
if (guest.hostWebContents !== contents) {
throw new Error(`Access denied to webContents`)
}
return guest
}
ipcMain.handle('openDevTools', (event, webContentsId) => {
const guest = getGuestForWebContents(webContentsId, event.sender)
guest.openDevTools()
})
// renderer
const { ipcRenderer } = require('electron')
ipcRenderer.invoke('openDevTools', webview.getWebContentsId())
```
### `webFrame.setLayoutZoomLevelLimits()`
Chromium has removed support for changing the layout zoom level limits, and it
is beyond Electron's capacity to maintain it. The function will emit a warning
in Electron 8.x, and cease to exist in Electron 9.x. The layout zoom level
limits are now fixed at a minimum of 0.25 and a maximum of 5.0, as defined
[here](https://chromium.googlesource.com/chromium/src/+/938b37a6d2886bf8335fc7db792f1eb46c65b2ae/third_party/blink/common/page/page_zoom.cc#11).
## Planned Breaking API Changes (7.0) ## Planned Breaking API Changes (7.0)
### Node Headers URL ### Node Headers URL
@@ -103,7 +152,7 @@ const idleTime = getSystemIdleTime()
### webFrame Isolated World APIs ### webFrame Isolated World APIs
```js ```js
// Removed in Elecron 7.0 // Removed in Electron 7.0
webFrame.setIsolatedWorldContentSecurityPolicy(worldId, csp) webFrame.setIsolatedWorldContentSecurityPolicy(worldId, csp)
webFrame.setIsolatedWorldHumanReadableName(worldId, name) webFrame.setIsolatedWorldHumanReadableName(worldId, name)
webFrame.setIsolatedWorldSecurityOrigin(worldId, securityOrigin) webFrame.setIsolatedWorldSecurityOrigin(worldId, securityOrigin)
@@ -121,6 +170,40 @@ webFrame.setIsolatedWorldInfo(
This property was removed in Chromium 77, and as such is no longer available. This property was removed in Chromium 77, and as such is no longer available.
### `webkitdirectory` attribute for `<input type="file"/>`
The `webkitdirectory` property on HTML file inputs allows them to select folders.
Previous versions of Electron had an incorrect implementation where the `event.target.files`
of the input returned a `FileList` that returned one `File` corresponding to the selected folder.
As of Electron 7, that `FileList` is now list of all files contained within
the folder, similarly to Chrome, Firefox, and Edge
([link to MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory)).
As an illustration, take a folder with this structure:
```console
folder
├── file1
├── file2
└── file3
```
In Electron <=6, this would return a `FileList` with a `File` object for:
```console
path/to/folder
```
In Electron 7, this now returns a `FileList` with a `File` object for:
```console
/path/to/folder/file3
/path/to/folder/file2
/path/to/folder/file1
```
Note that `webkitdirectory` no longer exposes the path to the selected folder.
If you require the path to the selected folder rather than the folder contents,
see the `dialog.showOpenDialog` API ([link](https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowopendialogbrowserwindow-options)).
## Planned Breaking API Changes (6.0) ## Planned Breaking API Changes (6.0)
### `win.setMenu(null)` ### `win.setMenu(null)`

View File

@@ -229,6 +229,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
unless hovered over in the top left of the window. These custom buttons prevent unless hovered over in the top left of the window. These custom buttons prevent
issues with mouse events that occur with the standard window toolbar buttons. issues with mouse events that occur with the standard window toolbar buttons.
**Note:** This option is currently experimental. **Note:** This option is currently experimental.
* `trafficLightPosition` [Point](structures/point.md) (optional) - Set a custom position for the traffic light buttons. Can only be used with `titleBarStyle` set to `hidden`
* `fullscreenWindowTitle` Boolean (optional) - Shows the title in the * `fullscreenWindowTitle` Boolean (optional) - Shows the title in the
title bar in full screen mode on macOS for all `titleBarStyle` options. title bar in full screen mode on macOS for all `titleBarStyle` options.
Default is `false`. Default is `false`.
@@ -272,8 +273,6 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
OS-level sandbox and disabling the Node.js engine. This is not the same as OS-level sandbox and disabling the Node.js engine. This is not the same as
the `nodeIntegration` option and the APIs available to the preload script the `nodeIntegration` option and the APIs available to the preload script
are more limited. Read more about the option [here](sandbox-option.md). are more limited. Read more about the option [here](sandbox-option.md).
**Note:** This option is currently experimental and may change or be
removed in future Electron releases.
* `enableRemoteModule` Boolean (optional) - Whether to enable the [`remote`](remote.md) module. * `enableRemoteModule` Boolean (optional) - Whether to enable the [`remote`](remote.md) module.
Default is `true`. Default is `true`.
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the * `session` [Session](session.md#class-session) (optional) - Sets the session used by the
@@ -373,6 +372,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
consecutive dialog protection is triggered. If not defined the default consecutive dialog protection is triggered. If not defined the default
message would be used, note that currently the default message is in message would be used, note that currently the default message is in
English and not localized. English and not localized.
* `disableDialogs` Boolean (optional) - Whether to disable dialogs
completely. Overrides `safeDialogs`. Default is `false`.
* `navigateOnDragDrop` Boolean (optional) - Whether dragging and dropping a * `navigateOnDragDrop` Boolean (optional) - Whether dragging and dropping a
file or link onto the page causes a navigation. Default is `false`. file or link onto the page causes a navigation. Default is `false`.
* `autoplayPolicy` String (optional) - Autoplay policy to apply to * `autoplayPolicy` String (optional) - Autoplay policy to apply to
@@ -385,6 +386,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
* `accessibleTitle` String (optional) - An alternative title string provided only * `accessibleTitle` String (optional) - An alternative title string provided only
to accessibility tools such as screen readers. This string is not directly to accessibility tools such as screen readers. This string is not directly
visible to users. visible to users.
* `spellcheck` Boolean (optional) - Whether to enable the builtin spellchecker.
Default is `false`.
When setting minimum or maximum window size with `minWidth`/`maxWidth`/ When setting minimum or maximum window size with `minWidth`/`maxWidth`/
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from `minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
@@ -515,7 +518,7 @@ Emitted when the window is restored from a minimized state.
Returns: Returns:
* `event` Event * `event` Event
* `newBounds` [`Rectangle`](structures/rectangle.md) - Size the window is being resized to. * `newBounds` [Rectangle](structures/rectangle.md) - Size the window is being resized to.
Emitted before the window is resized. Calling `event.preventDefault()` will prevent the window from being resized. Emitted before the window is resized. Calling `event.preventDefault()` will prevent the window from being resized.
@@ -530,7 +533,7 @@ Emitted after the window has been resized.
Returns: Returns:
* `event` Event * `event` Event
* `newBounds` [`Rectangle`](structures/rectangle.md) - Location the window is being moved to. * `newBounds` [Rectangle](structures/rectangle.md) - Location the window is being moved to.
Emitted before the window is moved. On Windows, calling `event.preventDefault()` will prevent the window from being moved. Emitted before the window is moved. On Windows, calling `event.preventDefault()` will prevent the window from being moved.
@@ -623,6 +626,12 @@ Returns:
Emitted on 3-finger swipe. Possible directions are `up`, `right`, `down`, `left`. Emitted on 3-finger swipe. Possible directions are `up`, `right`, `down`, `left`.
The method underlying this event is built to handle older macOS-style trackpad swiping,
where the content on the screen doesn't move with the swipe. Most macOS trackpads are not
configured to allow this kind of swiping anymore, so in order for it to emit properly the
'Swipe between pages' preference in `System Preferences > Trackpad > More Gestures` must be
set to 'Swipe with two or three fingers'.
#### Event: 'rotate-gesture' _macOS_ #### Event: 'rotate-gesture' _macOS_
Returns: Returns:
@@ -1579,7 +1588,7 @@ Same as `webContents.showDefinitionForSelection()`.
#### `win.setIcon(icon)` _Windows_ _Linux_ #### `win.setIcon(icon)` _Windows_ _Linux_
* `icon` [NativeImage](native-image.md) * `icon` [NativeImage](native-image.md) | String
Changes window icon. Changes window icon.
@@ -1625,7 +1634,7 @@ Returns `Boolean` - Whether the menu bar is visible.
* `visible` Boolean * `visible` Boolean
* `options` Object (optional) * `options` Object (optional)
* `visibleOnFullScreen` Boolean (optional) _macOS_ - Sets whether * `visibleOnFullScreen` Boolean (optional) _macOS_ - Sets whether
the window should be visible above fullscreen windows the window should be visible above fullscreen windows _deprecated_
Sets whether the window should be visible on all workspaces. Sets whether the window should be visible on all workspaces.
@@ -1733,6 +1742,17 @@ will remove the vibrancy effect on the window.
Note that `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` have been Note that `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` have been
deprecated and will be removed in an upcoming version of macOS. deprecated and will be removed in an upcoming version of macOS.
#### `win.setTrafficLightPosition(position)` _macOS_
* `position` [Point](structures/point.md)
Set a custom position for the traffic light buttons. Can only be used with `titleBarStyle` set to `hidden`.
#### `win.getTrafficLightPosition()` _macOS_
Returns `Point` - The current position for the traffic light buttons. Can only be used with `titleBarStyle`
set to `hidden`.
#### `win.setTouchBar(touchBar)` _macOS_ _Experimental_ #### `win.setTouchBar(touchBar)` _macOS_ _Experimental_
* `touchBar` TouchBar | null * `touchBar` TouchBar | null

View File

@@ -22,6 +22,9 @@ which the request is associated.
with which the request is associated. Defaults to the empty string. The with which the request is associated. Defaults to the empty string. The
`session` option prevails on `partition`. Thus if a `session` is explicitly `session` option prevails on `partition`. Thus if a `session` is explicitly
specified, `partition` is ignored. specified, `partition` is ignored.
* `useSessionCookies` Boolean (optional) - Whether to send cookies with this
request from the provided session. This will make the `net` request's
cookie behavior match a `fetch` request. Default is `false`.
* `protocol` String (optional) - The protocol scheme in the form 'scheme:'. * `protocol` String (optional) - The protocol scheme in the form 'scheme:'.
Currently supported values are 'http:' or 'https:'. Defaults to 'http:'. Currently supported values are 'http:' or 'https:'. Defaults to 'http:'.
* `host` String (optional) - The server host provided as a concatenation of * `host` String (optional) - The server host provided as a concatenation of
@@ -32,8 +35,8 @@ the hostname and the port number 'hostname:port'.
* `redirect` String (optional) - The redirect mode for this request. Should be * `redirect` String (optional) - The redirect mode for this request. Should be
one of `follow`, `error` or `manual`. Defaults to `follow`. When mode is `error`, one of `follow`, `error` or `manual`. Defaults to `follow`. When mode is `error`,
any redirection will be aborted. When mode is `manual` the redirection will be any redirection will be aborted. When mode is `manual` the redirection will be
deferred until [`request.followRedirect`](#requestfollowredirect) is invoked. Listen for the [`redirect`](#event-redirect) event in cancelled unless [`request.followRedirect`](#requestfollowredirect) is invoked
this mode to get more details about the redirect request. synchronously during the [`redirect`](#event-redirect) event.
`options` properties such as `protocol`, `host`, `hostname`, `port` and `path` `options` properties such as `protocol`, `host`, `hostname`, `port` and `path`
strictly follow the Node.js model as described in the strictly follow the Node.js model as described in the
@@ -70,8 +73,8 @@ Returns:
* `port` Integer * `port` Integer
* `realm` String * `realm` String
* `callback` Function * `callback` Function
* `username` String * `username` String (optional)
* `password` String * `password` String (optional)
Emitted when an authenticating proxy is asking for user credentials. Emitted when an authenticating proxy is asking for user credentials.
@@ -136,8 +139,11 @@ Returns:
* `redirectUrl` String * `redirectUrl` String
* `responseHeaders` Record<String, String[]> * `responseHeaders` Record<String, String[]>
Emitted when there is redirection and the mode is `manual`. Calling Emitted when the server returns a redirect response (e.g. 301 Moved
[`request.followRedirect`](#requestfollowredirect) will continue with the redirection. Permanently). Calling [`request.followRedirect`](#requestfollowredirect) will
continue with the redirection. If this event is handled,
[`request.followRedirect`](#requestfollowredirect) must be called
**synchronously**, otherwise the request will be cancelled.
### Instance Properties ### Instance Properties
@@ -214,7 +220,8 @@ response object,it will emit the `aborted` event.
#### `request.followRedirect()` #### `request.followRedirect()`
Continues any deferred redirection request when the redirection mode is `manual`. Continues any pending redirection. Can only be called during a `'redirect'`
event.
#### `request.getUploadProgress()` #### `request.getUploadProgress()`

View File

@@ -28,13 +28,14 @@ window.electron.doThing()
### Main World ### Main World
The "Main World" is the javascript context that your main renderer code runs in. By default the page you load in your renderer The "Main World" is the JavaScript context that your main renderer code runs in. By default, the
executes code in this world. page you load in your renderer executes code in this world.
### Isolated World ### Isolated World
When `contextIsolation` is enabled in your `webPreferences` your `preload` scripts run in an "Isolated World". You can read more about When `contextIsolation` is enabled in your `webPreferences`, your `preload` scripts run in an
context isolation and what it affects in the [BrowserWindow](browser-window.md) docs. "Isolated World". You can read more about context isolation and what it affects in the
[security](../tutorial/security.md#3-enable-context-isolation-for-remote-content) docs.
## Methods ## Methods
@@ -50,12 +51,12 @@ The `contextBridge` module has the following methods:
### API Objects ### API Objects
The `api` object provided to [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api-experimental) must be an object The `api` object provided to [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api-experimental) must be an object
whose keys are strings and values are a `Function`, `String`, `Number`, `Array`, `Boolean` or another nested object that meets the same conditions. whose keys are strings and values are a `Function`, `String`, `Number`, `Array`, `Boolean`, or another nested object that meets the same conditions.
`Function` values are proxied to the other context and all other values are **copied** and **frozen**. I.e. Any data / primitives sent in `Function` values are proxied to the other context and all other values are **copied** and **frozen**. Any data / primitives sent in
the API object become immutable and updates on either side of the bridge do not result in an update on the other side. the API object become immutable and updates on either side of the bridge do not result in an update on the other side.
An example of a complex API object is shown below. An example of a complex API object is shown below:
```javascript ```javascript
const { contextBridge } = require('electron') const { contextBridge } = require('electron')
@@ -90,22 +91,22 @@ results in some key limitations that we've outlined below.
#### Parameter / Error / Return Type support #### Parameter / Error / Return Type support
Because parameters, errors and return values are **copied** when they are sent over the bridge there are only certain types that can be used. Because parameters, errors and return values are **copied** when they are sent over the bridge, there are only certain types that can be used.
At a high level if the type you want to use can be serialized and un-serialized into the same object it will work. A table of type support At a high level, if the type you want to use can be serialized and deserialized into the same object it will work. A table of type support
has been included below for completeness. has been included below for completeness:
| Type | Complexity | Parameter Support | Return Value Support | Limitations | | Type | Complexity | Parameter Support | Return Value Support | Limitations |
| ---- | ---------- | ----------------- | -------------------- | ----------- | | ---- | ---------- | ----------------- | -------------------- | ----------- |
| `String` | Simple | ✅ | ✅ | N/A | | `String` | Simple | ✅ | ✅ | N/A |
| `Number` | Simple | ✅ | ✅ | N/A | | `Number` | Simple | ✅ | ✅ | N/A |
| `Boolean` | Simple | ✅ | ✅ | N/A | | `Boolean` | Simple | ✅ | ✅ | N/A |
| `Object` | Complex | ✅ | ✅ | Keys must be supported "Simple" types in this table. Values must be supported in this table. Prototype modifications are dropped. Sending custom classes will copy values but not the prototype. | | `Object` | Complex | ✅ | ✅ | Keys must be supported using only "Simple" types in this table. Values must be supported in this table. Prototype modifications are dropped. Sending custom classes will copy values but not the prototype. |
| `Array` | Complex | ✅ | ✅ | Same limitations as the `Object` type | | `Array` | Complex | ✅ | ✅ | Same limitations as the `Object` type |
| `Error` | Complex | ✅ | ✅ | Errors that are thrown are also copied, this can result in the message and stack trace of the error changing slightly due to being thrown in a different context | | `Error` | Complex | ✅ | ✅ | Errors that are thrown are also copied, this can result in the message and stack trace of the error changing slightly due to being thrown in a different context |
| `Promise` | Complex | ✅ | ✅ | Promises are only proxied if they are a the return value or exact parameter. Promises nested in arrays or obejcts will be dropped. | | `Promise` | Complex | ✅ | ✅ | Promises are only proxied if they are the return value or exact parameter. Promises nested in arrays or objects will be dropped. |
| `Function` | Complex | ✅ | ✅ | Prototype modifications are dropped. Sending classes or constructors will not work. | | `Function` | Complex | ✅ | ✅ | Prototype modifications are dropped. Sending classes or constructors will not work. |
| [Cloneable Types](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) | Simple | ✅ | ✅ | See the linked document on cloneable types | | [Cloneable Types](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) | Simple | ✅ | ✅ | See the linked document on cloneable types |
| `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped | | `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped |
If the type you care about is not in the above table it is probably not supported. If the type you care about is not in the above table, it is probably not supported.

View File

@@ -91,7 +91,11 @@ The `desktopCapturer` module has the following methods:
Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured. Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured.
**Note** Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher,
which can detected by [`systemPreferences.getMediaAccessStatus`].
[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia [`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia
[`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-macos
## Caveats ## Caveats

View File

@@ -120,7 +120,7 @@ Returns `Promise<Object>` - Resolve with an object containing the following:
* `canceled` Boolean - whether or not the dialog was canceled. * `canceled` Boolean - whether or not the dialog was canceled.
* `filePaths` String[] - An array of file paths chosen by the user. If the dialog is cancelled this will be an empty array. * `filePaths` String[] - An array of file paths chosen by the user. If the dialog is cancelled this will be an empty array.
* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated. * `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated. (For return values, see [table here](#bookmarks-array).)
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal. The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
@@ -215,7 +215,7 @@ The `filters` specifies an array of file types that can be displayed, see
Returns `Promise<Object>` - Resolve with an object containing the following: Returns `Promise<Object>` - Resolve with an object containing the following:
* `canceled` Boolean - whether or not the dialog was canceled. * `canceled` Boolean - whether or not the dialog was canceled.
* `filePath` String (optional) - If the dialog is canceled, this will be `undefined`. * `filePath` String (optional) - If the dialog is canceled, this will be `undefined`.
* `bookmark` String (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present. * `bookmark` String (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present. (For return values, see [table here](#bookmarks-array).)
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal. The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
@@ -269,6 +269,7 @@ Shows a message box, it will block the process until the message box is closed.
It returns the index of the clicked button. It returns the index of the clicked button.
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal. The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
If `browserWindow` is not shown dialog will not be attached to it. In such case It will be displayed as independed window.
### `dialog.showMessageBox([browserWindow, ]options)` ### `dialog.showMessageBox([browserWindow, ]options)`
@@ -350,6 +351,17 @@ On Windows the options are more limited, due to the Win32 APIs used:
* The `browserWindow` argument is ignored since it is not possible to make * The `browserWindow` argument is ignored since it is not possible to make
this confirmation dialog modal. this confirmation dialog modal.
## Bookmarks array
`showOpenDialog`, `showOpenDialogSync`, `showSaveDialog`, and `showSaveDialogSync` will return a `bookmarks` array.
| Build Type | securityScopedBookmarks boolean | Return Type | Return Value |
|------------|---------------------------------|:-----------:|--------------------------------|
| macOS mas | True | Success | `['LONGBOOKMARKSTRING']` |
| macOS mas | True | Error | `['']` (array of empty string) |
| macOS mas | False | NA | `[]` (empty array) |
| non mas | any | NA | `[]` (empty array) |
## Sheets ## Sheets
On macOS, dialogs are presented as sheets attached to a window if you provide On macOS, dialogs are presented as sheets attached to a window if you provide

View File

@@ -44,7 +44,12 @@ Unsupported options are:
--use-openssl-ca --use-openssl-ca
``` ```
`NODE_OPTIONS` are explicitly disallowed in packaged apps. `NODE_OPTIONS` are explicitly disallowed in packaged apps, except for the following:
```sh
--max-http-header-size
--http-parser
```
### `GOOGLE_API_KEY` ### `GOOGLE_API_KEY`

View File

@@ -77,6 +77,7 @@ only the next time a message is sent to `channel`, after which it is removed.
* `channel` String * `channel` String
* `listener` Function * `listener` Function
* `...args` any[]
Removes the specified `listener` from the listener array for the specified Removes the specified `listener` from the listener array for the specified
`channel`. `channel`.

View File

@@ -329,9 +329,9 @@ can be called on empty images.
[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer [buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer
## Properties ### Instance Properties
### `nativeImage.isMacTemplateImage` _macOS_ #### `nativeImage.isMacTemplateImage` _macOS_
A `Boolean` property that determines whether the image is considered a [template image](https://developer.apple.com/documentation/appkit/nsimage/1520017-template). A `Boolean` property that determines whether the image is considered a [template image](https://developer.apple.com/documentation/appkit/nsimage/1520017-template).

View File

@@ -217,11 +217,15 @@ that all statistics are reported in Kilobytes.
Returns `String` - The version of the host operating system. Returns `String` - The version of the host operating system.
Examples: Example:
* `macOS` -> `10.13.6` ```js
* `Windows` -> `10.0.17763` const version = process.getSystemVersion()
* `Linux` -> `4.15.0-45-generic` console.log(version)
// On macOS -> '10.13.6'
// On Windows -> '10.0.17763'
// On Linux -> '4.15.0-45-generic'
```
**Note:** It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`. **Note:** It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`.

View File

@@ -389,9 +389,7 @@ which sends a `Buffer` as a response.
* `url` String * `url` String
* `method` String (optional) * `method` String (optional)
* `session` Session | null (optional) * `session` Session | null (optional)
* `uploadData` Object (optional) * `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional)
* `contentType` String - MIME type of the content.
* `data` String - Content to be sent.
* `completion` Function (optional) * `completion` Function (optional)
* `error` Error * `error` Error

View File

@@ -105,6 +105,45 @@ Returns:
Emitted when a render process requests preconnection to a URL, generally due to Emitted when a render process requests preconnection to a URL, generally due to
a [resource hint](https://w3c.github.io/resource-hints/). a [resource hint](https://w3c.github.io/resource-hints/).
#### Event: 'spellcheck-dictionary-initialized'
Returns:
* `event` Event
* `languageCode` String - The language code of the dictionary file
Emitted when a hunspell dictionary file has been successfully initialized. This
occurs after the file has been downloaded.
#### Event: 'spellcheck-dictionary-download-begin'
Returns:
* `event` Event
* `languageCode` String - The language code of the dictionary file
Emitted when a hunspell dictionary file starts downloading
#### Event: 'spellcheck-dictionary-download-success'
Returns:
* `event` Event
* `languageCode` String - The language code of the dictionary file
Emitted when a hunspell dictionary file has been successfully downloaded
#### Event: 'spellcheck-dictionary-download-failure'
Returns:
* `event` Event
* `languageCode` String - The language code of the dictionary file
Emitted when a hunspell dictionary file download fails. For details
on the failure you should collect a netlog and inspect the download
request.
### Instance Methods ### Instance Methods
The following methods are available on instances of `Session`: The following methods are available on instances of `Session`:
@@ -456,10 +495,57 @@ this session just before normal `preload` scripts run.
Returns `String[]` an array of paths to preload scripts that have been Returns `String[]` an array of paths to preload scripts that have been
registered. registered.
#### `ses.setSpellCheckerLanguages(languages)`
* `languages` String[] - An array of language codes to enable the spellchecker for.
The built in spellchecker does not automatically detect what language a user is typing in. In order for the
spell checker to correctly check their words you must call this API with an array of language codes. You can
get the list of supported language codes with the `ses.availableSpellCheckerLanguages` property.
**Note:** On macOS the OS spellchecker is used and will detect your language automatically. This API is a no-op on macOS.
#### `ses.getSpellCheckerLanguages()`
Returns `String[]` - An array of language codes the spellchecker is enabled for. If this list is empty the spellchecker
will fallback to using `en-US`. By default on launch if this setting is an empty list Electron will try to populate this
setting with the current OS locale. This setting is persisted across restarts.
**Note:** On macOS the OS spellchecker is used and has it's own list of languages. This API is a no-op on macOS.
#### `ses.setSpellCheckerDictionaryDownloadURL(url)`
* `url` String - A base URL for Electron to download hunspell dictionaries from.
By default Electron will download hunspell dictionaries from the Chromium CDN. If you want to override this
behavior you can use this API to point the dictionary downloader at your own hosted version of the hunspell
dictionaries. We publish a `hunspell_dictionaries.zip` file with each release which contains the files you need
to host here, the file server must be **case insensitive** you must upload each file twice, once with the case it
has in the ZIP file and once with the filename as all lower case.
If the files present in `hunspell_dictionaries.zip` are available at `https://example.com/dictionaries/language-code.bdic`
then you should call this api with `ses.setSpellCheckerDictionaryDownloadURL('https://example.com/dictionaries/')`. Please
note the trailing slash. The URL to the dictionaries is formed as `${url}${filename}`.
**Note:** On macOS the OS spellchecker is used and therefore we do not download any dictionary files. This API is a no-op on macOS.
#### `ses.addWordToSpellCheckerDictionary(word)`
* `word` String - The word you want to add to the dictionary
Returns `Boolean` - Whether the word was successfully written to the custom dictionary.
**Note:** On macOS and Windows 10 this word will be written to the OS custom dictionary as well
### Instance Properties ### Instance Properties
The following properties are available on instances of `Session`: The following properties are available on instances of `Session`:
#### `ses.availableSpellCheckerLanguages` _Readonly_
A `String[]` array which consists of all the known available spell checker languages. Providing a language
code to the `setSpellCheckerLanaguages` API that isn't in this array will result in an error.
#### `ses.cookies` _Readonly_ #### `ses.cookies` _Readonly_
A [`Cookies`](cookies.md) object for this session. A [`Cookies`](cookies.md) object for this session.

View File

@@ -1,4 +1,4 @@
# ProtocolResponseUploadData Object # ProtocolResponseUploadData Object
* `contentType` String - MIME type of the content. * `contentType` String - MIME type of the content.
* `data` String - Content to be sent. * `data` String | Buffer - Content to be sent.

View File

@@ -369,14 +369,6 @@ Returns `String` - Can be `dark`, `light` or `unknown`.
Gets the macOS appearance setting that is currently applied to your application, Gets the macOS appearance setting that is currently applied to your application,
maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc) maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc)
Please note that until Electron is built targeting the 10.14 SDK, your application's
`effectiveAppearance` will default to 'light' and won't inherit the OS preference. In
the interim in order for your application to inherit the OS preference you must set the
`NSRequiresAquaSystemAppearance` key in your apps `Info.plist` to `false`. If you are
using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode`
packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
for more details.
**[Deprecated](modernization/property-updates.md)** **[Deprecated](modernization/property-updates.md)**
### `systemPreferences.getAppLevelAppearance()` _macOS_ _Deprecated_ ### `systemPreferences.getAppLevelAppearance()` _macOS_ _Deprecated_
@@ -404,8 +396,6 @@ Returns `Boolean` - whether or not this device has the ability to use Touch ID.
**NOTE:** This API will return `false` on macOS systems older than Sierra 10.12.2. **NOTE:** This API will return `false` on macOS systems older than Sierra 10.12.2.
**[Deprecated](modernization/property-updates.md)**
### `systemPreferences.promptTouchID(reason)` _macOS_ ### `systemPreferences.promptTouchID(reason)` _macOS_
* `reason` String - The reason you are asking for Touch ID authentication * `reason` String - The reason you are asking for Touch ID authentication
@@ -434,11 +424,13 @@ Returns `Boolean` - `true` if the current process is a trusted accessibility cli
### `systemPreferences.getMediaAccessStatus(mediaType)` _macOS_ ### `systemPreferences.getMediaAccessStatus(mediaType)` _macOS_
* `mediaType` String - `microphone` or `camera`. * `mediaType` String - Can be `microphone`, `camera` or `screen`.
Returns `String` - Can be `not-determined`, `granted`, `denied`, `restricted` or `unknown`. Returns `String` - Can be `not-determined`, `granted`, `denied`, `restricted` or `unknown`.
This user consent was not required until macOS 10.14 Mojave, so this method will always return `granted` if your system is running 10.13 High Sierra or lower. This user consent was not required on macOS 10.13 High Sierra or lower so this method will always return `granted`.
macOS 10.14 Mojave or higher requires consent for `microphone` and `camera` access.
macOS 10.15 Catalina or higher requires consent for `screen` access.
### `systemPreferences.askForMediaAccess(mediaType)` _macOS_ ### `systemPreferences.askForMediaAccess(mediaType)` _macOS_
@@ -478,11 +470,3 @@ A `String` property that can be `dark`, `light` or `unknown`.
Returns the macOS appearance setting that is currently applied to your application, Returns the macOS appearance setting that is currently applied to your application,
maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc) maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc)
Please note that until Electron is built targeting the 10.14 SDK, your application's
`effectiveAppearance` will default to 'light' and won't inherit the OS preference. In
the interim in order for your application to inherit the OS preference you must set the
`NSRequiresAquaSystemAppearance` key in your apps `Info.plist` to `false`. If you are
using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode`
packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
for more details.

View File

@@ -454,10 +454,8 @@ The usage is the same with [the `select-client-certificate` event of
Returns: Returns:
* `event` Event * `event` Event
* `request` Object * `authenticationResponseDetails` Object
* `method` String
* `url` URL * `url` URL
* `referrer` URL
* `authInfo` Object * `authInfo` Object
* `isProxy` Boolean * `isProxy` Boolean
* `scheme` String * `scheme` String
@@ -465,8 +463,8 @@ Returns:
* `port` Integer * `port` Integer
* `realm` String * `realm` String
* `callback` Function * `callback` Function
* `username` String * `username` String (optional)
* `password` String * `password` String (optional)
Emitted when `webContents` wants to do basic auth. Emitted when `webContents` wants to do basic auth.
@@ -570,6 +568,9 @@ Returns:
* `titleText` String - Title or alt text of the selection that the context * `titleText` String - Title or alt text of the selection that the context
was invoked on. was invoked on.
* `misspelledWord` String - The misspelled word under the cursor, if any. * `misspelledWord` String - The misspelled word under the cursor, if any.
* `dictionarySuggestions` String[] - An array of suggested words to show the
user to replace the `misspelledWord`. Only available if there is a misspelled
word and spellchecker is enabled.
* `frameCharset` String - The character encoding of the frame on which the * `frameCharset` String - The character encoding of the frame on which the
menu was invoked. menu was invoked.
* `inputFieldType` String - If the context menu was invoked on an input * `inputFieldType` String - If the context menu was invoked on an input
@@ -1041,6 +1042,17 @@ contents.executeJavaScript('fetch("https://jsonplaceholder.typicode.com/users/1"
}) })
``` ```
#### `contents.executeJavaScriptInIsolatedWorld(worldId, scripts[, userGesture])`
* `worldId` Integer - The ID of the world to run the javascript in, `0` is the default world, `999` is the world used by Electron's `contextIsolation` feature. You can provide any integer here.
* `scripts` [WebSource[]](structures/web-source.md)
* `userGesture` Boolean (optional) - Default is `false`.
Returns `Promise<any>` - A promise that resolves with the result of the executed code
or is rejected if the result of the code is a rejected promise.
Works like `executeJavaScript` but evaluates `scripts` in an isolated context.
#### `contents.setIgnoreMenuShortcuts(ignore)` _Experimental_ #### `contents.setIgnoreMenuShortcuts(ignore)` _Experimental_
* `ignore` Boolean * `ignore` Boolean
@@ -1067,11 +1079,13 @@ Returns `Boolean` - Whether audio is currently playing.
#### `contents.setZoomFactor(factor)` #### `contents.setZoomFactor(factor)`
* `factor` Number - Zoom factor. * `factor` Double - Zoom factor; default is 1.0.
Changes the zoom factor to the specified factor. Zoom factor is Changes the zoom factor to the specified factor. Zoom factor is
zoom percent divided by 100, so 300% = 3.0. zoom percent divided by 100, so 300% = 3.0.
The factor must be greater than 0.0.
**[Deprecated](modernization/property-updates.md)** **[Deprecated](modernization/property-updates.md)**
#### `contents.getZoomFactor()` #### `contents.getZoomFactor()`
@@ -1112,7 +1126,7 @@ Sets the maximum and minimum pinch-to-zoom level.
> contents.setVisualZoomLevelLimits(1, 3) > contents.setVisualZoomLevelLimits(1, 3)
> ``` > ```
#### `contents.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` #### `contents.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` _Deprecated_
* `minimumLevel` Number * `minimumLevel` Number
* `maximumLevel` Number * `maximumLevel` Number
@@ -1121,6 +1135,8 @@ Returns `Promise<void>`
Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. Sets the maximum and minimum layout-based (i.e. non-visual) zoom level.
**Deprecated:** This API is no longer supported by Chromium.
#### `contents.undo()` #### `contents.undo()`
Executes the editing command `undo` in web page. Executes the editing command `undo` in web page.
@@ -1233,11 +1249,34 @@ Returns `Promise<NativeImage>` - Resolves with a [NativeImage](native-image.md)
Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page. Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page.
#### `contents.isBeingCaptured()`
Returns `Boolean` - Whether this page is being captured. It returns true when the capturer count
is large then 0.
#### `contents.incrementCapturerCount([size, stayHidden])`
* `size` [Size](structures/size.md) (optional) - The perferred size for the capturer.
* `stayHidden` Boolean (optional) - Keep the page hidden instead of visible.
Increase the capturer count by one. The page is considered visible when its browser window is
hidden and the capturer count is non-zero. If you would like the page to stay hidden, you should ensure that `stayHidden` is set to true.
This also affects the Page Visibility API.
#### `contents.decrementCapturerCount([stayHidden])`
* `stayHidden` Boolean (optional) - Keep the page in hidden state instead of visible.
Decrease the capturer count by one. The page will be set to hidden or occluded state when its
browser window is hidden or occluded and the capturer count reaches zero. If you want to
decrease the hidden capturer count instead you should set `stayHidden` to true.
#### `contents.getPrinters()` #### `contents.getPrinters()`
Get the system printer list. Get the system printer list.
Returns [`PrinterInfo[]`](structures/printer-info.md). Returns [`PrinterInfo[]`](structures/printer-info.md)
#### `contents.print([options], [callback])` #### `contents.print([options], [callback])`
@@ -1245,7 +1284,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md).
* `silent` Boolean (optional) - Don't ask user for print settings. Default is `false`. * `silent` Boolean (optional) - Don't ask user for print settings. Default is `false`.
* `printBackground` Boolean (optional) - Prints the background color and image of * `printBackground` Boolean (optional) - Prints the background color and image of
the web page. Default is `false`. the web page. Default is `false`.
* `deviceName` String (optional) - Set the printer device name to use. Default is `''`. * `deviceName` String (optional) - Set the printer device name to use. Must be the system-defined name and not the 'friendly' name, e.g 'Brother_QL_820NWB' and not 'Brother QL-820NWB'.
* `color` Boolean (optional) - Set whether the printed web page will be in color or grayscale. Default is `true`. * `color` Boolean (optional) - Set whether the printed web page will be in color or grayscale. Default is `true`.
* `margins` Object (optional) * `margins` Object (optional)
* `marginType` String (optional) - Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen, you will also need to specify `top`, `bottom`, `left`, and `right`. * `marginType` String (optional) - Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen, you will also need to specify `top`, `bottom`, `left`, and `right`.
@@ -1267,7 +1306,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md).
* `footer` String (optional) - String to be printed as page footer. * `footer` String (optional) - String to be printed as page footer.
* `callback` Function (optional) * `callback` Function (optional)
* `success` Boolean - Indicates success of the print call. * `success` Boolean - Indicates success of the print call.
* `failureReason` String - Called back if the print fails; can be `cancelled` or `failed`. * `failureReason` String - Error description called back if the print fails.
Prints window's web page. When `silent` is set to `true`, Electron will pick Prints window's web page. When `silent` is set to `true`, Electron will pick
the system's default printer if `deviceName` is empty and the default settings for printing. the system's default printer if `deviceName` is empty and the default settings for printing.
@@ -1326,12 +1365,13 @@ win.loadURL('http://github.com')
win.webContents.on('did-finish-load', () => { win.webContents.on('did-finish-load', () => {
// Use default printing options // Use default printing options
win.webContents.printToPDF({}, (error, data) => { win.webContents.printToPDF({}).then(data => {
if (error) throw error
fs.writeFile('/tmp/print.pdf', data, (error) => { fs.writeFile('/tmp/print.pdf', data, (error) => {
if (error) throw error if (error) throw error
console.log('Write PDF successfully.') console.log('Write PDF successfully.')
}) })
}).catch(error => {
console.log(error)
}) })
}) })
``` ```

View File

@@ -22,11 +22,13 @@ The `WebFrame` class has the following instance methods:
### `webFrame.setZoomFactor(factor)` ### `webFrame.setZoomFactor(factor)`
* `factor` Number - Zoom factor. * `factor` Double - Zoom factor; default is 1.0.
Changes the zoom factor to the specified factor. Zoom factor is Changes the zoom factor to the specified factor. Zoom factor is
zoom percent divided by 100, so 300% = 3.0. zoom percent divided by 100, so 300% = 3.0.
The factor must be greater than 0.0.
### `webFrame.getZoomFactor()` ### `webFrame.getZoomFactor()`
Returns `Number` - The current zoom factor. Returns `Number` - The current zoom factor.
@@ -56,13 +58,15 @@ Sets the maximum and minimum pinch-to-zoom level.
> webFrame.setVisualZoomLevelLimits(1, 3) > webFrame.setVisualZoomLevelLimits(1, 3)
> ``` > ```
### `webFrame.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` ### `webFrame.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` _Deprecated_
* `minimumLevel` Number * `minimumLevel` Number
* `maximumLevel` Number * `maximumLevel` Number
Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. Sets the maximum and minimum layout-based (i.e. non-visual) zoom level.
**Deprecated:** This API is no longer supported by Chromium.
### `webFrame.setSpellCheckProvider(language, provider)` ### `webFrame.setSpellCheckProvider(language, provider)`
* `language` String * `language` String
@@ -74,6 +78,17 @@ Sets the maximum and minimum layout-based (i.e. non-visual) zoom level.
Sets a provider for spell checking in input fields and text areas. Sets a provider for spell checking in input fields and text areas.
If you want to use this method you must disable the builtin spellchecker when you
construct the window.
```js
const mainWindow = new BrowserWindow({
webPreferences: {
spellcheck: false
}
})
```
The `provider` must be an object that has a `spellCheck` method that accepts The `provider` must be an object that has a `spellCheck` method that accepts
an array of individual words for spellchecking. an array of individual words for spellchecking.
The `spellCheck` function runs asynchronously and calls the `callback` function The `spellCheck` function runs asynchronously and calls the `callback` function

View File

@@ -146,7 +146,8 @@ response are visible by the time this listener is fired.
* `timestamp` Double * `timestamp` Double
* `statusLine` String * `statusLine` String
* `statusCode` Integer * `statusCode` Integer
* `responseHeaders` Record<string, string> (optional) * `requestHeaders` Record<string, string>
* `responseHeaders` Record<string, string[]> (optional)
* `callback` Function * `callback` Function
* `headersReceivedResponse` Object * `headersReceivedResponse` Object
* `cancel` Boolean (optional) * `cancel` Boolean (optional)
@@ -175,7 +176,7 @@ The `callback` has to be called with a `response` object.
* `resourceType` String * `resourceType` String
* `referrer` String * `referrer` String
* `timestamp` Double * `timestamp` Double
* `responseHeaders` Record<string, string> (optional) * `responseHeaders` Record<string, string[]> (optional)
* `fromCache` Boolean - Indicates whether the response was fetched from disk * `fromCache` Boolean - Indicates whether the response was fetched from disk
cache. cache.
* `statusCode` Integer * `statusCode` Integer
@@ -205,7 +206,7 @@ and response headers are available.
* `ip` String (optional) - The server IP address that the request was * `ip` String (optional) - The server IP address that the request was
actually sent to. actually sent to.
* `fromCache` Boolean * `fromCache` Boolean
* `responseHeaders` Record<string, string> (optional) * `responseHeaders` Record<string, string[]> (optional)
The `listener` will be called with `listener(details)` when a server initiated The `listener` will be called with `listener(details)` when a server initiated
redirect is about to occur. redirect is about to occur.
@@ -224,10 +225,11 @@ redirect is about to occur.
* `resourceType` String * `resourceType` String
* `referrer` String * `referrer` String
* `timestamp` Double * `timestamp` Double
* `responseHeaders` Record<string, string> (optional) * `responseHeaders` Record<string, string[]> (optional)
* `fromCache` Boolean * `fromCache` Boolean
* `statusCode` Integer * `statusCode` Integer
* `statusLine` String * `statusLine` String
* `error` String
The `listener` will be called with `listener(details)` when a request is The `listener` will be called with `listener(details)` when a request is
completed. completed.

View File

@@ -635,7 +635,7 @@ Returns `Promise<void>`
Sets the maximum and minimum pinch-to-zoom level. Sets the maximum and minimum pinch-to-zoom level.
### `<webview>.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` ### `<webview>.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` _Deprecated_
* `minimumLevel` Number * `minimumLevel` Number
* `maximumLevel` Number * `maximumLevel` Number
@@ -644,11 +644,13 @@ Returns `Promise<void>`
Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. Sets the maximum and minimum layout-based (i.e. non-visual) zoom level.
**Deprecated:** This API is no longer supported by Chromium.
### `<webview>.showDefinitionForSelection()` _macOS_ ### `<webview>.showDefinitionForSelection()` _macOS_
Shows pop-up dictionary that searches the selected word on the page. Shows pop-up dictionary that searches the selected word on the page.
### `<webview>.getWebContents()` ### `<webview>.getWebContents()` _Deprecated_
Returns [`WebContents`](web-contents.md) - The web contents associated with Returns [`WebContents`](web-contents.md) - The web contents associated with
this `webview`. this `webview`.

View File

@@ -38,11 +38,15 @@ npm install --platform=win32 electron
## Proxies ## Proxies
If you need to use an HTTP proxy you can [set these environment variables][proxy-env]. If you need to use an HTTP proxy, you need to set the `ELECTRON_GET_USE_PROXY` variable to any
value, plus additional environment variables depending on your host system's Node version:
* [Node 10 and above][proxy-env-10]
* [Before Node 10][proxy-env]
## Custom Mirrors and Caches ## Custom Mirrors and Caches
During installation, the `electron` module will call out to During installation, the `electron` module will call out to
[`electron-download`][electron-download] to download prebuilt binaries of [`@electron/get`][electron-get] to download prebuilt binaries of
Electron for your platform. It will do so by contacting GitHub's Electron for your platform. It will do so by contacting GitHub's
release download page (`https://github.com/electron/electron/releases/tag/v$VERSION`, release download page (`https://github.com/electron/electron/releases/tag/v$VERSION`,
where `$VERSION` is the exact version of Electron). where `$VERSION` is the exact version of Electron).
@@ -52,7 +56,7 @@ can do so by either providing a mirror or an existing cache directory.
#### Mirror #### Mirror
You can use environment variables to override the base URL, the path at which to You can use environment variables to override the base URL, the path at which to
look for Electron binaries, and the binary filename. The url used by `electron-download` look for Electron binaries, and the binary filename. The url used by `@electron/get`
is composed as follows: is composed as follows:
```plaintext ```plaintext
@@ -62,11 +66,11 @@ url = ELECTRON_MIRROR + ELECTRON_CUSTOM_DIR + '/' + ELECTRON_CUSTOM_FILENAME
For instance, to use the China mirror: For instance, to use the China mirror:
```plaintext ```plaintext
ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/" ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/"
``` ```
#### Cache #### Cache
Alternatively, you can override the local cache. `electron-download` will cache Alternatively, you can override the local cache. `@electron/get` will cache
downloaded binaries in a local directory to not stress your network. You can use downloaded binaries in a local directory to not stress your network. You can use
that cache folder to provide custom builds of Electron or to avoid making contact that cache folder to provide custom builds of Electron or to avoid making contact
with the network at all. with the network at all.
@@ -85,16 +89,26 @@ The cache contains the version's official zip file as well as a checksum, stored
a text file. A typical cache might look like this: a text file. A typical cache might look like this:
```sh ```sh
├── electron-v1.7.9-darwin-x64.zip ├── httpsgithub.comelectronelectronreleasesdownloadv1.7.9electron-v1.7.9-darwin-x64.zip
── electron-v1.8.1-darwin-x64.zip │ └── electron-v1.7.9-darwin-x64.zip
├── electron-v1.8.2-beta.1-darwin-x64.zip ├── httpsgithub.comelectronelectronreleasesdownloadv1.7.9SHASUMS256.txt
├── electron-v1.8.2-beta.2-darwin-x64.zip │ └── SHASUMS256.txt
├── electron-v1.8.2-beta.3-darwin-x64.zip ├── httpsgithub.comelectronelectronreleasesdownloadv1.8.1electron-v1.8.1-darwin-x64.zip
├── SHASUMS256.txt-1.7.9 │ └── electron-v1.8.1-darwin-x64.zip
├── SHASUMS256.txt-1.8.1 ├── httpsgithub.comelectronelectronreleasesdownloadv1.8.1SHASUMS256.txt
── SHASUMS256.txt-1.8.2-beta.1 │ └── SHASUMS256.txt
├── SHASUMS256.txt-1.8.2-beta.2 ├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.1electron-v1.8.2-beta.1-darwin-x64.zip
├── SHASUMS256.txt-1.8.2-beta.3 │ └── electron-v1.8.2-beta.1-darwin-x64.zip
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.1SHASUMS256.txt
│ └── SHASUMS256.txt
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.2electron-v1.8.2-beta.2-darwin-x64.zip
│ └── electron-v1.8.2-beta.2-darwin-x64.zip
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.2SHASUMS256.txt
│ └── SHASUMS256.txt
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.3electron-v1.8.2-beta.3-darwin-x64.zip
│ └── electron-v1.8.2-beta.3-darwin-x64.zip
└── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.3SHASUMS256.txt
└── SHASUMS256.txt
``` ```
## Skip binary download ## Skip binary download
@@ -146,7 +160,8 @@ If you need to force a re-download of the asset and the SHASUM file set the
[npm]: https://docs.npmjs.com [npm]: https://docs.npmjs.com
[versioning]: ./electron-versioning.md [versioning]: ./electron-versioning.md
[releases]: https://github.com/electron/electron/releases [releases]: https://github.com/electron/electron/releases
[proxy-env]: https://github.com/request/request/tree/f0c4ec061141051988d1216c24936ad2e7d5c45d#controlling-proxy-behaviour-using-environment-variables [proxy-env-10]: https://github.com/gajus/global-agent/blob/v2.1.5/README.md#environment-variables
[electron-download]: https://github.com/electron-userland/electron-download [proxy-env]: https://github.com/np-maintain/global-tunnel/blob/v2.7.1/README.md#auto-config
[electron-get]: https://github.com/electron/get
[npm-permissions]: https://docs.npmjs.com/getting-started/fixing-npm-permissions [npm-permissions]: https://docs.npmjs.com/getting-started/fixing-npm-permissions
[unsafe-perm]: https://docs.npmjs.com/misc/config#unsafe-perm [unsafe-perm]: https://docs.npmjs.com/misc/config#unsafe-perm

View File

@@ -54,13 +54,13 @@ at once, consider the [Chrome Tracing] tool.
Chances are that your app could be a little leaner, faster, and generally less Chances are that your app could be a little leaner, faster, and generally less
resource-hungry if you attempt these steps. resource-hungry if you attempt these steps.
1) [Carelessly including modules](#1-carelessly-including-modules) 1. [Carelessly including modules](#1-carelessly-including-modules)
2) [Loading and running code too soon](#2-loading-and-running-code-too-soon) 2. [Loading and running code too soon](#2-loading-and-running-code-too-soon)
3) [Blocking the main process](#3-blocking-the-main-process) 3. [Blocking the main process](#3-blocking-the-main-process)
4) [Blocking the renderer process](#4-blocking-the-renderer-process) 4. [Blocking the renderer process](#4-blocking-the-renderer-process)
5) [Unnecessary polyfills](#5-unnecessary-polyfills) 5. [Unnecessary polyfills](#5-unnecessary-polyfills)
6) [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests) 6. [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests)
7) [Bundle your code](#7-bundle-your-code) 7. [Bundle your code](#7-bundle-your-code)
## 1) Carelessly including modules ## 1) Carelessly including modules
@@ -418,7 +418,6 @@ As of writing this article, the popular choices include [Webpack][webpack],
[performance-cpu-prof]: ../images/performance-cpu-prof.png [performance-cpu-prof]: ../images/performance-cpu-prof.png
[performance-heap-prof]: ../images/performance-heap-prof.png [performance-heap-prof]: ../images/performance-heap-prof.png
[chrome-devtools-tutorial]: https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/ [chrome-devtools-tutorial]: https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/
[chrome-tracing-tutorial]:
[worker-threads]: https://nodejs.org/api/worker_threads.html [worker-threads]: https://nodejs.org/api/worker_threads.html
[web-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers [web-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
[request-idle-callback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback [request-idle-callback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback

View File

@@ -61,7 +61,6 @@ The output should look roughly like this:
├── libgcrypt.so.11 ├── libgcrypt.so.11
├── libnode.so ├── libnode.so
├── locales ├── locales
├── natives_blob.bin
├── resources ├── resources
├── v8_context_snapshot.bin ├── v8_context_snapshot.bin
└── version └── version

View File

@@ -62,7 +62,6 @@ The output should look roughly like this:
│   ├── am.pak │   ├── am.pak
│   ├── ar.pak │   ├── ar.pak
│   ├── [...] │   ├── [...]
├── natives_blob.bin
├── node.dll ├── node.dll
├── resources ├── resources
│   ├── app │   ├── app

View File

@@ -69,4 +69,10 @@
<message name="IDS_PICTURE_IN_PICTURE_PREVIOUS_TRACK_CONTROL_ACCESSIBLE_TEXT" desc="Accessible text label used for the controls button in the Picture-in-Picture window. The button invokes previous track action."> <message name="IDS_PICTURE_IN_PICTURE_PREVIOUS_TRACK_CONTROL_ACCESSIBLE_TEXT" desc="Accessible text label used for the controls button in the Picture-in-Picture window. The button invokes previous track action.">
Previous track Previous track
</message> </message>
<message name="IDS_SPELLCHECK_DICTIONARY" use_name_for_id="true">
en-US
</message>
<message name="IDS_ACCEPT_LANGUAGES" use_name_for_id="true">
en-US,en
</message>
</grit-part> </grit-part>

View File

@@ -135,12 +135,12 @@ auto_filenames = {
"lib/common/api/module-list.ts", "lib/common/api/module-list.ts",
"lib/common/api/native-image.js", "lib/common/api/native-image.js",
"lib/common/api/shell.js", "lib/common/api/shell.js",
"lib/common/clipboard-utils.ts",
"lib/common/crash-reporter.js", "lib/common/crash-reporter.js",
"lib/common/define-properties.ts", "lib/common/define-properties.ts",
"lib/common/electron-binding-setup.ts", "lib/common/electron-binding-setup.ts",
"lib/common/remote/type-utils.ts", "lib/common/type-utils.ts",
"lib/common/web-view-methods.ts", "lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts", "lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.js", "lib/renderer/api/crash-reporter.js",
"lib/renderer/api/desktop-capturer.ts", "lib/renderer/api/desktop-capturer.ts",
@@ -165,6 +165,7 @@ auto_filenames = {
"lib/renderer/web-view/web-view-element.ts", "lib/renderer/web-view/web-view-element.ts",
"lib/renderer/web-view/web-view-impl.ts", "lib/renderer/web-view/web-view-impl.ts",
"lib/renderer/web-view/web-view-init.ts", "lib/renderer/web-view/web-view-init.ts",
"lib/renderer/window-setup.ts",
"lib/sandboxed_renderer/api/exports/electron.ts", "lib/sandboxed_renderer/api/exports/electron.ts",
"lib/sandboxed_renderer/api/module-list.ts", "lib/sandboxed_renderer/api/module-list.ts",
"lib/sandboxed_renderer/init.js", "lib/sandboxed_renderer/init.js",
@@ -188,6 +189,7 @@ auto_filenames = {
content_script_bundle_deps = [ content_script_bundle_deps = [
"lib/common/electron-binding-setup.ts", "lib/common/electron-binding-setup.ts",
"lib/common/webpack-globals-provider.ts",
"lib/content_script/init.js", "lib/content_script/init.js",
"lib/renderer/chrome-api.ts", "lib/renderer/chrome-api.ts",
"lib/renderer/extensions/event.ts", "lib/renderer/extensions/event.ts",
@@ -266,15 +268,15 @@ auto_filenames = {
"lib/common/api/module-list.ts", "lib/common/api/module-list.ts",
"lib/common/api/native-image.js", "lib/common/api/native-image.js",
"lib/common/api/shell.js", "lib/common/api/shell.js",
"lib/common/clipboard-utils.ts",
"lib/common/crash-reporter.js", "lib/common/crash-reporter.js",
"lib/common/define-properties.ts", "lib/common/define-properties.ts",
"lib/common/electron-binding-setup.ts", "lib/common/electron-binding-setup.ts",
"lib/common/init.ts", "lib/common/init.ts",
"lib/common/parse-features-string.js", "lib/common/parse-features-string.js",
"lib/common/remote/type-utils.ts",
"lib/common/reset-search-paths.ts", "lib/common/reset-search-paths.ts",
"lib/common/type-utils.ts",
"lib/common/web-view-methods.ts", "lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/ipc-renderer-internal-utils.ts", "lib/renderer/ipc-renderer-internal-utils.ts",
"lib/renderer/ipc-renderer-internal.ts", "lib/renderer/ipc-renderer-internal.ts",
"package.json", "package.json",
@@ -289,14 +291,14 @@ auto_filenames = {
"lib/common/api/module-list.ts", "lib/common/api/module-list.ts",
"lib/common/api/native-image.js", "lib/common/api/native-image.js",
"lib/common/api/shell.js", "lib/common/api/shell.js",
"lib/common/clipboard-utils.ts",
"lib/common/crash-reporter.js", "lib/common/crash-reporter.js",
"lib/common/define-properties.ts", "lib/common/define-properties.ts",
"lib/common/electron-binding-setup.ts", "lib/common/electron-binding-setup.ts",
"lib/common/init.ts", "lib/common/init.ts",
"lib/common/remote/type-utils.ts",
"lib/common/reset-search-paths.ts", "lib/common/reset-search-paths.ts",
"lib/common/type-utils.ts",
"lib/common/web-view-methods.ts", "lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts", "lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.js", "lib/renderer/api/crash-reporter.js",
"lib/renderer/api/desktop-capturer.ts", "lib/renderer/api/desktop-capturer.ts",
@@ -338,13 +340,13 @@ auto_filenames = {
"lib/common/api/module-list.ts", "lib/common/api/module-list.ts",
"lib/common/api/native-image.js", "lib/common/api/native-image.js",
"lib/common/api/shell.js", "lib/common/api/shell.js",
"lib/common/clipboard-utils.ts",
"lib/common/crash-reporter.js", "lib/common/crash-reporter.js",
"lib/common/define-properties.ts", "lib/common/define-properties.ts",
"lib/common/electron-binding-setup.ts", "lib/common/electron-binding-setup.ts",
"lib/common/init.ts", "lib/common/init.ts",
"lib/common/remote/type-utils.ts",
"lib/common/reset-search-paths.ts", "lib/common/reset-search-paths.ts",
"lib/common/type-utils.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts", "lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.js", "lib/renderer/api/crash-reporter.js",
"lib/renderer/api/desktop-capturer.ts", "lib/renderer/api/desktop-capturer.ts",

View File

@@ -22,100 +22,99 @@ filenames = {
] ]
lib_sources = [ lib_sources = [
"shell/app/atom_content_client.cc", "chromium_src/chrome/browser/process_singleton.h",
"shell/app/atom_content_client.h", "chromium_src/chrome/browser/process_singleton_posix.cc",
"shell/app/atom_main_delegate.cc", "chromium_src/chrome/browser/process_singleton_win.cc",
"shell/app/atom_main_delegate.h", "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc",
"shell/app/atom_main_delegate_mac.h", "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h",
"shell/app/atom_main_delegate_mac.mm", "shell/app/electron_content_client.cc",
"shell/app/electron_content_client.h",
"shell/app/electron_main_delegate.cc",
"shell/app/electron_main_delegate.h",
"shell/app/electron_main_delegate_mac.h",
"shell/app/electron_main_delegate_mac.mm",
"shell/app/command_line_args.cc", "shell/app/command_line_args.cc",
"shell/app/command_line_args.h", "shell/app/command_line_args.h",
"shell/app/uv_task_runner.cc", "shell/app/uv_task_runner.cc",
"shell/app/uv_task_runner.h", "shell/app/uv_task_runner.h",
"shell/browser/api/atom_api_app_mac.mm", "shell/browser/api/electron_api_app.cc",
"shell/browser/api/atom_api_app.cc", "shell/browser/api/electron_api_app.h",
"shell/browser/font_defaults.cc", "shell/browser/api/electron_api_app_mac.mm",
"shell/browser/font_defaults.h", "shell/browser/api/electron_api_auto_updater.cc",
"shell/browser/feature_list.cc", "shell/browser/api/electron_api_auto_updater.h",
"shell/browser/feature_list.h", "shell/browser/api/electron_api_browser_view.cc",
"shell/browser/api/atom_api_app.h", "shell/browser/api/electron_api_browser_view.h",
"shell/browser/api/atom_api_auto_updater.cc", "shell/browser/api/electron_api_browser_window.cc",
"shell/browser/api/atom_api_auto_updater.h", "shell/browser/api/electron_api_browser_window.h",
"shell/browser/api/atom_api_browser_view.cc", "shell/browser/api/electron_api_browser_window_mac.mm",
"shell/browser/api/atom_api_browser_view.h", "shell/browser/api/electron_api_browser_window_views.cc",
"shell/browser/api/atom_api_content_tracing.cc", "shell/browser/api/electron_api_content_tracing.cc",
"shell/browser/api/atom_api_cookies.cc", "shell/browser/api/electron_api_cookies.cc",
"shell/browser/api/atom_api_cookies.h", "shell/browser/api/electron_api_cookies.h",
"shell/browser/api/atom_api_data_pipe_holder.cc", "shell/browser/api/electron_api_data_pipe_holder.cc",
"shell/browser/api/atom_api_data_pipe_holder.h", "shell/browser/api/electron_api_data_pipe_holder.h",
"shell/browser/api/atom_api_debugger.cc", "shell/browser/api/electron_api_debugger.cc",
"shell/browser/api/atom_api_debugger.h", "shell/browser/api/electron_api_debugger.h",
"shell/browser/api/atom_api_dialog.cc", "shell/browser/api/electron_api_dialog.cc",
"shell/browser/api/atom_api_download_item.cc", "shell/browser/api/electron_api_download_item.cc",
"shell/browser/api/atom_api_download_item.h", "shell/browser/api/electron_api_download_item.h",
"shell/browser/api/atom_api_event.cc", "shell/browser/api/electron_api_event.cc",
"shell/browser/api/atom_api_global_shortcut.cc", "shell/browser/api/electron_api_global_shortcut.cc",
"shell/browser/api/atom_api_global_shortcut.h", "shell/browser/api/electron_api_global_shortcut.h",
"shell/browser/api/atom_api_in_app_purchase.cc", "shell/browser/api/electron_api_in_app_purchase.cc",
"shell/browser/api/atom_api_in_app_purchase.h", "shell/browser/api/electron_api_in_app_purchase.h",
"shell/browser/api/atom_api_menu.cc", "shell/browser/api/electron_api_menu.cc",
"shell/browser/api/atom_api_menu.h", "shell/browser/api/electron_api_menu.h",
"shell/browser/api/atom_api_menu_mac.h", "shell/browser/api/electron_api_menu_mac.h",
"shell/browser/api/atom_api_menu_mac.mm", "shell/browser/api/electron_api_menu_mac.mm",
"shell/browser/api/atom_api_menu_views.cc", "shell/browser/api/electron_api_menu_views.cc",
"shell/browser/api/atom_api_menu_views.h", "shell/browser/api/electron_api_menu_views.h",
"shell/browser/api/atom_api_native_theme.cc", "shell/browser/api/electron_api_native_theme.cc",
"shell/browser/api/atom_api_native_theme.h", "shell/browser/api/electron_api_native_theme.h",
"shell/browser/api/atom_api_native_theme_mac.mm", "shell/browser/api/electron_api_native_theme_mac.mm",
"shell/browser/api/atom_api_net.cc", "shell/browser/api/electron_api_net.cc",
"shell/browser/api/atom_api_net.h", "shell/browser/api/electron_api_net.h",
"shell/browser/api/atom_api_net_log.cc", "shell/browser/api/electron_api_net_log.cc",
"shell/browser/api/atom_api_net_log.h", "shell/browser/api/electron_api_net_log.h",
"shell/browser/api/atom_api_notification.cc", "shell/browser/api/electron_api_notification.cc",
"shell/browser/api/atom_api_notification.h", "shell/browser/api/electron_api_notification.h",
"shell/browser/api/atom_api_power_monitor_mac.mm", "shell/browser/api/electron_api_power_monitor.cc",
"shell/browser/api/atom_api_power_monitor_win.cc", "shell/browser/api/electron_api_power_monitor.h",
"shell/browser/api/atom_api_power_monitor.cc", "shell/browser/api/electron_api_power_monitor_mac.mm",
"shell/browser/api/atom_api_power_monitor.h", "shell/browser/api/electron_api_power_monitor_win.cc",
"shell/browser/api/atom_api_power_save_blocker.cc", "shell/browser/api/electron_api_power_save_blocker.cc",
"shell/browser/api/atom_api_power_save_blocker.h", "shell/browser/api/electron_api_power_save_blocker.h",
"shell/browser/api/atom_api_protocol_ns.cc", "shell/browser/api/electron_api_protocol_ns.cc",
"shell/browser/api/atom_api_protocol_ns.h", "shell/browser/api/electron_api_protocol_ns.h",
"shell/browser/api/atom_api_screen.cc", "shell/browser/api/electron_api_screen.cc",
"shell/browser/api/atom_api_screen.h", "shell/browser/api/electron_api_screen.h",
"shell/browser/api/atom_api_session.cc", "shell/browser/api/electron_api_session.cc",
"shell/browser/api/atom_api_session.h", "shell/browser/api/electron_api_session.h",
"shell/browser/api/atom_api_system_preferences.cc", "shell/browser/api/electron_api_system_preferences.cc",
"shell/browser/api/atom_api_system_preferences.h", "shell/browser/api/electron_api_system_preferences.h",
"shell/browser/api/atom_api_system_preferences_mac.mm", "shell/browser/api/electron_api_system_preferences_mac.mm",
"shell/browser/api/atom_api_system_preferences_win.cc", "shell/browser/api/electron_api_system_preferences_win.cc",
"shell/browser/api/atom_api_top_level_window.cc", "shell/browser/api/electron_api_top_level_window.cc",
"shell/browser/api/atom_api_top_level_window.h", "shell/browser/api/electron_api_top_level_window.h",
"shell/browser/api/atom_api_tray.cc", "shell/browser/api/electron_api_tray.cc",
"shell/browser/api/atom_api_tray.h", "shell/browser/api/electron_api_tray.h",
"shell/browser/api/atom_api_url_request_ns.cc", "shell/browser/api/electron_api_url_loader.cc",
"shell/browser/api/atom_api_url_request_ns.h", "shell/browser/api/electron_api_url_loader.h",
"shell/browser/api/atom_api_view.cc", "shell/browser/api/electron_api_view.cc",
"shell/browser/api/atom_api_view.h", "shell/browser/api/electron_api_view.h",
"shell/browser/api/atom_api_web_contents.cc", "shell/browser/api/electron_api_web_contents.cc",
"shell/browser/api/atom_api_web_contents.h", "shell/browser/api/electron_api_web_contents.h",
"shell/browser/api/atom_api_web_contents_impl.cc", "shell/browser/api/electron_api_web_contents_impl.cc",
"shell/browser/api/atom_api_web_contents_mac.mm", "shell/browser/api/electron_api_web_contents_mac.mm",
"shell/browser/api/atom_api_web_contents_view.cc", "shell/browser/api/electron_api_web_contents_view.cc",
"shell/browser/api/atom_api_web_contents_view.h", "shell/browser/api/electron_api_web_contents_view.h",
"shell/browser/api/atom_api_web_request_ns.cc", "shell/browser/api/electron_api_web_request_ns.cc",
"shell/browser/api/atom_api_web_request_ns.h", "shell/browser/api/electron_api_web_request_ns.h",
"shell/browser/api/atom_api_web_view_manager.cc", "shell/browser/api/electron_api_web_view_manager.cc",
"shell/browser/api/atom_api_browser_window.cc",
"shell/browser/api/atom_api_browser_window.h",
"shell/browser/api/atom_api_browser_window_mac.mm",
"shell/browser/api/atom_api_browser_window_views.cc",
"shell/browser/api/event.cc", "shell/browser/api/event.cc",
"shell/browser/api/event.h", "shell/browser/api/event.h",
"shell/browser/api/event_emitter_deprecated.cc", "shell/browser/api/event_emitter_deprecated.cc",
"shell/browser/api/event_emitter_deprecated.h", "shell/browser/api/event_emitter_deprecated.h",
"shell/browser/api/trackable_object.cc",
"shell/browser/api/trackable_object.h",
"shell/browser/api/frame_subscriber.cc", "shell/browser/api/frame_subscriber.cc",
"shell/browser/api/frame_subscriber.h", "shell/browser/api/frame_subscriber.h",
"shell/browser/api/gpu_info_enumerator.cc", "shell/browser/api/gpu_info_enumerator.cc",
@@ -126,67 +125,75 @@ filenames = {
"shell/browser/api/process_metric.h", "shell/browser/api/process_metric.h",
"shell/browser/api/save_page_handler.cc", "shell/browser/api/save_page_handler.cc",
"shell/browser/api/save_page_handler.h", "shell/browser/api/save_page_handler.h",
"shell/browser/api/trackable_object.cc",
"shell/browser/api/trackable_object.h",
"shell/browser/electron_autofill_driver.cc",
"shell/browser/electron_autofill_driver.h",
"shell/browser/electron_autofill_driver_factory.cc",
"shell/browser/electron_autofill_driver_factory.h",
"shell/browser/electron_browser_client.cc",
"shell/browser/electron_browser_client.h",
"shell/browser/electron_browser_context.cc",
"shell/browser/electron_browser_context.h",
"shell/browser/electron_browser_main_parts.cc",
"shell/browser/electron_browser_main_parts.h",
"shell/browser/electron_browser_main_parts_mac.mm",
"shell/browser/electron_browser_main_parts_posix.cc",
"shell/browser/electron_download_manager_delegate.cc",
"shell/browser/electron_download_manager_delegate.h",
"shell/browser/electron_gpu_client.cc",
"shell/browser/electron_gpu_client.h",
"shell/browser/electron_javascript_dialog_manager.cc",
"shell/browser/electron_javascript_dialog_manager.h",
"shell/browser/electron_navigation_throttle.cc",
"shell/browser/electron_navigation_throttle.h",
"shell/browser/electron_paths.h",
"shell/browser/electron_permission_manager.cc",
"shell/browser/electron_permission_manager.h",
"shell/browser/electron_quota_permission_context.cc",
"shell/browser/electron_quota_permission_context.h",
"shell/browser/electron_speech_recognition_manager_delegate.cc",
"shell/browser/electron_speech_recognition_manager_delegate.h",
"shell/browser/electron_web_ui_controller_factory.cc",
"shell/browser/electron_web_ui_controller_factory.h",
"shell/browser/auto_updater.cc", "shell/browser/auto_updater.cc",
"shell/browser/auto_updater.h", "shell/browser/auto_updater.h",
"shell/browser/auto_updater_mac.mm", "shell/browser/auto_updater_mac.mm",
"shell/browser/atom_autofill_driver_factory.cc",
"shell/browser/atom_autofill_driver_factory.h",
"shell/browser/atom_autofill_driver.cc",
"shell/browser/atom_autofill_driver.h",
"shell/browser/atom_browser_client.cc",
"shell/browser/atom_browser_client.h",
"shell/browser/atom_browser_context.cc",
"shell/browser/atom_browser_context.h",
"shell/browser/atom_download_manager_delegate.cc",
"shell/browser/atom_download_manager_delegate.h",
"shell/browser/atom_gpu_client.cc",
"shell/browser/atom_gpu_client.h",
"shell/browser/atom_browser_main_parts.cc",
"shell/browser/atom_browser_main_parts.h",
"shell/browser/atom_browser_main_parts_mac.mm",
"shell/browser/atom_browser_main_parts_posix.cc",
"shell/browser/atom_javascript_dialog_manager.cc",
"shell/browser/atom_javascript_dialog_manager.h",
"shell/browser/atom_navigation_throttle.h",
"shell/browser/atom_navigation_throttle.cc",
"shell/browser/atom_paths.h",
"shell/browser/atom_permission_manager.cc",
"shell/browser/atom_permission_manager.h",
"shell/browser/atom_quota_permission_context.cc",
"shell/browser/atom_quota_permission_context.h",
"shell/browser/atom_speech_recognition_manager_delegate.cc",
"shell/browser/atom_speech_recognition_manager_delegate.h",
"shell/browser/atom_web_ui_controller_factory.cc",
"shell/browser/atom_web_ui_controller_factory.h",
"shell/browser/browser.cc", "shell/browser/browser.cc",
"shell/browser/browser.h", "shell/browser/browser.h",
"shell/browser/browser_linux.cc", "shell/browser/browser_linux.cc",
"shell/browser/browser_mac.mm", "shell/browser/browser_mac.mm",
"shell/browser/browser_win.cc",
"shell/browser/browser_observer.h", "shell/browser/browser_observer.h",
"shell/browser/browser_process_impl.cc", "shell/browser/browser_process_impl.cc",
"shell/browser/browser_process_impl.h", "shell/browser/browser_process_impl.h",
"shell/browser/browser_win.cc",
"shell/browser/child_web_contents_tracker.cc", "shell/browser/child_web_contents_tracker.cc",
"shell/browser/child_web_contents_tracker.h", "shell/browser/child_web_contents_tracker.h",
"shell/browser/common_web_contents_delegate_mac.mm",
"shell/browser/common_web_contents_delegate_views.cc",
"shell/browser/common_web_contents_delegate.cc", "shell/browser/common_web_contents_delegate.cc",
"shell/browser/common_web_contents_delegate.h", "shell/browser/common_web_contents_delegate.h",
"shell/browser/common_web_contents_delegate_mac.mm",
"shell/browser/common_web_contents_delegate_views.cc",
"shell/browser/cookie_change_notifier.cc", "shell/browser/cookie_change_notifier.cc",
"shell/browser/cookie_change_notifier.h", "shell/browser/cookie_change_notifier.h",
"shell/browser/feature_list.cc",
"shell/browser/feature_list.h",
"shell/browser/font_defaults.cc",
"shell/browser/font_defaults.h",
"shell/browser/javascript_environment.cc", "shell/browser/javascript_environment.cc",
"shell/browser/javascript_environment.h", "shell/browser/javascript_environment.h",
"shell/browser/lib/bluetooth_chooser.cc", "shell/browser/lib/bluetooth_chooser.cc",
"shell/browser/lib/bluetooth_chooser.h", "shell/browser/lib/bluetooth_chooser.h",
"shell/browser/lib/power_observer.h", "shell/browser/lib/power_observer.h",
"shell/browser/lib/power_observer_linux.h",
"shell/browser/lib/power_observer_linux.cc", "shell/browser/lib/power_observer_linux.cc",
"shell/browser/lib/power_observer_linux.h",
"shell/browser/linux/unity_service.cc",
"shell/browser/linux/unity_service.h",
"shell/browser/login_handler.cc", "shell/browser/login_handler.cc",
"shell/browser/login_handler.h", "shell/browser/login_handler.h",
"shell/browser/mac/atom_application.h", "shell/browser/mac/electron_application.h",
"shell/browser/mac/atom_application.mm", "shell/browser/mac/electron_application.mm",
"shell/browser/mac/atom_application_delegate.h", "shell/browser/mac/electron_application_delegate.h",
"shell/browser/mac/atom_application_delegate.mm", "shell/browser/mac/electron_application_delegate.mm",
"shell/browser/mac/dict_util.h", "shell/browser/mac/dict_util.h",
"shell/browser/mac/dict_util.mm", "shell/browser/mac/dict_util.mm",
"shell/browser/mac/in_app_purchase.h", "shell/browser/mac/in_app_purchase.h",
@@ -195,48 +202,55 @@ filenames = {
"shell/browser/mac/in_app_purchase_observer.mm", "shell/browser/mac/in_app_purchase_observer.mm",
"shell/browser/mac/in_app_purchase_product.h", "shell/browser/mac/in_app_purchase_product.h",
"shell/browser/mac/in_app_purchase_product.mm", "shell/browser/mac/in_app_purchase_product.mm",
"shell/browser/microtasks_runner.cc",
"shell/browser/microtasks_runner.h",
"shell/browser/native_browser_view.cc",
"shell/browser/native_browser_view.h",
"shell/browser/native_browser_view_mac.h",
"shell/browser/native_browser_view_mac.mm",
"shell/browser/native_browser_view_views.h",
"shell/browser/native_browser_view_views.cc",
"shell/browser/native_window.cc",
"shell/browser/native_window.h",
"shell/browser/native_window_views_win.cc",
"shell/browser/native_window_views.cc",
"shell/browser/native_window_views.h",
"shell/browser/native_window_mac.h",
"shell/browser/native_window_mac.mm",
"shell/browser/native_window_observer.h",
"shell/browser/media/media_capture_devices_dispatcher.cc", "shell/browser/media/media_capture_devices_dispatcher.cc",
"shell/browser/media/media_capture_devices_dispatcher.h", "shell/browser/media/media_capture_devices_dispatcher.h",
"shell/browser/media/media_device_id_salt.cc", "shell/browser/media/media_device_id_salt.cc",
"shell/browser/media/media_device_id_salt.h", "shell/browser/media/media_device_id_salt.h",
"shell/browser/media/media_stream_devices_controller.cc", "shell/browser/media/media_stream_devices_controller.cc",
"shell/browser/media/media_stream_devices_controller.h", "shell/browser/media/media_stream_devices_controller.h",
"shell/browser/microtasks_runner.cc",
"shell/browser/microtasks_runner.h",
"shell/browser/native_browser_view.cc",
"shell/browser/native_browser_view.h",
"shell/browser/native_browser_view_mac.h",
"shell/browser/native_browser_view_mac.mm",
"shell/browser/native_browser_view_views.cc",
"shell/browser/native_browser_view_views.h",
"shell/browser/native_window.cc",
"shell/browser/native_window.h",
"shell/browser/native_window_mac.h",
"shell/browser/native_window_mac.mm",
"shell/browser/native_window_observer.h",
"shell/browser/native_window_views.cc",
"shell/browser/native_window_views.h",
"shell/browser/native_window_views_win.cc",
"shell/browser/net/asar/asar_url_loader.cc", "shell/browser/net/asar/asar_url_loader.cc",
"shell/browser/net/asar/asar_url_loader.h", "shell/browser/net/asar/asar_url_loader.h",
"shell/browser/net/atom_url_loader_factory.cc", "shell/browser/net/electron_url_loader_factory.cc",
"shell/browser/net/atom_url_loader_factory.h", "shell/browser/net/electron_url_loader_factory.h",
"shell/browser/net/cert_verifier_client.cc", "shell/browser/net/cert_verifier_client.cc",
"shell/browser/net/cert_verifier_client.h", "shell/browser/net/cert_verifier_client.h",
"shell/browser/net/proxying_url_loader_factory.cc",
"shell/browser/net/proxying_url_loader_factory.h",
"shell/browser/net/network_context_service_factory.cc",
"shell/browser/net/network_context_service_factory.h",
"shell/browser/net/network_context_service.cc", "shell/browser/net/network_context_service.cc",
"shell/browser/net/network_context_service.h", "shell/browser/net/network_context_service.h",
"shell/browser/net/network_context_service_factory.cc",
"shell/browser/net/network_context_service_factory.h",
"shell/browser/net/node_stream_loader.cc", "shell/browser/net/node_stream_loader.cc",
"shell/browser/net/node_stream_loader.h", "shell/browser/net/node_stream_loader.h",
"shell/browser/net/proxying_url_loader_factory.cc",
"shell/browser/net/proxying_url_loader_factory.h",
"shell/browser/net/proxying_websocket.cc",
"shell/browser/net/proxying_websocket.h",
"shell/browser/net/resolve_proxy_helper.cc", "shell/browser/net/resolve_proxy_helper.cc",
"shell/browser/net/resolve_proxy_helper.h", "shell/browser/net/resolve_proxy_helper.h",
"shell/browser/net/system_network_context_manager.cc", "shell/browser/net/system_network_context_manager.cc",
"shell/browser/net/system_network_context_manager.h", "shell/browser/net/system_network_context_manager.h",
"shell/browser/net/url_pipe_loader.cc", "shell/browser/net/url_pipe_loader.cc",
"shell/browser/net/url_pipe_loader.h", "shell/browser/net/url_pipe_loader.h",
"shell/browser/net/web_request_api_interface.h",
"shell/browser/network_hints_handler_impl.cc",
"shell/browser/network_hints_handler_impl.h",
"shell/browser/node_debugger.cc",
"shell/browser/node_debugger.h",
"shell/browser/notifications/linux/libnotify_notification.cc", "shell/browser/notifications/linux/libnotify_notification.cc",
"shell/browser/notifications/linux/libnotify_notification.h", "shell/browser/notifications/linux/libnotify_notification.h",
"shell/browser/notifications/linux/notification_presenter_linux.cc", "shell/browser/notifications/linux/notification_presenter_linux.cc",
@@ -269,67 +283,65 @@ filenames = {
"shell/browser/notifications/win/win32_notification.h", "shell/browser/notifications/win/win32_notification.h",
"shell/browser/notifications/win/windows_toast_notification.cc", "shell/browser/notifications/win/windows_toast_notification.cc",
"shell/browser/notifications/win/windows_toast_notification.h", "shell/browser/notifications/win/windows_toast_notification.h",
"shell/browser/node_debugger.cc",
"shell/browser/node_debugger.h",
"shell/browser/pref_store_delegate.cc", "shell/browser/pref_store_delegate.cc",
"shell/browser/pref_store_delegate.h", "shell/browser/pref_store_delegate.h",
"shell/browser/relauncher.cc",
"shell/browser/relauncher.h",
"shell/browser/relauncher_linux.cc", "shell/browser/relauncher_linux.cc",
"shell/browser/relauncher_mac.cc", "shell/browser/relauncher_mac.cc",
"shell/browser/relauncher_win.cc", "shell/browser/relauncher_win.cc",
"shell/browser/relauncher.cc",
"shell/browser/relauncher.h",
"shell/browser/renderer_host/electron_render_message_filter.cc",
"shell/browser/renderer_host/electron_render_message_filter.h",
"shell/browser/session_preferences.cc", "shell/browser/session_preferences.cc",
"shell/browser/session_preferences.h", "shell/browser/session_preferences.h",
"shell/browser/special_storage_policy.cc", "shell/browser/special_storage_policy.cc",
"shell/browser/special_storage_policy.h", "shell/browser/special_storage_policy.h",
"shell/browser/ui/accelerator_util.cc", "shell/browser/ui/accelerator_util.cc",
"shell/browser/ui/accelerator_util.h", "shell/browser/ui/accelerator_util.h",
"shell/browser/ui/atom_menu_model.cc", "shell/browser/ui/electron_menu_model.cc",
"shell/browser/ui/atom_menu_model.h", "shell/browser/ui/electron_menu_model.h",
"shell/browser/ui/autofill_popup.cc", "shell/browser/ui/autofill_popup.cc",
"shell/browser/ui/autofill_popup.h", "shell/browser/ui/autofill_popup.h",
"shell/browser/ui/certificate_trust.h", "shell/browser/ui/certificate_trust.h",
"shell/browser/ui/certificate_trust_mac.mm", "shell/browser/ui/certificate_trust_mac.mm",
"shell/browser/ui/certificate_trust_win.cc", "shell/browser/ui/certificate_trust_win.cc",
"shell/browser/ui/cocoa/atom_bundle_mover.h", "shell/browser/ui/cocoa/NSColor+Hex.h",
"shell/browser/ui/cocoa/atom_bundle_mover.mm", "shell/browser/ui/cocoa/NSColor+Hex.mm",
"shell/browser/ui/cocoa/atom_menu_controller.h", "shell/browser/ui/cocoa/NSString+ANSI.h",
"shell/browser/ui/cocoa/atom_menu_controller.mm", "shell/browser/ui/cocoa/NSString+ANSI.mm",
"shell/browser/ui/cocoa/atom_native_widget_mac.h", "shell/browser/ui/cocoa/electron_bundle_mover.h",
"shell/browser/ui/cocoa/atom_native_widget_mac.mm", "shell/browser/ui/cocoa/electron_bundle_mover.mm",
"shell/browser/ui/cocoa/atom_ns_window.h", "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h",
"shell/browser/ui/cocoa/atom_ns_window.mm", "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm",
"shell/browser/ui/cocoa/atom_ns_window_delegate.h", "shell/browser/ui/cocoa/electron_menu_controller.h",
"shell/browser/ui/cocoa/atom_ns_window_delegate.mm", "shell/browser/ui/cocoa/electron_menu_controller.mm",
"shell/browser/ui/cocoa/atom_preview_item.h", "shell/browser/ui/cocoa/electron_native_widget_mac.h",
"shell/browser/ui/cocoa/atom_preview_item.mm", "shell/browser/ui/cocoa/electron_native_widget_mac.mm",
"shell/browser/ui/cocoa/atom_touch_bar.h", "shell/browser/ui/cocoa/electron_ns_window.h",
"shell/browser/ui/cocoa/atom_touch_bar.mm", "shell/browser/ui/cocoa/electron_ns_window.mm",
"shell/browser/ui/cocoa/electron_ns_window_delegate.h",
"shell/browser/ui/cocoa/electron_ns_window_delegate.mm",
"shell/browser/ui/cocoa/electron_preview_item.h",
"shell/browser/ui/cocoa/electron_preview_item.mm",
"shell/browser/ui/cocoa/electron_touch_bar.h",
"shell/browser/ui/cocoa/electron_touch_bar.mm",
"shell/browser/ui/cocoa/delayed_native_view_host.cc", "shell/browser/ui/cocoa/delayed_native_view_host.cc",
"shell/browser/ui/cocoa/delayed_native_view_host.h", "shell/browser/ui/cocoa/delayed_native_view_host.h",
"shell/browser/ui/cocoa/views_delegate_mac.h",
"shell/browser/ui/cocoa/views_delegate_mac.mm",
"shell/browser/ui/cocoa/root_view_mac.mm",
"shell/browser/ui/cocoa/root_view_mac.h",
"shell/browser/ui/cocoa/atom_inspectable_web_contents_view.h",
"shell/browser/ui/cocoa/atom_inspectable_web_contents_view.mm",
"shell/browser/ui/cocoa/event_dispatching_window.h", "shell/browser/ui/cocoa/event_dispatching_window.h",
"shell/browser/ui/cocoa/event_dispatching_window.mm", "shell/browser/ui/cocoa/event_dispatching_window.mm",
"shell/browser/ui/cocoa/root_view_mac.h",
"shell/browser/ui/cocoa/root_view_mac.mm",
"shell/browser/ui/cocoa/views_delegate_mac.h",
"shell/browser/ui/cocoa/views_delegate_mac.mm",
"shell/browser/ui/devtools_manager_delegate.cc", "shell/browser/ui/devtools_manager_delegate.cc",
"shell/browser/ui/devtools_manager_delegate.h", "shell/browser/ui/devtools_manager_delegate.h",
"shell/browser/ui/devtools_ui.cc", "shell/browser/ui/devtools_ui.cc",
"shell/browser/ui/devtools_ui.h", "shell/browser/ui/devtools_ui.h",
"shell/browser/ui/drag_util.h",
"shell/browser/ui/drag_util_mac.mm", "shell/browser/ui/drag_util_mac.mm",
"shell/browser/ui/drag_util_views.cc", "shell/browser/ui/drag_util_views.cc",
"shell/browser/ui/drag_util.h",
"shell/browser/ui/file_dialog.h", "shell/browser/ui/file_dialog.h",
"shell/browser/ui/file_dialog_gtk.cc", "shell/browser/ui/file_dialog_gtk.cc",
"shell/browser/ui/file_dialog_mac.mm", "shell/browser/ui/file_dialog_mac.mm",
"shell/browser/ui/file_dialog_win.cc", "shell/browser/ui/file_dialog_win.cc",
"shell/browser/ui/util_gtk.cc",
"shell/browser/ui/util_gtk.h",
"shell/browser/ui/inspectable_web_contents.cc", "shell/browser/ui/inspectable_web_contents.cc",
"shell/browser/ui/inspectable_web_contents.h", "shell/browser/ui/inspectable_web_contents.h",
"shell/browser/ui/inspectable_web_contents_delegate.h", "shell/browser/ui/inspectable_web_contents_delegate.h",
@@ -344,20 +356,17 @@ filenames = {
"shell/browser/ui/message_box_gtk.cc", "shell/browser/ui/message_box_gtk.cc",
"shell/browser/ui/message_box_mac.mm", "shell/browser/ui/message_box_mac.mm",
"shell/browser/ui/message_box_win.cc", "shell/browser/ui/message_box_win.cc",
"shell/browser/ui/cocoa/NSColor+Hex.mm",
"shell/browser/ui/cocoa/NSColor+Hex.h",
"shell/browser/ui/cocoa/NSString+ANSI.mm",
"shell/browser/ui/cocoa/NSString+ANSI.h",
"shell/browser/ui/tray_icon.cc", "shell/browser/ui/tray_icon.cc",
"shell/browser/ui/tray_icon.h", "shell/browser/ui/tray_icon.h",
"shell/browser/ui/tray_icon_gtk.cc",
"shell/browser/ui/tray_icon_gtk.h",
"shell/browser/ui/tray_icon_cocoa.h", "shell/browser/ui/tray_icon_cocoa.h",
"shell/browser/ui/tray_icon_cocoa.mm", "shell/browser/ui/tray_icon_cocoa.mm",
"shell/browser/ui/tray_icon_gtk.cc",
"shell/browser/ui/tray_icon_gtk.h",
"shell/browser/ui/tray_icon_observer.h", "shell/browser/ui/tray_icon_observer.h",
"shell/browser/ui/tray_icon_win.cc", "shell/browser/ui/tray_icon_win.cc",
"shell/browser/ui/views/atom_views_delegate.cc", "shell/browser/ui/views/electron_views_delegate.cc",
"shell/browser/ui/views/atom_views_delegate.h", "shell/browser/ui/views/electron_views_delegate_win.cc",
"shell/browser/ui/views/electron_views_delegate.h",
"shell/browser/ui/views/autofill_popup_view.cc", "shell/browser/ui/views/autofill_popup_view.cc",
"shell/browser/ui/views/autofill_popup_view.h", "shell/browser/ui/views/autofill_popup_view.h",
"shell/browser/ui/views/frameless_view.cc", "shell/browser/ui/views/frameless_view.cc",
@@ -380,16 +389,16 @@ filenames = {
"shell/browser/ui/views/submenu_button.h", "shell/browser/ui/views/submenu_button.h",
"shell/browser/ui/views/win_frame_view.cc", "shell/browser/ui/views/win_frame_view.cc",
"shell/browser/ui/views/win_frame_view.h", "shell/browser/ui/views/win_frame_view.h",
"shell/browser/ui/win/atom_desktop_native_widget_aura.cc", "shell/browser/ui/win/electron_desktop_native_widget_aura.cc",
"shell/browser/ui/win/atom_desktop_native_widget_aura.h", "shell/browser/ui/win/electron_desktop_native_widget_aura.h",
"shell/browser/ui/win/atom_desktop_window_tree_host_win.cc", "shell/browser/ui/win/electron_desktop_window_tree_host_win.cc",
"shell/browser/ui/win/atom_desktop_window_tree_host_win.h", "shell/browser/ui/win/electron_desktop_window_tree_host_win.h",
"shell/browser/ui/win/jump_list.cc", "shell/browser/ui/win/jump_list.cc",
"shell/browser/ui/win/jump_list.h", "shell/browser/ui/win/jump_list.h",
"shell/browser/ui/win/notify_icon_host.cc",
"shell/browser/ui/win/notify_icon_host.h",
"shell/browser/ui/win/notify_icon.cc", "shell/browser/ui/win/notify_icon.cc",
"shell/browser/ui/win/notify_icon.h", "shell/browser/ui/win/notify_icon.h",
"shell/browser/ui/win/notify_icon_host.cc",
"shell/browser/ui/win/notify_icon_host.h",
"shell/browser/ui/win/taskbar_host.cc", "shell/browser/ui/win/taskbar_host.cc",
"shell/browser/ui/win/taskbar_host.h", "shell/browser/ui/win/taskbar_host.h",
"shell/browser/ui/x/event_disabler.cc", "shell/browser/ui/x/event_disabler.cc",
@@ -400,8 +409,6 @@ filenames = {
"shell/browser/ui/x/x_window_utils.h", "shell/browser/ui/x/x_window_utils.h",
"shell/browser/unresponsive_suppressor.cc", "shell/browser/unresponsive_suppressor.cc",
"shell/browser/unresponsive_suppressor.h", "shell/browser/unresponsive_suppressor.h",
"shell/browser/win/scoped_hstring.cc",
"shell/browser/win/scoped_hstring.h",
"shell/browser/web_contents_permission_helper.cc", "shell/browser/web_contents_permission_helper.cc",
"shell/browser/web_contents_permission_helper.h", "shell/browser/web_contents_permission_helper.h",
"shell/browser/web_contents_preferences.cc", "shell/browser/web_contents_preferences.cc",
@@ -414,46 +421,48 @@ filenames = {
"shell/browser/web_view_guest_delegate.h", "shell/browser/web_view_guest_delegate.h",
"shell/browser/web_view_manager.cc", "shell/browser/web_view_manager.cc",
"shell/browser/web_view_manager.h", "shell/browser/web_view_manager.h",
"shell/browser/win/scoped_hstring.cc",
"shell/browser/win/scoped_hstring.h",
"shell/browser/window_list.cc", "shell/browser/window_list.cc",
"shell/browser/window_list.h", "shell/browser/window_list.h",
"shell/browser/window_list_observer.h", "shell/browser/window_list_observer.h",
"shell/browser/zoom_level_delegate.cc", "shell/browser/zoom_level_delegate.cc",
"shell/browser/zoom_level_delegate.h", "shell/browser/zoom_level_delegate.h",
"shell/common/api/atom_api_asar.cc", "shell/common/api/electron_api_asar.cc",
"shell/common/api/atom_api_clipboard.cc", "shell/common/api/electron_api_clipboard.cc",
"shell/common/api/atom_api_clipboard.h", "shell/common/api/electron_api_clipboard.h",
"shell/common/api/atom_api_clipboard_mac.mm", "shell/common/api/electron_api_clipboard_mac.mm",
"shell/common/api/atom_api_command_line.cc", "shell/common/api/electron_api_command_line.cc",
"shell/common/api/atom_api_crash_reporter.cc", "shell/common/api/electron_api_crash_reporter.cc",
"shell/common/api/atom_api_key_weak_map.h", "shell/common/api/electron_api_key_weak_map.h",
"shell/common/api/atom_api_native_image.cc", "shell/common/api/electron_api_native_image.cc",
"shell/common/api/atom_api_native_image.h", "shell/common/api/electron_api_native_image.h",
"shell/common/api/atom_api_native_image_mac.mm", "shell/common/api/electron_api_native_image_mac.mm",
"shell/common/api/atom_api_shell.cc", "shell/common/api/electron_api_shell.cc",
"shell/common/api/atom_api_v8_util.cc", "shell/common/api/electron_api_v8_util.cc",
"shell/common/api/constructor.h",
"shell/common/api/electron_bindings.cc", "shell/common/api/electron_bindings.cc",
"shell/common/api/electron_bindings.h", "shell/common/api/electron_bindings.h",
"shell/common/api/constructor.h",
"shell/common/api/event_emitter_caller_deprecated.cc", "shell/common/api/event_emitter_caller_deprecated.cc",
"shell/common/api/event_emitter_caller_deprecated.h", "shell/common/api/event_emitter_caller_deprecated.h",
"shell/common/api/features.cc", "shell/common/api/features.cc",
"shell/common/api/locker.cc", "shell/common/api/locker.cc",
"shell/common/api/locker.h", "shell/common/api/locker.h",
"shell/common/application_info.cc",
"shell/common/application_info.h",
"shell/common/application_info_linux.cc",
"shell/common/application_info_mac.mm",
"shell/common/application_info_win.cc",
"shell/common/asar/archive.cc", "shell/common/asar/archive.cc",
"shell/common/asar/archive.h", "shell/common/asar/archive.h",
"shell/common/asar/asar_util.cc", "shell/common/asar/asar_util.cc",
"shell/common/asar/asar_util.h", "shell/common/asar/asar_util.h",
"shell/common/asar/scoped_temporary_file.cc", "shell/common/asar/scoped_temporary_file.cc",
"shell/common/asar/scoped_temporary_file.h", "shell/common/asar/scoped_temporary_file.h",
"shell/common/application_info_linux.cc", "shell/common/electron_command_line.cc",
"shell/common/application_info_mac.mm", "shell/common/electron_command_line.h",
"shell/common/application_info_win.cc", "shell/common/electron_constants.cc",
"shell/common/application_info.cc", "shell/common/electron_constants.h",
"shell/common/application_info.h",
"shell/common/atom_command_line.cc",
"shell/common/atom_command_line.h",
"shell/common/atom_constants.cc",
"shell/common/atom_constants.h",
"shell/common/color_util.cc", "shell/common/color_util.cc",
"shell/common/color_util.h", "shell/common/color_util.h",
"shell/common/crash_reporter/crash_reporter.cc", "shell/common/crash_reporter/crash_reporter.cc",
@@ -462,12 +471,15 @@ filenames = {
"shell/common/crash_reporter/crash_reporter_linux.h", "shell/common/crash_reporter/crash_reporter_linux.h",
"shell/common/crash_reporter/crash_reporter_mac.h", "shell/common/crash_reporter/crash_reporter_mac.h",
"shell/common/crash_reporter/crash_reporter_mac.mm", "shell/common/crash_reporter/crash_reporter_mac.mm",
"shell/common/crash_reporter/crash_reporter_win.h",
"shell/common/crash_reporter/crash_reporter_win.cc", "shell/common/crash_reporter/crash_reporter_win.cc",
"shell/common/crash_reporter/crash_reporter_win.h",
"shell/common/crash_reporter/linux/crash_dump_handler.cc", "shell/common/crash_reporter/linux/crash_dump_handler.cc",
"shell/common/crash_reporter/linux/crash_dump_handler.h", "shell/common/crash_reporter/linux/crash_dump_handler.h",
"shell/common/crash_reporter/win/crash_service_main.cc", "shell/common/crash_reporter/win/crash_service_main.cc",
"shell/common/crash_reporter/win/crash_service_main.h", "shell/common/crash_reporter/win/crash_service_main.h",
"shell/common/deprecate_util.cc",
"shell/common/deprecate_util.h",
"shell/common/gin_converters/blink_converter_gin_adapter.h",
"shell/common/gin_converters/callback_converter.h", "shell/common/gin_converters/callback_converter.h",
"shell/common/gin_converters/file_dialog_converter.cc", "shell/common/gin_converters/file_dialog_converter.cc",
"shell/common/gin_converters/file_dialog_converter.h", "shell/common/gin_converters/file_dialog_converter.h",
@@ -483,7 +495,6 @@ filenames = {
"shell/common/gin_converters/net_converter.cc", "shell/common/gin_converters/net_converter.cc",
"shell/common/gin_converters/net_converter.h", "shell/common/gin_converters/net_converter.h",
"shell/common/gin_converters/std_converter.h", "shell/common/gin_converters/std_converter.h",
"shell/common/gin_converters/blink_converter_gin_adapter.h",
"shell/common/gin_converters/value_converter_gin_adapter.h", "shell/common/gin_converters/value_converter_gin_adapter.h",
"shell/common/gin_helper/arguments.cc", "shell/common/gin_helper/arguments.cc",
"shell/common/gin_helper/arguments.h", "shell/common/gin_helper/arguments.h",
@@ -494,10 +505,10 @@ filenames = {
"shell/common/gin_helper/dictionary.h", "shell/common/gin_helper/dictionary.h",
"shell/common/gin_helper/error_thrower.cc", "shell/common/gin_helper/error_thrower.cc",
"shell/common/gin_helper/error_thrower.h", "shell/common/gin_helper/error_thrower.h",
"shell/common/gin_helper/event_emitter_caller.cc",
"shell/common/gin_helper/event_emitter_caller.h",
"shell/common/gin_helper/event_emitter.cc", "shell/common/gin_helper/event_emitter.cc",
"shell/common/gin_helper/event_emitter.h", "shell/common/gin_helper/event_emitter.h",
"shell/common/gin_helper/event_emitter_caller.cc",
"shell/common/gin_helper/event_emitter_caller.h",
"shell/common/gin_helper/function_template.cc", "shell/common/gin_helper/function_template.cc",
"shell/common/gin_helper/function_template.h", "shell/common/gin_helper/function_template.h",
"shell/common/gin_helper/object_template_builder.cc", "shell/common/gin_helper/object_template_builder.cc",
@@ -507,12 +518,10 @@ filenames = {
"shell/common/key_weak_map.h", "shell/common/key_weak_map.h",
"shell/common/keyboard_util.cc", "shell/common/keyboard_util.cc",
"shell/common/keyboard_util.h", "shell/common/keyboard_util.h",
"shell/common/deprecate_util.cc",
"shell/common/deprecate_util.h",
"shell/common/mouse_util.cc",
"shell/common/mouse_util.h",
"shell/common/mac/main_application_bundle.h", "shell/common/mac/main_application_bundle.h",
"shell/common/mac/main_application_bundle.mm", "shell/common/mac/main_application_bundle.mm",
"shell/common/mouse_util.cc",
"shell/common/mouse_util.h",
"shell/common/native_mate_converters/accelerator_converter.cc", "shell/common/native_mate_converters/accelerator_converter.cc",
"shell/common/native_mate_converters/accelerator_converter.h", "shell/common/native_mate_converters/accelerator_converter.h",
"shell/common/native_mate_converters/blink_converter.cc", "shell/common/native_mate_converters/blink_converter.cc",
@@ -545,51 +554,50 @@ filenames = {
"shell/common/node_bindings_win.cc", "shell/common/node_bindings_win.cc",
"shell/common/node_bindings_win.h", "shell/common/node_bindings_win.h",
"shell/common/node_includes.h", "shell/common/node_includes.h",
"shell/common/node_util.h",
"shell/common/node_util.cc", "shell/common/node_util.cc",
"shell/common/node_util.h",
"shell/common/options_switches.cc", "shell/common/options_switches.cc",
"shell/common/options_switches.h", "shell/common/options_switches.h",
"shell/common/platform_util.h", "shell/common/platform_util.h",
"shell/common/platform_util_linux.cc", "shell/common/platform_util_linux.cc",
"shell/common/platform_util_mac.mm", "shell/common/platform_util_mac.mm",
"shell/common/platform_util_win.cc", "shell/common/platform_util_win.cc",
"shell/common/promise_util.h",
"shell/common/promise_util.cc", "shell/common/promise_util.cc",
"shell/common/skia_util.h", "shell/common/promise_util.h",
"shell/common/skia_util.cc", "shell/common/skia_util.cc",
"shell/renderer/api/context_bridge/render_frame_context_bridge_store.cc", "shell/common/skia_util.h",
"shell/renderer/api/context_bridge/render_frame_context_bridge_store.h", "shell/renderer/api/context_bridge/object_cache.cc",
"shell/renderer/api/atom_api_context_bridge.cc", "shell/renderer/api/context_bridge/object_cache.h",
"shell/renderer/api/atom_api_context_bridge.h", "shell/renderer/api/context_bridge/render_frame_function_store.cc",
"shell/renderer/api/atom_api_renderer_ipc.cc", "shell/renderer/api/context_bridge/render_frame_function_store.h",
"shell/renderer/api/atom_api_spell_check_client.cc", "shell/renderer/api/electron_api_context_bridge.cc",
"shell/renderer/api/atom_api_spell_check_client.h", "shell/renderer/api/electron_api_context_bridge.h",
"shell/renderer/api/atom_api_web_frame.cc", "shell/renderer/api/electron_api_renderer_ipc.cc",
"shell/renderer/atom_autofill_agent.cc", "shell/renderer/api/electron_api_spell_check_client.cc",
"shell/renderer/atom_autofill_agent.h", "shell/renderer/api/electron_api_spell_check_client.h",
"shell/renderer/atom_render_frame_observer.cc", "shell/renderer/api/electron_api_web_frame.cc",
"shell/renderer/atom_render_frame_observer.h", "shell/renderer/electron_autofill_agent.cc",
"shell/renderer/atom_renderer_client.cc", "shell/renderer/electron_autofill_agent.h",
"shell/renderer/atom_renderer_client.h", "shell/renderer/electron_render_frame_observer.cc",
"shell/renderer/electron_render_frame_observer.h",
"shell/renderer/electron_renderer_client.cc",
"shell/renderer/electron_renderer_client.h",
"shell/renderer/electron_sandboxed_renderer_client.cc",
"shell/renderer/electron_sandboxed_renderer_client.h",
"shell/renderer/browser_exposed_renderer_interfaces.cc",
"shell/renderer/browser_exposed_renderer_interfaces.h",
"shell/renderer/content_settings_observer.cc", "shell/renderer/content_settings_observer.cc",
"shell/renderer/content_settings_observer.h", "shell/renderer/content_settings_observer.h",
"shell/renderer/electron_api_service_impl.cc", "shell/renderer/electron_api_service_impl.cc",
"shell/renderer/electron_api_service_impl.h", "shell/renderer/electron_api_service_impl.h",
"shell/renderer/atom_sandboxed_renderer_client.cc",
"shell/renderer/atom_sandboxed_renderer_client.h",
"shell/renderer/guest_view_container.cc", "shell/renderer/guest_view_container.cc",
"shell/renderer/guest_view_container.h", "shell/renderer/guest_view_container.h",
"shell/renderer/renderer_client_base.cc", "shell/renderer/renderer_client_base.cc",
"shell/renderer/renderer_client_base.h", "shell/renderer/renderer_client_base.h",
"shell/renderer/web_worker_observer.cc", "shell/renderer/web_worker_observer.cc",
"shell/renderer/web_worker_observer.h", "shell/renderer/web_worker_observer.h",
"shell/utility/atom_content_utility_client.cc", "shell/utility/electron_content_utility_client.cc",
"shell/utility/atom_content_utility_client.h", "shell/utility/electron_content_utility_client.h",
"chromium_src/chrome/browser/process_singleton_posix.cc",
"chromium_src/chrome/browser/process_singleton_win.cc",
"chromium_src/chrome/browser/process_singleton.h",
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc",
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h",
] ]
lib_sources_nss = [ lib_sources_nss = [
@@ -598,43 +606,43 @@ filenames = {
] ]
lib_sources_extensions = [ lib_sources_extensions = [
"shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc", "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
"shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h", "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h",
"shell/browser/extensions/atom_extensions_browser_client.cc", "shell/browser/extensions/electron_extensions_browser_client.cc",
"shell/browser/extensions/atom_extensions_browser_client.h", "shell/browser/extensions/electron_extensions_browser_client.h",
"shell/browser/extensions/atom_browser_context_keyed_service_factories.cc", "shell/browser/extensions/electron_browser_context_keyed_service_factories.cc",
"shell/browser/extensions/atom_browser_context_keyed_service_factories.h", "shell/browser/extensions/electron_browser_context_keyed_service_factories.h",
"shell/browser/extensions/atom_display_info_provider.cc", "shell/browser/extensions/electron_display_info_provider.cc",
"shell/browser/extensions/atom_display_info_provider.h", "shell/browser/extensions/electron_display_info_provider.h",
"shell/browser/extensions/atom_extension_host_delegate.cc", "shell/browser/extensions/electron_extension_host_delegate.cc",
"shell/browser/extensions/atom_extension_host_delegate.h", "shell/browser/extensions/electron_extension_host_delegate.h",
"shell/browser/extensions/atom_extension_loader.cc", "shell/browser/extensions/electron_extension_loader.cc",
"shell/browser/extensions/atom_extension_loader.h", "shell/browser/extensions/electron_extension_loader.h",
"shell/browser/extensions/atom_extension_system.cc", "shell/browser/extensions/electron_extension_system.cc",
"shell/browser/extensions/atom_extension_system.h", "shell/browser/extensions/electron_extension_system.h",
"shell/browser/extensions/atom_extension_system_factory.cc", "shell/browser/extensions/electron_extension_system_factory.cc",
"shell/browser/extensions/atom_extension_system_factory.h", "shell/browser/extensions/electron_extension_system_factory.h",
"shell/browser/extensions/atom_extension_web_contents_observer.cc", "shell/browser/extensions/electron_extension_web_contents_observer.cc",
"shell/browser/extensions/atom_extension_web_contents_observer.h", "shell/browser/extensions/electron_extension_web_contents_observer.h",
"shell/browser/extensions/atom_navigation_ui_data.cc", "shell/browser/extensions/electron_navigation_ui_data.cc",
"shell/browser/extensions/atom_navigation_ui_data.h", "shell/browser/extensions/electron_navigation_ui_data.h",
"shell/common/extensions/atom_extensions_api_provider.cc", "shell/common/extensions/electron_extensions_api_provider.cc",
"shell/common/extensions/atom_extensions_api_provider.h", "shell/common/extensions/electron_extensions_api_provider.h",
"shell/common/extensions/atom_extensions_client.cc", "shell/common/extensions/electron_extensions_client.cc",
"shell/common/extensions/atom_extensions_client.h", "shell/common/extensions/electron_extensions_client.h",
"shell/renderer/extensions/atom_extensions_renderer_client.cc", "shell/renderer/extensions/electron_extensions_renderer_client.cc",
"shell/renderer/extensions/atom_extensions_renderer_client.h", "shell/renderer/extensions/electron_extensions_renderer_client.h",
] ]
app_sources = [ app_sources = [
"shell/app/atom_main.cc", "shell/app/electron_main.cc",
"shell/app/atom_main.h", "shell/app/electron_main.h",
] ]
framework_sources = [ framework_sources = [
"shell/app/atom_library_main.h", "shell/app/electron_library_main.h",
"shell/app/atom_library_main.mm", "shell/app/electron_library_main.mm",
] ]
login_helper_sources = [ "shell/app/atom_login_helper.mm" ] login_helper_sources = [ "shell/app/electron_login_helper.mm" ]
} }

60
filenames.hunspell.gni Normal file
View File

@@ -0,0 +1,60 @@
hunspell_dictionaries = [
"//third_party/hunspell_dictionaries/af-ZA-3-0.bdic",
"//third_party/hunspell_dictionaries/bg-BG-3-0.bdic",
"//third_party/hunspell_dictionaries/ca-ES-3-0.bdic",
"//third_party/hunspell_dictionaries/cs-CZ-3-0.bdic",
"//third_party/hunspell_dictionaries/cy-GB-1-0.bdic",
"//third_party/hunspell_dictionaries/da-DK-3-0.bdic",
"//third_party/hunspell_dictionaries/de-DE-3-0.bdic",
"//third_party/hunspell_dictionaries/el-GR-3-0.bdic",
"//third_party/hunspell_dictionaries/en-AU-8-0.bdic",
"//third_party/hunspell_dictionaries/en-CA-8-0.bdic",
"//third_party/hunspell_dictionaries/en-GB-8-0.bdic",
"//third_party/hunspell_dictionaries/en-US-8-0.bdic",
"//third_party/hunspell_dictionaries/es-ES-3-0.bdic",
"//third_party/hunspell_dictionaries/et-EE-3-0.bdic",
"//third_party/hunspell_dictionaries/fa-IR-8-0.bdic",
"//third_party/hunspell_dictionaries/fo-FO-3-0.bdic",
"//third_party/hunspell_dictionaries/fr-FR-3-0.bdic",
"//third_party/hunspell_dictionaries/he-IL-3-0.bdic",
"//third_party/hunspell_dictionaries/hi-IN-3-0.bdic",
"//third_party/hunspell_dictionaries/hr-HR-3-0.bdic",
"//third_party/hunspell_dictionaries/hu-HU-3-0.bdic",
"//third_party/hunspell_dictionaries/hy-1-0.bdic",
"//third_party/hunspell_dictionaries/id-ID-3-0.bdic",
"//third_party/hunspell_dictionaries/it-IT-3-0.bdic",
"//third_party/hunspell_dictionaries/ko-3-0.bdic",
"//third_party/hunspell_dictionaries/lt-LT-3-0.bdic",
"//third_party/hunspell_dictionaries/lv-LV-3-0.bdic",
"//third_party/hunspell_dictionaries/nb-NO-3-0.bdic",
"//third_party/hunspell_dictionaries/nl-NL-3-0.bdic",
"//third_party/hunspell_dictionaries/pl-PL-3-0.bdic",
"//third_party/hunspell_dictionaries/pt-BR-3-0.bdic",
"//third_party/hunspell_dictionaries/pt-PT-3-0.bdic",
"//third_party/hunspell_dictionaries/ro-RO-3-0.bdic",
"//third_party/hunspell_dictionaries/ru-RU-3-0.bdic",
"//third_party/hunspell_dictionaries/sh-3-0.bdic",
"//third_party/hunspell_dictionaries/sh-4-0.bdic",
"//third_party/hunspell_dictionaries/sk-SK-3-0.bdic",
"//third_party/hunspell_dictionaries/sl-SI-3-0.bdic",
"//third_party/hunspell_dictionaries/sq-3-0.bdic",
"//third_party/hunspell_dictionaries/sr-3-0.bdic",
"//third_party/hunspell_dictionaries/sr-4-0.bdic",
"//third_party/hunspell_dictionaries/sv-SE-3-0.bdic",
"//third_party/hunspell_dictionaries/ta-IN-3-0.bdic",
"//third_party/hunspell_dictionaries/tg-TG-5-0.bdic",
"//third_party/hunspell_dictionaries/tr-TR-4-0.bdic",
"//third_party/hunspell_dictionaries/uk-UA-3-0.bdic",
"//third_party/hunspell_dictionaries/vi-VN-3-0.bdic",
"//third_party/hunspell_dictionaries/xx-XX-3-0.bdic",
]
hunspell_licenses = [
"//third_party/hunspell_dictionaries/COPYING",
"//third_party/hunspell_dictionaries/COPYING.Apache",
"//third_party/hunspell_dictionaries/COPYING.LESSER",
"//third_party/hunspell_dictionaries/COPYING.LGPL",
"//third_party/hunspell_dictionaries/COPYING.MIT",
"//third_party/hunspell_dictionaries/COPYING.MPL",
"//third_party/hunspell_dictionaries/LICENSE",
]

View File

@@ -1,21 +1,21 @@
import * as fs from 'fs' import * as fs from 'fs';
import * as path from 'path' import * as path from 'path';
import { deprecate, Menu } from 'electron' import { deprecate, Menu } from 'electron';
import { EventEmitter } from 'events' import { EventEmitter } from 'events';
const bindings = process.electronBinding('app') const bindings = process.electronBinding('app');
const commandLine = process.electronBinding('command_line') const commandLine = process.electronBinding('command_line');
const { app, App } = bindings const { app, App } = bindings;
// Only one app object permitted. // Only one app object permitted.
export default app export default app;
let dockMenu: Electron.Menu | null = null let dockMenu: Electron.Menu | null = null;
// App is an EventEmitter. // App is an EventEmitter.
Object.setPrototypeOf(App.prototype, EventEmitter.prototype) Object.setPrototypeOf(App.prototype, EventEmitter.prototype);
EventEmitter.call(app as any) EventEmitter.call(app as any);
Object.assign(app, { Object.assign(app, {
commandLine: { commandLine: {
@@ -24,99 +24,99 @@ Object.assign(app, {
appendSwitch: (theSwitch: string, value?: string) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)), appendSwitch: (theSwitch: string, value?: string) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)),
appendArgument: (arg: string) => commandLine.appendArgument(String(arg)) appendArgument: (arg: string) => commandLine.appendArgument(String(arg))
} as Electron.CommandLine } as Electron.CommandLine
}) });
// we define this here because it'd be overly complicated to // we define this here because it'd be overly complicated to
// do in native land // do in native land
Object.defineProperty(app, 'applicationMenu', { Object.defineProperty(app, 'applicationMenu', {
get () { get () {
return Menu.getApplicationMenu() return Menu.getApplicationMenu();
}, },
set (menu: Electron.Menu | null) { set (menu: Electron.Menu | null) {
return Menu.setApplicationMenu(menu) return Menu.setApplicationMenu(menu);
} }
}) });
App.prototype.isPackaged = (() => { App.prototype.isPackaged = (() => {
const execFile = path.basename(process.execPath).toLowerCase() const execFile = path.basename(process.execPath).toLowerCase();
if (process.platform === 'win32') { if (process.platform === 'win32') {
return execFile !== 'electron.exe' return execFile !== 'electron.exe';
} }
return execFile !== 'electron' return execFile !== 'electron';
})() })();
app._setDefaultAppPaths = (packagePath) => { app._setDefaultAppPaths = (packagePath) => {
// Set the user path according to application's name. // Set the user path according to application's name.
app.setPath('userData', path.join(app.getPath('appData'), app.name!)) app.setPath('userData', path.join(app.getPath('appData'), app.name!));
app.setPath('userCache', path.join(app.getPath('cache'), app.name!)) app.setPath('userCache', path.join(app.getPath('cache'), app.name!));
app.setAppPath(packagePath) app.setAppPath(packagePath);
// Add support for --user-data-dir= // Add support for --user-data-dir=
if (app.commandLine.hasSwitch('user-data-dir')) { if (app.commandLine.hasSwitch('user-data-dir')) {
const userDataDir = app.commandLine.getSwitchValue('user-data-dir') const userDataDir = app.commandLine.getSwitchValue('user-data-dir');
if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir) if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir);
} }
} };
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
const setDockMenu = app.dock!.setMenu const setDockMenu = app.dock!.setMenu;
app.dock!.setMenu = (menu) => { app.dock!.setMenu = (menu) => {
dockMenu = menu dockMenu = menu;
setDockMenu(menu) setDockMenu(menu);
} };
app.dock!.getMenu = () => dockMenu app.dock!.getMenu = () => dockMenu;
} }
if (process.platform === 'linux') { if (process.platform === 'linux') {
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m;
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m;
const getStatus = (pid: number) => { const getStatus = (pid: number) => {
try { try {
return fs.readFileSync(`/proc/${pid}/status`, 'utf8') return fs.readFileSync(`/proc/${pid}/status`, 'utf8');
} catch { } catch {
return '' return '';
} }
} };
const getEntry = (file: string, pattern: RegExp) => { const getEntry = (file: string, pattern: RegExp) => {
const match = file.match(pattern) const match = file.match(pattern);
return match ? parseInt(match[1], 10) : 0 return match ? parseInt(match[1], 10) : 0;
} };
const getProcessMemoryInfo = (pid: number) => { const getProcessMemoryInfo = (pid: number) => {
const file = getStatus(pid) const file = getStatus(pid);
return { return {
workingSetSize: getEntry(file, patternVmRSS), workingSetSize: getEntry(file, patternVmRSS),
peakWorkingSetSize: getEntry(file, patternVmHWM) peakWorkingSetSize: getEntry(file, patternVmHWM)
} };
} };
const nativeFn = app.getAppMetrics const nativeFn = app.getAppMetrics;
app.getAppMetrics = () => { app.getAppMetrics = () => {
const metrics = nativeFn.call(app) const metrics = nativeFn.call(app);
for (const metric of metrics) { for (const metric of metrics) {
metric.memory = getProcessMemoryInfo(metric.pid) metric.memory = getProcessMemoryInfo(metric.pid);
} }
return metrics return metrics;
} };
} }
// Routes the events to webContents. // Routes the events to webContents.
const events = ['login', 'certificate-error', 'select-client-certificate'] const events = ['certificate-error', 'select-client-certificate'];
for (const name of events) { for (const name of events) {
app.on(name as 'login', (event, webContents, ...args: any[]) => { app.on(name as 'certificate-error', (event, webContents, ...args: any[]) => {
webContents.emit(name, event, ...args) webContents.emit(name, event, ...args);
}) });
} }
// Property Deprecations // Property Deprecations
deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled') deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled');
deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount') deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount');
deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName') deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName');
// Wrappers for native classes. // Wrappers for native classes.
const { DownloadItem } = process.electronBinding('download_item') const { DownloadItem } = process.electronBinding('download_item');
Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype) Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype);

View File

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

View File

@@ -1,10 +1,10 @@
'use strict' 'use strict';
const EventEmitter = require('events').EventEmitter const EventEmitter = require('events').EventEmitter;
const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater') const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater');
// AutoUpdater is an EventEmitter. // AutoUpdater is an EventEmitter.
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype) Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
EventEmitter.call(autoUpdater) EventEmitter.call(autoUpdater);
module.exports = autoUpdater module.exports = autoUpdater;

View File

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

View File

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

View File

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

View File

@@ -1,49 +1,49 @@
'use strict' 'use strict';
const electron = require('electron') const electron = require('electron');
const { WebContentsView, TopLevelWindow, deprecate } = electron const { WebContentsView, TopLevelWindow, deprecate } = electron;
const { BrowserWindow } = process.electronBinding('window') const { BrowserWindow } = process.electronBinding('window');
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype) Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype);
BrowserWindow.prototype._init = function () { BrowserWindow.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
TopLevelWindow.prototype._init.call(this) TopLevelWindow.prototype._init.call(this);
// Avoid recursive require. // Avoid recursive require.
const { app } = electron const { app } = electron;
// Create WebContentsView. // Create WebContentsView.
this.setContentView(new WebContentsView(this.webContents)) this.setContentView(new WebContentsView(this.webContents));
const nativeSetBounds = this.setBounds const nativeSetBounds = this.setBounds;
this.setBounds = (bounds, ...opts) => { this.setBounds = (bounds, ...opts) => {
bounds = { bounds = {
...this.getBounds(), ...this.getBounds(),
...bounds ...bounds
} };
nativeSetBounds.call(this, bounds, ...opts) nativeSetBounds.call(this, bounds, ...opts);
} };
// window.resizeTo(...) // window.resizeTo(...)
// window.moveTo(...) // window.moveTo(...)
this.webContents.on('move', (event, size) => { this.webContents.on('move', (event, size) => {
this.setBounds(size) this.setBounds(size);
}) });
// Hide the auto-hide menu when webContents is focused. // Hide the auto-hide menu when webContents is focused.
this.webContents.on('activate', () => { this.webContents.on('activate', () => {
if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) { if (process.platform !== 'darwin' && this.autoHideMenuBar && this.isMenuBarVisible()) {
this.setMenuBarVisibility(false) this.setMenuBarVisibility(false);
} }
}) });
// Change window title to page title. // Change window title to page title.
this.webContents.on('page-title-updated', (event, title, ...args) => { this.webContents.on('page-title-updated', (event, title, ...args) => {
// Route the event to BrowserWindow. // Route the event to BrowserWindow.
this.emit('page-title-updated', event, title, ...args) this.emit('page-title-updated', event, title, ...args);
if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title) if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title);
}) });
// Sometimes the webContents doesn't get focus when window is shown, so we // Sometimes the webContents doesn't get focus when window is shown, so we
// have to force focusing on webContents in this case. The safest way is to // have to force focusing on webContents in this case. The safest way is to
@@ -54,144 +54,144 @@ BrowserWindow.prototype._init = function () {
// Finder, we still do it on all platforms in case of other bugs we don't // Finder, we still do it on all platforms in case of other bugs we don't
// know. // know.
this.webContents.once('load-url', function () { this.webContents.once('load-url', function () {
this.focus() this.focus();
}) });
// Redirect focus/blur event to app instance too. // Redirect focus/blur event to app instance too.
this.on('blur', (event) => { this.on('blur', (event) => {
app.emit('browser-window-blur', event, this) app.emit('browser-window-blur', event, this);
}) });
this.on('focus', (event) => { this.on('focus', (event) => {
app.emit('browser-window-focus', event, this) app.emit('browser-window-focus', event, this);
}) });
// Subscribe to visibilityState changes and pass to renderer process. // Subscribe to visibilityState changes and pass to renderer process.
let isVisible = this.isVisible() && !this.isMinimized() let isVisible = this.isVisible() && !this.isMinimized();
const visibilityChanged = () => { const visibilityChanged = () => {
const newState = this.isVisible() && !this.isMinimized() const newState = this.isVisible() && !this.isMinimized();
if (isVisible !== newState) { if (isVisible !== newState) {
isVisible = newState isVisible = newState;
const visibilityState = isVisible ? 'visible' : 'hidden' const visibilityState = isVisible ? 'visible' : 'hidden';
this.webContents.emit('-window-visibility-change', visibilityState) this.webContents.emit('-window-visibility-change', visibilityState);
} }
} };
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'] const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
for (const event of visibilityEvents) { for (const event of visibilityEvents) {
this.on(event, visibilityChanged) this.on(event, visibilityChanged);
} }
// Notify the creation of the window. // Notify the creation of the window.
const event = process.electronBinding('event').createEmpty() const event = process.electronBinding('event').createEmpty();
app.emit('browser-window-created', event, this) app.emit('browser-window-created', event, this);
Object.defineProperty(this, 'devToolsWebContents', { Object.defineProperty(this, 'devToolsWebContents', {
enumerable: true, enumerable: true,
configurable: false, configurable: false,
get () { get () {
return this.webContents.devToolsWebContents return this.webContents.devToolsWebContents;
} }
}) });
} };
const isBrowserWindow = (win) => { const isBrowserWindow = (win) => {
return win && win.constructor.name === 'BrowserWindow' return win && win.constructor.name === 'BrowserWindow';
} };
BrowserWindow.fromId = (id) => { BrowserWindow.fromId = (id) => {
const win = TopLevelWindow.fromId(id) const win = TopLevelWindow.fromId(id);
return isBrowserWindow(win) ? win : null return isBrowserWindow(win) ? win : null;
} };
BrowserWindow.getAllWindows = () => { BrowserWindow.getAllWindows = () => {
return TopLevelWindow.getAllWindows().filter(isBrowserWindow) return TopLevelWindow.getAllWindows().filter(isBrowserWindow);
} };
BrowserWindow.getFocusedWindow = () => { BrowserWindow.getFocusedWindow = () => {
for (const window of BrowserWindow.getAllWindows()) { for (const window of BrowserWindow.getAllWindows()) {
if (window.isFocused() || window.isDevToolsFocused()) return window if (window.isFocused() || window.isDevToolsFocused()) return window;
} }
return null return null;
} };
BrowserWindow.fromWebContents = (webContents) => { BrowserWindow.fromWebContents = (webContents) => {
for (const window of BrowserWindow.getAllWindows()) { for (const window of BrowserWindow.getAllWindows()) {
if (window.webContents.equal(webContents)) return window if (window.webContents && window.webContents.equal(webContents)) return window;
} }
return null return null;
} };
BrowserWindow.fromBrowserView = (browserView) => { BrowserWindow.fromBrowserView = (browserView) => {
for (const window of BrowserWindow.getAllWindows()) { for (const window of BrowserWindow.getAllWindows()) {
if (window.getBrowserView() === browserView) return window if (window.getBrowserView() === browserView) return window;
} }
return null return null;
} };
// Helpers. // Helpers.
Object.assign(BrowserWindow.prototype, { Object.assign(BrowserWindow.prototype, {
loadURL (...args) { loadURL (...args) {
return this.webContents.loadURL(...args) return this.webContents.loadURL(...args);
}, },
getURL (...args) { getURL (...args) {
return this.webContents.getURL() return this.webContents.getURL();
}, },
loadFile (...args) { loadFile (...args) {
return this.webContents.loadFile(...args) return this.webContents.loadFile(...args);
}, },
reload (...args) { reload (...args) {
return this.webContents.reload(...args) return this.webContents.reload(...args);
}, },
send (...args) { send (...args) {
return this.webContents.send(...args) return this.webContents.send(...args);
}, },
openDevTools (...args) { openDevTools (...args) {
return this.webContents.openDevTools(...args) return this.webContents.openDevTools(...args);
}, },
closeDevTools () { closeDevTools () {
return this.webContents.closeDevTools() return this.webContents.closeDevTools();
}, },
isDevToolsOpened () { isDevToolsOpened () {
return this.webContents.isDevToolsOpened() return this.webContents.isDevToolsOpened();
}, },
isDevToolsFocused () { isDevToolsFocused () {
return this.webContents.isDevToolsFocused() return this.webContents.isDevToolsFocused();
}, },
toggleDevTools () { toggleDevTools () {
return this.webContents.toggleDevTools() return this.webContents.toggleDevTools();
}, },
inspectElement (...args) { inspectElement (...args) {
return this.webContents.inspectElement(...args) return this.webContents.inspectElement(...args);
}, },
inspectSharedWorker () { inspectSharedWorker () {
return this.webContents.inspectSharedWorker() return this.webContents.inspectSharedWorker();
}, },
inspectServiceWorker () { inspectServiceWorker () {
return this.webContents.inspectServiceWorker() return this.webContents.inspectServiceWorker();
}, },
showDefinitionForSelection () { showDefinitionForSelection () {
return this.webContents.showDefinitionForSelection() return this.webContents.showDefinitionForSelection();
}, },
capturePage (...args) { capturePage (...args) {
return this.webContents.capturePage(...args) return this.webContents.capturePage(...args);
}, },
setTouchBar (touchBar) { setTouchBar (touchBar) {
electron.TouchBar._setOnWindow(touchBar, this) electron.TouchBar._setOnWindow(touchBar, this);
}, },
setBackgroundThrottling (allowed) { setBackgroundThrottling (allowed) {
this.webContents.setBackgroundThrottling(allowed) this.webContents.setBackgroundThrottling(allowed);
} }
}) });
// Deprecations // Deprecations
deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar') deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar');
deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable') deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable');
deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable') deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable');
deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable') deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable');
deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable') deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable');
deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable') deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable');
deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable') deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable');
module.exports = BrowserWindow module.exports = BrowserWindow;

View File

@@ -1,2 +1,2 @@
'use strict' 'use strict';
module.exports = process.electronBinding('content_tracing') module.exports = process.electronBinding('content_tracing');

View File

@@ -1,12 +1,12 @@
'use strict' 'use strict';
const CrashReporter = require('@electron/internal/common/crash-reporter') const CrashReporter = require('@electron/internal/common/crash-reporter');
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init') const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
class CrashReporterMain extends CrashReporter { class CrashReporterMain extends CrashReporter {
init (options) { init (options) {
return crashReporterInit(options) return crashReporterInit(options);
} }
} }
module.exports = new CrashReporterMain() module.exports = new CrashReporterMain();

View File

@@ -1,13 +1,13 @@
'use strict' 'use strict';
const { app, BrowserWindow, deprecate } = require('electron') const { app, BrowserWindow, deprecate } = require('electron');
const binding = process.electronBinding('dialog') const binding = process.electronBinding('dialog');
const v8Util = process.electronBinding('v8_util') const v8Util = process.electronBinding('v8_util');
const DialogType = { const DialogType = {
OPEN: 'OPEN', OPEN: 'OPEN',
SAVE: 'SAVE' SAVE: 'SAVE'
} };
const saveFileDialogProperties = { const saveFileDialogProperties = {
createDirectory: 1 << 0, createDirectory: 1 << 0,
@@ -15,7 +15,7 @@ const saveFileDialogProperties = {
treatPackageAsDirectory: 1 << 2, treatPackageAsDirectory: 1 << 2,
showOverwriteConfirmation: 1 << 3, showOverwriteConfirmation: 1 << 3,
dontAddToRecent: 1 << 4 dontAddToRecent: 1 << 4
} };
const openFileDialogProperties = { const openFileDialogProperties = {
openFile: 1 << 0, openFile: 1 << 0,
@@ -27,15 +27,15 @@ const openFileDialogProperties = {
noResolveAliases: 1 << 6, // macOS noResolveAliases: 1 << 6, // macOS
treatPackageAsDirectory: 1 << 7, // macOS treatPackageAsDirectory: 1 << 7, // macOS
dontAddToRecent: 1 << 8 // Windows dontAddToRecent: 1 << 8 // Windows
} };
const normalizeAccessKey = (text) => { const normalizeAccessKey = (text) => {
if (typeof text !== 'string') return text if (typeof text !== 'string') return text;
// macOS does not have access keys so remove single ampersands // macOS does not have access keys so remove single ampersands
// and replace double ampersands with a single ampersand // and replace double ampersands with a single ampersand
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
return text.replace(/&(&?)/g, '$1') return text.replace(/&(&?)/g, '$1');
} }
// Linux uses a single underscore as an access key prefix so escape // Linux uses a single underscore as an access key prefix so escape
@@ -44,41 +44,41 @@ const normalizeAccessKey = (text) => {
// a single underscore // a single underscore
if (process.platform === 'linux') { if (process.platform === 'linux') {
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => { return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
if (after === '&') return after if (after === '&') return after;
return `_${after}` return `_${after}`;
}) });
} }
return text return text;
} };
const checkAppInitialized = function () { const checkAppInitialized = function () {
if (!app.isReady()) { if (!app.isReady()) {
throw new Error('dialog module can only be used after app is ready') throw new Error('dialog module can only be used after app is ready');
} }
} };
const setupDialogProperties = (type, properties) => { const setupDialogProperties = (type, properties) => {
const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties;
let dialogProperties = 0 let dialogProperties = 0;
for (const prop in dialogPropertiesTypes) { for (const prop in dialogPropertiesTypes) {
if (properties.includes(prop)) { if (properties.includes(prop)) {
dialogProperties |= dialogPropertiesTypes[prop] dialogProperties |= dialogPropertiesTypes[prop];
} }
} }
return dialogProperties return dialogProperties;
} };
const saveDialog = (sync, window, options) => { const saveDialog = (sync, window, options) => {
checkAppInitialized() checkAppInitialized();
if (window && window.constructor !== BrowserWindow) { if (window && window.constructor !== BrowserWindow) {
options = window options = window;
window = null window = null;
} }
if (options == null) options = { title: 'Save' } if (options == null) options = { title: 'Save' };
const { const {
buttonLabel = '', buttonLabel = '',
@@ -90,33 +90,33 @@ const saveDialog = (sync, window, options) => {
securityScopedBookmarks = false, securityScopedBookmarks = false,
nameFieldLabel = '', nameFieldLabel = '',
showsTagField = true showsTagField = true
} = options } = options;
if (typeof title !== 'string') throw new TypeError('Title must be a string') if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string') if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string') if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
if (typeof message !== 'string') throw new TypeError('Message must be a string') if (typeof message !== 'string') throw new TypeError('Message must be a string');
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string') if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string');
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window } const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window };
settings.properties = setupDialogProperties(DialogType.SAVE, properties) settings.properties = setupDialogProperties(DialogType.SAVE, properties);
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings) return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings);
} };
const openDialog = (sync, window, options) => { const openDialog = (sync, window, options) => {
checkAppInitialized() checkAppInitialized();
if (window && window.constructor !== BrowserWindow) { if (window && window.constructor !== BrowserWindow) {
options = window options = window;
window = null window = null;
} }
if (options == null) { if (options == null) {
options = { options = {
title: 'Open', title: 'Open',
properties: ['openFile'] properties: ['openFile']
} };
} }
const { const {
@@ -127,33 +127,33 @@ const openDialog = (sync, window, options) => {
title = '', title = '',
message = '', message = '',
securityScopedBookmarks = false securityScopedBookmarks = false
} = options } = options;
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array') if (!Array.isArray(properties)) throw new TypeError('Properties must be an array');
if (typeof title !== 'string') throw new TypeError('Title must be a string') if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string') if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string') if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
if (typeof message !== 'string') throw new TypeError('Message must be a string') if (typeof message !== 'string') throw new TypeError('Message must be a string');
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window } const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window };
settings.properties = setupDialogProperties(DialogType.OPEN, properties) settings.properties = setupDialogProperties(DialogType.OPEN, properties);
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings) return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings);
} };
const messageBox = (sync, window, options) => { const messageBox = (sync, window, options) => {
checkAppInitialized() checkAppInitialized();
if (window && window.constructor !== BrowserWindow) { if (window && window.constructor !== BrowserWindow) {
options = window options = window;
window = null window = null;
} }
if (options == null) options = { type: 'none' } if (options == null) options = { type: 'none' };
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'] const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
const messageBoxOptions = { noLink: 1 << 0 } const messageBoxOptions = { noLink: 1 << 0 };
let { let {
buttons = [], buttons = [],
@@ -163,101 +163,104 @@ const messageBox = (sync, window, options) => {
defaultId = -1, defaultId = -1,
detail = '', detail = '',
icon = null, icon = null,
noLink = false,
message = '', message = '',
title = '', title = '',
type = 'none' type = 'none'
} = options } = options;
const messageBoxType = messageBoxTypes.indexOf(type) const messageBoxType = messageBoxTypes.indexOf(type);
if (messageBoxType === -1) throw new TypeError('Invalid message box type') if (messageBoxType === -1) throw new TypeError('Invalid message box type');
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array') if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array');
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey) if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey);
if (typeof title !== 'string') throw new TypeError('Title must be a string') if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof message !== 'string') throw new TypeError('Message must be a string') if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean');
if (typeof detail !== 'string') throw new TypeError('Detail must be a string') if (typeof message !== 'string') throw new TypeError('Message must be a string');
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string') if (typeof detail !== 'string') throw new TypeError('Detail must be a string');
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string');
checkboxChecked = !!checkboxChecked checkboxChecked = !!checkboxChecked;
if (checkboxChecked && !checkboxLabel) {
throw new Error('checkboxChecked requires that checkboxLabel also be passed');
}
// Choose a default button to get selected when dialog is cancelled. // Choose a default button to get selected when dialog is cancelled.
if (cancelId == null) { if (cancelId == null) {
// If the defaultId is set to 0, ensure the cancel button is a different index (1) // If the defaultId is set to 0, ensure the cancel button is a different index (1)
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0 cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
for (let i = 0; i < buttons.length; i++) { for (let i = 0; i < buttons.length; i++) {
const text = buttons[i].toLowerCase() const text = buttons[i].toLowerCase();
if (text === 'cancel' || text === 'no') { if (text === 'cancel' || text === 'no') {
cancelId = i cancelId = i;
break break;
} }
} }
} }
const flags = options.noLink ? messageBoxOptions.noLink : 0
const settings = { const settings = {
window, window,
messageBoxType, messageBoxType,
buttons, buttons,
defaultId, defaultId,
cancelId, cancelId,
flags, noLink,
title, title,
message, message,
detail, detail,
checkboxLabel, checkboxLabel,
checkboxChecked, checkboxChecked,
icon icon
} };
if (sync) { if (sync) {
return binding.showMessageBoxSync(settings) return binding.showMessageBoxSync(settings);
} else { } else {
return binding.showMessageBox(settings) return binding.showMessageBox(settings);
} }
} };
module.exports = { module.exports = {
showOpenDialog: function (window, options) { showOpenDialog: function (window, options) {
return openDialog(false, window, options) return openDialog(false, window, options);
}, },
showOpenDialogSync: function (window, options) { showOpenDialogSync: function (window, options) {
return openDialog(true, window, options) return openDialog(true, window, options);
}, },
showSaveDialog: function (window, options) { showSaveDialog: function (window, options) {
return saveDialog(false, window, options) return saveDialog(false, window, options);
}, },
showSaveDialogSync: function (window, options) { showSaveDialogSync: function (window, options) {
return saveDialog(true, window, options) return saveDialog(true, window, options);
}, },
showMessageBox: function (window, options) { showMessageBox: function (window, options) {
return messageBox(false, window, options) return messageBox(false, window, options);
}, },
showMessageBoxSync: function (window, options) { showMessageBoxSync: function (window, options) {
return messageBox(true, window, options) return messageBox(true, window, options);
}, },
showErrorBox: function (...args) { showErrorBox: function (...args) {
return binding.showErrorBox(...args) return binding.showErrorBox(...args);
}, },
showCertificateTrustDialog: function (window, options) { showCertificateTrustDialog: function (window, options) {
if (window && window.constructor !== BrowserWindow) options = window if (window && window.constructor !== BrowserWindow) options = window;
if (options == null || typeof options !== 'object') { if (options == null || typeof options !== 'object') {
throw new TypeError('options must be an object') throw new TypeError('options must be an object');
} }
const { certificate, message = '' } = options const { certificate, message = '' } = options;
if (certificate == null || typeof certificate !== 'object') { if (certificate == null || typeof certificate !== 'object') {
throw new TypeError('certificate must be an object') throw new TypeError('certificate must be an object');
} }
if (typeof message !== 'string') throw new TypeError('message must be a string') if (typeof message !== 'string') throw new TypeError('message must be a string');
return binding.showCertificateTrustDialog(window, certificate, message) return binding.showCertificateTrustDialog(window, certificate, message);
} }
} };

View File

@@ -1,6 +1,8 @@
import { defineProperties } from '@electron/internal/common/define-properties' import { defineProperties } from '@electron/internal/common/define-properties';
import { commonModuleList } from '@electron/internal/common/api/module-list' import { commonModuleList } from '@electron/internal/common/api/module-list';
import { browserModuleList } from '@electron/internal/browser/api/module-list' import { browserModuleList } from '@electron/internal/browser/api/module-list';
defineProperties(exports, commonModuleList) module.exports = {};
defineProperties(exports, browserModuleList)
defineProperties(module.exports, commonModuleList);
defineProperties(module.exports, browserModuleList);

View File

@@ -1,3 +1,3 @@
'use strict' 'use strict';
module.exports = process.electronBinding('global_shortcut').globalShortcut module.exports = process.electronBinding('global_shortcut').globalShortcut;

View File

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

View File

@@ -1,8 +1,8 @@
import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl' import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl';
const ipcMain = new IpcMainImpl() const ipcMain = new IpcMainImpl();
// Do not throw exception when channel name is "error". // Do not throw exception when channel name is "error".
ipcMain.on('error', () => {}) ipcMain.on('error', () => {});
export default ipcMain export default ipcMain;

View File

@@ -1,15 +1,15 @@
'use strict' 'use strict';
const { app } = require('electron') const { app } = require('electron');
const isMac = process.platform === 'darwin' const isMac = process.platform === 'darwin';
const isWindows = process.platform === 'win32' const isWindows = process.platform === 'win32';
const isLinux = process.platform === 'linux' const isLinux = process.platform === 'linux';
const roles = { const roles = {
about: { about: {
get label () { get label () {
return isLinux ? 'About' : `About ${app.name}` return isLinux ? 'About' : `About ${app.name}`;
} }
}, },
close: { close: {
@@ -38,7 +38,7 @@ const roles = {
accelerator: 'Shift+CmdOrCtrl+R', accelerator: 'Shift+CmdOrCtrl+R',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
windowMethod: (window) => { windowMethod: (window) => {
window.webContents.reloadIgnoringCache() window.webContents.reloadIgnoringCache();
} }
}, },
front: { front: {
@@ -49,7 +49,7 @@ const roles = {
}, },
hide: { hide: {
get label () { get label () {
return `Hide ${app.name}` return `Hide ${app.name}`;
}, },
accelerator: 'Command+H' accelerator: 'Command+H'
}, },
@@ -77,9 +77,9 @@ const roles = {
quit: { quit: {
get label () { get label () {
switch (process.platform) { switch (process.platform) {
case 'darwin': return `Quit ${app.name}` case 'darwin': return `Quit ${app.name}`;
case 'win32': return 'Exit' case 'win32': return 'Exit';
default: return 'Quit' default: return 'Quit';
} }
}, },
accelerator: isWindows ? undefined : 'CommandOrControl+Q', accelerator: isWindows ? undefined : 'CommandOrControl+Q',
@@ -101,7 +101,7 @@ const roles = {
accelerator: 'CommandOrControl+0', accelerator: 'CommandOrControl+0',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
webContentsMethod: (webContents) => { webContentsMethod: (webContents) => {
webContents.zoomLevel = 0 webContents.zoomLevel = 0;
} }
}, },
selectall: { selectall: {
@@ -134,7 +134,7 @@ const roles = {
label: 'Toggle Full Screen', label: 'Toggle Full Screen',
accelerator: isMac ? 'Control+Command+F' : 'F11', accelerator: isMac ? 'Control+Command+F' : 'F11',
windowMethod: (window) => { windowMethod: (window) => {
window.setFullScreen(!window.isFullScreen()) window.setFullScreen(!window.isFullScreen());
} }
}, },
undo: { undo: {
@@ -156,7 +156,7 @@ const roles = {
accelerator: 'CommandOrControl+Plus', accelerator: 'CommandOrControl+Plus',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
webContentsMethod: (webContents) => { webContentsMethod: (webContents) => {
webContents.zoomLevel += 0.5 webContents.zoomLevel += 0.5;
} }
}, },
zoomout: { zoomout: {
@@ -164,13 +164,13 @@ const roles = {
accelerator: 'CommandOrControl+-', accelerator: 'CommandOrControl+-',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
webContentsMethod: (webContents) => { webContentsMethod: (webContents) => {
webContents.zoomLevel -= 0.5 webContents.zoomLevel -= 0.5;
} }
}, },
// App submenu should be used for Mac only // App submenu should be used for Mac only
appmenu: { appmenu: {
get label () { get label () {
return app.name return app.name;
}, },
submenu: [ submenu: [
{ role: 'about' }, { role: 'about' },
@@ -249,71 +249,71 @@ const roles = {
]) ])
] ]
} }
} };
exports.roleList = roles exports.roleList = roles;
const canExecuteRole = (role) => { const canExecuteRole = (role) => {
if (!roles.hasOwnProperty(role)) return false if (!roles.hasOwnProperty(role)) return false;
if (!isMac) return true if (!isMac) return true;
// macOS handles all roles natively except for a few // macOS handles all roles natively except for a few
return roles[role].nonNativeMacOSRole return roles[role].nonNativeMacOSRole;
} };
exports.getDefaultLabel = (role) => { exports.getDefaultLabel = (role) => {
return roles.hasOwnProperty(role) ? roles[role].label : '' return roles.hasOwnProperty(role) ? roles[role].label : '';
} };
exports.getDefaultAccelerator = (role) => { exports.getDefaultAccelerator = (role) => {
if (roles.hasOwnProperty(role)) return roles[role].accelerator if (roles.hasOwnProperty(role)) return roles[role].accelerator;
} };
exports.shouldRegisterAccelerator = (role) => { exports.shouldRegisterAccelerator = (role) => {
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined;
return hasRoleRegister ? roles[role].registerAccelerator : true return hasRoleRegister ? roles[role].registerAccelerator : true;
} };
exports.getDefaultSubmenu = (role) => { exports.getDefaultSubmenu = (role) => {
if (!roles.hasOwnProperty(role)) return if (!roles.hasOwnProperty(role)) return;
let { submenu } = roles[role] let { submenu } = roles[role];
// remove null items from within the submenu // remove null items from within the submenu
if (Array.isArray(submenu)) { if (Array.isArray(submenu)) {
submenu = submenu.filter((item) => item != null) submenu = submenu.filter((item) => item != null);
} }
return submenu return submenu;
} };
exports.execute = (role, focusedWindow, focusedWebContents) => { exports.execute = (role, focusedWindow, focusedWebContents) => {
if (!canExecuteRole(role)) return false if (!canExecuteRole(role)) return false;
const { appMethod, webContentsMethod, windowMethod } = roles[role] const { appMethod, webContentsMethod, windowMethod } = roles[role];
if (appMethod) { if (appMethod) {
app[appMethod]() app[appMethod]();
return true return true;
} }
if (windowMethod && focusedWindow != null) { if (windowMethod && focusedWindow != null) {
if (typeof windowMethod === 'function') { if (typeof windowMethod === 'function') {
windowMethod(focusedWindow) windowMethod(focusedWindow);
} else { } else {
focusedWindow[windowMethod]() focusedWindow[windowMethod]();
} }
return true return true;
} }
if (webContentsMethod && focusedWebContents != null) { if (webContentsMethod && focusedWebContents != null) {
if (typeof webContentsMethod === 'function') { if (typeof webContentsMethod === 'function') {
webContentsMethod(focusedWebContents) webContentsMethod(focusedWebContents);
} else { } else {
focusedWebContents[webContentsMethod]() focusedWebContents[webContentsMethod]();
} }
return true return true;
} }
return false return false;
} };

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,11 @@
'use strict' 'use strict';
// TODO: Figure out a way to not duplicate this information between here and module-list // TODO: Figure out a way to not duplicate this information between here and module-list
// It is currently duplicated as module-list "require"s all the browser API file and the // It is currently duplicated as module-list "require"s all the browser API file and the
// remote module in the renderer process depends on that file. As a result webpack // remote module in the renderer process depends on that file. As a result webpack
// includes all the browser API files in the renderer process as well and we want to avoid that // includes all the browser API files in the renderer process as well and we want to avoid that
const features = process.electronBinding('features') const features = process.electronBinding('features');
// Browser side modules, please sort alphabetically. // Browser side modules, please sort alphabetically.
module.exports = [ module.exports = [
@@ -21,6 +21,7 @@ module.exports = [
{ name: 'inAppPurchase' }, { name: 'inAppPurchase' },
{ name: 'Menu' }, { name: 'Menu' },
{ name: 'MenuItem' }, { name: 'MenuItem' },
{ name: 'nativeTheme' },
{ name: 'net' }, { name: 'net' },
{ name: 'netLog' }, { name: 'netLog' },
{ name: 'Notification' }, { name: 'Notification' },
@@ -36,7 +37,7 @@ module.exports = [
{ name: 'View' }, { name: 'View' },
{ name: 'webContents' }, { name: 'webContents' },
{ name: 'WebContentsView' } { name: 'WebContentsView' }
] ];
if (features.isViewApiEnabled()) { if (features.isViewApiEnabled()) {
module.exports.push( module.exports.push(
@@ -47,5 +48,5 @@ if (features.isViewApiEnabled()) {
{ name: 'MdTextButton' }, { name: 'MdTextButton' },
{ name: 'ResizeArea' }, { name: 'ResizeArea' },
{ name: 'TextField' } { name: 'TextField' }
) );
} }

View File

@@ -1,6 +1,6 @@
// TODO: Updating this file also required updating the module-keys file // TODO: Updating this file also required updating the module-keys file
const features = process.electronBinding('features') const features = process.electronBinding('features');
// Browser side modules, please sort alphabetically. // Browser side modules, please sort alphabetically.
export const browserModuleList: ElectronInternal.ModuleEntry[] = [ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
@@ -32,7 +32,7 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
{ name: 'View', loader: () => require('./view') }, { name: 'View', loader: () => require('./view') },
{ name: 'webContents', loader: () => require('./web-contents') }, { name: 'webContents', loader: () => require('./web-contents') },
{ name: 'WebContentsView', loader: () => require('./web-contents-view') } { name: 'WebContentsView', loader: () => require('./web-contents-view') }
] ];
if (features.isViewApiEnabled()) { if (features.isViewApiEnabled()) {
browserModuleList.push( browserModuleList.push(
@@ -43,5 +43,5 @@ if (features.isViewApiEnabled()) {
{ name: 'MdTextButton', loader: () => require('./views/md-text-button') }, { name: 'MdTextButton', loader: () => require('./views/md-text-button') },
{ name: 'ResizeArea', loader: () => require('./views/resize-area') }, { name: 'ResizeArea', loader: () => require('./views/resize-area') },
{ name: 'TextField', loader: () => require('./views/text-field') } { name: 'TextField', loader: () => require('./views/text-field') }
) );
} }

View File

@@ -1,8 +1,8 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events';
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme') const { NativeTheme, nativeTheme } = process.electronBinding('native_theme');
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype) Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
EventEmitter.call(nativeTheme as any) EventEmitter.call(nativeTheme as any);
module.exports = nativeTheme module.exports = nativeTheme;

View File

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

View File

@@ -1,20 +1,16 @@
'use strict' 'use strict';
const url = require('url') const url = require('url');
const { EventEmitter } = require('events') const { EventEmitter } = require('events');
const { Readable } = require('stream') const { Readable, Writable } = require('stream');
const { app } = require('electron') const { app } = require('electron');
const { Session } = process.electronBinding('session') const { Session } = process.electronBinding('session');
const { net, Net } = process.electronBinding('net') const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net');
const { URLRequest } = net const { URLLoader } = net;
// Net is an EventEmitter. Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype);
Object.setPrototypeOf(Net.prototype, EventEmitter.prototype)
EventEmitter.call(net)
Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype) const kSupportedProtocols = new Set(['http:', 'https:']);
const kSupportedProtocols = new Set(['http:', 'https:'])
// set of headers that Node.js discards duplicates for // set of headers that Node.js discards duplicates for
// see https://nodejs.org/api/http.html#http_message_headers // see https://nodejs.org/api/http.html#http_message_headers
@@ -37,375 +33,445 @@ const discardableDuplicateHeaders = new Set([
'server', 'server',
'age', 'age',
'expires' 'expires'
]) ]);
class IncomingMessage extends Readable { class IncomingMessage extends Readable {
constructor (urlRequest) { constructor (responseHead) {
super() super();
this.urlRequest = urlRequest this._shouldPush = false;
this.shouldPush = false this._data = [];
this.data = [] this._responseHead = responseHead;
this.urlRequest.on('data', (event, chunk) => {
this._storeInternalData(chunk)
this._pushInternalData()
})
this.urlRequest.on('end', () => {
this._storeInternalData(null)
this._pushInternalData()
})
} }
get statusCode () { get statusCode () {
return this.urlRequest.statusCode return this._responseHead.statusCode;
} }
get statusMessage () { get statusMessage () {
return this.urlRequest.statusMessage return this._responseHead.statusMessage;
} }
get headers () { get headers () {
const filteredHeaders = {} const filteredHeaders = {};
const rawHeaders = this.urlRequest.rawResponseHeaders const { rawHeaders } = this._responseHead;
Object.keys(rawHeaders).forEach(header => { rawHeaders.forEach(header => {
if (header in filteredHeaders && discardableDuplicateHeaders.has(header)) { if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) &&
discardableDuplicateHeaders.has(header.key)) {
// do nothing with discardable duplicate headers // do nothing with discardable duplicate headers
} else { } else {
if (header === 'set-cookie') { if (header.key === 'set-cookie') {
// keep set-cookie as an array per Node.js rules // keep set-cookie as an array per Node.js rules
// see https://nodejs.org/api/http.html#http_message_headers // see https://nodejs.org/api/http.html#http_message_headers
filteredHeaders[header] = rawHeaders[header] if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
filteredHeaders[header.key].push(header.value);
} else {
filteredHeaders[header.key] = [header.value];
}
} else { } else {
// for non-cookie headers, the values are joined together with ', ' // for non-cookie headers, the values are joined together with ', '
filteredHeaders[header] = rawHeaders[header].join(', ') if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
filteredHeaders[header.key] += `, ${header.value}`;
} else {
filteredHeaders[header.key] = header.value;
}
} }
} }
}) });
return filteredHeaders return filteredHeaders;
} }
get httpVersion () { get httpVersion () {
return `${this.httpVersionMajor}.${this.httpVersionMinor}` return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
} }
get httpVersionMajor () { get httpVersionMajor () {
return this.urlRequest.httpVersionMajor return this._responseHead.httpVersion.major;
} }
get httpVersionMinor () { get httpVersionMinor () {
return this.urlRequest.httpVersionMinor return this._responseHead.httpVersion.minor;
} }
get rawTrailers () { get rawTrailers () {
throw new Error('HTTP trailers are not supported') throw new Error('HTTP trailers are not supported');
} }
get trailers () { get trailers () {
throw new Error('HTTP trailers are not supported') throw new Error('HTTP trailers are not supported');
} }
_storeInternalData (chunk) { _storeInternalData (chunk) {
this.data.push(chunk) this._data.push(chunk);
this._pushInternalData();
} }
_pushInternalData () { _pushInternalData () {
while (this.shouldPush && this.data.length > 0) { while (this._shouldPush && this._data.length > 0) {
const chunk = this.data.shift() const chunk = this._data.shift();
this.shouldPush = this.push(chunk) this._shouldPush = this.push(chunk);
} }
} }
_read () { _read () {
this.shouldPush = true this._shouldPush = true;
this._pushInternalData() this._pushInternalData();
} }
} }
URLRequest.prototype._emitRequestEvent = function (isAsync, ...rest) { /** Writable stream that buffers up everything written to it. */
if (isAsync) { class SlurpStream extends Writable {
process.nextTick(() => { constructor () {
this.clientRequest.emit(...rest) super();
}) this._data = Buffer.alloc(0);
}
_write (chunk, encoding, callback) {
this._data = Buffer.concat([this._data, chunk]);
callback();
}
data () { return this._data; }
}
class ChunkedBodyStream extends Writable {
constructor (clientRequest) {
super();
this._clientRequest = clientRequest;
}
_write (chunk, encoding, callback) {
if (this._downstream) {
this._downstream.write(chunk).then(callback, callback);
} else {
// the contract of _write is that we won't be called again until we call
// the callback, so we're good to just save a single chunk.
this._pendingChunk = chunk;
this._pendingCallback = callback;
// The first write to a chunked body stream begins the request.
this._clientRequest._startRequest();
}
}
_final (callback) {
this._downstream.done();
callback();
}
startReading (pipe) {
if (this._downstream) {
throw new Error('two startReading calls???');
}
this._downstream = pipe;
if (this._pendingChunk) {
const doneWriting = (maybeError) => {
const cb = this._pendingCallback;
delete this._pendingCallback;
delete this._pendingChunk;
cb(maybeError);
};
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting);
}
}
}
function parseOptions (options) {
if (typeof options === 'string') {
options = url.parse(options);
} else { } else {
this.clientRequest.emit(...rest) options = { ...options };
} }
const method = (options.method || 'GET').toUpperCase();
let urlStr = options.url;
if (!urlStr) {
const urlObj = {};
const protocol = options.protocol || 'http:';
if (!kSupportedProtocols.has(protocol)) {
throw new Error('Protocol "' + protocol + '" not supported');
}
urlObj.protocol = protocol;
if (options.host) {
urlObj.host = options.host;
} else {
if (options.hostname) {
urlObj.hostname = options.hostname;
} else {
urlObj.hostname = 'localhost';
}
if (options.port) {
urlObj.port = options.port;
}
}
if (options.path && / /.test(options.path)) {
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
// with an additional rule for ignoring percentage-escaped characters
// but that's a) hard to capture in a regular expression that performs
// well, and b) possibly too restrictive for real-world usage. That's
// why it only scans for spaces because those are guaranteed to create
// an invalid request.
throw new TypeError('Request path contains unescaped characters');
}
const pathObj = url.parse(options.path || '/');
urlObj.pathname = pathObj.pathname;
urlObj.search = pathObj.search;
urlObj.hash = pathObj.hash;
urlStr = url.format(urlObj);
}
const redirectPolicy = options.redirect || 'follow';
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
throw new Error('redirect mode should be one of follow, error or manual');
}
if (options.headers != null && typeof options.headers !== 'object') {
throw new TypeError('headers must be an object');
}
const urlLoaderOptions = {
method: method,
url: urlStr,
redirectPolicy,
extraHeaders: options.headers || {},
useSessionCookies: options.useSessionCookies || false
};
for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders)) {
if (!_isValidHeaderName(name)) {
throw new Error(`Invalid header name: '${name}'`);
}
if (!_isValidHeaderValue(value.toString())) {
throw new Error(`Invalid value for header '${name}': '${value}'`);
}
}
if (options.session) {
if (options.session instanceof Session) {
urlLoaderOptions.session = options.session;
} else {
throw new TypeError('`session` should be an instance of the Session class');
}
} else if (options.partition) {
if (typeof options.partition === 'string') {
urlLoaderOptions.partition = options.partition;
} else {
throw new TypeError('`partition` should be a string');
}
}
return urlLoaderOptions;
} }
URLRequest.prototype._emitResponseEvent = function (isAsync, ...rest) { class ClientRequest extends Writable {
if (isAsync) {
process.nextTick(() => {
this._response.emit(...rest)
})
} else {
this._response.emit(...rest)
}
}
class ClientRequest extends EventEmitter {
constructor (options, callback) { constructor (options, callback) {
super() super({ autoDestroy: true });
if (!app.isReady()) { if (!app.isReady()) {
throw new Error('net module can only be used after app is ready') throw new Error('net module can only be used after app is ready');
} }
if (typeof options === 'string') {
options = url.parse(options)
} else {
options = Object.assign({}, options)
}
const method = (options.method || 'GET').toUpperCase()
let urlStr = options.url
if (!urlStr) {
const urlObj = {}
const protocol = options.protocol || 'http:'
if (!kSupportedProtocols.has(protocol)) {
throw new Error('Protocol "' + protocol + '" not supported')
}
urlObj.protocol = protocol
if (options.host) {
urlObj.host = options.host
} else {
if (options.hostname) {
urlObj.hostname = options.hostname
} else {
urlObj.hostname = 'localhost'
}
if (options.port) {
urlObj.port = options.port
}
}
if (options.path && / /.test(options.path)) {
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
// with an additional rule for ignoring percentage-escaped characters
// but that's a) hard to capture in a regular expression that performs
// well, and b) possibly too restrictive for real-world usage. That's
// why it only scans for spaces because those are guaranteed to create
// an invalid request.
throw new TypeError('Request path contains unescaped characters')
}
const pathObj = url.parse(options.path || '/')
urlObj.pathname = pathObj.pathname
urlObj.search = pathObj.search
urlObj.hash = pathObj.hash
urlStr = url.format(urlObj)
}
const redirectPolicy = options.redirect || 'follow'
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
throw new Error('redirect mode should be one of follow, error or manual')
}
const urlRequestOptions = {
method: method,
url: urlStr,
redirect: redirectPolicy
}
if (options.session) {
if (options.session instanceof Session) {
urlRequestOptions.session = options.session
} else {
throw new TypeError('`session` should be an instance of the Session class')
}
} else if (options.partition) {
if (typeof options.partition === 'string') {
urlRequestOptions.partition = options.partition
} else {
throw new TypeError('`partition` should be a string')
}
}
const urlRequest = new URLRequest(urlRequestOptions)
// Set back and forward links.
this.urlRequest = urlRequest
urlRequest.clientRequest = this
// This is a copy of the extra headers structure held by the native
// net::URLRequest. The main reason is to keep the getHeader API synchronous
// after the request starts.
this.extraHeaders = {}
if (options.headers) {
for (const key in options.headers) {
this.setHeader(key, options.headers[key])
}
}
// Set when the request uses chunked encoding. Can be switched
// to true only once and never set back to false.
this.chunkedEncodingEnabled = false
urlRequest.on('response', () => {
const response = new IncomingMessage(urlRequest)
urlRequest._response = response
this.emit('response', response)
})
urlRequest.on('login', (event, authInfo, callback) => {
this.emit('login', authInfo, (username, password) => {
// If null or undefined username/password, force to empty string.
if (username === null || username === undefined) {
username = ''
}
if (typeof username !== 'string') {
throw new Error('username must be a string')
}
if (password === null || password === undefined) {
password = ''
}
if (typeof password !== 'string') {
throw new Error('password must be a string')
}
callback(username, password)
})
})
if (callback) { if (callback) {
this.once('response', callback) this.once('response', callback);
} }
}
get chunkedEncoding () { const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options);
return this.chunkedEncodingEnabled this._urlLoaderOptions = urlLoaderOptions;
this._redirectPolicy = redirectPolicy;
this._started = false;
} }
set chunkedEncoding (value) { set chunkedEncoding (value) {
if (!this.urlRequest.notStarted) { if (this._started) {
throw new Error('Can\'t set the transfer encoding, headers have been sent') throw new Error('chunkedEncoding can only be set before the request is started');
}
if (typeof this._chunkedEncoding !== 'undefined') {
throw new Error('chunkedEncoding can only be set once');
}
this._chunkedEncoding = !!value;
if (this._chunkedEncoding) {
this._body = new ChunkedBodyStream(this);
this._urlLoaderOptions.body = (pipe) => {
this._body.startReading(pipe);
};
} }
this.chunkedEncodingEnabled = value
} }
setHeader (name, value) { setHeader (name, value) {
if (typeof name !== 'string') { if (typeof name !== 'string') {
throw new TypeError('`name` should be a string in setHeader(name, value)') throw new TypeError('`name` should be a string in setHeader(name, value)');
} }
if (value == null) { if (value == null) {
throw new Error('`value` required in setHeader("' + name + '", value)') throw new Error('`value` required in setHeader("' + name + '", value)');
} }
if (!this.urlRequest.notStarted) { if (this._started || this._firstWrite) {
throw new Error('Can\'t set headers after they are sent') throw new Error('Can\'t set headers after they are sent');
}
if (!_isValidHeaderName(name)) {
throw new Error(`Invalid header name: '${name}'`);
}
if (!_isValidHeaderValue(value.toString())) {
throw new Error(`Invalid value for header '${name}': '${value}'`);
} }
const key = name.toLowerCase() const key = name.toLowerCase();
this.extraHeaders[key] = value this._urlLoaderOptions.extraHeaders[key] = value;
this.urlRequest.setExtraHeader(name, value.toString())
} }
getHeader (name) { getHeader (name) {
if (name == null) { if (name == null) {
throw new Error('`name` is required for getHeader(name)') throw new Error('`name` is required for getHeader(name)');
} }
if (!this.extraHeaders) { const key = name.toLowerCase();
return return this._urlLoaderOptions.extraHeaders[key];
}
const key = name.toLowerCase()
return this.extraHeaders[key]
} }
removeHeader (name) { removeHeader (name) {
if (name == null) { if (name == null) {
throw new Error('`name` is required for removeHeader(name)') throw new Error('`name` is required for removeHeader(name)');
} }
if (!this.urlRequest.notStarted) { if (this._started || this._firstWrite) {
throw new Error('Can\'t remove headers after they are sent') throw new Error('Can\'t remove headers after they are sent');
} }
const key = name.toLowerCase() const key = name.toLowerCase();
delete this.extraHeaders[key] delete this._urlLoaderOptions.extraHeaders[key];
this.urlRequest.removeExtraHeader(name)
} }
_write (chunk, encoding, callback, isLast) { _write (chunk, encoding, callback) {
const chunkIsString = typeof chunk === 'string' this._firstWrite = true;
const chunkIsBuffer = chunk instanceof Buffer if (!this._body) {
if (!chunkIsString && !chunkIsBuffer) { this._body = new SlurpStream();
throw new TypeError('First argument must be a string or Buffer') this._body.on('finish', () => {
this._urlLoaderOptions.body = this._body.data();
this._startRequest();
});
} }
// TODO: is this the right way to forward to another stream?
if (chunkIsString) { this._body.write(chunk, encoding, callback);
// We convert all strings into binary buffers.
chunk = Buffer.from(chunk, encoding)
}
// Since writing to the network is asynchronous, we conservatively
// assume that request headers are written after delivering the first
// buffer to the network IO thread.
if (this.urlRequest.notStarted) {
this.urlRequest.setChunkedUpload(this.chunkedEncoding)
}
// Headers are assumed to be sent on first call to _writeBuffer,
// i.e. after the first call to write or end.
const result = this.urlRequest.write(chunk, isLast)
// The write callback is fired asynchronously to mimic Node.js.
if (callback) {
process.nextTick(callback)
}
return result
} }
write (data, encoding, callback) { _final (callback) {
if (this.urlRequest.finished) { if (this._body) {
const error = new Error('Write after end') // TODO: is this the right way to forward to another stream?
process.nextTick(writeAfterEndNT, this, error, callback) this._body.end(callback);
return true } else {
// end() called without a body, go ahead and start the request
this._startRequest();
callback();
} }
return this._write(data, encoding, callback, false)
} }
end (data, encoding, callback) { _startRequest () {
if (this.urlRequest.finished) { this._started = true;
return false const stringifyValues = (obj) => {
} const ret = {};
for (const k of Object.keys(obj)) {
ret[k] = obj[k].toString();
}
return ret;
};
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) };
this._urlLoader = new URLLoader(opts);
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
const response = this._response = new IncomingMessage(responseHead);
this.emit('response', response);
});
this._urlLoader.on('data', (event, data) => {
this._response._storeInternalData(Buffer.from(data));
});
this._urlLoader.on('complete', () => {
if (this._response) { this._response._storeInternalData(null); }
});
this._urlLoader.on('error', (event, netErrorString) => {
const error = new Error(netErrorString);
if (this._response) this._response.destroy(error);
this._die(error);
});
if (typeof data === 'function') { this._urlLoader.on('login', (event, authInfo, callback) => {
callback = data const handled = this.emit('login', authInfo, callback);
encoding = null if (!handled) {
data = null // If there were no listeners, cancel the authentication request.
} else if (typeof encoding === 'function') { callback();
callback = encoding }
encoding = null });
}
data = data || '' this._urlLoader.on('redirect', (event, redirectInfo, headers) => {
const { statusCode, newMethod, newUrl } = redirectInfo;
if (this._redirectPolicy === 'error') {
this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`));
} else if (this._redirectPolicy === 'manual') {
let _followRedirect = false;
this._followRedirectCb = () => { _followRedirect = true; };
try {
this.emit('redirect', statusCode, newMethod, newUrl, headers);
} finally {
this._followRedirectCb = null;
if (!_followRedirect && !this._aborted) {
this._die(new Error('Redirect was cancelled'));
}
}
} else if (this._redirectPolicy === 'follow') {
// Calling followRedirect() when the redirect policy is 'follow' is
// allowed but does nothing. (Perhaps it should throw an error
// though...? Since the redirect will happen regardless.)
try {
this._followRedirectCb = () => {};
this.emit('redirect', statusCode, newMethod, newUrl, headers);
} finally {
this._followRedirectCb = null;
}
} else {
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`));
}
});
return this._write(data, encoding, callback, true) this._urlLoader.on('upload-progress', (event, position, total) => {
this._uploadProgress = { active: true, started: true, current: position, total };
this.emit('upload-progress', position, total); // Undocumented, for now
});
this._urlLoader.on('download-progress', (event, current) => {
if (this._response) {
this._response.emit('download-progress', current); // Undocumented, for now
}
});
} }
followRedirect () { followRedirect () {
this.urlRequest.followRedirect() if (this._followRedirectCb) {
this._followRedirectCb();
} else {
throw new Error('followRedirect() called, but was not waiting for a redirect');
}
} }
abort () { abort () {
this.urlRequest.cancel() if (!this._aborted) {
process.nextTick(() => { this.emit('abort'); });
}
this._aborted = true;
this._die();
}
_die (err) {
this.destroy(err);
if (this._urlLoader) {
this._urlLoader.cancel();
if (this._response) this._response.destroy(err);
}
} }
getUploadProgress () { getUploadProgress () {
return this.urlRequest.getUploadProgress() return this._uploadProgress ? { ...this._uploadProgress } : { active: false };
} }
} }
function writeAfterEndNT (self, error, callback) {
self.emit('error', error)
if (callback) callback(error)
}
Net.prototype.request = function (options, callback) { Net.prototype.request = function (options, callback) {
return new ClientRequest(options, callback) return new ClientRequest(options, callback);
} };
net.ClientRequest = ClientRequest net.ClientRequest = ClientRequest;
module.exports = net module.exports = net;

View File

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

View File

@@ -1,28 +1,38 @@
'use strict' 'use strict';
import { createLazyInstance } from '../utils' import { createLazyInstance } from '../utils';
const { EventEmitter } = require('events') const { EventEmitter } = require('events');
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor') const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor');
// PowerMonitor is an EventEmitter. // PowerMonitor is an EventEmitter.
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype) Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype);
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true) const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true);
// On Linux we need to call blockShutdown() to subscribe to shutdown event.
if (process.platform === 'linux') { if (process.platform === 'linux') {
powerMonitor.on('newListener', (event:string) => { // In order to delay system shutdown when e.preventDefault() is invoked
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { // on a powerMonitor 'shutdown' event, we need an org.freedesktop.login1
powerMonitor.blockShutdown() // shutdown delay lock. For more details see the "Taking Delay Locks"
} // section of https://www.freedesktop.org/wiki/Software/systemd/inhibit/
}) //
// So here we watch for 'shutdown' listeners to be added or removed and
powerMonitor.on('removeListener', (event: string) => { // set or unset our shutdown delay lock accordingly.
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { const { app } = require('electron');
powerMonitor.unblockShutdown() app.whenReady().then(() => {
} powerMonitor.on('newListener', (event: string) => {
}) // whenever the listener count is incremented to one...
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
powerMonitor.blockShutdown();
}
});
powerMonitor.on('removeListener', (event: string) => {
// whenever the listener count is decremented to zero...
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
powerMonitor.unblockShutdown();
}
});
});
} }
module.exports = powerMonitor module.exports = powerMonitor;

View File

@@ -1,3 +1,3 @@
'use strict' 'use strict';
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker;

View File

@@ -1,29 +1,29 @@
import { app, session } from 'electron' import { app, session } from 'electron';
// Global protocol APIs. // Global protocol APIs.
const protocol = process.electronBinding('protocol') const protocol = process.electronBinding('protocol');
// Fallback protocol APIs of default session. // Fallback protocol APIs of default session.
Object.setPrototypeOf(protocol, new Proxy({}, { Object.setPrototypeOf(protocol, new Proxy({}, {
get (_target, property) { get (_target, property) {
if (!app.isReady()) return if (!app.isReady()) return;
const protocol = session.defaultSession!.protocol const protocol = session.defaultSession!.protocol;
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return;
// Returning a native function directly would throw error. // Returning a native function directly would throw error.
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args) return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args);
}, },
ownKeys () { ownKeys () {
if (!app.isReady()) return [] if (!app.isReady()) return [];
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol)) return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol));
}, },
getOwnPropertyDescriptor () { getOwnPropertyDescriptor () {
return { configurable: true, enumerable: true } return { configurable: true, enumerable: true };
} }
})) }));
export default protocol export default protocol;

View File

@@ -1,10 +1,10 @@
'use strict' 'use strict';
import { createLazyInstance } from '../utils' import { createLazyInstance } from '../utils';
const { EventEmitter } = require('events') const { EventEmitter } = require('events');
const { Screen, createScreen } = process.electronBinding('screen') const { Screen, createScreen } = process.electronBinding('screen');
// Screen is an EventEmitter. // Screen is an EventEmitter.
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype) Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
module.exports = createLazyInstance(createScreen, Screen, true) module.exports = createLazyInstance(createScreen, Screen, true);

View File

@@ -1,52 +1,53 @@
'use strict' 'use strict';
const { EventEmitter } = require('events') const { EventEmitter } = require('events');
const { app, deprecate } = require('electron') const { app, deprecate } = require('electron');
const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session') const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session');
// Public API. // Public API.
Object.defineProperties(exports, { Object.defineProperties(exports, {
defaultSession: { defaultSession: {
enumerable: true, enumerable: true,
get () { return fromPartition('') } get () { return fromPartition(''); }
}, },
fromPartition: { fromPartition: {
enumerable: true, enumerable: true,
value: fromPartition value: fromPartition
} }
}) });
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype) Object.setPrototypeOf(Session.prototype, EventEmitter.prototype);
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype) Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype);
Session.prototype._init = function () { Session.prototype._init = function () {
app.emit('session-created', this) app.emit('session-created', this);
} };
const _originalStartLogging = NetLog.prototype.startLogging const _originalStartLogging = NetLog.prototype.startLogging;
NetLog.prototype.startLogging = function (path, ...args) { NetLog.prototype.startLogging = function (path, ...args) {
this._currentlyLoggingPath = path this._currentlyLoggingPath = path;
try { try {
return _originalStartLogging.call(this, path, ...args) return _originalStartLogging.call(this, path, ...args);
} catch (e) { } catch (e) {
this._currentlyLoggingPath = null this._currentlyLoggingPath = null;
throw e throw e;
} }
} };
const _originalStopLogging = NetLog.prototype.stopLogging const _originalStopLogging = NetLog.prototype.stopLogging;
NetLog.prototype.stopLogging = function () { NetLog.prototype.stopLogging = function () {
this._currentlyLoggingPath = null const logPath = this._currentlyLoggingPath;
return _originalStopLogging.call(this) this._currentlyLoggingPath = null;
} return _originalStopLogging.call(this).then(() => logPath);
};
const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath') const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath');
Object.defineProperties(NetLog.prototype, { Object.defineProperties(NetLog.prototype, {
currentlyLoggingPath: { currentlyLoggingPath: {
enumerable: true, enumerable: true,
get () { get () {
currentlyLoggingPathDeprecated() currentlyLoggingPathDeprecated();
return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath;
} }
} }
}) });

View File

@@ -1,10 +1,10 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events';
import { deprecate } from 'electron' import { deprecate } from 'electron';
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences') const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences');
// SystemPreferences is an EventEmitter. // SystemPreferences is an EventEmitter.
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype) Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
EventEmitter.call(systemPreferences) EventEmitter.call(systemPreferences);
if ('appLevelAppearance' in systemPreferences) { if ('appLevelAppearance' in systemPreferences) {
deprecate.fnToProperty( deprecate.fnToProperty(
@@ -12,7 +12,7 @@ if ('appLevelAppearance' in systemPreferences) {
'appLevelAppearance', 'appLevelAppearance',
'_getAppLevelAppearance', '_getAppLevelAppearance',
'_setAppLevelAppearance' '_setAppLevelAppearance'
) );
} }
if ('effectiveAppearance' in systemPreferences) { if ('effectiveAppearance' in systemPreferences) {
@@ -20,23 +20,23 @@ if ('effectiveAppearance' in systemPreferences) {
SystemPreferences.prototype, SystemPreferences.prototype,
'effectiveAppearance', 'effectiveAppearance',
'_getEffectiveAppearance' '_getEffectiveAppearance'
) );
} }
SystemPreferences.prototype.isDarkMode = deprecate.moveAPI( SystemPreferences.prototype.isDarkMode = deprecate.moveAPI(
SystemPreferences.prototype.isDarkMode, SystemPreferences.prototype.isDarkMode,
'systemPreferences.isDarkMode()', 'systemPreferences.isDarkMode()',
'nativeTheme.shouldUseDarkColors' 'nativeTheme.shouldUseDarkColors'
) );
SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI( SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI(
SystemPreferences.prototype.isInvertedColorScheme, SystemPreferences.prototype.isInvertedColorScheme,
'systemPreferences.isInvertedColorScheme()', 'systemPreferences.isInvertedColorScheme()',
'nativeTheme.shouldUseInvertedColorScheme' 'nativeTheme.shouldUseInvertedColorScheme'
) );
SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI( SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI(
SystemPreferences.prototype.isHighContrastColorScheme, SystemPreferences.prototype.isHighContrastColorScheme,
'systemPreferences.isHighContrastColorScheme()', 'systemPreferences.isHighContrastColorScheme()',
'nativeTheme.shouldUseHighContrastColors' 'nativeTheme.shouldUseHighContrastColors'
) );
module.exports = systemPreferences module.exports = systemPreferences;

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
'use strict' 'use strict';
const { EventEmitter } = require('events') const { EventEmitter } = require('events');
const { deprecate } = require('electron') const { deprecate } = require('electron');
const { Tray } = process.electronBinding('tray') const { Tray } = process.electronBinding('tray');
Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype) Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype);
module.exports = Tray module.exports = Tray;

View File

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

View File

@@ -1,15 +1,15 @@
'use strict' 'use strict';
const electron = require('electron') const electron = require('electron');
const { LayoutManager } = electron const { LayoutManager } = electron;
const { BoxLayout } = process.electronBinding('box_layout') const { BoxLayout } = process.electronBinding('box_layout');
Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype) Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype);
BoxLayout.prototype._init = function () { BoxLayout.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
LayoutManager.prototype._init.call(this) LayoutManager.prototype._init.call(this);
} };
module.exports = BoxLayout module.exports = BoxLayout;

View File

@@ -1,15 +1,15 @@
'use strict' 'use strict';
const electron = require('electron') const electron = require('electron');
const { View } = electron const { View } = electron;
const { Button } = process.electronBinding('button') const { Button } = process.electronBinding('button');
Object.setPrototypeOf(Button.prototype, View.prototype) Object.setPrototypeOf(Button.prototype, View.prototype);
Button.prototype._init = function () { Button.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
View.prototype._init.call(this) View.prototype._init.call(this);
} };
module.exports = Button module.exports = Button;

View File

@@ -1,15 +1,15 @@
'use strict' 'use strict';
const electron = require('electron') const electron = require('electron');
const { Button } = electron const { Button } = electron;
const { LabelButton } = process.electronBinding('label_button') const { LabelButton } = process.electronBinding('label_button');
Object.setPrototypeOf(LabelButton.prototype, Button.prototype) Object.setPrototypeOf(LabelButton.prototype, Button.prototype);
LabelButton.prototype._init = function () { LabelButton.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
Button.prototype._init.call(this) Button.prototype._init.call(this);
} };
module.exports = LabelButton module.exports = LabelButton;

View File

@@ -1,8 +1,8 @@
'use strict' 'use strict';
const { LayoutManager } = process.electronBinding('layout_manager') const { LayoutManager } = process.electronBinding('layout_manager');
LayoutManager.prototype._init = function () { LayoutManager.prototype._init = function () {
} };
module.exports = LayoutManager module.exports = LayoutManager;

View File

@@ -1,15 +1,15 @@
'use strict' 'use strict';
const electron = require('electron') const electron = require('electron');
const { LabelButton } = electron const { LabelButton } = electron;
const { MdTextButton } = process.electronBinding('md_text_button') const { MdTextButton } = process.electronBinding('md_text_button');
Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype) Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype);
MdTextButton.prototype._init = function () { MdTextButton.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
LabelButton.prototype._init.call(this) LabelButton.prototype._init.call(this);
} };
module.exports = MdTextButton module.exports = MdTextButton;

View File

@@ -1,15 +1,15 @@
'use strict' 'use strict';
const electron = require('electron') const electron = require('electron');
const { View } = electron const { View } = electron;
const { ResizeArea } = process.electronBinding('resize_area') const { ResizeArea } = process.electronBinding('resize_area');
Object.setPrototypeOf(ResizeArea.prototype, View.prototype) Object.setPrototypeOf(ResizeArea.prototype, View.prototype);
ResizeArea.prototype._init = function () { ResizeArea.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
View.prototype._init.call(this) View.prototype._init.call(this);
} };
module.exports = ResizeArea module.exports = ResizeArea;

View File

@@ -1,15 +1,15 @@
'use strict' 'use strict';
const electron = require('electron') const electron = require('electron');
const { View } = electron const { View } = electron;
const { TextField } = process.electronBinding('text_field') const { TextField } = process.electronBinding('text_field');
Object.setPrototypeOf(TextField.prototype, View.prototype) Object.setPrototypeOf(TextField.prototype, View.prototype);
TextField.prototype._init = function () { TextField.prototype._init = function () {
// Call parent class's _init. // Call parent class's _init.
View.prototype._init.call(this) View.prototype._init.call(this);
} };
module.exports = TextField module.exports = TextField;

View File

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

View File

@@ -1,25 +1,26 @@
'use strict' 'use strict';
const features = process.electronBinding('features') const features = process.electronBinding('features');
const { EventEmitter } = require('events') const { EventEmitter } = require('events');
const electron = require('electron') const electron = require('electron');
const path = require('path') const path = require('path');
const url = require('url') const url = require('url');
const { app, ipcMain, session, deprecate } = electron const { app, ipcMain, session, deprecate } = electron;
const NavigationController = require('@electron/internal/browser/navigation-controller') const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') const NavigationController = require('@electron/internal/browser/navigation-controller');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
// session is not used here, the purpose is to make sure session is initalized // session is not used here, the purpose is to make sure session is initalized
// before the webContents module. // before the webContents module.
// eslint-disable-next-line // eslint-disable-next-line
session session
let nextId = 0 let nextId = 0;
const getNextId = function () { const getNextId = function () {
return ++nextId return ++nextId;
} };
// Stock page sizes // Stock page sizes
const PDFPageSizes = { const PDFPageSizes = {
@@ -60,7 +61,7 @@ const PDFPageSizes = {
width_microns: 279400, width_microns: 279400,
custom_display_name: 'Tabloid' custom_display_name: 'Tabloid'
} }
} };
// Default printing setting // Default printing setting
const defaultPrintingSetting = { const defaultPrintingSetting = {
@@ -71,7 +72,6 @@ const defaultPrintingSetting = {
headerFooterEnabled: false, headerFooterEnabled: false,
marginsType: 0, marginsType: 0,
isFirstRequest: false, isFirstRequest: false,
requestID: getNextId(),
previewUIID: 0, previewUIID: 0,
previewModifiable: true, previewModifiable: true,
printToPDF: true, printToPDF: true,
@@ -91,83 +91,83 @@ const defaultPrintingSetting = {
collate: true, collate: true,
shouldPrintBackgrounds: false, shouldPrintBackgrounds: false,
shouldPrintSelectionOnly: false shouldPrintSelectionOnly: false
} };
// JavaScript implementations of WebContents. // JavaScript implementations of WebContents.
const binding = process.electronBinding('web_contents') const binding = process.electronBinding('web_contents');
const { WebContents } = binding const { WebContents } = binding;
Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype) Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype);
Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype) Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype);
// WebContents::send(channel, args..) // WebContents::send(channel, args..)
// WebContents::sendToAll(channel, args..) // WebContents::sendToAll(channel, args..)
WebContents.prototype.send = function (channel, ...args) { WebContents.prototype.send = function (channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument') throw new Error('Missing required channel argument');
} }
const internal = false const internal = false;
const sendToAll = false const sendToAll = false;
return this._send(internal, sendToAll, channel, args) return this._send(internal, sendToAll, channel, args);
} };
WebContents.prototype.sendToAll = function (channel, ...args) { WebContents.prototype.sendToAll = function (channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument') throw new Error('Missing required channel argument');
} }
const internal = false const internal = false;
const sendToAll = true const sendToAll = true;
return this._send(internal, sendToAll, channel, args) return this._send(internal, sendToAll, channel, args);
} };
WebContents.prototype._sendInternal = function (channel, ...args) { WebContents.prototype._sendInternal = function (channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument') throw new Error('Missing required channel argument');
} }
const internal = true const internal = true;
const sendToAll = false const sendToAll = false;
return this._send(internal, sendToAll, channel, args) return this._send(internal, sendToAll, channel, args);
} };
WebContents.prototype._sendInternalToAll = function (channel, ...args) { WebContents.prototype._sendInternalToAll = function (channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument') throw new Error('Missing required channel argument');
} }
const internal = true const internal = true;
const sendToAll = true const sendToAll = true;
return this._send(internal, sendToAll, channel, args) return this._send(internal, sendToAll, channel, args);
} };
WebContents.prototype.sendToFrame = function (frameId, channel, ...args) { WebContents.prototype.sendToFrame = function (frameId, channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument') throw new Error('Missing required channel argument');
} else if (typeof frameId !== 'number') { } else if (typeof frameId !== 'number') {
throw new Error('Missing required frameId argument') throw new Error('Missing required frameId argument');
} }
const internal = false const internal = false;
const sendToAll = false const sendToAll = false;
return this._sendToFrame(internal, sendToAll, frameId, channel, args) return this._sendToFrame(internal, sendToAll, frameId, channel, args);
} };
WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) { WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) {
if (typeof channel !== 'string') { if (typeof channel !== 'string') {
throw new Error('Missing required channel argument') throw new Error('Missing required channel argument');
} else if (typeof frameId !== 'number') { } else if (typeof frameId !== 'number') {
throw new Error('Missing required frameId argument') throw new Error('Missing required frameId argument');
} }
const internal = true const internal = true;
const sendToAll = false const sendToAll = false;
return this._sendToFrame(internal, sendToAll, frameId, channel, args) return this._sendToFrame(internal, sendToAll, frameId, channel, args);
} };
// Following methods are mapped to webFrame. // Following methods are mapped to webFrame.
const webFrameMethods = [ const webFrameMethods = [
@@ -176,59 +176,65 @@ const webFrameMethods = [
'removeInsertedCSS', 'removeInsertedCSS',
'setLayoutZoomLevelLimits', 'setLayoutZoomLevelLimits',
'setVisualZoomLevelLimits' 'setVisualZoomLevelLimits'
] ];
for (const method of webFrameMethods) { for (const method of webFrameMethods) {
WebContents.prototype[method] = function (...args) { WebContents.prototype[method] = function (...args) {
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args) return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args);
} };
} }
const executeJavaScript = (contents, code, hasUserGesture) => { const waitTillCanExecuteJavaScript = async (webContents) => {
return ipcMainUtils.invokeInWebContents(contents, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture) if (webContents.getURL() && !webContents.isLoadingMainFrame()) return;
}
return new Promise((resolve) => {
webContents.once('did-stop-loading', () => {
resolve();
});
});
};
// Make sure WebContents::executeJavaScript would run the code only when the // Make sure WebContents::executeJavaScript would run the code only when the
// WebContents has been loaded. // WebContents has been loaded.
WebContents.prototype.executeJavaScript = function (code, hasUserGesture) { WebContents.prototype.executeJavaScript = async function (code, hasUserGesture) {
if (this.getURL() && !this.isLoadingMainFrame()) { await waitTillCanExecuteJavaScript(this);
return executeJavaScript(this, code, hasUserGesture) return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture);
} else { };
return new Promise((resolve, reject) => { WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (code, hasUserGesture) {
this.once('did-stop-loading', () => { await waitTillCanExecuteJavaScript(this);
executeJavaScript(this, code, hasUserGesture).then(resolve, reject) return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', code, hasUserGesture);
}) };
})
}
}
// Translate the options of printToPDF. // Translate the options of printToPDF.
WebContents.prototype.printToPDF = function (options) { WebContents.prototype.printToPDF = function (options) {
const printingSetting = Object.assign({}, defaultPrintingSetting) const printingSetting = {
...defaultPrintingSetting,
requestID: getNextId()
};
if (options.landscape) { if (options.landscape) {
printingSetting.landscape = options.landscape printingSetting.landscape = options.landscape;
} }
if (options.fitToPageEnabled) { if (options.fitToPageEnabled) {
printingSetting.fitToPageEnabled = options.fitToPageEnabled printingSetting.fitToPageEnabled = options.fitToPageEnabled;
} }
if (options.scaleFactor) { if (options.scaleFactor) {
printingSetting.scaleFactor = options.scaleFactor printingSetting.scaleFactor = options.scaleFactor;
} }
if (options.marginsType) { if (options.marginsType) {
printingSetting.marginsType = options.marginsType printingSetting.marginsType = options.marginsType;
} }
if (options.printSelectionOnly) { if (options.printSelectionOnly) {
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly;
} }
if (options.printBackground) { if (options.printBackground) {
printingSetting.shouldPrintBackgrounds = options.printBackground printingSetting.shouldPrintBackgrounds = options.printBackground;
} }
if (options.pageSize) { if (options.pageSize) {
const pageSize = options.pageSize const pageSize = options.pageSize;
if (typeof pageSize === 'object') { if (typeof pageSize === 'object') {
if (!pageSize.height || !pageSize.width) { if (!pageSize.height || !pageSize.width) {
return Promise.reject(new Error('Must define height and width for pageSize')) return Promise.reject(new Error('Must define height and width for pageSize'));
} }
// Dimensions in Microns // Dimensions in Microns
// 1 meter = 10^6 microns // 1 meter = 10^6 microns
@@ -237,46 +243,49 @@ WebContents.prototype.printToPDF = function (options) {
custom_display_name: 'Custom', custom_display_name: 'Custom',
height_microns: Math.ceil(pageSize.height), height_microns: Math.ceil(pageSize.height),
width_microns: Math.ceil(pageSize.width) width_microns: Math.ceil(pageSize.width)
} };
} else if (PDFPageSizes[pageSize]) { } else if (PDFPageSizes[pageSize]) {
printingSetting.mediaSize = PDFPageSizes[pageSize] printingSetting.mediaSize = PDFPageSizes[pageSize];
} else { } else {
return Promise.reject(new Error(`Does not support pageSize with ${pageSize}`)) return Promise.reject(new Error(`Does not support pageSize with ${pageSize}`));
} }
} else { } else {
printingSetting.mediaSize = PDFPageSizes['A4'] printingSetting.mediaSize = PDFPageSizes['A4'];
} }
// Chromium expects this in a 0-100 range number, not as float // Chromium expects this in a 0-100 range number, not as float
printingSetting.scaleFactor = Math.ceil(printingSetting.scaleFactor) % 100 printingSetting.scaleFactor = Math.ceil(printingSetting.scaleFactor) % 100;
// PrinterType enum from //printing/print_job_constants.h
printingSetting.printerType = 2;
if (features.isPrintingEnabled()) { if (features.isPrintingEnabled()) {
return this._printToPDF(printingSetting) return this._printToPDF(printingSetting);
} else { } else {
return Promise.reject(new Error('Printing feature is disabled')) return Promise.reject(new Error('Printing feature is disabled'));
} }
} };
WebContents.prototype.print = function (...args) { WebContents.prototype.print = function (...args) {
if (features.isPrintingEnabled()) { if (features.isPrintingEnabled()) {
this._print(...args) this._print(...args);
} else { } else {
console.error('Error: Printing feature is disabled.') console.error('Error: Printing feature is disabled.');
} }
} };
WebContents.prototype.getPrinters = function () { WebContents.prototype.getPrinters = function () {
if (features.isPrintingEnabled()) { if (features.isPrintingEnabled()) {
return this._getPrinters() return this._getPrinters();
} else { } else {
console.error('Error: Printing feature is disabled.') console.error('Error: Printing feature is disabled.');
return [];
} }
} };
WebContents.prototype.loadFile = function (filePath, options = {}) { WebContents.prototype.loadFile = function (filePath, options = {}) {
if (typeof filePath !== 'string') { if (typeof filePath !== 'string') {
throw new Error('Must pass filePath as a string') throw new Error('Must pass filePath as a string');
} }
const { query, search, hash } = options const { query, search, hash } = options;
return this.loadURL(url.format({ return this.loadURL(url.format({
protocol: 'file', protocol: 'file',
@@ -285,99 +294,105 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
query, query,
search, search,
hash hash
})) }));
} };
const addReplyToEvent = (event) => { const addReplyToEvent = (event) => {
event.reply = (...args) => { event.reply = (...args) => {
event.sender.sendToFrame(event.frameId, ...args) event.sender.sendToFrame(event.frameId, ...args);
} };
} };
const addReplyInternalToEvent = (event) => { const addReplyInternalToEvent = (event) => {
Object.defineProperty(event, '_replyInternal', { Object.defineProperty(event, '_replyInternal', {
configurable: false, configurable: false,
enumerable: false, enumerable: false,
value: (...args) => { value: (...args) => {
event.sender._sendToFrameInternal(event.frameId, ...args) event.sender._sendToFrameInternal(event.frameId, ...args);
} }
}) });
} };
const addReturnValueToEvent = (event) => { const addReturnValueToEvent = (event) => {
Object.defineProperty(event, 'returnValue', { Object.defineProperty(event, 'returnValue', {
set: (value) => event.sendReply([value]), set: (value) => event.sendReply([value]),
get: () => {} get: () => {}
}) });
} };
let warnedAboutRendererProcessReuse = false;
// Add JavaScript wrappers for WebContents class. // Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () { WebContents.prototype._init = function () {
if (!process.noDeprecation && !warnedAboutRendererProcessReuse && app._allowRendererProcessReuseIsDefaultValue) {
deprecate.log('The default value of app.allowRendererProcessReuse is deprecated, it is currently "false". It will change to be "true" in Electron 9. For more information please check https://github.com/electron/electron/issues/18397');
warnedAboutRendererProcessReuse = true;
}
// The navigation controller. // The navigation controller.
NavigationController.call(this, this) NavigationController.call(this, this);
// Every remote callback from renderer process would add a listener to the // Every remote callback from renderer process would add a listener to the
// render-view-deleted event, so ignore the listeners warning. // render-view-deleted event, so ignore the listeners warning.
this.setMaxListeners(0) this.setMaxListeners(0);
// Dispatch IPC messages to the ipc module. // Dispatch IPC messages to the ipc module.
this.on('-ipc-message', function (event, internal, channel, args) { this.on('-ipc-message', function (event, internal, channel, args) {
if (internal) { if (internal) {
addReplyInternalToEvent(event) addReplyInternalToEvent(event);
ipcMainInternal.emit(channel, event, ...args) ipcMainInternal.emit(channel, event, ...args);
} else { } else {
addReplyToEvent(event) addReplyToEvent(event);
this.emit('ipc-message', event, channel, ...args) this.emit('ipc-message', event, channel, ...args);
ipcMain.emit(channel, event, ...args) ipcMain.emit(channel, event, ...args);
} }
}) });
this.on('-ipc-invoke', function (event, internal, channel, args) { this.on('-ipc-invoke', function (event, internal, channel, args) {
event._reply = (result) => event.sendReply({ result }) event._reply = (result) => event.sendReply({ result });
event._throw = (error) => { event._throw = (error) => {
console.error(`Error occurred in handler for '${channel}':`, error) console.error(`Error occurred in handler for '${channel}':`, error);
event.sendReply({ error: error.toString() }) event.sendReply({ error: error.toString() });
} };
const target = internal ? ipcMainInternal : ipcMain const target = internal ? ipcMainInternal : ipcMain;
if (target._invokeHandlers.has(channel)) { if (target._invokeHandlers.has(channel)) {
target._invokeHandlers.get(channel)(event, ...args) target._invokeHandlers.get(channel)(event, ...args);
} else { } else {
event._throw(`No handler registered for '${channel}'`) event._throw(`No handler registered for '${channel}'`);
} }
}) });
this.on('-ipc-message-sync', function (event, internal, channel, args) { this.on('-ipc-message-sync', function (event, internal, channel, args) {
addReturnValueToEvent(event) addReturnValueToEvent(event);
if (internal) { if (internal) {
addReplyInternalToEvent(event) addReplyInternalToEvent(event);
ipcMainInternal.emit(channel, event, ...args) ipcMainInternal.emit(channel, event, ...args);
} else { } else {
addReplyToEvent(event) addReplyToEvent(event);
this.emit('ipc-message-sync', event, channel, ...args) this.emit('ipc-message-sync', event, channel, ...args);
ipcMain.emit(channel, event, ...args) ipcMain.emit(channel, event, ...args);
} }
}) });
// Handle context menu action request from pepper plugin. // Handle context menu action request from pepper plugin.
this.on('pepper-context-menu', function (event, params, callback) { this.on('pepper-context-menu', function (event, params, callback) {
// Access Menu via electron.Menu to prevent circular require. // Access Menu via electron.Menu to prevent circular require.
const menu = electron.Menu.buildFromTemplate(params.menu) const menu = electron.Menu.buildFromTemplate(params.menu);
menu.popup({ menu.popup({
window: event.sender.getOwnerBrowserWindow(), window: event.sender.getOwnerBrowserWindow(),
x: params.x, x: params.x,
y: params.y, y: params.y,
callback callback
}) });
}) });
this.on('crashed', (event, ...args) => { this.on('crashed', (event, ...args) => {
app.emit('renderer-process-crashed', event, this, ...args) app.emit('renderer-process-crashed', event, this, ...args);
}) });
// The devtools requests the webContents to reload. // The devtools requests the webContents to reload.
this.on('devtools-reload-page', function () { this.on('devtools-reload-page', function () {
this.reload() this.reload();
}) });
// Handle window.open for BrowserWindow and BrowserView. // Handle window.open for BrowserWindow and BrowserView.
if (['browserView', 'window'].includes(this.getType())) { if (['browserView', 'window'].includes(this.getType())) {
@@ -389,11 +404,9 @@ WebContents.prototype._init = function () {
show: true, show: true,
width: 800, width: 800,
height: 600 height: 600
} };
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures, postData);
event, url, referrer, frameName, disposition, });
options, additionalFeatures, postData)
})
// Create a new browser window for the native implementation of // Create a new browser window for the native implementation of
// "window.open", used in sandbox and nativeWindowOpen mode. // "window.open", used in sandbox and nativeWindowOpen mode.
@@ -401,8 +414,8 @@ WebContents.prototype._init = function () {
userGesture, left, top, width, height, url, frameName) => { userGesture, left, top, width, height, url, frameName) => {
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' && if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
disposition !== 'background-tab')) { disposition !== 'background-tab')) {
event.preventDefault() event.preventDefault();
return return;
} }
const options = { const options = {
@@ -412,51 +425,54 @@ WebContents.prototype._init = function () {
width: width || 800, width: width || 800,
height: height || 600, height: height || 600,
webContents webContents
} };
const referrer = { url: '', policy: 'default' } const referrer = { url: '', policy: 'default' };
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', internalWindowOpen(event, url, referrer, frameName, disposition, options);
event, url, referrer, frameName, disposition, options) });
})
} }
const event = process.electronBinding('event').createEmpty() this.on('login', (event, ...args) => {
app.emit('web-contents-created', event, this) app.emit('login', event, this, ...args);
} });
const event = process.electronBinding('event').createEmpty();
app.emit('web-contents-created', event, this);
};
// Deprecations // Deprecations
deprecate.fnToProperty(WebContents.prototype, 'audioMuted', '_isAudioMuted', '_setAudioMuted') deprecate.fnToProperty(WebContents.prototype, 'audioMuted', '_isAudioMuted', '_setAudioMuted');
deprecate.fnToProperty(WebContents.prototype, 'userAgent', '_getUserAgent', '_setUserAgent') deprecate.fnToProperty(WebContents.prototype, 'userAgent', '_getUserAgent', '_setUserAgent');
deprecate.fnToProperty(WebContents.prototype, 'zoomLevel', '_getZoomLevel', '_setZoomLevel') deprecate.fnToProperty(WebContents.prototype, 'zoomLevel', '_getZoomLevel', '_setZoomLevel');
deprecate.fnToProperty(WebContents.prototype, 'zoomFactor', '_getZoomFactor', '_setZoomFactor') deprecate.fnToProperty(WebContents.prototype, 'zoomFactor', '_getZoomFactor', '_setZoomFactor');
deprecate.fnToProperty(WebContents.prototype, 'frameRate', '_getFrameRate', '_setFrameRate') deprecate.fnToProperty(WebContents.prototype, 'frameRate', '_getFrameRate', '_setFrameRate');
// JavaScript wrapper of Debugger. // JavaScript wrapper of Debugger.
const { Debugger } = process.electronBinding('debugger') const { Debugger } = process.electronBinding('debugger');
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype) Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype);
// Public APIs. // Public APIs.
module.exports = { module.exports = {
create (options = {}) { create (options = {}) {
return binding.create(options) return binding.create(options);
}, },
fromId (id) { fromId (id) {
return binding.fromId(id) return binding.fromId(id);
}, },
getFocusedWebContents () { getFocusedWebContents () {
let focused = null let focused = null;
for (const contents of binding.getAllWebContents()) { for (const contents of binding.getAllWebContents()) {
if (!contents.isFocused()) continue if (!contents.isFocused()) continue;
if (focused == null) focused = contents if (focused == null) focused = contents;
// Return webview web contents which may be embedded inside another // Return webview web contents which may be embedded inside another
// web contents that is also reporting as focused // web contents that is also reporting as focused
if (contents.getType() === 'webview') return contents if (contents.getType() === 'webview') return contents;
} }
return focused return focused;
}, },
getAllWebContents () { getAllWebContents () {
return binding.getAllWebContents() return binding.getAllWebContents();
} }
} };

View File

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

View File

@@ -1,25 +1,25 @@
'use strict' 'use strict';
const { app } = require('electron') const { app } = require('electron');
const path = require('path') const path = require('path');
const getTempDirectory = function () { const getTempDirectory = function () {
try { try {
return app.getPath('temp') return app.getPath('temp');
} catch { } catch {
// Delibrately laze-load the os module, this file is on the hot // Delibrately laze-load the os module, this file is on the hot
// path when booting Electron and os takes between 5 - 8ms to load and we do not need it yet // path when booting Electron and os takes between 5 - 8ms to load and we do not need it yet
return require('os').tmpdir() return require('os').tmpdir();
} }
} };
exports.crashReporterInit = function (options) { exports.crashReporterInit = function (options) {
const productName = options.productName || app.name const productName = options.productName || app.name;
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`) const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`);
return { return {
productName, productName,
crashesDirectory, crashesDirectory,
appVersion: app.getVersion() appVersion: app.getVersion()
} };
} };

View File

@@ -1,11 +1,11 @@
import { shell, Menu } from 'electron' import { shell, Menu } from 'electron';
const v8Util = process.electronBinding('v8_util') const v8Util = process.electronBinding('v8_util');
const isMac = process.platform === 'darwin' const isMac = process.platform === 'darwin';
export const setDefaultApplicationMenu = () => { export const setDefaultApplicationMenu = () => {
if (v8Util.getHiddenValue<boolean>(global, 'applicationMenuSet')) return if (v8Util.getHiddenValue<boolean>(global, 'applicationMenuSet')) return;
const helpMenu: Electron.MenuItemConstructorOptions = { const helpMenu: Electron.MenuItemConstructorOptions = {
role: 'help', role: 'help',
@@ -13,32 +13,32 @@ export const setDefaultApplicationMenu = () => {
{ {
label: 'Learn More', label: 'Learn More',
click: async () => { click: async () => {
await shell.openExternal('https://electronjs.org') await shell.openExternal('https://electronjs.org');
} }
}, },
{ {
label: 'Documentation', label: 'Documentation',
click: async () => { click: async () => {
const version = process.versions.electron const version = process.versions.electron;
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`) await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
} }
}, },
{ {
label: 'Community Discussions', label: 'Community Discussions',
click: async () => { click: async () => {
await shell.openExternal('https://discuss.atom.io/c/electron') await shell.openExternal('https://discuss.atom.io/c/electron');
} }
}, },
{ {
label: 'Search Issues', label: 'Search Issues',
click: async () => { click: async () => {
await shell.openExternal('https://github.com/electron/electron/issues') await shell.openExternal('https://github.com/electron/electron/issues');
} }
} }
] ]
} };
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' } const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
const template: Electron.MenuItemConstructorOptions[] = [ const template: Electron.MenuItemConstructorOptions[] = [
...(isMac ? [macAppMenu] : []), ...(isMac ? [macAppMenu] : []),
{ role: 'fileMenu' }, { role: 'fileMenu' },
@@ -46,8 +46,8 @@ export const setDefaultApplicationMenu = () => {
{ role: 'viewMenu' }, { role: 'viewMenu' },
{ role: 'windowMenu' }, { role: 'windowMenu' },
helpMenu helpMenu
] ];
const menu = Menu.buildFromTemplate(template) const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu) Menu.setApplicationMenu(menu);
} };

View File

@@ -1,66 +1,66 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events';
const { createDesktopCapturer } = process.electronBinding('desktop_capturer') const { createDesktopCapturer } = process.electronBinding('desktop_capturer');
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b) const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
let currentlyRunning: { let currentlyRunning: {
options: ElectronInternal.GetSourcesOptions; options: ElectronInternal.GetSourcesOptions;
getSources: Promise<ElectronInternal.GetSourcesResult[]>; getSources: Promise<ElectronInternal.GetSourcesResult[]>;
}[] = [] }[] = [];
export const getSources = (event: Electron.IpcMainEvent, options: ElectronInternal.GetSourcesOptions) => { export const getSources = (event: Electron.IpcMainEvent, options: ElectronInternal.GetSourcesOptions) => {
for (const running of currentlyRunning) { for (const running of currentlyRunning) {
if (deepEqual(running.options, options)) { if (deepEqual(running.options, options)) {
// If a request is currently running for the same options // If a request is currently running for the same options
// return that promise // return that promise
return running.getSources return running.getSources;
} }
} }
const getSources = new Promise<ElectronInternal.GetSourcesResult[]>((resolve, reject) => { const getSources = new Promise<ElectronInternal.GetSourcesResult[]>((resolve, reject) => {
let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer() let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer();
const stopRunning = () => { const stopRunning = () => {
if (capturer) { if (capturer) {
capturer.emit = null capturer.emit = null;
capturer = null capturer = null;
} }
// Remove from currentlyRunning once we resolve or reject // Remove from currentlyRunning once we resolve or reject
currentlyRunning = currentlyRunning.filter(running => running.options !== options) currentlyRunning = currentlyRunning.filter(running => running.options !== options);
} };
const emitter = new EventEmitter() const emitter = new EventEmitter();
emitter.once('error', (event, error: string) => { emitter.once('error', (event, error: string) => {
stopRunning() stopRunning();
reject(error) reject(error);
}) });
emitter.once('finished', (event, sources: Electron.DesktopCapturerSource[], fetchWindowIcons: boolean) => { emitter.once('finished', (event, sources: Electron.DesktopCapturerSource[], fetchWindowIcons: boolean) => {
stopRunning() stopRunning();
resolve(sources.map(source => ({ resolve(sources.map(source => ({
id: source.id, id: source.id,
name: source.name, name: source.name,
thumbnail: source.thumbnail.toDataURL(), thumbnail: source.thumbnail.toDataURL(),
display_id: source.display_id, display_id: source.display_id,
appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null
}))) })));
}) });
capturer.emit = emitter.emit.bind(emitter) capturer.emit = emitter.emit.bind(emitter);
capturer.startHandling(options.captureWindow, options.captureScreen, options.thumbnailSize, options.fetchWindowIcons) capturer.startHandling(options.captureWindow, options.captureScreen, options.thumbnailSize, options.fetchWindowIcons);
// If the WebContents is destroyed before receiving result, just remove the // If the WebContents is destroyed before receiving result, just remove the
// reference to emit and the capturer itself so that it never dispatches // reference to emit and the capturer itself so that it never dispatches
// back to the renderer // back to the renderer
event.sender.once('destroyed', () => stopRunning()) event.sender.once('destroyed', () => stopRunning());
}) });
currentlyRunning.push({ currentlyRunning.push({
options, options,
getSources getSources
}) });
return getSources return getSources;
} };

View File

@@ -1,9 +1,9 @@
import { dialog, Menu } from 'electron' import { dialog, Menu } from 'electron';
import * as fs from 'fs' import * as fs from 'fs';
import * as url from 'url' import * as url from 'url';
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal' import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils' import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: number) => void) { const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: number) => void) {
return items.map(function (item) { return items.map(function (item) {
@@ -23,15 +23,15 @@ const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id:
type: 'normal', type: 'normal',
label: item.label, label: item.label,
enabled: item.enabled enabled: item.enabled
} };
if (item.id != null) { if (item.id != null) {
transformed.click = () => handler(item.id) transformed.click = () => handler(item.id);
} }
return transformed return transformed;
}) });
} };
const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] { const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] {
return [ return [
@@ -44,56 +44,56 @@ const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] {
{ role: 'pasteAndMatchStyle' }, { role: 'pasteAndMatchStyle' },
{ role: 'delete' }, { role: 'delete' },
{ role: 'selectAll' } { role: 'selectAll' }
] ];
} };
const isChromeDevTools = function (pageURL: string) { const isChromeDevTools = function (pageURL: string) {
const { protocol } = url.parse(pageURL) const { protocol } = url.parse(pageURL);
return protocol === 'devtools:' return protocol === 'devtools:';
} };
const assertChromeDevTools = function (contents: Electron.WebContents, api: string) { const assertChromeDevTools = function (contents: Electron.WebContents, api: string) {
const pageURL = contents._getURL() const pageURL = contents._getURL();
if (!isChromeDevTools(pageURL)) { if (!isChromeDevTools(pageURL)) {
console.error(`Blocked ${pageURL} from calling ${api}`) console.error(`Blocked ${pageURL} from calling ${api}`);
throw new Error(`Blocked ${api}`) throw new Error(`Blocked ${api}`);
} }
} };
ipcMainInternal.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainInvokeEvent, items: ContextMenuItem[], isEditMenu: boolean) { ipcMainInternal.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainInvokeEvent, items: ContextMenuItem[], isEditMenu: boolean) {
return new Promise(resolve => { return new Promise(resolve => {
assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()') assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()');
const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve) const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve);
const menu = Menu.buildFromTemplate(template) const menu = Menu.buildFromTemplate(template);
const window = event.sender.getOwnerBrowserWindow() const window = event.sender.getOwnerBrowserWindow();
menu.popup({ window, callback: () => resolve() }) menu.popup({ window, callback: () => resolve() });
}) });
}) });
ipcMainInternal.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainInvokeEvent) { ipcMainInternal.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainInvokeEvent) {
assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()') assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()');
const result = await dialog.showOpenDialog({}) const result = await dialog.showOpenDialog({});
if (result.canceled) return [] if (result.canceled) return [];
const path = result.filePaths[0] const path = result.filePaths[0];
const data = await fs.promises.readFile(path) const data = await fs.promises.readFile(path);
return [path, data] return [path, data];
}) });
ipcMainUtils.handleSync('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainInvokeEvent, message: string = '', title: string = '') { ipcMainUtils.handleSync('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainInvokeEvent, message: string = '', title: string = '') {
assertChromeDevTools(event.sender, 'window.confirm()') assertChromeDevTools(event.sender, 'window.confirm()');
const options = { const options = {
message: String(message), message: String(message),
title: String(title), title: String(title),
buttons: ['OK', 'Cancel'], buttons: ['OK', 'Cancel'],
cancelId: 1 cancelId: 1
} };
const window = event.sender.getOwnerBrowserWindow() const window = event.sender.getOwnerBrowserWindow();
const { response } = await dialog.showMessageBox(window, options) const { response } = await dialog.showMessageBox(window, options);
return response === 0 return response === 0;
}) });

View File

@@ -1,13 +1,14 @@
'use strict' 'use strict';
const { webContents } = require('electron') const { webContents } = require('electron');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const parseFeaturesString = require('@electron/internal/common/parse-features-string') const parseFeaturesString = require('@electron/internal/common/parse-features-string');
const { syncMethods, asyncMethods } = require('@electron/internal/common/web-view-methods') const { syncMethods, asyncMethods, properties } = require('@electron/internal/common/web-view-methods');
const { serialize } = require('@electron/internal/common/type-utils');
// Doesn't exist in early initialization. // Doesn't exist in early initialization.
let webViewManager = null let webViewManager = null;
const supportedWebViewEvents = [ const supportedWebViewEvents = [
'load-commit', 'load-commit',
@@ -42,155 +43,155 @@ const supportedWebViewEvents = [
'found-in-page', 'found-in-page',
'did-change-theme-color', 'did-change-theme-color',
'update-target-url' 'update-target-url'
] ];
const guestInstances = {} const guestInstances = {};
const embedderElementsMap = {} const embedderElementsMap = {};
function sanitizeOptionsForGuest (options) { function sanitizeOptionsForGuest (options) {
const ret = { ...options } const ret = { ...options };
// WebContents values can't be sent over IPC. // WebContents values can't be sent over IPC.
delete ret.webContents delete ret.webContents;
return ret return ret;
} }
// Create a new guest instance. // Create a new guest instance.
const createGuest = function (embedder, params) { const createGuest = function (embedder, params) {
if (webViewManager == null) { if (webViewManager == null) {
webViewManager = process.electronBinding('web_view_manager') webViewManager = process.electronBinding('web_view_manager');
} }
const guest = webContents.create({ const guest = webContents.create({
type: 'webview', type: 'webview',
partition: params.partition, partition: params.partition,
embedder: embedder embedder: embedder
}) });
const guestInstanceId = guest.id const guestInstanceId = guest.id;
guestInstances[guestInstanceId] = { guestInstances[guestInstanceId] = {
guest: guest, guest: guest,
embedder: embedder embedder: embedder
} };
// Clear the guest from map when it is destroyed. // Clear the guest from map when it is destroyed.
guest.once('destroyed', () => { guest.once('destroyed', () => {
if (guestInstanceId in guestInstances) { if (Object.prototype.hasOwnProperty.call(guestInstances, guestInstanceId)) {
detachGuest(embedder, guestInstanceId) detachGuest(embedder, guestInstanceId);
} }
}) });
// Init guest web view after attached. // Init guest web view after attached.
guest.once('did-attach', function (event) { guest.once('did-attach', function (event) {
params = this.attachParams params = this.attachParams;
delete this.attachParams delete this.attachParams;
const previouslyAttached = this.viewInstanceId != null const previouslyAttached = this.viewInstanceId != null;
this.viewInstanceId = params.instanceId this.viewInstanceId = params.instanceId;
// Only load URL and set size on first attach // Only load URL and set size on first attach
if (previouslyAttached) { if (previouslyAttached) {
return return;
} }
if (params.src) { if (params.src) {
const opts = {} const opts = {};
if (params.httpreferrer) { if (params.httpreferrer) {
opts.httpReferrer = params.httpreferrer opts.httpReferrer = params.httpreferrer;
} }
if (params.useragent) { if (params.useragent) {
opts.userAgent = params.useragent opts.userAgent = params.useragent;
} }
this.loadURL(params.src, opts) this.loadURL(params.src, opts);
} }
embedder.emit('did-attach-webview', event, guest) embedder.emit('did-attach-webview', event, guest);
}) });
const sendToEmbedder = (channel, ...args) => { const sendToEmbedder = (channel, ...args) => {
if (!embedder.isDestroyed()) { if (!embedder.isDestroyed()) {
embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args) embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args);
} }
} };
// Dispatch events to embedder. // Dispatch events to embedder.
const fn = function (event) { const fn = function (event) {
guest.on(event, function (_, ...args) { guest.on(event, function (_, ...args) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args) sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args);
}) });
} };
for (const event of supportedWebViewEvents) { for (const event of supportedWebViewEvents) {
fn(event) fn(event);
} }
guest.on('new-window', function (event, url, frameName, disposition, options, additionalFeatures, referrer) { guest.on('new-window', function (event, url, frameName, disposition, options, additionalFeatures, referrer) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', 'new-window', url, sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', 'new-window', url,
frameName, disposition, sanitizeOptionsForGuest(options), frameName, disposition, sanitizeOptionsForGuest(options),
additionalFeatures, referrer) additionalFeatures, referrer);
}) });
// Dispatch guest's IPC messages to embedder. // Dispatch guest's IPC messages to embedder.
guest.on('ipc-message-host', function (_, channel, args) { guest.on('ipc-message-host', function (_, channel, args) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args) sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args);
}) });
// Notify guest of embedder window visibility when it is ready // Notify guest of embedder window visibility when it is ready
// FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed // FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed
guest.on('dom-ready', function () { guest.on('dom-ready', function () {
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId];
if (guestInstance != null && guestInstance.visibilityState != null) { if (guestInstance != null && guestInstance.visibilityState != null) {
guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState) guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState);
} }
}) });
// Forward internal web contents event to embedder to handle // Forward internal web contents event to embedder to handle
// native window.open setup // native window.open setup
guest.on('-add-new-contents', (...args) => { guest.on('-add-new-contents', (...args) => {
if (guest.getLastWebPreferences().nativeWindowOpen === true) { if (guest.getLastWebPreferences().nativeWindowOpen === true) {
const embedder = getEmbedder(guestInstanceId) const embedder = getEmbedder(guestInstanceId);
if (embedder != null) { if (embedder != null) {
embedder.emit('-add-new-contents', ...args) embedder.emit('-add-new-contents', ...args);
} }
} }
}) });
return guestInstanceId return guestInstanceId;
} };
// Attach the guest to an element of embedder. // Attach the guest to an element of embedder.
const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
const embedder = event.sender const embedder = event.sender;
// Destroy the old guest when attaching. // Destroy the old guest when attaching.
const key = `${embedder.id}-${elementInstanceId}` const key = `${embedder.id}-${elementInstanceId}`;
const oldGuestInstanceId = embedderElementsMap[key] const oldGuestInstanceId = embedderElementsMap[key];
if (oldGuestInstanceId != null) { if (oldGuestInstanceId != null) {
// Reattachment to the same guest is just a no-op. // Reattachment to the same guest is just a no-op.
if (oldGuestInstanceId === guestInstanceId) { if (oldGuestInstanceId === guestInstanceId) {
return return;
} }
const oldGuestInstance = guestInstances[oldGuestInstanceId] const oldGuestInstance = guestInstances[oldGuestInstanceId];
if (oldGuestInstance) { if (oldGuestInstance) {
oldGuestInstance.guest.detachFromOuterFrame() oldGuestInstance.guest.detachFromOuterFrame();
} }
} }
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId];
// If this isn't a valid guest instance then do nothing. // If this isn't a valid guest instance then do nothing.
if (!guestInstance) { if (!guestInstance) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`) throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`);
} }
const { guest } = guestInstance const { guest } = guestInstance;
if (guest.hostWebContents !== event.sender) { if (guest.hostWebContents !== event.sender) {
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`) throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`);
} }
// If this guest is already attached to an element then remove it // If this guest is already attached to an element then remove it
if (guestInstance.elementInstanceId) { if (guestInstance.elementInstanceId) {
const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}` const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}`;
delete embedderElementsMap[oldKey] delete embedderElementsMap[oldKey];
// Remove guest from embedder if moving across web views // Remove guest from embedder if moving across web views
if (guest.viewInstanceId !== params.instanceId) { if (guest.viewInstanceId !== params.instanceId) {
webViewManager.removeGuest(guestInstance.embedder, guestInstanceId) webViewManager.removeGuest(guestInstance.embedder, guestInstanceId);
guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`) guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`);
} }
} }
@@ -200,12 +201,12 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false, nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
enableRemoteModule: params.enableremotemodule, enableRemoteModule: params.enableremotemodule,
plugins: params.plugins, plugins: params.plugins,
zoomFactor: embedder.getZoomFactor(), zoomFactor: embedder.zoomFactor,
disablePopups: !params.allowpopups, disablePopups: !params.allowpopups,
webSecurity: !params.disablewebsecurity, webSecurity: !params.disablewebsecurity,
enableBlinkFeatures: params.blinkfeatures, enableBlinkFeatures: params.blinkfeatures,
disableBlinkFeatures: params.disableblinkfeatures disableBlinkFeatures: params.disableblinkfeatures
} };
// parse the 'webpreferences' attribute string, if set // parse the 'webpreferences' attribute string, if set
// this uses the same parsing rules as window.open uses for its features // this uses the same parsing rules as window.open uses for its features
@@ -213,14 +214,14 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
parseFeaturesString(params.webpreferences, function (key, value) { parseFeaturesString(params.webpreferences, function (key, value) {
if (value === undefined) { if (value === undefined) {
// no value was specified, default it to true // no value was specified, default it to true
value = true value = true;
} }
webPreferences[key] = value webPreferences[key] = value;
}) });
} }
if (params.preload) { if (params.preload) {
webPreferences.preloadURL = params.preload webPreferences.preloadURL = params.preload;
} }
// Security options that guest will always inherit from embedder // Security options that guest will always inherit from embedder
@@ -232,184 +233,208 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
['enableRemoteModule', false], ['enableRemoteModule', false],
['sandbox', true], ['sandbox', true],
['nodeIntegrationInSubFrames', false] ['nodeIntegrationInSubFrames', false]
]) ]);
// Inherit certain option values from embedder // Inherit certain option values from embedder
const lastWebPreferences = embedder.getLastWebPreferences() const lastWebPreferences = embedder.getLastWebPreferences();
for (const [name, value] of inheritedWebPreferences) { for (const [name, value] of inheritedWebPreferences) {
if (lastWebPreferences[name] === value) { if (lastWebPreferences[name] === value) {
webPreferences[name] = value webPreferences[name] = value;
} }
} }
embedder.emit('will-attach-webview', event, webPreferences, params) embedder.emit('will-attach-webview', event, webPreferences, params);
if (event.defaultPrevented) { if (event.defaultPrevented) {
if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId;
guest.destroy() guest.destroy();
return return;
} }
guest.attachParams = params guest.attachParams = params;
embedderElementsMap[key] = guestInstanceId embedderElementsMap[key] = guestInstanceId;
guest.setEmbedder(embedder) guest.setEmbedder(embedder);
guestInstance.embedder = embedder guestInstance.embedder = embedder;
guestInstance.elementInstanceId = elementInstanceId guestInstance.elementInstanceId = elementInstanceId;
watchEmbedder(embedder) watchEmbedder(embedder);
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences) webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences);
guest.attachToIframe(embedder, embedderFrameId) guest.attachToIframe(embedder, embedderFrameId);
} };
// Remove an guest-embedder relationship. // Remove an guest-embedder relationship.
const detachGuest = function (embedder, guestInstanceId) { const detachGuest = function (embedder, guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId];
if (embedder !== guestInstance.embedder) { if (embedder !== guestInstance.embedder) {
return return;
} }
webViewManager.removeGuest(embedder, guestInstanceId) webViewManager.removeGuest(embedder, guestInstanceId);
delete guestInstances[guestInstanceId] delete guestInstances[guestInstanceId];
const key = `${embedder.id}-${guestInstance.elementInstanceId}` const key = `${embedder.id}-${guestInstance.elementInstanceId}`;
delete embedderElementsMap[key] delete embedderElementsMap[key];
} };
// Once an embedder has had a guest attached we watch it for destruction to // Once an embedder has had a guest attached we watch it for destruction to
// destroy any remaining guests. // destroy any remaining guests.
const watchedEmbedders = new Set() const watchedEmbedders = new Set();
const watchEmbedder = function (embedder) { const watchEmbedder = function (embedder) {
if (watchedEmbedders.has(embedder)) { if (watchedEmbedders.has(embedder)) {
return return;
} }
watchedEmbedders.add(embedder) watchedEmbedders.add(embedder);
// Forward embedder window visiblity change events to guest // Forward embedder window visiblity change events to guest
const onVisibilityChange = function (visibilityState) { const onVisibilityChange = function (visibilityState) {
for (const guestInstanceId in guestInstances) { for (const guestInstanceId of Object.keys(guestInstances)) {
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId];
guestInstance.visibilityState = visibilityState guestInstance.visibilityState = visibilityState;
if (guestInstance.embedder === embedder) { if (guestInstance.embedder === embedder) {
guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState) guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState);
} }
} }
} };
embedder.on('-window-visibility-change', onVisibilityChange) embedder.on('-window-visibility-change', onVisibilityChange);
embedder.once('will-destroy', () => { embedder.once('will-destroy', () => {
// Usually the guestInstances is cleared when guest is destroyed, but it // Usually the guestInstances is cleared when guest is destroyed, but it
// may happen that the embedder gets manually destroyed earlier than guest, // may happen that the embedder gets manually destroyed earlier than guest,
// and the embedder will be invalid in the usual code path. // and the embedder will be invalid in the usual code path.
for (const guestInstanceId in guestInstances) { for (const guestInstanceId of Object.keys(guestInstances)) {
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId];
if (guestInstance.embedder === embedder) { if (guestInstance.embedder === embedder) {
detachGuest(embedder, parseInt(guestInstanceId)) detachGuest(embedder, parseInt(guestInstanceId));
} }
} }
// Clear the listeners. // Clear the listeners.
embedder.removeListener('-window-visibility-change', onVisibilityChange) embedder.removeListener('-window-visibility-change', onVisibilityChange);
watchedEmbedders.delete(embedder) watchedEmbedders.delete(embedder);
}) });
} };
const isWebViewTagEnabledCache = new WeakMap() const isWebViewTagEnabledCache = new WeakMap();
const isWebViewTagEnabled = function (contents) { const isWebViewTagEnabled = function (contents) {
if (!isWebViewTagEnabledCache.has(contents)) { if (!isWebViewTagEnabledCache.has(contents)) {
const webPreferences = contents.getLastWebPreferences() || {} const webPreferences = contents.getLastWebPreferences() || {};
isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag) isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag);
} }
return isWebViewTagEnabledCache.get(contents) return isWebViewTagEnabledCache.get(contents);
} };
const makeSafeHandler = function (channel, handler) { const makeSafeHandler = function (channel, handler) {
return (event, ...args) => { return (event, ...args) => {
if (isWebViewTagEnabled(event.sender)) { if (isWebViewTagEnabled(event.sender)) {
return handler(event, ...args) return handler(event, ...args);
} else { } else {
console.error(`<webview> IPC message ${channel} sent by WebContents with <webview> disabled (${event.sender.id})`) console.error(`<webview> IPC message ${channel} sent by WebContents with <webview> disabled (${event.sender.id})`);
throw new Error('<webview> disabled') throw new Error('<webview> disabled');
} }
} };
} };
const handleMessage = function (channel, handler) { const handleMessage = function (channel, handler) {
ipcMainInternal.handle(channel, makeSafeHandler(channel, handler)) ipcMainInternal.handle(channel, makeSafeHandler(channel, handler));
} };
const handleMessageSync = function (channel, handler) { const handleMessageSync = function (channel, handler) {
ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler)) ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler));
} };
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) { handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) {
return createGuest(event.sender, params) return createGuest(event.sender, params);
}) });
handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) { handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) {
return createGuest(event.sender, params) return createGuest(event.sender, params);
}) });
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
try { try {
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params) attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params);
} catch (error) { } catch (error) {
console.error(`Guest attach failed: ${error}`) console.error(`Guest attach failed: ${error}`);
} }
}) });
// this message is sent by the actual <webview> // this message is sent by the actual <webview>
ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) { ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
const guest = getGuest(guestInstanceId) const guest = getGuest(guestInstanceId);
if (guest === event.sender) { if (guest === event.sender) {
event.sender.emit('focus-change', {}, focus, guestInstanceId) event.sender.emit('focus-change', {}, focus, guestInstanceId);
} else { } else {
console.error(`focus-change for guestInstanceId: ${guestInstanceId}`) console.error(`focus-change for guestInstanceId: ${guestInstanceId}`);
} }
}) });
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) { handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) {
const guest = getGuestForWebContents(guestInstanceId, event.sender) const guest = getGuestForWebContents(guestInstanceId, event.sender);
if (!asyncMethods.has(method)) { if (!asyncMethods.has(method)) {
throw new Error(`Invalid method: ${method}`) throw new Error(`Invalid method: ${method}`);
} }
return guest[method](...args) return guest[method](...args);
}) });
handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) { handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) {
const guest = getGuestForWebContents(guestInstanceId, event.sender) const guest = getGuestForWebContents(guestInstanceId, event.sender);
if (!syncMethods.has(method)) { if (!syncMethods.has(method)) {
throw new Error(`Invalid method: ${method}`) throw new Error(`Invalid method: ${method}`);
} }
return guest[method](...args) return guest[method](...args);
}) });
handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', function (event, guestInstanceId, property) {
const guest = getGuestForWebContents(guestInstanceId, event.sender);
if (!properties.has(property)) {
throw new Error(`Invalid property: ${property}`);
}
return guest[property];
});
handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', function (event, guestInstanceId, property, val) {
const guest = getGuestForWebContents(guestInstanceId, event.sender);
if (!properties.has(property)) {
throw new Error(`Invalid property: ${property}`);
}
guest[property] = val;
});
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', async function (event, guestInstanceId, args) {
const guest = getGuestForWebContents(guestInstanceId, event.sender);
return serialize(await guest.capturePage(...args));
});
// Returns WebContents from its guest id hosted in given webContents. // Returns WebContents from its guest id hosted in given webContents.
const getGuestForWebContents = function (guestInstanceId, contents) { const getGuestForWebContents = function (guestInstanceId, contents) {
const guest = getGuest(guestInstanceId) const guest = getGuest(guestInstanceId);
if (!guest) { if (!guest) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`) throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`);
} }
if (guest.hostWebContents !== contents) { if (guest.hostWebContents !== contents) {
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`) throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`);
} }
return guest return guest;
} };
// Returns WebContents from its guest id. // Returns WebContents from its guest id.
const getGuest = function (guestInstanceId) { const getGuest = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId];
if (guestInstance != null) return guestInstance.guest if (guestInstance != null) return guestInstance.guest;
} };
// Returns the embedder of the guest. // Returns the embedder of the guest.
const getEmbedder = function (guestInstanceId) { const getEmbedder = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId];
if (guestInstance != null) return guestInstance.embedder if (guestInstance != null) return guestInstance.embedder;
} };
exports.getGuestForWebContents = getGuestForWebContents exports.getGuestForWebContents = getGuestForWebContents;
exports.isWebViewTagEnabled = isWebViewTagEnabled exports.isWebViewTagEnabled = isWebViewTagEnabled;

View File

@@ -1,13 +1,14 @@
'use strict' 'use strict';
const { BrowserWindow, webContents } = require('electron') const electron = require('electron');
const { isSameOrigin } = process.electronBinding('v8_util') const { BrowserWindow } = electron;
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') const { isSameOrigin } = process.electronBinding('v8_util');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const parseFeaturesString = require('@electron/internal/common/parse-features-string') const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const parseFeaturesString = require('@electron/internal/common/parse-features-string');
const hasProp = {}.hasOwnProperty const hasProp = {}.hasOwnProperty;
const frameToGuest = new Map() const frameToGuest = new Map();
// Security options that child windows will always inherit from parent windows // Security options that child windows will always inherit from parent windows
const inheritedWebPreferences = new Map([ const inheritedWebPreferences = new Map([
@@ -19,107 +20,109 @@ const inheritedWebPreferences = new Map([
['sandbox', true], ['sandbox', true],
['webviewTag', false], ['webviewTag', false],
['nodeIntegrationInSubFrames', false] ['nodeIntegrationInSubFrames', false]
]) ]);
// Copy attribute of |parent| to |child| if it is not defined in |child|. // Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent, visited) { const mergeOptions = function (child, parent, visited) {
// Check for circular reference. // Check for circular reference.
if (visited == null) visited = new Set() if (visited == null) visited = new Set();
if (visited.has(parent)) return if (visited.has(parent)) return;
visited.add(parent) visited.add(parent);
for (const key in parent) { for (const key in parent) {
if (key === 'type') continue if (key === 'type') continue;
if (!hasProp.call(parent, key)) continue if (!hasProp.call(parent, key)) continue;
if (key in child && key !== 'webPreferences') continue if (key in child && key !== 'webPreferences') continue;
const value = parent[key] const value = parent[key];
if (typeof value === 'object' && !Array.isArray(value)) { if (typeof value === 'object' && !Array.isArray(value)) {
child[key] = mergeOptions(child[key] || {}, value, visited) child[key] = mergeOptions(child[key] || {}, value, visited);
} else { } else {
child[key] = value child[key] = value;
} }
} }
visited.delete(parent) visited.delete(parent);
return child return child;
} };
// Merge |options| with the |embedder|'s window's options. // Merge |options| with the |embedder|'s window's options.
const mergeBrowserWindowOptions = function (embedder, options) { const mergeBrowserWindowOptions = function (embedder, options) {
if (options.webPreferences == null) { if (options.webPreferences == null) {
options.webPreferences = {} options.webPreferences = {};
} }
if (embedder.browserWindowOptions != null) { if (embedder.browserWindowOptions != null) {
let parentOptions = embedder.browserWindowOptions let parentOptions = embedder.browserWindowOptions;
// if parent's visibility is available, that overrides 'show' flag (#12125) // if parent's visibility is available, that overrides 'show' flag (#12125)
const win = BrowserWindow.fromWebContents(embedder.webContents) const win = BrowserWindow.fromWebContents(embedder.webContents);
if (win != null) { if (win != null) {
parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() } parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() };
} }
// Inherit the original options if it is a BrowserWindow. // Inherit the original options if it is a BrowserWindow.
mergeOptions(options, parentOptions) mergeOptions(options, parentOptions);
} else { } else {
// Or only inherit webPreferences if it is a webview. // Or only inherit webPreferences if it is a webview.
mergeOptions(options.webPreferences, embedder.getLastWebPreferences()) mergeOptions(options.webPreferences, embedder.getLastWebPreferences());
} }
// Inherit certain option values from parent window // Inherit certain option values from parent window
const webPreferences = embedder.getLastWebPreferences() const webPreferences = embedder.getLastWebPreferences();
for (const [name, value] of inheritedWebPreferences) { for (const [name, value] of inheritedWebPreferences) {
if (webPreferences[name] === value) { if (webPreferences[name] === value) {
options.webPreferences[name] = value options.webPreferences[name] = value;
} }
} }
// Sets correct openerId here to give correct options to 'new-window' event handler if (!webPreferences.nativeWindowOpen) {
options.webPreferences.openerId = embedder.id // Sets correct openerId here to give correct options to 'new-window' event handler
options.webPreferences.openerId = embedder.id;
}
return options return options;
} };
// Setup a new guest with |embedder| // Setup a new guest with |embedder|
const setupGuest = function (embedder, frameName, guest, options) { const setupGuest = function (embedder, frameName, guest, options) {
// When |embedder| is destroyed we should also destroy attached guest, and if // When |embedder| is destroyed we should also destroy attached guest, and if
// guest is closed by user then we should prevent |embedder| from double // guest is closed by user then we should prevent |embedder| from double
// closing guest. // closing guest.
const guestId = guest.webContents.id const guestId = guest.webContents.id;
const closedByEmbedder = function () { const closedByEmbedder = function () {
guest.removeListener('closed', closedByUser) guest.removeListener('closed', closedByUser);
guest.destroy() guest.destroy();
} };
const closedByUser = function () { const closedByUser = function () {
embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId) embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId);
embedder.removeListener('current-render-view-deleted', closedByEmbedder) embedder.removeListener('current-render-view-deleted', closedByEmbedder);
} };
embedder.once('current-render-view-deleted', closedByEmbedder) embedder.once('current-render-view-deleted', closedByEmbedder);
guest.once('closed', closedByUser) guest.once('closed', closedByUser);
if (frameName) { if (frameName) {
frameToGuest.set(frameName, guest) frameToGuest.set(frameName, guest);
guest.frameName = frameName guest.frameName = frameName;
guest.once('closed', function () { guest.once('closed', function () {
frameToGuest.delete(frameName) frameToGuest.delete(frameName);
}) });
} }
return guestId return guestId;
} };
// Create a new guest created by |embedder| with |options|. // Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, referrer, frameName, options, postData) { const createGuest = function (embedder, url, referrer, frameName, options, postData) {
let guest = frameToGuest.get(frameName) let guest = frameToGuest.get(frameName);
if (frameName && (guest != null)) { if (frameName && (guest != null)) {
guest.loadURL(url) guest.loadURL(url);
return guest.webContents.id return guest.webContents.id;
} }
// Remember the embedder window's id. // Remember the embedder window's id.
if (options.webPreferences == null) { if (options.webPreferences == null) {
options.webPreferences = {} options.webPreferences = {};
} }
guest = new BrowserWindow(options) guest = new BrowserWindow(options);
if (!options.webContents) { if (!options.webContents) {
// We should not call `loadURL` if the window was constructed from an // We should not call `loadURL` if the window was constructed from an
// existing webContents (window.open in a sandboxed renderer). // existing webContents (window.open in a sandboxed renderer).
@@ -128,233 +131,236 @@ const createGuest = function (embedder, url, referrer, frameName, options, postD
// webContents is not necessary (it will navigate there anyway). // webContents is not necessary (it will navigate there anyway).
const loadOptions = { const loadOptions = {
httpReferrer: referrer httpReferrer: referrer
} };
if (postData != null) { if (postData != null) {
loadOptions.postData = postData loadOptions.postData = postData;
loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded' loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded';
if (postData.length > 0) { if (postData.length > 0) {
const postDataFront = postData[0].bytes.toString() const postDataFront = postData[0].bytes.toString();
const boundary = /^--.*[^-\r\n]/.exec(postDataFront) const boundary = /^--.*[^-\r\n]/.exec(postDataFront);
if (boundary != null) { if (boundary != null) {
loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}` loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`;
} }
} }
} }
guest.loadURL(url, loadOptions) guest.loadURL(url, loadOptions);
} }
return setupGuest(embedder, frameName, guest, options) return setupGuest(embedder, frameName, guest, options);
} };
const getGuestWindow = function (guestContents) { const getGuestWindow = function (guestContents) {
let guestWindow = BrowserWindow.fromWebContents(guestContents) let guestWindow = BrowserWindow.fromWebContents(guestContents);
if (guestWindow == null) { if (guestWindow == null) {
const hostContents = guestContents.hostWebContents const hostContents = guestContents.hostWebContents;
if (hostContents != null) { if (hostContents != null) {
guestWindow = BrowserWindow.fromWebContents(hostContents) guestWindow = BrowserWindow.fromWebContents(hostContents);
} }
} }
if (!guestWindow) { if (!guestWindow) {
throw new Error('getGuestWindow failed') throw new Error('getGuestWindow failed');
} }
return guestWindow return guestWindow;
} };
const isChildWindow = function (sender, target) { const isChildWindow = function (sender, target) {
return target.getLastWebPreferences().openerId === sender.id return target.getLastWebPreferences().openerId === sender.id;
} };
const isRelatedWindow = function (sender, target) { const isRelatedWindow = function (sender, target) {
return isChildWindow(sender, target) || isChildWindow(target, sender) return isChildWindow(sender, target) || isChildWindow(target, sender);
} };
const isScriptableWindow = function (sender, target) { const isScriptableWindow = function (sender, target) {
return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL()) return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL());
} };
const isNodeIntegrationEnabled = function (sender) { const isNodeIntegrationEnabled = function (sender) {
return sender.getLastWebPreferences().nodeIntegration === true return sender.getLastWebPreferences().nodeIntegration === true;
} };
// Checks whether |sender| can access the |target|: // Checks whether |sender| can access the |target|:
const canAccessWindow = function (sender, target) { const canAccessWindow = function (sender, target) {
return isChildWindow(sender, target) || return isChildWindow(sender, target) ||
isScriptableWindow(sender, target) || isScriptableWindow(sender, target) ||
isNodeIntegrationEnabled(sender) isNodeIntegrationEnabled(sender);
} };
// Routed window.open messages with raw options // Routed window.open messages with raw options
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => { ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
if (url == null || url === '') url = 'about:blank' if (url == null || url === '') url = 'about:blank';
if (frameName == null) frameName = '' if (frameName == null) frameName = '';
if (features == null) features = '' if (features == null) features = '';
const options = {} const options = {};
const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor'] const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor'];
const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag'] const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag'];
const disposition = 'new-window' const disposition = 'new-window';
// Used to store additional features // Used to store additional features
const additionalFeatures = [] const additionalFeatures = [];
// Parse the features // Parse the features
parseFeaturesString(features, function (key, value) { parseFeaturesString(features, function (key, value) {
if (value === undefined) { if (value === undefined) {
additionalFeatures.push(key) additionalFeatures.push(key);
} else { } else {
// Don't allow webPreferences to be set since it must be an object // Don't allow webPreferences to be set since it must be an object
// that cannot be directly overridden // that cannot be directly overridden
if (key === 'webPreferences') return if (key === 'webPreferences') return;
if (webPreferences.includes(key)) { if (webPreferences.includes(key)) {
if (options.webPreferences == null) { if (options.webPreferences == null) {
options.webPreferences = {} options.webPreferences = {};
} }
options.webPreferences[key] = value options.webPreferences[key] = value;
} else { } else {
options[key] = value options[key] = value;
} }
} }
}) });
if (options.left) { if (options.left) {
if (options.x == null) { if (options.x == null) {
options.x = options.left options.x = options.left;
} }
} }
if (options.top) { if (options.top) {
if (options.y == null) { if (options.y == null) {
options.y = options.top options.y = options.top;
} }
} }
if (options.title == null) { if (options.title == null) {
options.title = frameName options.title = frameName;
} }
if (options.width == null) { if (options.width == null) {
options.width = 800 options.width = 800;
} }
if (options.height == null) { if (options.height == null) {
options.height = 600 options.height = 600;
} }
for (const name of ints) { for (const name of ints) {
if (options[name] != null) { if (options[name] != null) {
options[name] = parseInt(options[name], 10) options[name] = parseInt(options[name], 10);
} }
} }
const referrer = { url: '', policy: 'default' } const referrer = { url: '', policy: 'default' };
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event, internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures);
url, referrer, frameName, disposition, options, additionalFeatures) });
})
// Routed window.open messages with fully parsed options // Routed window.open messages with fully parsed options
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer, function internalWindowOpen (event, url, referrer, frameName, disposition, options, additionalFeatures, postData) {
frameName, disposition, options, additionalFeatures, postData) { options = mergeBrowserWindowOptions(event.sender, options);
options = mergeBrowserWindowOptions(event.sender, options) event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer);
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer) const { newGuest } = event;
const { newGuest } = event
if ((event.sender.getType() === 'webview' && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) { if ((event.sender.getType() === 'webview' && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) {
if (newGuest != null) { if (newGuest != null) {
if (options.webContents === newGuest.webContents) { if (options.webContents === newGuest.webContents) {
// the webContents is not changed, so set defaultPrevented to false to // the webContents is not changed, so set defaultPrevented to false to
// stop the callers of this event from destroying the webContents. // stop the callers of this event from destroying the webContents.
event.defaultPrevented = false event.defaultPrevented = false;
} }
event.returnValue = setupGuest(event.sender, frameName, newGuest, options) event.returnValue = setupGuest(event.sender, frameName, newGuest, options);
} else { } else {
event.returnValue = null event.returnValue = null;
} }
} else { } else {
event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData) event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData);
} }
}) }
const makeSafeHandler = function (handler) { const makeSafeHandler = function (handler) {
return (event, guestId, ...args) => { return (event, guestId, ...args) => {
const guestContents = webContents.fromId(guestId) // Access webContents via electron to prevent circular require.
const guestContents = electron.webContents.fromId(guestId);
if (!guestContents) { if (!guestContents) {
throw new Error(`Invalid guestId: ${guestId}`) throw new Error(`Invalid guestId: ${guestId}`);
} }
return handler(event, guestContents, ...args) return handler(event, guestContents, ...args);
} };
} };
const handleMessage = function (channel, handler) { const handleMessage = function (channel, handler) {
ipcMainInternal.handle(channel, makeSafeHandler(handler)) ipcMainInternal.handle(channel, makeSafeHandler(handler));
} };
const handleMessageSync = function (channel, handler) { const handleMessageSync = function (channel, handler) {
ipcMainUtils.handleSync(channel, makeSafeHandler(handler)) ipcMainUtils.handleSync(channel, makeSafeHandler(handler));
} };
const assertCanAccessWindow = function (contents, guestContents) { const securityCheck = function (contents, guestContents, check) {
if (!canAccessWindow(contents, guestContents)) { if (!check(contents, guestContents)) {
console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`) console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`);
throw new Error(`Access denied to guestId: ${guestContents.id}`) throw new Error(`Access denied to guestId: ${guestContents.id}`);
} }
} };
const windowMethods = new Set([ const windowMethods = new Set([
'destroy', 'destroy',
'focus', 'focus',
'blur' 'blur'
]) ]);
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => { handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => {
assertCanAccessWindow(event.sender, guestContents) securityCheck(event.sender, guestContents, canAccessWindow);
if (!windowMethods.has(method)) { if (!windowMethods.has(method)) {
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`);
throw new Error(`Invalid method: ${method}`) throw new Error(`Invalid method: ${method}`);
} }
return getGuestWindow(guestContents)[method](...args) return getGuestWindow(guestContents)[method](...args);
}) });
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestContents, message, targetOrigin, sourceOrigin) => { handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestContents, message, targetOrigin, sourceOrigin) => {
if (targetOrigin == null) { if (targetOrigin == null) {
targetOrigin = '*' targetOrigin = '*';
} }
// The W3C does not seem to have word on how postMessage should work when the // The W3C does not seem to have word on how postMessage should work when the
// origins do not match, so we do not do |canAccessWindow| check here since // origins do not match, so we do not do |canAccessWindow| check here since
// postMessage across origins is useful and not harmful. // postMessage across origins is useful and not harmful.
securityCheck(event.sender, guestContents, isRelatedWindow);
if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) { if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
const sourceId = event.sender.id const sourceId = event.sender.id;
guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin);
} }
}) });
const webContentsMethodsAsync = new Set([ const webContentsMethodsAsync = new Set([
'loadURL', 'loadURL',
'executeJavaScript', 'executeJavaScript',
'print' 'print'
]) ]);
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => { handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
assertCanAccessWindow(event.sender, guestContents) securityCheck(event.sender, guestContents, canAccessWindow);
if (!webContentsMethodsAsync.has(method)) { if (!webContentsMethodsAsync.has(method)) {
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`);
throw new Error(`Invalid method: ${method}`) throw new Error(`Invalid method: ${method}`);
} }
return guestContents[method](...args) return guestContents[method](...args);
}) });
const webContentsMethodsSync = new Set([ const webContentsMethodsSync = new Set([
'getURL' 'getURL'
]) ]);
handleMessageSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => { handleMessageSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
assertCanAccessWindow(event.sender, guestContents) securityCheck(event.sender, guestContents, canAccessWindow);
if (!webContentsMethodsSync.has(method)) { if (!webContentsMethodsSync.has(method)) {
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`);
throw new Error(`Invalid method: ${method}`) throw new Error(`Invalid method: ${method}`);
} }
return guestContents[method](...args) return guestContents[method](...args);
}) });
exports.internalWindowOpen = internalWindowOpen;

View File

@@ -1,45 +1,47 @@
import { Buffer } from 'buffer' import { Buffer } from 'buffer';
import * as fs from 'fs' import * as fs from 'fs';
import * as path from 'path' import * as path from 'path';
import * as util from 'util' import * as util from 'util';
const Module = require('module') const Module = require('module');
// We modified the original process.argv to let node.js load the init.js, // We modified the original process.argv to let node.js load the init.js,
// we need to restore it here. // we need to restore it here.
process.argv.splice(1, 1) process.argv.splice(1, 1);
// Clear search paths. // Clear search paths.
require('../common/reset-search-paths') require('../common/reset-search-paths');
// Import common settings. // Import common settings.
require('@electron/internal/common/init') require('@electron/internal/common/init');
if (process.platform === 'win32') { if (process.platform === 'win32') {
// Redirect node's console to use our own implementations, since node can not // Redirect node's console to use our own implementations, since node can not
// handle console output when running as GUI program. // handle console output when running as GUI program.
const consoleLog = (format: any, ...args: any[]) => { const consoleLog = (...args: any[]) => {
return process.log(util.format(format, ...args) + '\n') // @ts-ignore this typing is incorrect; 'format' is an optional parameter
} // See https://nodejs.org/api/util.html#util_util_format_format_args
return process.log(util.format(...args) + '\n');
};
const streamWrite: NodeJS.WritableStream['write'] = function (chunk: Buffer | string, encoding?: any, callback?: Function) { const streamWrite: NodeJS.WritableStream['write'] = function (chunk: Buffer | string, encoding?: any, callback?: Function) {
if (Buffer.isBuffer(chunk)) { if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString(encoding) chunk = chunk.toString(encoding);
} }
process.log(chunk) process.log(chunk);
if (callback) { if (callback) {
callback() callback();
} }
return true return true;
} };
console.log = console.error = console.warn = consoleLog console.log = console.error = console.warn = consoleLog;
process.stdout.write = process.stderr.write = streamWrite process.stdout.write = process.stderr.write = streamWrite;
} }
// Don't quit on fatal error. // Don't quit on fatal error.
process.on('uncaughtException', function (error) { process.on('uncaughtException', function (error) {
// Do nothing if the user has a custom uncaught exception handler. // Do nothing if the user has a custom uncaught exception handler.
if (process.listenerCount('uncaughtException') > 1) { if (process.listenerCount('uncaughtException') > 1) {
return return;
} }
// Show error in GUI. // Show error in GUI.
@@ -48,18 +50,18 @@ process.on('uncaughtException', function (error) {
// so we import it inside the handler down here // so we import it inside the handler down here
import('electron') import('electron')
.then(({ dialog }) => { .then(({ dialog }) => {
const stack = error.stack ? error.stack : `${error.name}: ${error.message}` const stack = error.stack ? error.stack : `${error.name}: ${error.message}`;
const message = 'Uncaught Exception:\n' + stack const message = 'Uncaught Exception:\n' + stack;
dialog.showErrorBox('A JavaScript error occurred in the main process', message) dialog.showErrorBox('A JavaScript error occurred in the main process', message);
}) });
}) });
// Emit 'exit' event on quit. // Emit 'exit' event on quit.
const { app } = require('electron') const { app } = require('electron');
app.on('quit', function (event, exitCode) { app.on('quit', function (event, exitCode) {
process.emit('exit', exitCode) process.emit('exit', exitCode);
}) });
if (process.platform === 'win32') { if (process.platform === 'win32') {
// If we are a Squirrel.Windows-installed app, set app user model ID // If we are a Squirrel.Windows-installed app, set app user model ID
@@ -76,138 +78,139 @@ if (process.platform === 'win32') {
// form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call // form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
// app.setAppUserModelId with a matching identifier so that renderer processes // app.setAppUserModelId with a matching identifier so that renderer processes
// will inherit this value. // will inherit this value.
const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe') const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe');
if (fs.existsSync(updateDotExe)) { if (fs.existsSync(updateDotExe)) {
const packageDir = path.dirname(path.resolve(updateDotExe)) const packageDir = path.dirname(path.resolve(updateDotExe));
const packageName = path.basename(packageDir).replace(/\s/g, '') const packageName = path.basename(packageDir).replace(/\s/g, '');
const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '') const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '');
app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`) app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`);
} }
} }
// Map process.exit to app.exit, which quits gracefully. // Map process.exit to app.exit, which quits gracefully.
process.exit = app.exit as () => never process.exit = app.exit as () => never;
// Load the RPC server. // Load the RPC server.
require('@electron/internal/browser/rpc-server') require('@electron/internal/browser/rpc-server');
// Load the guest view manager. // Load the guest view manager.
require('@electron/internal/browser/guest-view-manager') require('@electron/internal/browser/guest-view-manager');
require('@electron/internal/browser/guest-window-manager') require('@electron/internal/browser/guest-window-manager');
// Now we try to load app's package.json. // Now we try to load app's package.json.
let packagePath = null let packagePath = null;
let packageJson = null let packageJson = null;
const searchPaths = ['app', 'app.asar', 'default_app.asar'] const searchPaths = ['app', 'app.asar', 'default_app.asar'];
if (process.resourcesPath) { if (process.resourcesPath) {
for (packagePath of searchPaths) { for (packagePath of searchPaths) {
try { try {
packagePath = path.join(process.resourcesPath, packagePath) packagePath = path.join(process.resourcesPath, packagePath);
packageJson = Module._load(path.join(packagePath, 'package.json')) packageJson = Module._load(path.join(packagePath, 'package.json'));
break break;
} catch { } catch {
continue continue;
} }
} }
} }
if (packageJson == null) { if (packageJson == null) {
process.nextTick(function () { process.nextTick(function () {
return process.exit(1) return process.exit(1);
}) });
throw new Error('Unable to find a valid app') throw new Error('Unable to find a valid app');
} }
// Set application's version. // Set application's version.
if (packageJson.version != null) { if (packageJson.version != null) {
app.setVersion(packageJson.version) app.setVersion(packageJson.version);
} }
// Set application's name. // Set application's name.
if (packageJson.productName != null) { if (packageJson.productName != null) {
app.name = `${packageJson.productName}`.trim() app.name = `${packageJson.productName}`.trim();
} else if (packageJson.name != null) { } else if (packageJson.name != null) {
app.name = `${packageJson.name}`.trim() app.name = `${packageJson.name}`.trim();
} }
// Set application's desktop name. // Set application's desktop name.
if (packageJson.desktopName != null) { if (packageJson.desktopName != null) {
app.setDesktopName(packageJson.desktopName) app.setDesktopName(packageJson.desktopName);
} else { } else {
app.setDesktopName(`${app.name}.desktop`) app.setDesktopName(`${app.name}.desktop`);
} }
// Set v8 flags, delibrately lazy load so that apps that do not use this // Set v8 flags, delibrately lazy load so that apps that do not use this
// feature do not pay the price // feature do not pay the price
if (packageJson.v8Flags != null) { if (packageJson.v8Flags != null) {
require('v8').setFlagsFromString(packageJson.v8Flags) require('v8').setFlagsFromString(packageJson.v8Flags);
} }
app._setDefaultAppPaths(packagePath) app._setDefaultAppPaths(packagePath);
// Load the chrome devtools support. // Load the chrome devtools support.
require('@electron/internal/browser/devtools') require('@electron/internal/browser/devtools');
const features = process.electronBinding('features') const features = process.electronBinding('features');
// Load the chrome extension support. // Load the chrome extension support.
if (!features.isExtensionsEnabled()) { if (!features.isExtensionsEnabled()) {
require('@electron/internal/browser/chrome-extension') require('@electron/internal/browser/chrome-extension');
} }
if (features.isRemoteModuleEnabled()) { if (features.isRemoteModuleEnabled()) {
require('@electron/internal/browser/remote/server') require('@electron/internal/browser/remote/server');
} }
// Load protocol module to ensure it is populated on app ready // Load protocol module to ensure it is populated on app ready
require('@electron/internal/browser/api/protocol') require('@electron/internal/browser/api/protocol');
// Set main startup script of the app. // Set main startup script of the app.
const mainStartupScript = packageJson.main || 'index.js' const mainStartupScript = packageJson.main || 'index.js';
const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME'] const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME'];
function currentPlatformSupportsAppIndicator () { function currentPlatformSupportsAppIndicator () {
if (process.platform !== 'linux') return false if (process.platform !== 'linux') return false;
const currentDesktop = process.env.XDG_CURRENT_DESKTOP const currentDesktop = process.env.XDG_CURRENT_DESKTOP;
if (!currentDesktop) return false if (!currentDesktop) return false;
if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true;
// ubuntu based or derived session (default ubuntu one, communitheme…) supports // ubuntu based or derived session (default ubuntu one, communitheme…) supports
// indicator too. // indicator too.
if (/ubuntu/ig.test(currentDesktop)) return true if (/ubuntu/ig.test(currentDesktop)) return true;
return false return false;
} }
// Workaround for electron/electron#5050 and electron/electron#9046 // Workaround for electron/electron#5050 and electron/electron#9046
if (currentPlatformSupportsAppIndicator()) { if (currentPlatformSupportsAppIndicator()) {
process.env.XDG_CURRENT_DESKTOP = 'Unity' process.env.XDG_CURRENT_DESKTOP = 'Unity';
} }
// Quit when all windows are closed and no other one is listening to this. // Quit when all windows are closed and no other one is listening to this.
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
if (app.listenerCount('window-all-closed') === 1) { if (app.listenerCount('window-all-closed') === 1) {
app.quit() app.quit();
} }
}) });
Promise.all([ const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu');
import('@electron/internal/browser/default-menu'),
app.whenReady() // Create default menu.
]).then(([{ setDefaultApplicationMenu }]) => { //
// Create default menu // Note that the task must be added before loading any app, so we can make sure
setDefaultApplicationMenu() // the call is maded before any user window is created, otherwise the default
}) // menu may show even when user explicitly hides the menu.
app.once('ready', setDefaultApplicationMenu);
if (packagePath) { if (packagePath) {
// Finally load app's main.js and transfer control to C++. // Finally load app's main.js and transfer control to C++.
process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false) process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false);
Module._load(path.join(packagePath, mainStartupScript), Module, true) Module._load(path.join(packagePath, mainStartupScript), Module, true);
} else { } else {
console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)') console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)');
console.error('This normally means you\'ve damaged the Electron package somehow') console.error('This normally means you\'ve damaged the Electron package somehow');
} }

View File

@@ -1,33 +1,33 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events';
import { IpcMainInvokeEvent } from 'electron' import { IpcMainInvokeEvent } from 'electron';
export class IpcMainImpl extends EventEmitter { export class IpcMainImpl extends EventEmitter {
private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map(); private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map();
handle: Electron.IpcMain['handle'] = (method, fn) => { handle: Electron.IpcMain['handle'] = (method, fn) => {
if (this._invokeHandlers.has(method)) { if (this._invokeHandlers.has(method)) {
throw new Error(`Attempted to register a second handler for '${method}'`) throw new Error(`Attempted to register a second handler for '${method}'`);
} }
if (typeof fn !== 'function') { if (typeof fn !== 'function') {
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`) throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`);
} }
this._invokeHandlers.set(method, async (e, ...args) => { this._invokeHandlers.set(method, async (e, ...args) => {
try { try {
(e as any)._reply(await Promise.resolve(fn(e, ...args))) (e as any)._reply(await Promise.resolve(fn(e, ...args)));
} catch (err) { } catch (err) {
(e as any)._throw(err) (e as any)._throw(err);
} }
}) });
} }
handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => { handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => {
this.handle(method, (e, ...args) => { this.handle(method, (e, ...args) => {
this.removeHandler(method) this.removeHandler(method);
return fn(e, ...args) return fn(e, ...args);
}) });
} }
removeHandler (method: string) { removeHandler (method: string) {
this._invokeHandlers.delete(method) this._invokeHandlers.delete(method);
} }
} }

View File

@@ -1,44 +1,44 @@
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal' import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
type IPCHandler = (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any type IPCHandler = (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any
export const handleSync = function <T extends IPCHandler> (channel: string, handler: T) { export const handleSync = function <T extends IPCHandler> (channel: string, handler: T) {
ipcMainInternal.on(channel, async (event, ...args) => { ipcMainInternal.on(channel, async (event, ...args) => {
try { try {
event.returnValue = [null, await handler(event, ...args)] event.returnValue = [null, await handler(event, ...args)];
} catch (error) { } catch (error) {
event.returnValue = [error] event.returnValue = [error];
} }
}) });
} };
let nextId = 0 let nextId = 0;
export function invokeInWebContents<T> (sender: Electron.WebContentsInternal, sendToAll: boolean, command: string, ...args: any[]) { export function invokeInWebContents<T> (sender: Electron.WebContentsInternal, sendToAll: boolean, command: string, ...args: any[]) {
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
const requestId = ++nextId const requestId = ++nextId;
const channel = `${command}_RESPONSE_${requestId}` const channel = `${command}_RESPONSE_${requestId}`;
ipcMainInternal.on(channel, function handler ( ipcMainInternal.on(channel, function handler (
event, error: Electron.SerializedError, result: any event, error: Electron.SerializedError, result: any
) { ) {
if (event.sender !== sender) { if (event.sender !== sender) {
console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`) console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`);
return return;
} }
ipcMainInternal.removeListener(channel, handler) ipcMainInternal.removeListener(channel, handler);
if (error) { if (error) {
reject(error) reject(error);
} else { } else {
resolve(result) resolve(result);
} }
}) });
if (sendToAll) { if (sendToAll) {
sender._sendInternalToAll(command, requestId, ...args) sender._sendInternalToAll(command, requestId, ...args);
} else { } else {
sender._sendInternal(command, requestId, ...args) sender._sendInternal(command, requestId, ...args);
} }
}) });
} }

Some files were not shown because too many files have changed in this diff Show More