Compare commits

..

115 Commits

Author SHA1 Message Date
Electron Bot
959e80cc53 Bump v7.2.2 2020-04-14 07:59:21 -07:00
trop[bot]
97010bf947 fix: don't assign NSAlert to window which is not visible (#23089)
* 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

* Update docs/api/dialog.md

Co-authored-by: Cezary Kulakowski <cezary@openfin.co>
Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-04-14 10:57:22 -04:00
Jeremy Apthorp
372c5b9518 chore: cherry-pick 85f708fa7ab8 from chromium (#23048) 2020-04-13 12:28:42 -07:00
Jeremy Apthorp
4dc2c2800c fix: cherry-pick 2aac556145af from v8 (#23059) 2020-04-13 09:38:12 -07:00
Jeremy Apthorp
ba1b75c218 chore: cherry-pick e246871765f5 from chromium (#23040)
* chore: cherry-pick e246871765f5 from chromium

* resolve patch conflicts

* fix build
2020-04-13 11:30:20 -04:00
Pedro Pontes
5dac4531ca fix: backport d82a02c837d3 from webrtc. (#23037)
ACM: Corrected temporary buffer size

This CL corrects the temporary buffers size in the
pre-processing of the capture audio before encoding.

As part of this it removes the ACM-specific hardcoding
of the size and instead ensures that the size of the
temporary buffer matches that of the AudioFrame.
2020-04-13 10:38:03 -04:00
Jeremy Apthorp
3cb788a030 fix: cherry-pick 09d14728ca251 from v8 (#23043) 2020-04-10 10:30:41 -07:00
trop[bot]
9984dd9017 fix: persist maximizable state when toggling fullscreen (#23019)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-04-10 09:40:53 -07:00
Jeremy Apthorp
7efa4ccfb3 chore: cherry-pick 54454ec7fbcb from chromium (#23017) 2020-04-09 17:39:21 -07:00
Jeremy Apthorp
3bf5ad22f4 chore: cherry-pick 913247c378d5 from chromium (#23011) 2020-04-09 17:17:14 -07:00
Jeremy Apthorp
6e94fb3f9f chore: cherry-pick 2cd0af7ea205 from chromium (#23015) 2020-04-09 17:06:15 -07:00
Jeremy Apthorp
9c92d87e2f chore: cherry-pick 4c57222340cf from chromium (#23009) 2020-04-09 15:56:36 -07:00
Jeremy Apthorp
8dcf7fc0b9 chore: cherry-pick 3ac8883297e1 from chromium (#23013) 2020-04-09 14:12:15 -07:00
Jeremy Apthorp
c7fd17a723 chore: cherry-pick a0a48e0132bc from chromium (#22983) 2020-04-09 13:04:05 -07:00
Jeremy Apthorp
b68f8999ca chore: cherry-pick 3030db702eee from chromium (#22986) 2020-04-09 12:35:02 -07:00
Jeremy Apthorp
35cb95b3d8 chore: cherry-pick 728c6deeffe1 from chromium (#22981) 2020-04-09 11:56:53 -07:00
Pedro Pontes
3b3cc0215a fix: backport b52f7fb5933a from WebRTC. (#23044)
[DirectX] Fix vector allocation for raw data handling.

std::vector::reserve has the effect to reserve space in memory but does
not affect the result of size(), which keeps on returning 0. If size is
0, however, data() might either return null or not [1].

This CL fixes the use of reserve() in favour of resize() which
effectively allocates the memory in the vector and updates its size.
This way size() returns a value bigger than 0 and data() returns a valid
pointer.

[1] https://en.cppreference.com/w/cpp/container/vector/data

Fixed: chromium:1059764
Change-Id: Ida3dbe643710c6895f09b9da87b0075b7d7b28df
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/170470
Reviewed-by: Jamie Walch <jamiewalch@chromium.org>
Commit-Queue: Armando Miraglia <armax@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30836}
2020-04-09 10:34:02 -04:00
Jeremy Apthorp
3cb2484184 chore: cherry-pick d4564dcc2520 from chromium (#22979) 2020-04-08 17:37:55 -07:00
Jeremy Apthorp
bafcac7913 ci: auto-3way patches and detect changes (#23033) 2020-04-08 14:36:36 -07:00
Samuel Attard
6a0c701a43 build: auto-generate the codesigning cert used for macOS CI testing runs (#17668) (#23003)
* 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
2020-04-08 10:59:34 -04:00
Jeremy Apthorp
5ed3bd8724 chore: cherry-pick adc8f05aa3ab from chromium (#22977) 2020-04-08 10:52:55 -04:00
Shelley Vohr
779e3250c0 fix: crash on invalid zoomFactor (#22710) 2020-04-08 10:28:39 -04:00
trop[bot]
4c5197fe6e fix: nullptr check when closing windows (#23024) 2020-04-07 22:11:03 -07:00
Jeremy Apthorp
72a75889de chore: cherry-pick b5950ad76471 from chromium (#22945) 2020-04-07 11:33:53 -07:00
trop[bot]
242658102a build: set merge=union for .patches (#22990)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-04-07 09:55:20 -07:00
Jeremy Apthorp
7586295378 chore: cherry-pick db71a0afc1d0 from chromium (#22943) 2020-04-06 13:12:36 -07:00
Jeremy Apthorp
e7fce15152 chore: cherry-pick fd211b44535c from chromium (#22869) 2020-04-01 11:00:50 -07:00
Electron Bot
456c9f09a2 Bump v7.2.1 2020-03-23 22:00:48 -07:00
Samuel Attard
f229d12056 Revert "Bump v7.2.1"
This reverts commit 4a2d94d031.
2020-03-23 21:59:14 -07:00
Electron Bot
4a2d94d031 Bump v7.2.1 2020-03-23 20:52:44 -07:00
Samuel Attard
df09e278a1 Revert "Bump v7.2.1"
This reverts commit 10f65979ff.
2020-03-23 20:51:07 -07:00
Electron Bot
10f65979ff Bump v7.2.1 2020-03-23 19:39:36 -07:00
Samuel Attard
cac3884d75 Revert "fix: better window hierarchy checks"
This reverts commit c16c4c25ed.
2020-03-23 19:36:17 -07:00
Electron Bot
b9734edfd1 Bump v7.2.0 2020-03-23 14:18:41 -07:00
Samuel Attard
ace3216392 reset version for 7.2.0 2020-03-23 14:17:13 -07:00
Samuel Attard
12cb11f99b feat: add support for net requests to use the session cookie store (#22808)
* 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

* chore: fix tsc compilation

* spec: set-cookie will not work until Electron 8 for samesite net module requests
2020-03-23 14:10:50 -07:00
Samuel Attard
c16c4c25ed fix: better window hierarchy checks 2020-03-23 14:09:47 -07:00
LuoJinghua
8e30e21ee6 feat: Exposing methods required by capturing a hidden webContents (#21894) 2020-03-23 10:27:15 -07:00
Samuel Attard
a80f7fc81f build: enable JS semicolons (#22787) 2020-03-23 09:18:46 -07:00
trop[bot]
83e7b00e7c fix: prevent crash in ListValue v8 converter when conversion fails (#22757) 2020-03-19 14:02:15 -07:00
trop[bot]
eabb872345 test: no need to loadURL in menu test (#22767)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-03-19 16:34:27 +09:00
trop[bot]
b12090a129 fix: remove bad usages of for-in and guard against it (#22729)
* 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:37:57 +09:00
trop[bot]
0bf12e4e76 chore: don't delete nightly tag after draft (#22625)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-10 17:04:43 +09:00
trop[bot]
411b9f2ae7 build: upload sentry src bundles to symbol S3 bucket (#22618)
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
2020-03-09 16:24:50 -07:00
Shelley Vohr
a632f1f1b7 fix: prevent potential modal window close segfault (#22540) 2020-03-04 22:24:34 -08:00
Cheng Zhao
2543517443 fix: destroy node platform after destroying wrappers (#22536)
Co-authored-by: Cheng Zhao <zcbenz@electronjs.org>
2020-03-05 13:07:17 +09:00
Cheng Zhao
5736df41c8 fix: properly forward properties to webview (#22512)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-05 10:06:53 +09:00
trop[bot]
fbbe8afc3e fix: bail early if no printers on the network (#22517)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-04 16:19:16 +09:00
loc
6fdd055642 fix: port CL that fixes ARIA tree impl for macOS (#22424) 2020-03-02 18:25:58 +00:00
Electron Bot
0f082ecd5c Bump v7.1.14 2020-02-27 16:07:16 -08:00
Jeremy Apthorp
f29a600fd8 fix: OOB access in ReadableStream::Close (#22435)
* fix: move ReadableStream requests onto the stack before iteration

* fix: Streams: Convert state DCHECKs to CHECKs

* fix patches
2020-02-27 15:53:01 -08:00
Samuel Attard
1bde50636f fix: backport v8 patch for type inference issue (#22428)
* fix: backport v8 patch for type inference issue

* Update .patches
2020-02-27 13:49:12 -08:00
Jeremy Apthorp
7d739a3608 fix: backport fix for icu crash (#22420) 2020-02-27 12:50:18 -08:00
trop[bot]
8d3f0f47c1 fix: pass safeDialogs preference properly (#22376)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-02-26 22:04:24 +00:00
trop[bot]
dfd338d8bf test: disable clipboard tests for WOA (#22389)
Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-02-26 16:45:50 +09:00
Jeremy Apthorp
dd242f4b28 fix: emit will-navigate for sandboxed contents (#22188) (#22329)
* fix: emit will-navigate for sandboxed contents (#22188)

* Update atom_sandboxed_renderer_client.cc

* Update atom_sandboxed_renderer_client.cc

* Update api-browser-window-spec.ts
2020-02-26 10:37:43 +09:00
Robo
4c61f62149 fix: crash in seccomp-bpf sandbox with glibc 2.31 (#22338) 2020-02-24 21:13:01 +00:00
trop[bot]
fd654afae9 docs: clean up protocol docs (#22307)
* docs: clean up protocol docs

* Fix capitalization

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

* fix: use --disable-gpu-memory-buffer-compositor-resources for MAS build
2020-02-24 12:09:25 +09:00
John Kleinschmidt
0e8aa5c252 ci: update to CircleCI v2 config and APIs (#22310)
* ci: Update to CircleCI v2 config and APIs

This reverts commit e6807b387c.

* build: update release build endpoint from /jobs to /job (#21232)


(cherry picked from commit 41f1569c46)

* chore: workflows and pipeline state were split in the circle API (#21441)


(cherry picked from commit ec0edb757a)

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-21 15:03:33 -05:00
Electron Bot
edac24c9be Bump v7.1.13 2020-02-20 09:38:02 -08:00
Electron Bot
0251f5aa93 Revert "Bump v7.1.13"
This reverts commit cccd3778f8.
2020-02-19 15:31:15 -08:00
Electron Bot
cccd3778f8 Bump v7.1.13 2020-02-19 15:28:25 -08:00
trop[bot]
680f3432ae build: only strip binaries on linux (#22288)
Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-02-19 18:25:59 -05:00
John Kleinschmidt
420fb46121 Revert "Bump v7.1.13"
This reverts commit d7247bc77d.
2020-02-19 11:19:33 -05:00
trop[bot]
6ef605eaa2 doc: remove accidental deprecation (#22266)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-02-19 13:44:26 +09:00
Electron Bot
d7247bc77d Bump v7.1.13 2020-02-18 12:21:15 -08:00
trop[bot]
5e6e118595 build: preserve timestamps when stripping files (#22094) (#22261)
* 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)

Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-02-18 15:19:43 -05:00
trop[bot]
b1f19970e7 fix: don't include breakpad_symbols dir in dsym.zip (#22220)
Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-02-18 12:23:01 -05:00
Shelley Vohr
c30ecc38d0 fix: crash on custom printing margins (#22164) (#22187) 2020-02-17 09:44:53 +09:00
trop[bot]
14d9b8a8b4 fix: no-arg console.log is undefined (#22173)
Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-02-14 15:32:47 +09:00
Shelley Vohr
f0d872947d chore: default to sys printer and prevent bad default crash (#22012) 2020-02-12 17:45:53 +00:00
Cheng Zhao
40a212e132 fix: make webRequest work with WebSocket (7-1-x) (#22141)
* fix: make webRequest work with WebSocket

* test: install modules for spec-main
2020-02-12 15:14:57 +09:00
Shelley Vohr
cb9b3b29af fix: keep references to active menus created by api Menu (#22151)
Without this such menus would be destroyed by js garbage collector even
when they are still displayed.

Co-authored-by: CezaryKulakowski <50166166+CezaryKulakowski@users.noreply.github.com>
2020-02-12 09:00:03 +09:00
trop[bot]
da65c881e2 ci: strip mksnapshot binaries on Linux (#22156)
Related to #21086.

Co-authored-by: Alexey Kuzmin <alex.s.kuzmin@gmail.com>
2020-02-11 15:45:08 -05:00
Electron Bot
aa5e51dbe0 Bump v7.1.12 2020-02-10 11:08:32 -08:00
trop[bot]
b5d38bfff0 fix: flash plugin (#22109)
* 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:49:43 +09:00
trop[bot]
a3054443f4 fix: use a WeakPtr so we do not UAF the store in FunctionLifetimeMonitor (#22112)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-10 10:47:43 +09:00
Jeremy Apthorp
1f631ca337 ci: fix ELECTRON_OUT_DIR (#21994) (#22020) 2020-02-05 10:31:03 +09:00
Jeremy Apthorp
e7576019e9 fix: use legacy base::Value serializers for ipc (7-1-x) (#21922) 2020-01-31 11:38:55 -08:00
trop[bot]
77448c75c6 fix: return path from netLog.stopLogging (#21988)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-01-31 11:21:52 -08:00
Robo
538ebabf7e fix: crash with icu when input locale is invalid (#21969)
* fix: crash with icu when input locale is invalid

Backports https://github.com/unicode-org/icu/pull/632

* spec: add regression tests
2020-01-31 14:35:54 +09:00
Electron Bot
d17dfabfcb Bump v7.1.11 2020-01-29 18:23:02 -08:00
trop[bot]
b6cb396893 fix: ensure web_contents is not nullptr in UpdateDraggableRegions (#21964)
* 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:43 -08:00
Alexey Kuzmin
4da56797c3 chore: fix linter errors in .mm files (#21934) 2020-01-29 17:04:25 +00:00
trop[bot]
570e4f6a34 fix: use powerMonitor.on() only after app is ready (#21941)
* fix: use powerMonitor.on() only after app is ready

powerMonitor can't be used until the app is ready; however, on Linux,
powerMonitor.on() was called as soon as lib/browser/api/power-monitor.ts
was loaded.

This patch takes @vladimiry's suggestion of wrapping that in an
app.on('ready') handler to prevent powerMonitor.on() from being called
prematurely.

Fixes #21716

* refactor: handle import powerMonitor timing issue

Fix the previous commit's app-is-ready handler by checking to see if
app is already ready when power-monitor.ts is loaded.

* refactor: use app.whenReady() for simpler logic

* chore: use a consistent comment formatting style

Co-authored-by: Charles Kerr <ckerr@github.com>
2020-01-29 18:44:56 +09:00
trop[bot]
8c5a2c0a4a fix: About Panel credits should be dark mode aware (#21924)
* 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:22 +09:00
trop[bot]
1c1e9fc7a7 docs: clean up context bridge API docs (#21916) 2020-01-28 02:01:51 +00:00
Jeremy Apthorp
0a3a0def25 chore: remove comented code in renderer_ipc (#21918) 2020-01-27 17:08:03 -08:00
Shelley Vohr
ae9ee76e56 fix: window.print() only working once (#21911) 2020-01-27 23:09:21 +00:00
trop[bot]
7908013a49 Update browser-window.md (#21900)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-01-27 14:55:58 +09:00
Robo
c7bb1d0300 fix: font rendering with hi-dpi transitions on Catalina (#21872)
Backports https://chromium-review.googlesource.com/c/chromium/src/+/1902508
2020-01-23 10:46:09 -08:00
trop[bot]
4cf67fea52 docs: added info on bookmark return values for securityScopedBookmarks (#21873) 2020-01-22 18:37:43 -08:00
trop[bot]
2c29a4e93d fix: Windows checkboxChecked edge case (#21860)
Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-01-23 09:33:05 +09:00
Electron Bot
04234f41f2 Bump v7.1.10 2020-01-22 11:14:27 -08:00
Samuel Attard
0799c8721c fix: backport patch for zero-size pixels in blink (#21857) 2020-01-22 16:39:05 +09:00
Cheng Zhao
442174da0f fix: call SetCanActivate in setFocusable (#21855) 2020-01-22 14:45:04 +09:00
Samuel Attard
ef94a79cac refactor: try just using regular [Sync] for MessageSync (#21776) 2020-01-22 10:39:27 +09:00
trop[bot]
38afcff63a fix: crash when restoring minimized hidden window (#21820)
Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-01-20 15:43:42 +09:00
trop[bot]
7ddfffb723 fix: pass full response headers in net module (#21770)
* 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

* fix: 7.x has different type for raw headers

Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-01-15 16:06:05 +09:00
Robo
f8e601adaf Fix memory leak in generator functions (#21773)
Backports https://chromium-review.googlesource.com/c/v8/v8/+/1967317
2020-01-15 09:56:58 +09:00
trop[bot]
dd27520637 fix: stream protocols not completing (#21758)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-01-14 16:09:37 +09:00
Electron Bot
85c07d0715 Bump v7.1.9 2020-01-13 09:10:46 -08:00
Cheng Zhao
4dac36d111 fix: disable private macOS APIs in MAS build except for CAContext/CALayerHost (7-1-x) (#21575)
* fix: add patch to disable remote accessibility APIs

* fix: add patch to disable private window frame APIs

* fix: add patch to disable NSURLFileTypeMappings API
2020-01-13 09:09:07 -08:00
trop[bot]
ae27218296 fix: don't fallback to OpenFolderViaShell (#21749)
Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-01-13 14:55:55 +09:00
trop[bot]
c92efa4ec5 fix: prefer occluded rather than unloading layout info (#21750)
Co-authored-by: loc <andy@slack-corp.com>
2020-01-13 14:55:24 +09:00
Milan Burda
cbf499b18a fix: load window-setup in sandboxed renderer (#21696) 2020-01-13 10:25:20 +09:00
trop[bot]
7b1117e186 fix: don't unnecessarily copy draggable regions (#21723)
In some calls to `BrowserWindow::UpdateDraggableRegions` the parameter
`regions` points to object's variable `draggable_regions_` which we later
try to update with data received in the parameter. In these cases coping
is unnecessary. Additionally after this code is executed `draggable_regions_`
would be empty and as a result whole window would be undraggable.

Co-authored-by: CezaryKulakowski <50166166+CezaryKulakowski@users.noreply.github.com>
2020-01-13 10:04:59 +09:00
trop[bot]
3bd3d9440a docs: responseHeaders should be Record<string, string[]> (#21742)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2020-01-13 09:51:47 +09:00
trop[bot]
704cdd3a2b fix: avoid contextBridge double free on garbage collection (#21736)
* 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:55:59 -08:00
trop[bot]
c56e5abd8c fix: Notification crash in before-quit (#21719) 2020-01-10 09:08:09 -08:00
Electron Bot
ec61041d41 Bump v7.1.8 2020-01-08 11:40:06 -08:00
trop[bot]
32b51bce75 refactor: throw error for getLastCrashReport if crashReporter not started (#21683) 2020-01-07 09:15:21 -05:00
trop[bot]
e0576ef11a fix: highlight defaulted button correctly (#21652) 2020-01-03 11:03:03 -05:00
trop[bot]
a889ec7957 fix: SimpleURLLoaderWrapper redirects (#21566) (#21645) 2020-01-01 09:32:26 +09:00
466 changed files with 20135 additions and 14760 deletions

View File

@@ -1,3 +1,46 @@
version: 2.1
parameters:
upload-to-s3:
type: string
default: '1'
run-lint:
type: boolean
default: true
run-build-linux:
type: boolean
default: true
run-build-mac:
type: boolean
default: true
run-linux-x64-publish:
type: boolean
default: false
run-linux-ia32-publish:
type: boolean
default: false
run-linux-arm-publish:
type: boolean
default: false
run-linux-arm64-publish:
type: boolean
default: false
run-osx-publish:
type: boolean
default: false
run-mas-publish:
type: boolean
default: false
# The config expects the following environment variables to be set:
# - "SLACK_WEBHOOK" Slack hook URL to send notifications.
#
@@ -88,22 +131,31 @@ env-enable-sccache: &env-enable-sccache
env-send-slack-notifications: &env-send-slack-notifications
NOTIFY_SLACK: true
env-global: &env-global
ELECTRON_OUT_DIR: Default
env-linux-medium: &env-linux-medium
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 3
env-linux-2xlarge: &env-linux-2xlarge
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 34
env-linux-2xlarge-release: &env-linux-2xlarge-release
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 16
env-machine-mac: &env-machine-mac
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 6
env-mac-large: &env-mac-large
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 18
env-mac-large-release: &env-mac-large-release
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 8
env-disable-crash-reporter-tests: &env-disable-crash-reporter-tests
@@ -165,7 +217,25 @@ step-gclient-sync: &step-gclient-sync
$GCLIENT_EXTRA_ARGS \
"$CIRCLE_REPOSITORY_URL"
gclient sync --with_branch_heads --with_tags
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 gclient sync --with_branch_heads --with_tags
# Re-export all the patches to check if there were changes.
python src/electron/script/export_all_patches.py src/electron/patches/config.json
cd src/electron
git update-index --refresh || true
if ! git diff-index --quiet HEAD --; then
# There are changes to the patches. Make a git commit with the updated patches
git add patches
GIT_COMMITTER_NAME="Electron Bot" GIT_COMMITTER_EMAIL="anonymous@electronjs.org" git commit -m "update patches" --author="Electron Bot <anonymous@electronjs.org>"
# Export it
mkdir -p ../../patches
git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch
echo
echo "======================================================================"
echo "There were changes to the patches when applying."
echo "Check the CI artifacts for a patch you can apply to fix it."
echo "======================================================================"
exit 1
fi
fi
step-setup-env-for-build: &step-setup-env-for-build
@@ -250,7 +320,7 @@ step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac
command: |
if [ "`uname`" == "Darwin" ]; then
cd src/electron
./script/codesign/import-testing-cert-ci.sh
./script/codesign/generate-identity.sh
fi
step-install-gnutar-on-mac: &step-install-gnutar-on-mac
@@ -346,7 +416,9 @@ step-electron-chromedriver-build: &step-electron-chromedriver-build
command: |
cd src
ninja -C out/Default chrome/test/chromedriver -j $NUMBER_OF_NINJA_PROCESSES
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver
if [ "`uname`" == "Linux" ]; then
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver
fi
ninja -C out/Default electron:electron_chromedriver_zip
step-electron-chromedriver-store: &step-electron-chromedriver-store
@@ -466,6 +538,7 @@ step-mksnapshot-build: &step-mksnapshot-build
name: mksnapshot build
command: |
cd src
ninja -C out/Default electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES
if [ "`uname`" != "Darwin" ]; then
if [ "$TARGET_ARCH" == "arm" ]; then
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/mksnapshot
@@ -473,6 +546,7 @@ step-mksnapshot-build: &step-mksnapshot-build
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x64_v8_arm64/mksnapshot
else
electron/script/strip-binaries.py --file $PWD/out/Default/mksnapshot
electron/script/strip-binaries.py --file $PWD/out/Default/v8_context_snapshot_generator
fi
fi
ninja -C out/Default electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
@@ -489,6 +563,8 @@ step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
if [ "$GENERATE_SYMBOLS" == "true" ]; then
cd src
ninja -C out/Default electron:electron_symbols
cd out/Default/breakpad_symbols
find . -name \*.sym -print0 | xargs -0 npx @sentry/cli@1.51.1 difutil bundle-sources
fi
step-maybe-zip-symbols: &step-maybe-zip-symbols
@@ -661,7 +737,7 @@ steps-lint: &steps-lint
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
gn_version="$(curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
cipd ensure -ensure-file - -root . <<-CIPD
cipd ensure -ensure-file - -root . \<<-CIPD
\$ServiceURL https://chrome-infra-packages.appspot.com/
@Subdir src/buildtools/linux64
gn/gn/linux-amd64 $gn_version
@@ -706,6 +782,8 @@ steps-checkout-fast: &steps-checkout-fast
- *step-set-git-cache-path
# This sync call only runs if .circle-sync-done is an EMPTY file
- *step-gclient-sync
- store_artifacts:
path: patches
# These next few steps reset Electron to the correct commit regardless of which cache was restored
- run:
name: Wipe Electron
@@ -737,6 +815,8 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache
- *step-set-git-cache-path
# This sync call only runs if .circle-sync-done is an EMPTY file
- *step-gclient-sync
- store_artifacts:
path: patches
- *step-save-git-cache
# These next few steps reset Electron to the correct commit regardless of which cache was restored
- run:
@@ -961,7 +1041,6 @@ steps-tests: &steps-tests
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
command: |
cd src
export ELECTRON_OUT_DIR=Default
(cd electron && node script/yarn test -- --ci --enable-logging)
- run:
name: Check test results existence
@@ -994,7 +1073,6 @@ steps-test-nan: &steps-test-nan
name: Run Nan Tests
command: |
cd src
export ELECTRON_OUT_DIR=Default
node electron/script/nan-spec-runner.js
steps-test-node: &steps-test-node
@@ -1009,7 +1087,6 @@ steps-test-node: &steps-test-node
name: Run Node Tests
command: |
cd src
export ELECTRON_OUT_DIR=Default
node electron/script/node-spec-runner.js junit
- store_test_results:
path: src/junit
@@ -1018,7 +1095,6 @@ chromium-upgrade-branches: &chromium-upgrade-branches
/chromium\-upgrade\/[0-9]+/
# List of all jobs.
version: 2
jobs:
# Layer 0: Lint. Standalone.
lint:
@@ -1138,6 +1214,8 @@ jobs:
<<: *env-linux-2xlarge-release
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
<<: *env-release-build
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
linux-ia32-debug:
@@ -1188,6 +1266,8 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
<<: *env-ia32
<<: *env-release-build
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
linux-arm-debug:
@@ -1238,7 +1318,9 @@ jobs:
<<: *env-linux-2xlarge-release
<<: *env-arm
<<: *env-release-build
<<: *env-enable-sccache
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
linux-arm64-debug:
@@ -1305,7 +1387,9 @@ jobs:
<<: *env-linux-2xlarge-release
<<: *env-arm64
<<: *env-release-build
<<: *env-enable-sccache
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
osx-testing:
@@ -1363,7 +1447,9 @@ jobs:
environment:
<<: *env-mac-large-release
<<: *env-release-build
<<: *env-enable-sccache
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
mas-testing:
@@ -1427,7 +1513,9 @@ jobs:
<<: *env-mac-large-release
<<: *env-mas
<<: *env-release-build
<<: *env-enable-sccache
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
# Layer 3: Tests.
@@ -1683,11 +1771,56 @@ jobs:
workflows:
version: 2
# The publish workflows below each contain one job so that they are
# compatible with how sudowoodo works today. If these workflows are
# changed to have multiple jobs, then scripts/release/ci-release-build.js
# will need to be updated and there will most likely need to be changes to
# sudowoodo
publish-x64-linux:
when: << pipeline.parameters.run-linux-x64-publish >>
jobs:
- linux-x64-publish:
context: release-env
publish-ia32-linux:
when: << pipeline.parameters.run-linux-ia32-publish >>
jobs:
- linux-ia32-publish:
context: release-env
publish-arm-linux:
when: << pipeline.parameters.run-linux-arm-publish >>
jobs:
- linux-arm-publish:
context: release-env
publish-arm64-linux:
when: << pipeline.parameters.run-linux-arm64-publish >>
jobs:
- linux-arm64-publish:
context: release-env
publish-osx:
when: << pipeline.parameters.run-osx-publish >>
jobs:
- osx-publish:
context: release-env
publish-mas:
when: << pipeline.parameters.run-mas-publish >>
jobs:
- mas-publish:
context: release-env
lint:
when: << pipeline.parameters.run-lint >>
jobs:
- lint
build-linux:
when: << pipeline.parameters.run-build-linux >>
jobs:
- linux-checkout-fast
- linux-checkout-and-save-cache
@@ -1754,6 +1887,7 @@ workflows:
- linux-checkout-fast
build-mac:
when: << pipeline.parameters.run-build-mac >>
jobs:
- mac-checkout-fast
- mac-checkout-and-save-cache
@@ -1808,11 +1942,11 @@ workflows:
- master
- *chromium-upgrade-branches
jobs:
- linux-checkout
- linux-checkout-fast
- linux-x64-release:
requires:
- linux-checkout
- linux-checkout-fast
- linux-x64-release-tests:
requires:
- linux-x64-release
@@ -1824,7 +1958,7 @@ workflows:
- linux-x64-release
- linux-x64-chromedriver:
requires:
- linux-checkout
- linux-checkout-fast
- linux-x64-release-summary:
requires:
- linux-x64-release
@@ -1834,7 +1968,7 @@ workflows:
- linux-ia32-release:
requires:
- linux-checkout
- linux-checkout-fast
- linux-ia32-release-tests:
requires:
- linux-ia32-release
@@ -1846,7 +1980,7 @@ workflows:
- linux-ia32-release
- linux-ia32-chromedriver:
requires:
- linux-checkout
- linux-checkout-fast
- linux-ia32-release-summary:
requires:
- linux-ia32-release
@@ -1856,10 +1990,10 @@ workflows:
- linux-arm-release:
requires:
- linux-checkout
- linux-checkout-fast
- linux-arm-chromedriver:
requires:
- linux-checkout
- linux-checkout-fast
- linux-arm-release-summary:
requires:
- linux-arm-release
@@ -1868,10 +2002,10 @@ workflows:
- linux-arm64-release:
requires:
- linux-checkout
- linux-checkout-fast
- linux-arm64-chromedriver:
requires:
- linux-checkout
- linux-checkout-fast
- linux-arm64-release-summary:
requires:
- linux-arm64-release
@@ -1887,11 +2021,11 @@ workflows:
- master
- *chromium-upgrade-branches
jobs:
- mac-checkout
- mac-checkout-fast
- osx-release:
requires:
- mac-checkout
- mac-checkout-fast
- osx-release-tests:
requires:
- osx-release
@@ -1903,7 +2037,7 @@ workflows:
- osx-release
- osx-chromedriver:
requires:
- mac-checkout
- mac-checkout-fast
- osx-release-summary:
requires:
- osx-release
@@ -1913,7 +2047,7 @@ workflows:
- mas-release:
requires:
- mac-checkout
- mac-checkout-fast
- mas-release-tests:
requires:
- mas-release
@@ -1925,7 +2059,7 @@ workflows:
- mas-release
- mas-chromedriver:
requires:
- mac-checkout
- mac-checkout-fast
- mas-release-summary:
requires:
- mas-release

View File

@@ -6,9 +6,11 @@
"browser": true
},
"rules": {
"semi": ["error", "always"],
"no-var": "error",
"no-unused-vars": 0,
"no-global-assign": 0,
"guard-for-in": 2,
"@typescript-eslint/no-unused-vars": ["error", {
"vars": "all",
"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
# files to be checked out with LF endings even if core.autocrlf is true.
*.patch text eol=lf
patches/**/.patches merge=union

View File

@@ -1289,12 +1289,18 @@ 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") {
data_deps = [
"//v8:mksnapshot($v8_snapshot_toolchain)",
"//tools/v8_context_snapshot:v8_context_snapshot_generator",
":licenses",
]
data_deps = mksnapshot_deps
outputs = [
"$root_build_dir/mksnapshot.zip",
]

2
DEPS
View File

@@ -152,3 +152,5 @@ hooks = [
recursedeps = [
'src',
]
# Touch DEPS to bust cache

View File

@@ -1 +1 @@
7.1.7
7.2.2

View File

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

View File

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

View File

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

View File

@@ -711,34 +711,34 @@ Clears the recent documents list.
### `app.setAsDefaultProtocolClient(protocol[, path, args])`
* `protocol` String - The name of your protocol, without `://`. If you want your
app to handle `electron://` links, call this method with `electron` as the
parameter.
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Defaults to an empty array
* `protocol` String - The name of your protocol, without `://`. For example,
if you want your app to handle `electron://` links, call this method with
`electron` as the parameter.
* `path` String (optional) _Windows_ - The path to the Electron executable.
Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Arguments passed to the executable.
Defaults to an empty array
Returns `Boolean` - Whether the call succeeded.
This method sets the current executable as the default handler for a protocol
(aka URI scheme). It allows you to integrate your app deeper into the operating
system. Once registered, all links with `your-protocol://` will be opened with
the current executable. The whole link, including protocol, will be passed to
your 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.
Sets the current executable as the default handler for a protocol (aka URI
scheme). It allows you to integrate your app deeper into the operating system.
Once registered, all links with `your-protocol://` will be opened with the
current executable. The whole link, including protocol, will be passed to your
application as a parameter.
**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
change the file with a simple text editor or script during build time.
Please refer to [Apple's documentation][CFBundleURLTypes] for details.
your app's `info.plist`, which cannot be modified at runtime. However, you can
change the file during build time via [Electron Forge][electron-forge],
[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
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
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_
@@ -757,10 +757,8 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler.
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Defaults to an empty array
Returns `Boolean`
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.
Returns `Boolean` - Whether the current executable is the default handler for a
protocol (aka URI scheme).
**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
@@ -768,7 +766,7 @@ this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the
macOS machine. Please refer to
[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.setUserTasks(tasks)` _Windows_
@@ -1186,8 +1184,9 @@ Show the app's about panel options. These options can be overridden with `app.se
* `website` String (optional) _Linux_ - The app's website.
* `iconPath` String (optional) _Linux_ - Path to the app's icon. 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
`.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.
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.
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()`
@@ -1308,6 +1307,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/
[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
[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
[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

View File

@@ -273,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
the `nodeIntegration` option and the APIs available to the preload script
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.
Default is `true`.
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the

View File

@@ -22,6 +22,9 @@ which the request is associated.
with which the request is associated. Defaults to the empty string. The
`session` option prevails on `partition`. Thus if a `session` is explicitly
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:'.
Currently supported values are 'http:' or 'https:'. Defaults to 'http:'.
* `host` String (optional) - The server host provided as a concatenation of

View File

@@ -28,13 +28,14 @@ window.electron.doThing()
### 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
executes code in this world.
The "Main World" is the JavaScript context that your main renderer code runs in. By default, the
page you load in your renderer executes code in this world.
### Isolated World
When `contextIsolation` is enabled in your `webPreferences` your `preload` scripts run in an "Isolated World". You can read more about
context isolation and what it affects in the [BrowserWindow](browser-window.md) docs.
When `contextIsolation` is enabled in your `webPreferences`, your `preload` scripts run in an
"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
@@ -50,12 +51,12 @@ The `contextBridge` module has the following methods:
### API Objects
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.
An example of a complex API object is shown below.
An example of a complex API object is shown below:
```javascript
const { contextBridge } = require('electron')
@@ -90,22 +91,22 @@ results in some key limitations that we've outlined below.
#### 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.
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
has been included below for completeness.
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 deserialized into the same object it will work. A table of type support
has been included below for completeness:
| Type | Complexity | Parameter Support | Return Value Support | Limitations |
| ---- | ---------- | ----------------- | -------------------- | ----------- |
| `String` | Simple | ✅ | ✅ | N/A |
| `Number` | 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 |
| `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. |
| [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 |
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

@@ -118,7 +118,7 @@ Returns `Promise<Object>` - Resolve with an object containing the following:
* `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.
* `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.
@@ -200,7 +200,7 @@ The `filters` specifies an array of file types that can be displayed, see
Returns `Promise<Object>` - Resolve with an object containing the following:
* `canceled` Boolean - whether or not the dialog was canceled.
* `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.
@@ -254,6 +254,7 @@ Shows a message box, it will block the process until the message box is closed.
It returns the index of the clicked button.
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
If the `browserWindow` is not shown, the dialog will not be attached to it. In that case it will be displayed as an independent window.
### `dialog.showMessageBox([browserWindow, ]options)`
@@ -335,6 +336,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
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
On macOS, dialogs are presented as sheets attached to a window if you provide

View File

@@ -402,8 +402,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.
**[Deprecated](modernization/property-updates.md)**
### `systemPreferences.promptTouchID(reason)` _macOS_
* `reason` String - The reason you are asking for Touch ID authentication

View File

@@ -1066,11 +1066,13 @@ Returns `Boolean` - Whether audio is currently playing.
#### `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
zoom percent divided by 100, so 300% = 3.0.
The factor must be greater than 0.0.
**[Deprecated](modernization/property-updates.md)**
#### `contents.getZoomFactor()`
@@ -1232,6 +1234,25 @@ 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.
#### `contents.isBeingCaptured()`
Returns `Boolean` - Whether this page is being captured. It returns true when the capturer count
is large then 0.
#### `contents.incrementCapturerCount([size])`
* `size` [Size](structures/size.md) (optional) - The perferred size for the capturer.
Increase the capturer count by one. The page is considered visible when its browser window is
hidden and the capturer count is non-zero.
This also affects the Page Visibility API.
#### `contents.decrementCapturerCount()`
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.
#### `contents.getPrinters()`
Get the system printer list.
@@ -1244,7 +1265,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
* `silent` Boolean (optional) - Don't ask user for print settings. Default is `false`.
* `printBackground` Boolean (optional) - Prints the background color and image of
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`.
* `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`.
@@ -1264,7 +1285,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
* `vertical` Number (optional) - The vertical dpi.
* `callback` Function (optional)
* `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
the system's default printer if `deviceName` is empty and the default settings for printing.

View File

@@ -22,11 +22,13 @@ The `WebFrame` class has the following instance methods:
### `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
zoom percent divided by 100, so 300% = 3.0.
The factor must be greater than 0.0.
### `webFrame.getZoomFactor()`
Returns `Number` - The current zoom factor.

View File

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

View File

@@ -166,6 +166,7 @@ auto_filenames = {
"lib/renderer/web-view/web-view-element.ts",
"lib/renderer/web-view/web-view-impl.ts",
"lib/renderer/web-view/web-view-init.ts",
"lib/renderer/window-setup.ts",
"lib/sandboxed_renderer/api/exports/electron.js",
"lib/sandboxed_renderer/api/module-list.js",
"lib/sandboxed_renderer/init.js",

View File

@@ -221,6 +221,8 @@ filenames = {
"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/proxying_websocket.cc",
"shell/browser/net/proxying_websocket.h",
"shell/browser/net/network_context_service_factory.cc",
"shell/browser/net/network_context_service_factory.h",
"shell/browser/net/network_context_service.cc",
@@ -233,6 +235,7 @@ filenames = {
"shell/browser/net/system_network_context_manager.h",
"shell/browser/net/url_pipe_loader.cc",
"shell/browser/net/url_pipe_loader.h",
"shell/browser/net/web_request_api_interface.h",
"shell/browser/notifications/linux/libnotify_notification.cc",
"shell/browser/notifications/linux/libnotify_notification.h",
"shell/browser/notifications/linux/notification_presenter_linux.cc",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +1,2 @@
'use strict'
module.exports = process.electronBinding('content_tracing')
'use strict';
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 { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
const CrashReporter = require('@electron/internal/common/crash-reporter');
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
class CrashReporterMain extends CrashReporter {
init (options) {
return crashReporterInit(options)
return crashReporterInit(options);
}
}
module.exports = new CrashReporterMain()
module.exports = new CrashReporterMain();

View File

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

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const common = require('@electron/internal/common/api/exports/electron')
const common = require('@electron/internal/common/api/exports/electron');
// since browser module list is also used in renderer, keep it separate.
const moduleList = require('@electron/internal/browser/api/module-list')
const moduleList = require('@electron/internal/browser/api/module-list');
// Import common modules.
common.defineProperties(exports)
common.defineProperties(exports);
for (const module of moduleList) {
Object.defineProperty(exports, module.name, {
enumerable: !module.private,
get: common.handleESModule(module.loader)
})
});
}

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') {
const { EventEmitter } = require('events')
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase')
const { EventEmitter } = require('events');
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase');
// inAppPurchase is an EventEmitter.
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype)
EventEmitter.call(inAppPurchase)
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype);
EventEmitter.call(inAppPurchase);
module.exports = inAppPurchase
module.exports = inAppPurchase;
} else {
module.exports = {
purchaseProduct: (productID, quantity, callback) => {
throw new Error('The inAppPurchase module can only be used on macOS')
throw new Error('The inAppPurchase module can only be used on macOS');
},
canMakePayments: () => false,
getReceiptURL: () => ''
}
};
}

View File

@@ -1,40 +1,40 @@
import { EventEmitter } from 'events'
import { IpcMainInvokeEvent } from 'electron'
import { EventEmitter } from 'events';
import { IpcMainInvokeEvent } from 'electron';
class IpcMain extends EventEmitter {
private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map();
handle: Electron.IpcMain['handle'] = (method, fn) => {
if (this._invokeHandlers.has(method)) {
throw new Error(`Attempted to register a second handler for '${method}'`)
throw new Error(`Attempted to register a second handler for '${method}'`);
}
if (typeof fn !== 'function') {
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`)
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`);
}
this._invokeHandlers.set(method, async (e, ...args) => {
try {
(e as any)._reply(await Promise.resolve(fn(e, ...args)))
(e as any)._reply(await Promise.resolve(fn(e, ...args)));
} catch (err) {
(e as any)._throw(err)
(e as any)._throw(err);
}
})
});
}
handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => {
this.handle(method, (e, ...args) => {
this.removeHandler(method)
return fn(e, ...args)
})
this.removeHandler(method);
return fn(e, ...args);
});
}
removeHandler (method: string) {
this._invokeHandlers.delete(method)
this._invokeHandlers.delete(method);
}
}
const ipcMain = new IpcMain()
const ipcMain = new IpcMain();
// 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 isWindows = process.platform === 'win32'
const isLinux = process.platform === 'linux'
const isMac = process.platform === 'darwin';
const isWindows = process.platform === 'win32';
const isLinux = process.platform === 'linux';
const roles = {
about: {
get label () {
return isLinux ? 'About' : `About ${app.name}`
return isLinux ? 'About' : `About ${app.name}`;
}
},
close: {
@@ -38,7 +38,7 @@ const roles = {
accelerator: 'Shift+CmdOrCtrl+R',
nonNativeMacOSRole: true,
windowMethod: (window) => {
window.webContents.reloadIgnoringCache()
window.webContents.reloadIgnoringCache();
}
},
front: {
@@ -49,7 +49,7 @@ const roles = {
},
hide: {
get label () {
return `Hide ${app.name}`
return `Hide ${app.name}`;
},
accelerator: 'Command+H'
},
@@ -77,9 +77,9 @@ const roles = {
quit: {
get label () {
switch (process.platform) {
case 'darwin': return `Quit ${app.name}`
case 'win32': return 'Exit'
default: return 'Quit'
case 'darwin': return `Quit ${app.name}`;
case 'win32': return 'Exit';
default: return 'Quit';
}
},
accelerator: isWindows ? undefined : 'CommandOrControl+Q',
@@ -101,7 +101,7 @@ const roles = {
accelerator: 'CommandOrControl+0',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.zoomLevel = 0
webContents.zoomLevel = 0;
}
},
selectall: {
@@ -134,7 +134,7 @@ const roles = {
label: 'Toggle Full Screen',
accelerator: isMac ? 'Control+Command+F' : 'F11',
windowMethod: (window) => {
window.setFullScreen(!window.isFullScreen())
window.setFullScreen(!window.isFullScreen());
}
},
undo: {
@@ -156,7 +156,7 @@ const roles = {
accelerator: 'CommandOrControl+Plus',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.zoomLevel += 0.5
webContents.zoomLevel += 0.5;
}
},
zoomout: {
@@ -164,13 +164,13 @@ const roles = {
accelerator: 'CommandOrControl+-',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.zoomLevel -= 0.5
webContents.zoomLevel -= 0.5;
}
},
// App submenu should be used for Mac only
appmenu: {
get label () {
return app.name
return app.name;
},
submenu: [
{ role: 'about' },
@@ -249,71 +249,71 @@ const roles = {
])
]
}
}
};
exports.roleList = roles
exports.roleList = roles;
const canExecuteRole = (role) => {
if (!roles.hasOwnProperty(role)) return false
if (!isMac) return true
if (!roles.hasOwnProperty(role)) return false;
if (!isMac) return true;
// macOS handles all roles natively except for a few
return roles[role].nonNativeMacOSRole
}
return roles[role].nonNativeMacOSRole;
};
exports.getDefaultLabel = (role) => {
return roles.hasOwnProperty(role) ? roles[role].label : ''
}
return roles.hasOwnProperty(role) ? roles[role].label : '';
};
exports.getDefaultAccelerator = (role) => {
if (roles.hasOwnProperty(role)) return roles[role].accelerator
}
if (roles.hasOwnProperty(role)) return roles[role].accelerator;
};
exports.shouldRegisterAccelerator = (role) => {
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined
return hasRoleRegister ? roles[role].registerAccelerator : true
}
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined;
return hasRoleRegister ? roles[role].registerAccelerator : true;
};
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
if (Array.isArray(submenu)) {
submenu = submenu.filter((item) => item != null)
submenu = submenu.filter((item) => item != null);
}
return submenu
}
return submenu;
};
exports.execute = (role, focusedWindow, focusedWebContents) => {
if (!canExecuteRole(role)) return false
if (!canExecuteRole(role)) return false;
const { appMethod, webContentsMethod, windowMethod } = roles[role]
const { appMethod, webContentsMethod, windowMethod } = roles[role];
if (appMethod) {
app[appMethod]()
return true
app[appMethod]();
return true;
}
if (windowMethod && focusedWindow != null) {
if (typeof windowMethod === 'function') {
windowMethod(focusedWindow)
windowMethod(focusedWindow);
} else {
focusedWindow[windowMethod]()
focusedWindow[windowMethod]();
}
return true
return true;
}
if (webContentsMethod && focusedWebContents != null) {
if (typeof webContentsMethod === 'function') {
webContentsMethod(focusedWebContents)
webContentsMethod(focusedWebContents);
} else {
focusedWebContents[webContentsMethod]()
focusedWebContents[webContentsMethod]();
}
return true
return true;
}
return false
}
return false;
};

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

View File

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

View File

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

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
// It is currently duplicated as module-list "require"s all the browser API file and the
// remote module in the renderer process depends on that file. As a result webpack
// includes all the browser API files in the renderer process as well and we want to avoid that
const features = process.electronBinding('features')
const features = process.electronBinding('features');
// Browser side modules, please sort alphabetically.
module.exports = [
@@ -37,7 +37,7 @@ module.exports = [
{ name: 'View' },
{ name: 'webContents' },
{ name: 'WebContentsView' }
]
];
if (features.isViewApiEnabled()) {
module.exports.push(
@@ -48,5 +48,5 @@ if (features.isViewApiEnabled()) {
{ name: 'MdTextButton' },
{ name: 'ResizeArea' },
{ name: 'TextField' }
)
);
}

View File

@@ -1,8 +1,8 @@
'use strict'
'use strict';
// 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.
module.exports = [
@@ -34,7 +34,7 @@ module.exports = [
{ name: 'View', loader: () => require('./view') },
{ name: 'webContents', loader: () => require('./web-contents') },
{ name: 'WebContentsView', loader: () => require('./web-contents-view') }
]
];
if (features.isViewApiEnabled()) {
module.exports.push(
@@ -45,5 +45,5 @@ if (features.isViewApiEnabled()) {
{ name: 'MdTextButton', loader: () => require('./views/md-text-button') },
{ name: 'ResizeArea', loader: () => require('./views/resize-area') },
{ name: 'TextField', loader: () => require('./views/text-field') }
)
);
}

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)
EventEmitter.call(nativeTheme as any)
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
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,
// it is now a property of session module.
const { app, session } = require('electron')
const { app, session } = require('electron');
// Fallback to default session.
Object.setPrototypeOf(module.exports, new Proxy({}, {
get (target, property) {
if (!app.isReady()) return
if (!app.isReady()) return;
const netLog = session.defaultSession.netLog
const netLog = session.defaultSession.netLog;
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return;
// check for properties on the prototype chain that aren't functions
if (typeof netLog[property] !== 'function') return netLog[property]
if (typeof netLog[property] !== 'function') return netLog[property];
// Returning a native function directly would throw error.
return (...args) => netLog[property](...args)
return (...args) => netLog[property](...args);
},
ownKeys () {
if (!app.isReady()) return []
if (!app.isReady()) return [];
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog))
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog));
},
getOwnPropertyDescriptor (target) {
return { configurable: true, enumerable: true }
return { configurable: true, enumerable: true };
}
}))
}));

View File

@@ -1,16 +1,16 @@
'use strict'
'use strict';
const url = require('url')
const { EventEmitter } = require('events')
const { Readable, Writable } = require('stream')
const { app } = require('electron')
const { Session } = process.electronBinding('session')
const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net')
const { URLLoader } = net
const url = require('url');
const { EventEmitter } = require('events');
const { Readable, Writable } = require('stream');
const { app } = require('electron');
const { Session } = process.electronBinding('session');
const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net');
const { URLLoader } = net;
Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype)
Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype);
const kSupportedProtocols = new Set(['http:', 'https:'])
const kSupportedProtocols = new Set(['http:', 'https:']);
// set of headers that Node.js discards duplicates for
// see https://nodejs.org/api/http.html#http_message_headers
@@ -33,166 +33,175 @@ const discardableDuplicateHeaders = new Set([
'server',
'age',
'expires'
])
]);
class IncomingMessage extends Readable {
constructor (responseHead) {
super()
this._shouldPush = false
this._data = []
this._responseHead = responseHead
super();
this._shouldPush = false;
this._data = [];
this._responseHead = responseHead;
}
get statusCode () {
return this._responseHead.statusCode
return this._responseHead.statusCode;
}
get statusMessage () {
return this._responseHead.statusMessage
return this._responseHead.statusMessage;
}
get headers () {
const filteredHeaders = {}
const rawHeaders = this._responseHead.headers
Object.keys(rawHeaders).forEach(header => {
if (header in filteredHeaders && discardableDuplicateHeaders.has(header)) {
const filteredHeaders = {};
const { rawHeaders } = this._responseHead;
rawHeaders.forEach(header => {
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) &&
discardableDuplicateHeaders.has(header.key)) {
// do nothing with discardable duplicate headers
} else {
if (header === 'set-cookie') {
if (header.key === 'set-cookie') {
// keep set-cookie as an array per Node.js rules
// see https://nodejs.org/api/http.html#http_message_headers
filteredHeaders[header] = rawHeaders[header]
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
filteredHeaders[header.key].push(header.value);
} else {
filteredHeaders[header.key] = [header.value];
}
} else {
// 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 () {
return `${this.httpVersionMajor}.${this.httpVersionMinor}`
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
}
get httpVersionMajor () {
return this._responseHead.httpVersion.major
return this._responseHead.httpVersion.major;
}
get httpVersionMinor () {
return this._responseHead.httpVersion.minor
return this._responseHead.httpVersion.minor;
}
get rawTrailers () {
throw new Error('HTTP trailers are not supported')
throw new Error('HTTP trailers are not supported');
}
get trailers () {
throw new Error('HTTP trailers are not supported')
throw new Error('HTTP trailers are not supported');
}
_storeInternalData (chunk) {
this._data.push(chunk)
this._pushInternalData()
this._data.push(chunk);
this._pushInternalData();
}
_pushInternalData () {
while (this._shouldPush && this._data.length > 0) {
const chunk = this._data.shift()
this._shouldPush = this.push(chunk)
const chunk = this._data.shift();
this._shouldPush = this.push(chunk);
}
}
_read () {
this._shouldPush = true
this._pushInternalData()
this._shouldPush = true;
this._pushInternalData();
}
}
/** Writable stream that buffers up everything written to it. */
class SlurpStream extends Writable {
constructor () {
super()
this._data = Buffer.alloc(0)
super();
this._data = Buffer.alloc(0);
}
_write (chunk, encoding, callback) {
this._data = Buffer.concat([this._data, chunk])
callback()
this._data = Buffer.concat([this._data, chunk]);
callback();
}
data () { return this._data }
data () { return this._data; }
}
class ChunkedBodyStream extends Writable {
constructor (clientRequest) {
super()
this._clientRequest = clientRequest
super();
this._clientRequest = clientRequest;
}
_write (chunk, encoding, callback) {
if (this._downstream) {
this._downstream.write(chunk).then(callback, callback)
this._downstream.write(chunk).then(callback, callback);
} else {
// the contract of _write is that we won't be called again until we call
// the callback, so we're good to just save a single chunk.
this._pendingChunk = chunk
this._pendingCallback = callback
this._pendingChunk = chunk;
this._pendingCallback = callback;
// The first write to a chunked body stream begins the request.
this._clientRequest._startRequest()
this._clientRequest._startRequest();
}
}
_final (callback) {
this._downstream.done()
callback()
this._downstream.done();
callback();
}
startReading (pipe) {
if (this._downstream) {
throw new Error('two startReading calls???')
throw new Error('two startReading calls???');
}
this._downstream = pipe
this._downstream = pipe;
if (this._pendingChunk) {
const doneWriting = (maybeError) => {
const cb = this._pendingCallback
delete this._pendingCallback
delete this._pendingChunk
cb(maybeError)
}
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting)
const cb = this._pendingCallback;
delete this._pendingCallback;
delete this._pendingChunk;
cb(maybeError);
};
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting);
}
}
}
function parseOptions (options) {
if (typeof options === 'string') {
options = url.parse(options)
options = url.parse(options);
} else {
options = { ...options }
options = { ...options };
}
const method = (options.method || 'GET').toUpperCase()
let urlStr = options.url
const method = (options.method || 'GET').toUpperCase();
let urlStr = options.url;
if (!urlStr) {
const urlObj = {}
const protocol = options.protocol || 'http:'
const urlObj = {};
const protocol = options.protocol || 'http:';
if (!kSupportedProtocols.has(protocol)) {
throw new Error('Protocol "' + protocol + '" not supported')
throw new Error('Protocol "' + protocol + '" not supported');
}
urlObj.protocol = protocol
urlObj.protocol = protocol;
if (options.host) {
urlObj.host = options.host
urlObj.host = options.host;
} else {
if (options.hostname) {
urlObj.hostname = options.hostname
urlObj.hostname = options.hostname;
} else {
urlObj.hostname = 'localhost'
urlObj.hostname = 'localhost';
}
if (options.port) {
urlObj.port = options.port
urlObj.port = options.port;
}
}
@@ -203,203 +212,204 @@ function parseOptions (options) {
// well, and b) possibly too restrictive for real-world usage. That's
// why it only scans for spaces because those are guaranteed to create
// an invalid request.
throw new TypeError('Request path contains unescaped characters')
throw new TypeError('Request path contains unescaped characters');
}
const pathObj = url.parse(options.path || '/')
urlObj.pathname = pathObj.pathname
urlObj.search = pathObj.search
urlObj.hash = pathObj.hash
urlStr = url.format(urlObj)
const pathObj = url.parse(options.path || '/');
urlObj.pathname = pathObj.pathname;
urlObj.search = pathObj.search;
urlObj.hash = pathObj.hash;
urlStr = url.format(urlObj);
}
const redirectPolicy = options.redirect || 'follow'
const redirectPolicy = options.redirect || 'follow';
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
throw new Error('redirect mode should be one of follow, error or manual')
throw new Error('redirect mode should be one of follow, error or manual');
}
if (options.headers != null && typeof options.headers !== 'object') {
throw new TypeError('headers must be an object')
throw new TypeError('headers must be an object');
}
const urlLoaderOptions = {
method: method,
url: urlStr,
redirectPolicy,
extraHeaders: options.headers || {}
}
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}'`)
throw new Error(`Invalid header name: '${name}'`);
}
if (!_isValidHeaderValue(value.toString())) {
throw new Error(`Invalid value for header '${name}': '${value}'`)
throw new Error(`Invalid value for header '${name}': '${value}'`);
}
}
if (options.session) {
if (options.session instanceof Session) {
urlLoaderOptions.session = options.session
urlLoaderOptions.session = options.session;
} else {
throw new TypeError('`session` should be an instance of the Session class')
throw new TypeError('`session` should be an instance of the Session class');
}
} else if (options.partition) {
if (typeof options.partition === 'string') {
urlLoaderOptions.partition = options.partition
urlLoaderOptions.partition = options.partition;
} else {
throw new TypeError('`partition` should be a string')
throw new TypeError('`partition` should be a string');
}
}
return urlLoaderOptions
return urlLoaderOptions;
}
class ClientRequest extends Writable {
constructor (options, callback) {
super({ autoDestroy: true })
super({ autoDestroy: true });
if (!app.isReady()) {
throw new Error('net module can only be used after app is ready')
throw new Error('net module can only be used after app is ready');
}
if (callback) {
this.once('response', callback)
this.once('response', callback);
}
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options)
this._urlLoaderOptions = urlLoaderOptions
this._redirectPolicy = redirectPolicy
this._started = false
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options);
this._urlLoaderOptions = urlLoaderOptions;
this._redirectPolicy = redirectPolicy;
this._started = false;
}
set chunkedEncoding (value) {
if (this._started) {
throw new Error('chunkedEncoding can only be set before the request is started')
throw new Error('chunkedEncoding can only be set before the request is started');
}
if (typeof this._chunkedEncoding !== 'undefined') {
throw new Error('chunkedEncoding can only be set once')
throw new Error('chunkedEncoding can only be set once');
}
this._chunkedEncoding = !!value
this._chunkedEncoding = !!value;
if (this._chunkedEncoding) {
this._body = new ChunkedBodyStream(this)
this._body = new ChunkedBodyStream(this);
this._urlLoaderOptions.body = (pipe) => {
this._body.startReading(pipe)
}
this._body.startReading(pipe);
};
}
}
setHeader (name, value) {
if (typeof name !== 'string') {
throw new TypeError('`name` should be a string in setHeader(name, value)')
throw new TypeError('`name` should be a string in setHeader(name, value)');
}
if (value == null) {
throw new Error('`value` required in setHeader("' + name + '", value)')
throw new Error('`value` required in setHeader("' + name + '", value)');
}
if (this._started || this._firstWrite) {
throw new Error('Can\'t set headers after they are sent')
throw new Error('Can\'t set headers after they are sent');
}
if (!_isValidHeaderName(name)) {
throw new Error(`Invalid header name: '${name}'`)
throw new Error(`Invalid header name: '${name}'`);
}
if (!_isValidHeaderValue(value.toString())) {
throw new Error(`Invalid value for header '${name}': '${value}'`)
throw new Error(`Invalid value for header '${name}': '${value}'`);
}
const key = name.toLowerCase()
this._urlLoaderOptions.extraHeaders[key] = value
const key = name.toLowerCase();
this._urlLoaderOptions.extraHeaders[key] = value;
}
getHeader (name) {
if (name == null) {
throw new Error('`name` is required for getHeader(name)')
throw new Error('`name` is required for getHeader(name)');
}
const key = name.toLowerCase()
return this._urlLoaderOptions.extraHeaders[key]
const key = name.toLowerCase();
return this._urlLoaderOptions.extraHeaders[key];
}
removeHeader (name) {
if (name == null) {
throw new Error('`name` is required for removeHeader(name)')
throw new Error('`name` is required for removeHeader(name)');
}
if (this._started || this._firstWrite) {
throw new Error('Can\'t remove headers after they are sent')
throw new Error('Can\'t remove headers after they are sent');
}
const key = name.toLowerCase()
delete this._urlLoaderOptions.extraHeaders[key]
const key = name.toLowerCase();
delete this._urlLoaderOptions.extraHeaders[key];
}
_write (chunk, encoding, callback) {
this._firstWrite = true
this._firstWrite = true;
if (!this._body) {
this._body = new SlurpStream()
this._body = new SlurpStream();
this._body.on('finish', () => {
this._urlLoaderOptions.body = this._body.data()
this._startRequest()
})
this._urlLoaderOptions.body = this._body.data();
this._startRequest();
});
}
// TODO: is this the right way to forward to another stream?
this._body.write(chunk, encoding, callback)
this._body.write(chunk, encoding, callback);
}
_final (callback) {
if (this._body) {
// TODO: is this the right way to forward to another stream?
this._body.end(callback)
this._body.end(callback);
} else {
// end() called without a body, go ahead and start the request
this._startRequest()
callback()
this._startRequest();
callback();
}
}
_startRequest () {
this._started = true
this._started = true;
const stringifyValues = (obj) => {
const ret = {}
for (const k in obj) {
ret[k] = obj[k].toString()
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)
return ret;
};
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) };
this._urlLoader = new URLLoader(opts);
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
const response = this._response = new IncomingMessage(responseHead)
this.emit('response', response)
})
const response = this._response = new IncomingMessage(responseHead);
this.emit('response', response);
});
this._urlLoader.on('data', (event, data) => {
this._response._storeInternalData(Buffer.from(data))
})
this._response._storeInternalData(Buffer.from(data));
});
this._urlLoader.on('complete', () => {
if (this._response) { this._response._storeInternalData(null) }
})
if (this._response) { this._response._storeInternalData(null); }
});
this._urlLoader.on('error', (event, netErrorString) => {
const error = new Error(netErrorString)
if (this._response) this._response.destroy(error)
this._die(error)
})
const error = new Error(netErrorString);
if (this._response) this._response.destroy(error);
this._die(error);
});
this._urlLoader.on('login', (event, authInfo, callback) => {
const handled = this.emit('login', authInfo, callback)
const handled = this.emit('login', authInfo, callback);
if (!handled) {
// If there were no listeners, cancel the authentication request.
callback()
callback();
}
})
});
this._urlLoader.on('redirect', (event, redirectInfo, headers) => {
const { statusCode, newMethod, newUrl } = redirectInfo
const { statusCode, newMethod, newUrl } = redirectInfo;
if (this._redirectPolicy === 'error') {
this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`))
this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`));
} else if (this._redirectPolicy === 'manual') {
let _followRedirect = false
this._followRedirectCb = () => { _followRedirect = true }
let _followRedirect = false;
this._followRedirectCb = () => { _followRedirect = true; };
try {
this.emit('redirect', statusCode, newMethod, newUrl, headers)
this.emit('redirect', statusCode, newMethod, newUrl, headers);
} finally {
this._followRedirectCb = null
if (!_followRedirect) {
this._die(new Error('Redirect was cancelled'))
this._followRedirectCb = null;
if (!_followRedirect && !this._aborted) {
this._die(new Error('Redirect was cancelled'));
}
}
} else if (this._redirectPolicy === 'follow') {
@@ -407,61 +417,61 @@ class ClientRequest extends Writable {
// allowed but does nothing. (Perhaps it should throw an error
// though...? Since the redirect will happen regardless.)
try {
this._followRedirectCb = () => {}
this.emit('redirect', statusCode, newMethod, newUrl, headers)
this._followRedirectCb = () => {};
this.emit('redirect', statusCode, newMethod, newUrl, headers);
} finally {
this._followRedirectCb = null
this._followRedirectCb = null;
}
} else {
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`))
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`));
}
})
});
this._urlLoader.on('upload-progress', (event, position, total) => {
this._uploadProgress = { active: true, started: true, current: position, total }
this.emit('upload-progress', position, total) // Undocumented, for now
})
this._uploadProgress = { active: true, started: true, current: position, total };
this.emit('upload-progress', position, total); // Undocumented, for now
});
this._urlLoader.on('download-progress', (event, current) => {
if (this._response) {
this._response.emit('download-progress', current) // Undocumented, for now
this._response.emit('download-progress', current); // Undocumented, for now
}
})
});
}
followRedirect () {
if (this._followRedirectCb) {
this._followRedirectCb()
this._followRedirectCb();
} else {
throw new Error('followRedirect() called, but was not waiting for a redirect')
throw new Error('followRedirect() called, but was not waiting for a redirect');
}
}
abort () {
if (!this._aborted) {
process.nextTick(() => { this.emit('abort') })
process.nextTick(() => { this.emit('abort'); });
}
this._aborted = true
this._die()
this._aborted = true;
this._die();
}
_die (err) {
this.destroy(err)
this.destroy(err);
if (this._urlLoader) {
this._urlLoader.cancel()
if (this._response) this._response.destroy(err)
this._urlLoader.cancel();
if (this._response) this._response.destroy(err);
}
}
getUploadProgress () {
return this._uploadProgress ? { ...this._uploadProgress } : { active: false }
return this._uploadProgress ? { ...this._uploadProgress } : { active: false };
}
}
Net.prototype.request = function (options, callback) {
return new ClientRequest(options, callback)
}
return new ClientRequest(options, callback);
};
net.ClientRequest = ClientRequest
net.ClientRequest = ClientRequest;
module.exports = net
module.exports = net;

View File

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

View File

@@ -1,28 +1,38 @@
'use strict'
'use strict';
import { createLazyInstance } from '../utils'
import { createLazyInstance } from '../utils';
const { EventEmitter } = require('events')
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor')
const { EventEmitter } = require('events');
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor');
// PowerMonitor is an EventEmitter.
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype);
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true)
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true);
// On Linux we need to call blockShutdown() to subscribe to shutdown event.
if (process.platform === 'linux') {
powerMonitor.on('newListener', (event:string) => {
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
powerMonitor.blockShutdown()
}
})
powerMonitor.on('removeListener', (event: string) => {
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
powerMonitor.unblockShutdown()
}
})
// In order to delay system shutdown when e.preventDefault() is invoked
// on a powerMonitor 'shutdown' event, we need an org.freedesktop.login1
// 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
// set or unset our shutdown delay lock accordingly.
const { app } = require('electron');
app.whenReady().then(() => {
powerMonitor.on('newListener', (event: string) => {
// whenever the listener count is incremented to one...
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
powerMonitor.blockShutdown();
}
});
powerMonitor.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.
const protocol = process.electronBinding('protocol')
const protocol = process.electronBinding('protocol');
// Fallback protocol APIs of default session.
Object.setPrototypeOf(protocol, new Proxy({}, {
get (_target, property) {
if (!app.isReady()) return
if (!app.isReady()) return;
const protocol = session.defaultSession!.protocol
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return
const protocol = session.defaultSession!.protocol;
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return;
// Returning a native function directly would throw error.
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args)
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args);
},
ownKeys () {
if (!app.isReady()) return []
if (!app.isReady()) return [];
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol))
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol));
},
getOwnPropertyDescriptor () {
return { configurable: true, enumerable: true }
return { configurable: true, enumerable: true };
}
}))
}));
export default protocol
export default protocol;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 () {
}
};
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 { MdTextButton } = process.electronBinding('md_text_button')
const { LabelButton } = electron;
const { MdTextButton } = process.electronBinding('md_text_button');
Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype)
Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype);
MdTextButton.prototype._init = function () {
// Call parent class's _init.
LabelButton.prototype._init.call(this)
}
LabelButton.prototype._init.call(this);
};
module.exports = MdTextButton
module.exports = MdTextButton;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
import { EventEmitter } from 'events'
import { EventEmitter } from 'events';
const emitter = new EventEmitter()
const emitter = new EventEmitter();
// Do not throw exception when channel name is "error".
emitter.on('error', () => {})
emitter.on('error', () => {});
export const ipcMainInternal = emitter as ElectronInternal.IpcMainInternal
export const ipcMainInternal = emitter as ElectronInternal.IpcMainInternal;

View File

@@ -1,23 +1,23 @@
'use strict'
'use strict';
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
// The history operation in renderer is redirected to browser.
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) {
event.sender.goBack()
})
event.sender.goBack();
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD', function (event) {
event.sender.goForward()
})
event.sender.goForward();
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (event, offset) {
event.sender.goToOffset(offset)
})
event.sender.goToOffset(offset);
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
event.returnValue = event.sender.length()
})
event.returnValue = event.sender.length();
});
// JavaScript implementation of Chromium's NavigationController.
// Instead of relying on Chromium for history control, we compeletely do history
@@ -26,64 +26,64 @@ ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
// process is restarted everytime.
const NavigationController = (function () {
function NavigationController (webContents) {
this.webContents = webContents
this.clearHistory()
this.webContents = webContents;
this.clearHistory();
// webContents may have already navigated to a page.
if (this.webContents._getURL()) {
this.currentIndex++
this.history.push(this.webContents._getURL())
this.currentIndex++;
this.history.push(this.webContents._getURL());
}
this.webContents.on('navigation-entry-commited', (event, url, inPage, replaceEntry) => {
if (this.inPageIndex > -1 && !inPage) {
// Navigated to a new page, clear in-page mark.
this.inPageIndex = -1
this.inPageIndex = -1;
} else if (this.inPageIndex === -1 && inPage && !replaceEntry) {
// Started in-page navigations.
this.inPageIndex = this.currentIndex
this.inPageIndex = this.currentIndex;
}
if (this.pendingIndex >= 0) {
// Go to index.
this.currentIndex = this.pendingIndex
this.pendingIndex = -1
this.history[this.currentIndex] = url
this.currentIndex = this.pendingIndex;
this.pendingIndex = -1;
this.history[this.currentIndex] = url;
} else if (replaceEntry) {
// Non-user initialized navigation.
this.history[this.currentIndex] = url
this.history[this.currentIndex] = url;
} else {
// Normal navigation. Clear history.
this.history = this.history.slice(0, this.currentIndex + 1)
this.currentIndex++
this.history.push(url)
this.history = this.history.slice(0, this.currentIndex + 1);
this.currentIndex++;
this.history.push(url);
}
})
});
}
NavigationController.prototype.loadURL = function (url, options) {
if (options == null) {
options = {}
options = {};
}
const p = new Promise((resolve, reject) => {
const resolveAndCleanup = () => {
removeListeners()
resolve()
}
removeListeners();
resolve();
};
const rejectAndCleanup = (errorCode, errorDescription, url) => {
const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`)
Object.assign(err, { errno: errorCode, code: errorDescription, url })
removeListeners()
reject(err)
}
const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`);
Object.assign(err, { errno: errorCode, code: errorDescription, url });
removeListeners();
reject(err);
};
const finishListener = () => {
resolveAndCleanup()
}
resolveAndCleanup();
};
const failListener = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => {
if (isMainFrame) {
rejectAndCleanup(errorCode, errorDescription, validatedURL)
rejectAndCleanup(errorCode, errorDescription, validatedURL);
}
}
};
let navigationStarted = false
let navigationStarted = false;
const navigationListener = (event, url, isSameDocument, isMainFrame, frameProcessId, frameRoutingId, navigationId) => {
if (isMainFrame) {
if (navigationStarted && !isSameDocument) {
@@ -96,11 +96,11 @@ const NavigationController = (function () {
// considered navigation events but are triggered with isSameDocument.
// We can ignore these to allow virtual routing on page load as long
// as the routing does not leave the document
return rejectAndCleanup(-3, 'ERR_ABORTED', url)
return rejectAndCleanup(-3, 'ERR_ABORTED', url);
}
navigationStarted = true
navigationStarted = true;
}
}
};
const stopLoadingListener = () => {
// By the time we get here, either 'finish' or 'fail' should have fired
// if the navigation occurred. However, in some situations (e.g. when
@@ -110,134 +110,134 @@ const NavigationController = (function () {
// TODO(jeremy): enumerate all the cases in which this can happen. If
// the only one is with a bad scheme, perhaps ERR_INVALID_ARGUMENT
// would be more appropriate.
rejectAndCleanup(-2, 'ERR_FAILED', url)
}
rejectAndCleanup(-2, 'ERR_FAILED', url);
};
const removeListeners = () => {
this.webContents.removeListener('did-finish-load', finishListener)
this.webContents.removeListener('did-fail-load', failListener)
this.webContents.removeListener('did-start-navigation', navigationListener)
this.webContents.removeListener('did-stop-loading', stopLoadingListener)
}
this.webContents.on('did-finish-load', finishListener)
this.webContents.on('did-fail-load', failListener)
this.webContents.on('did-start-navigation', navigationListener)
this.webContents.on('did-stop-loading', stopLoadingListener)
})
this.webContents.removeListener('did-finish-load', finishListener);
this.webContents.removeListener('did-fail-load', failListener);
this.webContents.removeListener('did-start-navigation', navigationListener);
this.webContents.removeListener('did-stop-loading', stopLoadingListener);
};
this.webContents.on('did-finish-load', finishListener);
this.webContents.on('did-fail-load', failListener);
this.webContents.on('did-start-navigation', navigationListener);
this.webContents.on('did-stop-loading', stopLoadingListener);
});
// Add a no-op rejection handler to silence the unhandled rejection error.
p.catch(() => {})
this.pendingIndex = -1
this.webContents._loadURL(url, options)
this.webContents.emit('load-url', url, options)
return p
}
p.catch(() => {});
this.pendingIndex = -1;
this.webContents._loadURL(url, options);
this.webContents.emit('load-url', url, options);
return p;
};
NavigationController.prototype.getURL = function () {
if (this.currentIndex === -1) {
return ''
return '';
} else {
return this.history[this.currentIndex]
return this.history[this.currentIndex];
}
}
};
NavigationController.prototype.stop = function () {
this.pendingIndex = -1
return this.webContents._stop()
}
this.pendingIndex = -1;
return this.webContents._stop();
};
NavigationController.prototype.reload = function () {
this.pendingIndex = this.currentIndex
return this.webContents._loadURL(this.getURL(), {})
}
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {});
};
NavigationController.prototype.reloadIgnoringCache = function () {
this.pendingIndex = this.currentIndex
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {
extraHeaders: 'pragma: no-cache\n',
reloadIgnoringCache: true
})
}
});
};
NavigationController.prototype.canGoBack = function () {
return this.getActiveIndex() > 0
}
return this.getActiveIndex() > 0;
};
NavigationController.prototype.canGoForward = function () {
return this.getActiveIndex() < this.history.length - 1
}
return this.getActiveIndex() < this.history.length - 1;
};
NavigationController.prototype.canGoToIndex = function (index) {
return index >= 0 && index < this.history.length
}
return index >= 0 && index < this.history.length;
};
NavigationController.prototype.canGoToOffset = function (offset) {
return this.canGoToIndex(this.currentIndex + offset)
}
return this.canGoToIndex(this.currentIndex + offset);
};
NavigationController.prototype.clearHistory = function () {
this.history = []
this.currentIndex = -1
this.pendingIndex = -1
this.inPageIndex = -1
}
this.history = [];
this.currentIndex = -1;
this.pendingIndex = -1;
this.inPageIndex = -1;
};
NavigationController.prototype.goBack = function () {
if (!this.canGoBack()) {
return
return;
}
this.pendingIndex = this.getActiveIndex() - 1
this.pendingIndex = this.getActiveIndex() - 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goBack()
return this.webContents._goBack();
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {})
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
}
};
NavigationController.prototype.goForward = function () {
if (!this.canGoForward()) {
return
return;
}
this.pendingIndex = this.getActiveIndex() + 1
this.pendingIndex = this.getActiveIndex() + 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goForward()
return this.webContents._goForward();
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {})
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
}
};
NavigationController.prototype.goToIndex = function (index) {
if (!this.canGoToIndex(index)) {
return
return;
}
this.pendingIndex = index
return this.webContents._loadURL(this.history[this.pendingIndex], {})
}
this.pendingIndex = index;
return this.webContents._loadURL(this.history[this.pendingIndex], {});
};
NavigationController.prototype.goToOffset = function (offset) {
if (!this.canGoToOffset(offset)) {
return
return;
}
const pendingIndex = this.currentIndex + offset
const pendingIndex = this.currentIndex + offset;
if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
this.pendingIndex = pendingIndex
return this.webContents._goToOffset(offset)
this.pendingIndex = pendingIndex;
return this.webContents._goToOffset(offset);
} else {
return this.goToIndex(pendingIndex)
return this.goToIndex(pendingIndex);
}
}
};
NavigationController.prototype.getActiveIndex = function () {
if (this.pendingIndex === -1) {
return this.currentIndex
return this.currentIndex;
} else {
return this.pendingIndex
return this.pendingIndex;
}
}
};
NavigationController.prototype.length = function () {
return this.history.length
}
return this.history.length;
};
return NavigationController
})()
return NavigationController;
})();
module.exports = NavigationController
module.exports = NavigationController;

View File

@@ -1,51 +1,51 @@
'use strict'
'use strict';
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
const getOwnerKey = (webContents, contextId) => {
return `${webContents.id}-${contextId}`
}
return `${webContents.id}-${contextId}`;
};
class ObjectsRegistry {
constructor () {
this.nextId = 0
this.nextId = 0;
// Stores all objects by ref-counting.
// (id) => {object, count}
this.storage = {}
this.storage = {};
// Stores the IDs + refCounts of objects referenced by WebContents.
// (ownerKey) => { id: refCount }
this.owners = {}
this.owners = {};
}
// Register a new object and return its assigned ID. If the object is already
// registered then the already assigned ID would be returned.
add (webContents, contextId, obj) {
// Get or assign an ID to the object.
const id = this.saveToStorage(obj)
const id = this.saveToStorage(obj);
// Add object to the set of referenced objects.
const ownerKey = getOwnerKey(webContents, contextId)
let owner = this.owners[ownerKey]
const ownerKey = getOwnerKey(webContents, contextId);
let owner = this.owners[ownerKey];
if (!owner) {
owner = this.owners[ownerKey] = new Map()
this.registerDeleteListener(webContents, contextId)
owner = this.owners[ownerKey] = new Map();
this.registerDeleteListener(webContents, contextId);
}
if (!owner.has(id)) {
owner.set(id, 0)
owner.set(id, 0);
// Increase reference count if not referenced before.
this.storage[id].count++
this.storage[id].count++;
}
owner.set(id, owner.get(id) + 1)
return id
owner.set(id, owner.get(id) + 1);
return id;
}
// Get an object according to its ID.
get (id) {
const pointer = this.storage[id]
if (pointer != null) return pointer.object
const pointer = this.storage[id];
if (pointer != null) return pointer.object;
}
// Dereference an object according to its ID.
@@ -60,79 +60,79 @@ class ObjectsRegistry {
// For more details on why we do renderer side ref counting see
// https://github.com/electron/electron/pull/17464
remove (webContents, contextId, id, rendererSideRefCount) {
const ownerKey = getOwnerKey(webContents, contextId)
const owner = this.owners[ownerKey]
const ownerKey = getOwnerKey(webContents, contextId);
const owner = this.owners[ownerKey];
if (owner && owner.has(id)) {
const newRefCount = owner.get(id) - rendererSideRefCount
const newRefCount = owner.get(id) - rendererSideRefCount;
// Only completely remove if the number of references GCed in the
// renderer is the same as the number of references we sent them
if (newRefCount <= 0) {
// Remove the reference in owner.
owner.delete(id)
owner.delete(id);
// Dereference from the storage.
this.dereference(id)
this.dereference(id);
} else {
owner.set(id, newRefCount)
owner.set(id, newRefCount);
}
}
}
// Clear all references to objects refrenced by the WebContents.
clear (webContents, contextId) {
const ownerKey = getOwnerKey(webContents, contextId)
const owner = this.owners[ownerKey]
if (!owner) return
const ownerKey = getOwnerKey(webContents, contextId);
const owner = this.owners[ownerKey];
if (!owner) return;
for (const id of owner.keys()) this.dereference(id)
for (const id of owner.keys()) this.dereference(id);
delete this.owners[ownerKey]
delete this.owners[ownerKey];
}
// Private: Saves the object into storage and assigns an ID for it.
saveToStorage (object) {
let id = v8Util.getHiddenValue(object, 'atomId')
let id = v8Util.getHiddenValue(object, 'atomId');
if (!id) {
id = ++this.nextId
id = ++this.nextId;
this.storage[id] = {
count: 0,
object: object
}
v8Util.setHiddenValue(object, 'atomId', id)
};
v8Util.setHiddenValue(object, 'atomId', id);
}
return id
return id;
}
// Private: Dereference the object from store.
dereference (id) {
const pointer = this.storage[id]
const pointer = this.storage[id];
if (pointer == null) {
return
return;
}
pointer.count -= 1
pointer.count -= 1;
if (pointer.count === 0) {
v8Util.deleteHiddenValue(pointer.object, 'atomId')
delete this.storage[id]
v8Util.deleteHiddenValue(pointer.object, 'atomId');
delete this.storage[id];
}
}
// Private: Clear the storage when renderer process is destroyed.
registerDeleteListener (webContents, contextId) {
// contextId => ${processHostId}-${contextCount}
const processHostId = contextId.split('-')[0]
const processHostId = contextId.split('-')[0];
const listener = (event, deletedProcessHostId) => {
if (deletedProcessHostId &&
deletedProcessHostId.toString() === processHostId) {
webContents.removeListener('render-view-deleted', listener)
this.clear(webContents, contextId)
webContents.removeListener('render-view-deleted', listener);
this.clear(webContents, contextId);
}
}
};
// Note that the "render-view-deleted" event may not be emitted on time when
// the renderer process get destroyed because of navigation, we rely on the
// renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to
// guard this situation.
webContents.on('render-view-deleted', listener)
webContents.on('render-view-deleted', listener);
}
}
module.exports = new ObjectsRegistry()
module.exports = new ObjectsRegistry();

View File

@@ -1,135 +1,135 @@
'use strict'
'use strict';
const electron = require('electron')
const { EventEmitter } = require('events')
const fs = require('fs')
const electron = require('electron');
const { EventEmitter } = require('events');
const fs = require('fs');
const v8Util = process.electronBinding('v8_util')
const eventBinding = process.electronBinding('event')
const clipboard = process.electronBinding('clipboard')
const features = process.electronBinding('features')
const v8Util = process.electronBinding('v8_util');
const eventBinding = process.electronBinding('event');
const clipboard = process.electronBinding('clipboard');
const features = process.electronBinding('features');
const { getContentScripts } = require('@electron/internal/browser/chrome-extension')
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
const objectsRegistry = require('@electron/internal/browser/objects-registry')
const guestViewManager = require('@electron/internal/browser/guest-view-manager')
const bufferUtils = require('@electron/internal/common/buffer-utils')
const errorUtils = require('@electron/internal/common/error-utils')
const typeUtils = require('@electron/internal/common/type-utils')
const { isPromise } = require('@electron/internal/common/is-promise')
const { getContentScripts } = require('@electron/internal/browser/chrome-extension');
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const objectsRegistry = require('@electron/internal/browser/objects-registry');
const guestViewManager = require('@electron/internal/browser/guest-view-manager');
const bufferUtils = require('@electron/internal/common/buffer-utils');
const errorUtils = require('@electron/internal/common/error-utils');
const typeUtils = require('@electron/internal/common/type-utils');
const { isPromise } = require('@electron/internal/common/is-promise');
const hasProp = {}.hasOwnProperty
const hasProp = {}.hasOwnProperty;
// The internal properties of Function.
const FUNCTION_PROPERTIES = [
'length', 'name', 'arguments', 'caller', 'prototype'
]
];
// The remote functions in renderer processes.
// id => Function
const rendererFunctions = v8Util.createDoubleIDWeakMap()
const rendererFunctions = v8Util.createDoubleIDWeakMap();
// Return the description of object's members:
const getObjectMembers = function (object) {
let names = Object.getOwnPropertyNames(object)
let names = Object.getOwnPropertyNames(object);
// For Function, we should not override following properties even though they
// are "own" properties.
if (typeof object === 'function') {
names = names.filter((name) => {
return !FUNCTION_PROPERTIES.includes(name)
})
return !FUNCTION_PROPERTIES.includes(name);
});
}
// Map properties to descriptors.
return names.map((name) => {
const descriptor = Object.getOwnPropertyDescriptor(object, name)
const member = { name, enumerable: descriptor.enumerable, writable: false }
const descriptor = Object.getOwnPropertyDescriptor(object, name);
const member = { name, enumerable: descriptor.enumerable, writable: false };
if (descriptor.get === undefined && typeof object[name] === 'function') {
member.type = 'method'
member.type = 'method';
} else {
if (descriptor.set || descriptor.writable) member.writable = true
member.type = 'get'
if (descriptor.set || descriptor.writable) member.writable = true;
member.type = 'get';
}
return member
})
}
return member;
});
};
// Return the description of object's prototype.
const getObjectPrototype = function (object) {
const proto = Object.getPrototypeOf(object)
if (proto === null || proto === Object.prototype) return null
const proto = Object.getPrototypeOf(object);
if (proto === null || proto === Object.prototype) return null;
return {
members: getObjectMembers(proto),
proto: getObjectPrototype(proto)
}
}
};
};
// Convert a real value into meta data.
const valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
// Determine the type of value.
const meta = { type: typeof value }
const meta = { type: typeof value };
if (meta.type === 'object') {
// Recognize certain types of objects.
if (value === null) {
meta.type = 'value'
meta.type = 'value';
} else if (bufferUtils.isBuffer(value)) {
meta.type = 'buffer'
meta.type = 'buffer';
} else if (Array.isArray(value)) {
meta.type = 'array'
meta.type = 'array';
} else if (value instanceof Error) {
meta.type = 'error'
meta.type = 'error';
} else if (value instanceof Date) {
meta.type = 'date'
meta.type = 'date';
} else if (isPromise(value)) {
meta.type = 'promise'
meta.type = 'promise';
} else if (hasProp.call(value, 'callee') && value.length != null) {
// Treat the arguments object as array.
meta.type = 'array'
meta.type = 'array';
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
// Treat simple objects as value.
meta.type = 'value'
meta.type = 'value';
}
}
// Fill the meta object according to value's type.
if (meta.type === 'array') {
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject));
} else if (meta.type === 'object' || meta.type === 'function') {
meta.name = value.constructor ? value.constructor.name : ''
meta.name = value.constructor ? value.constructor.name : '';
// Reference the original value if it's an object, because when it's
// passed to renderer we would assume the renderer keeps a reference of
// it.
meta.id = objectsRegistry.add(sender, contextId, value)
meta.members = getObjectMembers(value)
meta.proto = getObjectPrototype(value)
meta.id = objectsRegistry.add(sender, contextId, value);
meta.members = getObjectMembers(value);
meta.proto = getObjectPrototype(value);
} else if (meta.type === 'buffer') {
meta.value = bufferUtils.bufferToMeta(value)
meta.value = bufferUtils.bufferToMeta(value);
} else if (meta.type === 'promise') {
// Add default handler to prevent unhandled rejections in main process
// Instead they should appear in the renderer process
value.then(function () {}, function () {})
value.then(function () {}, function () {});
meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
value.then(onFulfilled, onRejected)
})
value.then(onFulfilled, onRejected);
});
} else if (meta.type === 'error') {
meta.members = plainObjectToMeta(value)
meta.members = plainObjectToMeta(value);
// Error.name is not part of own properties.
meta.members.push({
name: 'name',
value: value.name
})
});
} else if (meta.type === 'date') {
meta.value = value.getTime()
meta.value = value.getTime();
} else {
meta.type = 'value'
meta.value = value
meta.type = 'value';
meta.value = value;
}
return meta
}
return meta;
};
// Convert object to meta by value.
const plainObjectToMeta = function (obj) {
@@ -137,404 +137,407 @@ const plainObjectToMeta = function (obj) {
return {
name: name,
value: obj[name]
}
})
}
};
});
};
// Convert Error into meta data.
const exceptionToMeta = function (error) {
return {
type: 'exception',
value: errorUtils.serialize(error)
}
}
};
};
const throwRPCError = function (message) {
const error = new Error(message)
error.code = 'EBADRPC'
error.errno = -72
throw error
}
const error = new Error(message);
error.code = 'EBADRPC';
error.errno = -72;
throw error;
};
const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => {
const location = v8Util.getHiddenValue(callIntoRenderer, 'location')
const location = v8Util.getHiddenValue(callIntoRenderer, 'location');
let message = `Attempting to call a function in a renderer window that has been closed or released.` +
`\nFunction provided here: ${location}`
`\nFunction provided here: ${location}`;
if (sender instanceof EventEmitter) {
const remoteEvents = sender.eventNames().filter((eventName) => {
return sender.listeners(eventName).includes(callIntoRenderer)
})
return sender.listeners(eventName).includes(callIntoRenderer);
});
if (remoteEvents.length > 0) {
message += `\nRemote event names: ${remoteEvents.join(', ')}`
message += `\nRemote event names: ${remoteEvents.join(', ')}`;
remoteEvents.forEach((eventName) => {
sender.removeListener(eventName, callIntoRenderer)
})
sender.removeListener(eventName, callIntoRenderer);
});
}
}
console.warn(message)
}
console.warn(message);
};
// Convert array of meta data from renderer into array of real values.
const unwrapArgs = function (sender, frameId, contextId, args) {
const metaToValue = function (meta) {
switch (meta.type) {
case 'value':
return meta.value
return meta.value;
case 'remote-object':
return objectsRegistry.get(meta.id)
return objectsRegistry.get(meta.id);
case 'array':
return unwrapArgs(sender, frameId, contextId, meta.value)
return unwrapArgs(sender, frameId, contextId, meta.value);
case 'buffer':
return bufferUtils.metaToBuffer(meta.value)
return bufferUtils.metaToBuffer(meta.value);
case 'date':
return new Date(meta.value)
return new Date(meta.value);
case 'promise':
return Promise.resolve({
then: metaToValue(meta.then)
})
});
case 'object': {
const ret = {}
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
const ret = {};
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
for (const { name, value } of meta.members) {
ret[name] = metaToValue(value)
ret[name] = metaToValue(value);
}
return ret
return ret;
}
case 'function-with-return-value':
const returnValue = metaToValue(meta.value)
const returnValue = metaToValue(meta.value);
return function () {
return returnValue
}
return returnValue;
};
case 'function': {
// Merge contextId and meta.id, since meta.id can be the same in
// different webContents.
const objectId = [contextId, meta.id]
const objectId = [contextId, meta.id];
// Cache the callbacks in renderer.
if (rendererFunctions.has(objectId)) {
return rendererFunctions.get(objectId)
return rendererFunctions.get(objectId);
}
const callIntoRenderer = function (...args) {
let succeed = false
let succeed = false;
if (!sender.isDestroyed()) {
succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args));
}
if (!succeed) {
removeRemoteListenersAndLogWarning(this, callIntoRenderer)
removeRemoteListenersAndLogWarning(this, callIntoRenderer);
}
}
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location)
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
};
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location);
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length });
v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender)
rendererFunctions.set(objectId, callIntoRenderer)
return callIntoRenderer
v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender);
rendererFunctions.set(objectId, callIntoRenderer);
return callIntoRenderer;
}
default:
throw new TypeError(`Unknown type: ${meta.type}`)
throw new TypeError(`Unknown type: ${meta.type}`);
}
}
return args.map(metaToValue)
}
};
return args.map(metaToValue);
};
const isRemoteModuleEnabledCache = new WeakMap()
const isRemoteModuleEnabledCache = new WeakMap();
const isRemoteModuleEnabled = function (contents) {
if (!isRemoteModuleEnabledCache.has(contents)) {
isRemoteModuleEnabledCache.set(contents, contents._isRemoteModuleEnabled())
isRemoteModuleEnabledCache.set(contents, contents._isRemoteModuleEnabled());
}
return isRemoteModuleEnabledCache.get(contents)
}
return isRemoteModuleEnabledCache.get(contents);
};
const handleRemoteCommand = function (channel, handler) {
ipcMainInternal.on(channel, (event, contextId, ...args) => {
let returnValue
let returnValue;
if (!isRemoteModuleEnabled(event.sender)) {
event.returnValue = null
return
event.returnValue = null;
return;
}
try {
returnValue = handler(event, contextId, ...args)
returnValue = handler(event, contextId, ...args);
} catch (error) {
returnValue = exceptionToMeta(error)
returnValue = exceptionToMeta(error);
}
if (returnValue !== undefined) {
event.returnValue = returnValue
event.returnValue = returnValue;
}
})
}
});
};
const emitCustomEvent = function (contents, eventName, ...args) {
const event = eventBinding.createWithSender(contents)
const event = eventBinding.createWithSender(contents);
electron.app.emit(eventName, event, contents, ...args)
contents.emit(eventName, event, ...args)
electron.app.emit(eventName, event, contents, ...args);
contents.emit(eventName, event, ...args);
return event
}
return event;
};
handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) {
const objectId = [passedContextId, id]
const objectId = [passedContextId, id];
if (!rendererFunctions.has(objectId)) {
// Do nothing if the error has already been reported before.
return
return;
}
removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId))
})
removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId));
});
handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName) {
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName)
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.require('${moduleName}')`)
throw new Error(`Blocked remote.require('${moduleName}')`);
} else {
customEvent.returnValue = process.mainModule.require(moduleName)
customEvent.returnValue = process.mainModule.require(moduleName);
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) {
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName)
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`)
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`);
} else {
customEvent.returnValue = electron[moduleName]
customEvent.returnValue = electron[moduleName];
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) {
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName)
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getGlobal('${globalName}')`)
throw new Error(`Blocked remote.getGlobal('${globalName}')`);
} else {
customEvent.returnValue = global[globalName]
customEvent.returnValue = global[globalName];
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window')
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window');
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWindow()')
throw new Error('Blocked remote.getCurrentWindow()');
} else {
customEvent.returnValue = event.sender.getOwnerBrowserWindow()
customEvent.returnValue = event.sender.getOwnerBrowserWindow();
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents')
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents');
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWebContents()')
throw new Error('Blocked remote.getCurrentWebContents()');
} else {
customEvent.returnValue = event.sender
customEvent.returnValue = event.sender;
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const constructor = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const constructor = objectsRegistry.get(id);
if (constructor == null) {
throwRPCError(`Cannot call constructor on missing remote object ${id}`)
throwRPCError(`Cannot call constructor on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, new constructor(...args))
})
return valueToMeta(event.sender, contextId, new constructor(...args));
});
handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const func = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const func = objectsRegistry.get(id);
if (func == null) {
throwRPCError(`Cannot call function on missing remote object ${id}`)
throwRPCError(`Cannot call function on missing remote object ${id}`);
}
try {
return valueToMeta(event.sender, contextId, func(...args), true)
return valueToMeta(event.sender, contextId, func(...args), true);
} catch (error) {
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`)
err.cause = error
throw err
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
err.cause = error;
throw err;
}
})
});
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const object = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const object = objectsRegistry.get(id);
if (object == null) {
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, new object[method](...args))
})
return valueToMeta(event.sender, contextId, new object[method](...args));
});
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const object = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const object = objectsRegistry.get(id);
if (object == null) {
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`)
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`);
}
try {
return valueToMeta(event.sender, contextId, object[method](...args), true)
return valueToMeta(event.sender, contextId, object[method](...args), true);
} catch (error) {
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`)
err.cause = error
throw err
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
err.cause = error;
throw err;
}
})
});
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const obj = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const obj = objectsRegistry.get(id);
if (obj == null) {
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`)
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`);
}
obj[name] = args[0]
return null
})
obj[name] = args[0];
return null;
});
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
const obj = objectsRegistry.get(id)
const obj = objectsRegistry.get(id);
if (obj == null) {
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, obj[name])
})
return valueToMeta(event.sender, contextId, obj[name]);
});
handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id, rendererSideRefCount) {
objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount)
})
objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount);
});
handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
objectsRegistry.clear(event.sender, contextId)
})
objectsRegistry.clear(event.sender, contextId);
});
handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender)
const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender);
const customEvent = emitCustomEvent(event.sender, 'remote-get-guest-web-contents', guest)
const customEvent = emitCustomEvent(event.sender, 'remote-get-guest-web-contents', guest);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getGuestForWebContents()`)
throw new Error(`Blocked remote.getGuestForWebContents()`);
} else {
customEvent.returnValue = guest
customEvent.returnValue = guest;
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
// Implements window.close()
ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
const window = event.sender.getOwnerBrowserWindow()
const window = event.sender.getOwnerBrowserWindow();
if (window) {
window.close()
window.close();
}
event.returnValue = null
})
event.returnValue = null;
});
ipcMainUtils.handle('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
return crashReporterInit(options)
})
return crashReporterInit(options);
});
ipcMainUtils.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
return event.sender.getLastWebPreferences()
})
return event.sender.getLastWebPreferences();
});
// Methods not listed in this set are called directly in the renderer process.
const allowedClipboardMethods = (() => {
switch (process.platform) {
case 'darwin':
return new Set(['readFindText', 'writeFindText'])
return new Set(['readFindText', 'writeFindText']);
case 'linux':
return new Set(Object.keys(clipboard))
return new Set(Object.keys(clipboard));
default:
return new Set()
return new Set();
}
})()
})();
ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) {
if (!allowedClipboardMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
throw new Error(`Invalid method: ${method}`);
}
return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args)))
})
return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args)));
});
if (features.isDesktopCapturerEnabled()) {
const desktopCapturer = require('@electron/internal/browser/desktop-capturer')
const desktopCapturer = require('@electron/internal/browser/desktop-capturer');
ipcMainUtils.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, ...args) {
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources')
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources');
if (customEvent.defaultPrevented) {
console.error('Blocked desktopCapturer.getSources()')
return []
console.error('Blocked desktopCapturer.getSources()');
return [];
}
return desktopCapturer.getSources(event, ...args)
})
return desktopCapturer.getSources(event, ...args);
});
}
const getPreloadScript = async function (preloadPath) {
let preloadSrc = null
let preloadError = null
let preloadSrc = null;
let preloadError = null;
try {
preloadSrc = (await fs.promises.readFile(preloadPath)).toString()
preloadSrc = (await fs.promises.readFile(preloadPath)).toString();
} catch (err) {
preloadError = errorUtils.serialize(err)
preloadError = errorUtils.serialize(err);
}
return { preloadPath, preloadSrc, preloadError }
}
return { preloadPath, preloadSrc, preloadError };
};
ipcMainUtils.handle('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts())
ipcMainUtils.handle('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts());
ipcMainUtils.handle('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
const preloadPaths = event.sender._getPreloadPaths()
const preloadPaths = event.sender._getPreloadPaths();
const webPreferences = event.sender.getLastWebPreferences() || {};
return {
contentScripts: getContentScripts(),
preloadScripts: await Promise.all(preloadPaths.map(path => getPreloadScript(path))),
isRemoteModuleEnabled: isRemoteModuleEnabled(event.sender),
isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender),
guestInstanceId: webPreferences.guestInstanceId,
openerId: webPreferences.openerId,
process: {
arch: process.arch,
platform: process.platform,
@@ -543,9 +546,9 @@ ipcMainUtils.handle('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
versions: process.versions,
execPath: process.helperExecPath
}
}
})
};
});
ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) {
event.sender.emit('preload-error', event, preloadPath, errorUtils.deserialize(error))
})
event.sender.emit('preload-error', event, preloadPath, errorUtils.deserialize(error));
});

View File

@@ -1,4 +1,4 @@
import { EventEmitter } from 'events'
import { EventEmitter } from 'events';
/**
* Creates a lazy instance of modules that can't be required before the
@@ -16,23 +16,23 @@ export function createLazyInstance (
holder: Object,
isEventEmitter: Boolean
) {
let lazyModule: Object
const module: any = {}
for (const method in (holder as any).prototype) {
let lazyModule: Object;
const module: any = {};
for (const method in (holder as any).prototype) { // eslint-disable-line guard-for-in
module[method] = (...args: any) => {
// create new instance of module at runtime if none exists
if (!lazyModule) {
lazyModule = creator()
if (isEventEmitter) EventEmitter.call(lazyModule as any)
lazyModule = creator();
if (isEventEmitter) EventEmitter.call(lazyModule as any);
}
// check for properties on the prototype chain that aren't functions
if (typeof (lazyModule as any)[method] !== 'function') {
return (lazyModule as any)[method]
return (lazyModule as any)[method];
}
return (lazyModule as any)[method](...args)
}
return (lazyModule as any)[method](...args);
};
}
return module
return module;
}

View File

@@ -1,29 +1,29 @@
'use strict'
'use strict';
const clipboard = process.electronBinding('clipboard')
const clipboard = process.electronBinding('clipboard');
if (process.type === 'renderer') {
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
const typeUtils = require('@electron/internal/common/type-utils')
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
const typeUtils = require('@electron/internal/common/type-utils');
const makeRemoteMethod = function (method) {
return (...args) => {
args = typeUtils.serialize(args)
const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args)
return typeUtils.deserialize(result)
}
}
args = typeUtils.serialize(args);
const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args);
return typeUtils.deserialize(result);
};
};
if (process.platform === 'linux') {
// On Linux we could not access clipboard in renderer process.
for (const method of Object.keys(clipboard)) {
clipboard[method] = makeRemoteMethod(method)
clipboard[method] = makeRemoteMethod(method);
}
} else if (process.platform === 'darwin') {
// Read/write to find pasteboard over IPC since only main process is notified of changes
clipboard.readFindText = makeRemoteMethod('readFindText')
clipboard.writeFindText = makeRemoteMethod('writeFindText')
clipboard.readFindText = makeRemoteMethod('readFindText');
clipboard.writeFindText = makeRemoteMethod('writeFindText');
}
}
module.exports = clipboard
module.exports = clipboard;

View File

@@ -1,95 +1,95 @@
let deprecationHandler: ElectronInternal.DeprecationHandler | null = null
let deprecationHandler: ElectronInternal.DeprecationHandler | null = null;
function warnOnce (oldName: string, newName?: string) {
let warned = false
let warned = false;
const msg = newName
? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.`
: `'${oldName}' is deprecated and will be removed.`
: `'${oldName}' is deprecated and will be removed.`;
return () => {
if (!warned && !process.noDeprecation) {
warned = true
deprecate.log(msg)
warned = true;
deprecate.log(msg);
}
}
};
}
const deprecate: ElectronInternal.DeprecationUtil = {
warnOnce,
setHandler: (handler) => { deprecationHandler = handler },
setHandler: (handler) => { deprecationHandler = handler; },
getHandler: () => deprecationHandler,
warn: (oldName, newName) => {
if (!process.noDeprecation) {
deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`)
deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`);
}
},
log: (message) => {
if (typeof deprecationHandler === 'function') {
deprecationHandler(message)
deprecationHandler(message);
} else if (process.throwDeprecation) {
throw new Error(message)
throw new Error(message);
} else if (process.traceDeprecation) {
return console.trace(message)
return console.trace(message);
} else {
return console.warn(`(electron) ${message}`)
return console.warn(`(electron) ${message}`);
}
},
// remove a function with no replacement
removeFunction: (fn, removedName) => {
if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`) }
if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`); }
// wrap the deprecated function to warn user
const warn = warnOnce(`${fn.name} function`)
const warn = warnOnce(`${fn.name} function`);
return function (this: any) {
warn()
fn.apply(this, arguments)
}
warn();
fn.apply(this, arguments);
};
},
// change the name of a function
renameFunction: (fn, newName) => {
const warn = warnOnce(`${fn.name} function`, `${newName} function`)
const warn = warnOnce(`${fn.name} function`, `${newName} function`);
return function (this: any) {
warn()
return fn.apply(this, arguments)
}
warn();
return fn.apply(this, arguments);
};
},
moveAPI: (fn: Function, oldUsage: string, newUsage: string) => {
const warn = warnOnce(oldUsage, newUsage)
const warn = warnOnce(oldUsage, newUsage);
return function (this: any) {
warn()
return fn.apply(this, arguments)
}
warn();
return fn.apply(this, arguments);
};
},
// change the name of an event
event: (emitter, oldName, newName) => {
const warn = newName.startsWith('-') /* internal event */
? warnOnce(`${oldName} event`)
: warnOnce(`${oldName} event`, `${newName} event`)
: warnOnce(`${oldName} event`, `${newName} event`);
return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) {
if (this.listenerCount(oldName) !== 0) {
warn()
this.emit(oldName, ...args)
warn();
this.emit(oldName, ...args);
}
})
});
},
// deprecate a getter/setter function pair in favor of a property
fnToProperty: (prototype: any, prop: string, getter: string, setter?: string) => {
const withWarnOnce = function (obj: any, key: any, oldName: string, newName: string) {
const warn = warnOnce(oldName, newName)
const method = obj[key]
const warn = warnOnce(oldName, newName);
const method = obj[key];
return function (this: any, ...args: any) {
warn()
return method.apply(this, args)
}
}
warn();
return method.apply(this, args);
};
};
prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`)
prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`);
if (setter) {
prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`)
prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`);
}
},
@@ -97,107 +97,107 @@ const deprecate: ElectronInternal.DeprecationUtil = {
removeProperty: (o, removedName) => {
// if the property's already been removed, warn about it
if (!(removedName in o)) {
deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`)
deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`);
}
// wrap the deprecated property in an accessor to warn
const warn = warnOnce(removedName)
let val = o[removedName]
const warn = warnOnce(removedName);
let val = o[removedName];
return Object.defineProperty(o, removedName, {
configurable: true,
get: () => {
warn()
return val
warn();
return val;
},
set: newVal => {
warn()
val = newVal
warn();
val = newVal;
}
})
});
},
// deprecate a callback-based function in favor of one returning a Promise
promisify: <T extends (...args: any[]) => any>(fn: T): T => {
const fnName = fn.name || 'function'
const oldName = `${fnName} with callbacks`
const newName = `${fnName} with Promises`
const warn = warnOnce(oldName, newName)
const fnName = fn.name || 'function';
const oldName = `${fnName} with callbacks`;
const newName = `${fnName} with Promises`;
const warn = warnOnce(oldName, newName);
return function (this: any, ...params: any[]) {
let cb: Function | undefined
let cb: Function | undefined;
if (params.length > 0 && typeof params[params.length - 1] === 'function') {
cb = params.pop()
cb = params.pop();
}
const promise = fn.apply(this, params)
if (!cb) return promise
if (process.enablePromiseAPIs) warn()
const promise = fn.apply(this, params);
if (!cb) return promise;
if (process.enablePromiseAPIs) warn();
return promise
.then((res: any) => {
process.nextTick(() => {
cb!.length === 2 ? cb!(null, res) : cb!(res)
})
return res
cb!.length === 2 ? cb!(null, res) : cb!(res);
});
return res;
}, (err: Error) => {
process.nextTick(() => {
cb!.length === 2 ? cb!(err) : cb!()
})
throw err
})
} as T
cb!.length === 2 ? cb!(err) : cb!();
});
throw err;
});
} as T;
},
// convertPromiseValue: Temporarily disabled until it's used
// deprecate a callback-based function in favor of one returning a Promise
promisifyMultiArg: <T extends (...args: any[]) => any>(fn: T /* convertPromiseValue: (v: any) => any */): T => {
const fnName = fn.name || 'function'
const oldName = `${fnName} with callbacks`
const newName = `${fnName} with Promises`
const warn = warnOnce(oldName, newName)
const fnName = fn.name || 'function';
const oldName = `${fnName} with callbacks`;
const newName = `${fnName} with Promises`;
const warn = warnOnce(oldName, newName);
return function (this: any, ...params) {
let cb: Function | undefined
let cb: Function | undefined;
if (params.length > 0 && typeof params[params.length - 1] === 'function') {
cb = params.pop()
cb = params.pop();
}
const promise = fn.apply(this, params)
if (!cb) return promise
if (process.enablePromiseAPIs) warn()
const promise = fn.apply(this, params);
if (!cb) return promise;
if (process.enablePromiseAPIs) warn();
return promise
.then((res: any) => {
process.nextTick(() => {
// eslint-disable-next-line standard/no-callback-literal
cb!.length > 2 ? cb!(null, ...res) : cb!(...res)
})
cb!.length > 2 ? cb!(null, ...res) : cb!(...res);
});
}, (err: Error) => {
process.nextTick(() => cb!(err))
})
} as T
process.nextTick(() => cb!(err));
});
} as T;
},
// change the name of a property
renameProperty: (o, oldName, newName) => {
const warn = warnOnce(oldName, newName)
const warn = warnOnce(oldName, newName);
// if the new property isn't there yet,
// inject it and warn about it
if ((oldName in o) && !(newName in o)) {
warn()
o[newName] = (o as any)[oldName]
warn();
o[newName] = (o as any)[oldName];
}
// wrap the deprecated property in an accessor to warn
// and redirect to the new property
return Object.defineProperty(o, oldName, {
get: () => {
warn()
return o[newName]
warn();
return o[newName];
},
set: value => {
warn()
o[newName] = value
warn();
o[newName] = value;
}
})
});
}
}
};
export default deprecate
export default deprecate;

View File

@@ -1,12 +1,12 @@
'use strict'
'use strict';
const moduleList = require('@electron/internal/common/api/module-list')
const moduleList = require('@electron/internal/common/api/module-list');
exports.handleESModule = (loader) => () => {
const value = loader()
if (value.__esModule && value.default) return value.default
return value
}
const value = loader();
if (value.__esModule && value.default) return value.default;
return value;
};
exports.memoizedGetter = (getter) => {
/*
@@ -14,24 +14,24 @@ exports.memoizedGetter = (getter) => {
* node module cache anyway at `Module._cache`. This memoization
* is dramatically faster than relying on nodes module cache however
*/
let memoizedValue = null
let memoizedValue = null;
return () => {
if (memoizedValue === null) {
memoizedValue = getter()
memoizedValue = getter();
}
return memoizedValue
}
}
return memoizedValue;
};
};
// Attaches properties to |targetExports|.
exports.defineProperties = function (targetExports) {
const descriptors = {}
const descriptors = {};
for (const module of moduleList) {
descriptors[module.name] = {
enumerable: !module.private,
get: exports.handleESModule(module.loader)
}
};
}
return Object.defineProperties(targetExports, descriptors)
}
return Object.defineProperties(targetExports, descriptors);
};

View File

@@ -1,4 +1,4 @@
'use strict'
'use strict';
// Common modules, please sort alphabetically
module.exports = [
@@ -7,4 +7,4 @@ module.exports = [
{ name: 'shell', loader: () => require('./shell') },
// The internal modules, invisible unless you know their names.
{ name: 'deprecate', loader: () => require('./deprecate'), private: true }
]
];

View File

@@ -1,8 +1,8 @@
'use strict'
'use strict';
const { deprecate } = require('electron')
const { NativeImage, nativeImage } = process.electronBinding('native_image')
const { deprecate } = require('electron');
const { NativeImage, nativeImage } = process.electronBinding('native_image');
deprecate.fnToProperty(NativeImage.prototype, 'isMacTemplateImage', '_isTemplateImage', '_setTemplateImage')
deprecate.fnToProperty(NativeImage.prototype, 'isMacTemplateImage', '_isTemplateImage', '_setTemplateImage');
module.exports = nativeImage
module.exports = nativeImage;

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
'use strict'
'use strict';
/* global require */
// Monkey-patch the fs module.
require('electron/js2c/asar').wrapFsWithAsar(require('fs'))
require('electron/js2c/asar').wrapFsWithAsar(require('fs'));

View File

@@ -1,4 +1,4 @@
import { Buffer } from 'buffer'
import { Buffer } from 'buffer';
const typedArrays: Record<string, Function> = {
Buffer,
@@ -12,31 +12,31 @@ const typedArrays: Record<string, Function> = {
Uint32Array,
Float32Array,
Float64Array
}
};
type BufferLike = Buffer | ArrayBuffer | ArrayBufferView
function getType (value: BufferLike) {
for (const type of Object.keys(typedArrays)) {
if (value instanceof typedArrays[type]) {
return type
return type;
}
}
throw new Error('Invalid buffer')
throw new Error('Invalid buffer');
}
function getBuffer (value: BufferLike) {
if (value instanceof Buffer) {
return value
return value;
} else if (value instanceof ArrayBuffer) {
return Buffer.from(value)
return Buffer.from(value);
} else {
return Buffer.from(value.buffer, value.byteOffset, value.byteLength)
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
}
}
export function isBuffer (value: BufferLike) {
return ArrayBuffer.isView(value) || value instanceof ArrayBuffer
return ArrayBuffer.isView(value) || value instanceof ArrayBuffer;
}
interface BufferMeta {
@@ -52,20 +52,20 @@ export function bufferToMeta (value: BufferLike): BufferMeta {
// NB. We only use length when decoding Int8Array and friends.
// For other buffer-like types this is expected to be undefined.
length: (value as Buffer).length
}
};
}
export function metaToBuffer (value: BufferMeta) {
const constructor = typedArrays[value.type]
const data = getBuffer(value.data)
const constructor = typedArrays[value.type];
const data = getBuffer(value.data);
if (constructor === Buffer) {
return data
return data;
} else if (constructor === ArrayBuffer) {
return data.buffer
return data.buffer;
} else if (constructor) {
return new (constructor as any)(data.buffer, data.byteOffset, value.length)
return new (constructor as any)(data.buffer, data.byteOffset, value.length);
} else {
return data
return data;
}
}

View File

@@ -1,19 +1,19 @@
'use strict'
'use strict';
const binding = process.electronBinding('crash_reporter')
const binding = process.electronBinding('crash_reporter');
class CrashReporter {
contructor () {
this.productName = null
this.crashesDirectory = null
this.productName = null;
this.crashesDirectory = null;
}
init (options) {
throw new Error('Not implemented')
throw new Error('Not implemented');
}
start (options) {
if (options == null) options = {}
if (options == null) options = {};
const {
productName,
@@ -22,76 +22,81 @@ class CrashReporter {
ignoreSystemCrashHandler = false,
submitURL,
uploadToServer = true
} = options
} = options;
if (companyName == null) throw new Error('companyName is a required option to crashReporter.start')
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start')
if (companyName == null) throw new Error('companyName is a required option to crashReporter.start');
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
const ret = this.init({
submitURL,
productName
})
});
this.productName = ret.productName
this.crashesDirectory = ret.crashesDirectory
this.productName = ret.productName;
this.crashesDirectory = ret.crashesDirectory;
if (extra._productName == null) extra._productName = ret.productName
if (extra._companyName == null) extra._companyName = companyName
if (extra._version == null) extra._version = ret.appVersion
if (extra._productName == null) extra._productName = ret.productName;
if (extra._companyName == null) extra._companyName = companyName;
if (extra._version == null) extra._version = ret.appVersion;
binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra)
binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra);
}
getLastCrashReport () {
const reports = this.getUploadedReports()
.sort((a, b) => {
const ats = (a && a.date) ? new Date(a.date).getTime() : 0
const bts = (b && b.date) ? new Date(b.date).getTime() : 0
return bts - ats
})
const ats = (a && a.date) ? new Date(a.date).getTime() : 0;
const bts = (b && b.date) ? new Date(b.date).getTime() : 0;
return bts - ats;
});
return (reports.length > 0) ? reports[0] : null
return (reports.length > 0) ? reports[0] : null;
}
getUploadedReports () {
return binding.getUploadedReports(this.getCrashesDirectory())
const crashDir = this.getCrashesDirectory();
if (!crashDir) {
throw new Error('crashReporter has not been started');
}
return binding.getUploadedReports(crashDir);
}
getCrashesDirectory () {
return this.crashesDirectory
return this.crashesDirectory;
}
getProductName () {
return this.productName
return this.productName;
}
getUploadToServer () {
if (process.type === 'browser') {
return binding.getUploadToServer()
return binding.getUploadToServer();
} else {
throw new Error('getUploadToServer can only be called from the main process')
throw new Error('getUploadToServer can only be called from the main process');
}
}
setUploadToServer (uploadToServer) {
if (process.type === 'browser') {
return binding.setUploadToServer(uploadToServer)
return binding.setUploadToServer(uploadToServer);
} else {
throw new Error('setUploadToServer can only be called from the main process')
throw new Error('setUploadToServer can only be called from the main process');
}
}
addExtraParameter (key, value) {
binding.addExtraParameter(key, value)
binding.addExtraParameter(key, value);
}
removeExtraParameter (key) {
binding.removeExtraParameter(key)
binding.removeExtraParameter(key);
}
getParameters () {
return binding.getParameters()
return binding.getParameters();
}
}
module.exports = CrashReporter
module.exports = CrashReporter;

View File

@@ -1,13 +1,13 @@
export function electronBindingSetup (binding: typeof process['_linkedBinding'], processType: typeof process['type']): typeof process['electronBinding'] {
return function electronBinding (name: string) {
try {
return binding(`atom_${processType}_${name}`)
return binding(`atom_${processType}_${name}`);
} catch (error) {
if (/No such module/.test(error.message)) {
return binding(`atom_common_${name}`)
return binding(`atom_common_${name}`);
} else {
throw error
throw error;
}
}
}
};
}

View File

@@ -6,18 +6,18 @@ const constructors = new Map([
[SyntaxError.name, SyntaxError],
[TypeError.name, TypeError],
[URIError.name, URIError]
])
]);
export function deserialize (error: Electron.SerializedError): Electron.ErrorWithCause {
if (error && error.__ELECTRON_SERIALIZED_ERROR__ && constructors.has(error.name)) {
const constructor = constructors.get(error.name)
const deserializedError = new constructor!(error.message) as Electron.ErrorWithCause
deserializedError.stack = error.stack
deserializedError.from = error.from
deserializedError.cause = exports.deserialize(error.cause)
return deserializedError
const constructor = constructors.get(error.name);
const deserializedError = new constructor!(error.message) as Electron.ErrorWithCause;
deserializedError.stack = error.stack;
deserializedError.from = error.from;
deserializedError.cause = exports.deserialize(error.cause);
return deserializedError;
}
return error
return error;
}
export function serialize (error: Electron.ErrorWithCause): Electron.SerializedError {
@@ -31,7 +31,7 @@ export function serialize (error: Electron.ErrorWithCause): Electron.SerializedE
from: process.type as Electron.ProcessType,
cause: exports.serialize(error.cause),
__ELECTRON_SERIALIZED_ERROR__: true
}
};
}
return error
return error;
}

View File

@@ -1,10 +1,10 @@
import * as util from 'util'
import * as util from 'util';
import { electronBindingSetup } from '@electron/internal/common/electron-binding-setup'
import { electronBindingSetup } from '@electron/internal/common/electron-binding-setup';
const timers = require('timers')
const timers = require('timers');
process.electronBinding = electronBindingSetup(process._linkedBinding, process.type)
process.electronBinding = electronBindingSetup(process._linkedBinding, process.type);
type AnyFn = (...args: any[]) => any
@@ -17,11 +17,11 @@ type AnyFn = (...args: any[]) => any
const wrapWithActivateUvLoop = function <T extends AnyFn> (func: T): T {
return wrap(func, function (func) {
return function (this: any, ...args: any[]) {
process.activateUvLoop()
return func.apply(this, args)
}
}) as T
}
process.activateUvLoop();
return func.apply(this, args);
};
}) as T;
};
/**
* Casts to any below for func are due to Typescript not supporting symbols
@@ -30,41 +30,41 @@ const wrapWithActivateUvLoop = function <T extends AnyFn> (func: T): T {
* Refs: https://github.com/Microsoft/TypeScript/issues/1863
*/
function wrap <T extends AnyFn> (func: T, wrapper: (fn: AnyFn) => T) {
const wrapped = wrapper(func)
const wrapped = wrapper(func);
if ((func as any)[util.promisify.custom]) {
(wrapped as any)[util.promisify.custom] = wrapper((func as any)[util.promisify.custom])
(wrapped as any)[util.promisify.custom] = wrapper((func as any)[util.promisify.custom]);
}
return wrapped
return wrapped;
}
process.nextTick = wrapWithActivateUvLoop(process.nextTick)
process.nextTick = wrapWithActivateUvLoop(process.nextTick);
global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate)
global.clearImmediate = timers.clearImmediate
global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
global.clearImmediate = timers.clearImmediate;
// setTimeout needs to update the polling timeout of the event loop, when
// called under Chromium's event loop the node's event loop won't get a chance
// to update the timeout, so we have to force the node's event loop to
// recalculate the timeout in browser process.
timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout)
timers.setInterval = wrapWithActivateUvLoop(timers.setInterval)
timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout);
timers.setInterval = wrapWithActivateUvLoop(timers.setInterval);
// Only override the global setTimeout/setInterval impls in the browser process
if (process.type === 'browser') {
global.setTimeout = timers.setTimeout
global.setInterval = timers.setInterval
global.setTimeout = timers.setTimeout;
global.setInterval = timers.setInterval;
}
if (process.platform === 'win32') {
// Always returns EOF for stdin stream.
const { Readable } = require('stream')
const stdin = new Readable()
stdin.push(null)
const { Readable } = require('stream');
const stdin = new Readable();
stdin.push(null);
Object.defineProperty(process, 'stdin', {
configurable: false,
enumerable: true,
get () {
return stdin
return stdin;
}
})
});
}

View File

@@ -8,5 +8,5 @@ export function isPromise (val: any) {
val.constructor.reject instanceof Function &&
val.constructor.resolve &&
val.constructor.resolve instanceof Function
)
);
}

View File

@@ -1,21 +1,21 @@
'use strict'
'use strict';
// parses a feature string that has the format used in window.open()
// - `features` input string
// - `emit` function(key, value) - called for each parsed KV
module.exports = function parseFeaturesString (features, emit) {
features = `${features}`.trim()
features = `${features}`.trim();
// split the string by ','
features.split(/\s*,\s*/).forEach((feature) => {
// expected form is either a key by itself or a key/value pair in the form of
// 'key=value'
let [key, value] = feature.split(/\s*=\s*/)
if (!key) return
let [key, value] = feature.split(/\s*=\s*/);
if (!key) return;
// interpret the value as a boolean, if possible
value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value
value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value;
// emit the parsed pair
emit(key, value)
})
}
emit(key, value);
});
};

View File

@@ -1,42 +1,42 @@
import * as path from 'path'
import * as path from 'path';
const Module = require('module')
const Module = require('module');
// Clear Node's global search paths.
Module.globalPaths.length = 0
Module.globalPaths.length = 0;
// Prevent Node from adding paths outside this app to search paths.
const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep
const originalNodeModulePaths = Module._nodeModulePaths
const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep;
const originalNodeModulePaths = Module._nodeModulePaths;
Module._nodeModulePaths = function (from: string) {
const paths: string[] = originalNodeModulePaths(from)
const fromPath = path.resolve(from) + path.sep
const paths: string[] = originalNodeModulePaths(from);
const fromPath = path.resolve(from) + path.sep;
// If "from" is outside the app then we do nothing.
if (fromPath.startsWith(resourcesPathWithTrailingSlash)) {
return paths.filter(function (candidate) {
return candidate.startsWith(resourcesPathWithTrailingSlash)
})
return candidate.startsWith(resourcesPathWithTrailingSlash);
});
} else {
return paths
return paths;
}
}
};
// Make a fake Electron module that we will insert into the module cache
const electronModule = new Module('electron', null)
electronModule.id = 'electron'
electronModule.loaded = true
electronModule.filename = 'electron'
const electronModule = new Module('electron', null);
electronModule.id = 'electron';
electronModule.loaded = true;
electronModule.filename = 'electron';
Object.defineProperty(electronModule, 'exports', {
get: () => require('electron')
})
});
Module._cache['electron'] = electronModule
Module._cache['electron'] = electronModule;
const originalResolveFilename = Module._resolveFilename
const originalResolveFilename = Module._resolveFilename;
Module._resolveFilename = function (request: string, parent: NodeModule, isMain: boolean) {
if (request === 'electron') {
return 'electron'
return 'electron';
} else {
return originalResolveFilename(request, parent, isMain)
return originalResolveFilename(request, parent, isMain);
}
}
};

View File

@@ -1,10 +1,10 @@
const { nativeImage, NativeImage } = process.electronBinding('native_image')
const { nativeImage, NativeImage } = process.electronBinding('native_image');
const objectMap = function (source: Object, mapper: (value: any) => any) {
const sourceEntries = Object.entries(source)
const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)])
return Object.fromEntries(targetEntries)
}
const sourceEntries = Object.entries(source);
const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]);
return Object.fromEntries(targetEntries);
};
export function serialize (value: any): any {
if (value instanceof NativeImage) {
@@ -12,28 +12,28 @@ export function serialize (value: any): any {
buffer: value.toBitmap(),
size: value.getSize(),
__ELECTRON_SERIALIZED_NativeImage__: true
}
};
} else if (Array.isArray(value)) {
return value.map(serialize)
return value.map(serialize);
} else if (value instanceof Buffer) {
return value
return value;
} else if (value instanceof Object) {
return objectMap(value, serialize)
return objectMap(value, serialize);
} else {
return value
return value;
}
}
export function deserialize (value: any): any {
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
return nativeImage.createFromBitmap(value.buffer, value.size)
return nativeImage.createFromBitmap(value.buffer, value.size);
} else if (Array.isArray(value)) {
return value.map(deserialize)
return value.map(deserialize);
} else if (value instanceof Buffer) {
return value
return value;
} else if (value instanceof Object) {
return objectMap(value, deserialize)
return objectMap(value, deserialize);
} else {
return value
return value;
}
}

View File

@@ -48,7 +48,15 @@ export const syncMethods = new Set([
'getZoomLevel',
'setZoomFactor',
'setZoomLevel'
])
]);
export const properties = new Set([
'audioMuted',
'userAgent',
'zoomLevel',
'zoomFactor',
'frameRate'
]);
export const asyncMethods = new Set([
'loadURL',
@@ -62,4 +70,4 @@ export const asyncMethods = new Set([
'setVisualZoomLevelLimits',
'print',
'printToPDF'
])
]);

View File

@@ -5,4 +5,4 @@
//
// Will mutate this captured one as well and that is OK.
export const Promise = global.Promise
export const Promise = global.Promise;

View File

@@ -1,37 +1,37 @@
'use strict'
'use strict';
/* global nodeProcess, isolatedWorld, worldId */
const { EventEmitter } = require('events')
const { EventEmitter } = require('events');
process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer')
process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer');
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
// The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the
// "ipc-internal" hidden value
v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'))
v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'));
// The process object created by webpack is not an event emitter, fix it so
// the API is more compatible with non-sandboxed renderers.
for (const prop of Object.keys(EventEmitter.prototype)) {
if (process.hasOwnProperty(prop)) {
delete process[prop]
delete process[prop];
}
}
Object.setPrototypeOf(process, EventEmitter.prototype)
Object.setPrototypeOf(process, EventEmitter.prototype);
const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args')
const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args');
if (isolatedWorldArgs) {
const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs
const { windowSetup } = require('@electron/internal/renderer/window-setup')
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs;
const { windowSetup } = require('@electron/internal/renderer/window-setup');
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen);
}
const extensionId = v8Util.getHiddenValue(isolatedWorld, `extension-${worldId}`)
const extensionId = v8Util.getHiddenValue(isolatedWorld, `extension-${worldId}`);
if (extensionId) {
const chromeAPI = require('@electron/internal/renderer/chrome-api')
chromeAPI.injectTo(extensionId, window)
const chromeAPI = require('@electron/internal/renderer/chrome-api');
chromeAPI.injectTo(extensionId, window);
}

View File

@@ -1,27 +1,27 @@
'use strict'
'use strict';
/* global nodeProcess, isolatedWorld */
process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer')
process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer');
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
// The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the
// "ipc-internal" hidden value
v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'))
v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'));
const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl')
const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl');
if (webViewImpl) {
// Must setup the WebView element in main world.
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element')
setupWebView(v8Util, webViewImpl)
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element');
setupWebView(v8Util, webViewImpl);
}
const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args')
const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args');
if (isolatedWorldArgs) {
const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs
const { windowSetup } = require('@electron/internal/renderer/window-setup')
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs;
const { windowSetup } = require('@electron/internal/renderer/window-setup');
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen);
}

View File

@@ -1,20 +1,20 @@
const { hasSwitch } = process.electronBinding('command_line')
const binding = process.electronBinding('context_bridge')
const { hasSwitch } = process.electronBinding('command_line');
const binding = process.electronBinding('context_bridge');
const contextIsolationEnabled = hasSwitch('context-isolation')
const contextIsolationEnabled = hasSwitch('context-isolation');
const checkContextIsolationEnabled = () => {
if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled')
}
if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled');
};
const contextBridge = {
exposeInMainWorld: (key: string, api: Record<string, any>) => {
checkContextIsolationEnabled()
return binding.exposeAPIInMainWorld(key, api)
checkContextIsolationEnabled();
return binding.exposeAPIInMainWorld(key, api);
},
debugGC: () => binding._debugGCMaps({})
}
};
if (!binding._debugGCMaps) delete contextBridge.debugGC
if (!binding._debugGCMaps) delete contextBridge.debugGC;
export default contextBridge
export default contextBridge;

View File

@@ -1,12 +1,12 @@
'use strict'
'use strict';
const CrashReporter = require('@electron/internal/common/crash-reporter')
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
const CrashReporter = require('@electron/internal/common/crash-reporter');
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
class CrashReporterRenderer extends CrashReporter {
init (options) {
return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options)
return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options);
}
}
module.exports = new CrashReporterRenderer()
module.exports = new CrashReporterRenderer();

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