Compare commits

...

98 Commits

Author SHA1 Message Date
Electron Bot
a342ab7ce7 Bump v11.0.0-nightly.20200731 2020-07-31 07:31:51 -07:00
Samuel Attard
c3258d6c4e fix: provide AXTextChangeValueStartMarker for macOS a11y value change notifications (#24801) 2020-07-30 21:01:26 -07:00
Jeremy Rose
b5cd9ce0b3 refactor: ginify WebContents (#24651) 2020-07-30 09:17:57 -07:00
Electron Bot
e7fc19c98e Bump v11.0.0-nightly.20200730 2020-07-30 07:33:03 -07:00
Shelley Vohr
948cc08265 chore: add V8 crash information to crashReporter (#24771)
* feat: add V8 crash information to crashReporter

* Address review feedback
2020-07-29 19:04:24 -07:00
Jeremy Rose
d93bb34ac4 fix: increase max crash key value length (#24782)
* fix: increase max crash key value length

* chore: fix linting

* chore: fix linux

* Update spec-main/api-crash-reporter-spec.ts

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
2020-07-29 19:04:04 -07:00
Robo
01023435c0 fix: save crash reports locally when uploadToServer: false on linux (#24778)
* fix: generate dumps under crashDumps folder in linux

* Update spec-main/api-crash-reporter-spec.ts

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2020-07-29 17:45:02 -07:00
Samuel Attard
06cb550c75 build: upload sentry src bundles on windows as well (#24773) 2020-07-29 12:41:53 -07:00
George Xu
1b175a0609 feat: add capabilities to app.getLoginItemSettings() and app.setLoginItemSettings() (#24494)
* fixed

* semantic commit

* add comments to browser.h

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-07-29 10:08:37 -07:00
Electron Bot
75372e933f Bump v11.0.0-nightly.20200729 2020-07-29 07:31:20 -07:00
Samuel Attard
f859d4ce44 test: fail tests if the spec runner promise rejects (#24762) 2020-07-28 20:36:35 -07:00
Jeremy Rose
9c37e12e2d refactor: ginify app (#24679) 2020-07-28 16:43:43 -07:00
Jeremy Rose
834408a50f fix: wrap OnWindowMessage w/ handlescope (#24716) 2020-07-28 15:09:56 -07:00
Robo
b6321cc22d fix: crash when navigating from a page with webview that has inherited zoom level (#24757)
* fix: cleanup webview zoom level observers on navigation

* add spec

* webview should be on same partition

* wait for webview to finish loading
2020-07-28 13:00:44 -07:00
Jeremy Rose
38fafe4986 refactor: ginify autoUpdater (#24678) 2020-07-28 11:04:34 -07:00
Jeremy Rose
e6cf5906f6 refactor: ginify Screen (#24677) 2020-07-28 11:04:01 -07:00
Jeremy Rose
362da77c0a refactor: ginify SystemPreferences (#24675) 2020-07-28 11:03:30 -07:00
Electron Bot
3f45fc24bb Bump v11.0.0-nightly.20200728 2020-07-28 10:45:24 -07:00
Samuel Attard
75c3a426ea Revert "Bump v11.0.0-nightly.20200728"
This reverts commit c6c986648f.
2020-07-28 10:43:32 -07:00
Shelley Vohr
4e15273b9f build: fix failing arm64 builds on macOS (#24748) 2020-07-28 08:42:15 -07:00
Electron Bot
c6c986648f Bump v11.0.0-nightly.20200728 2020-07-28 07:31:27 -07:00
Samuel Attard
bac232689d build: only move folder if it exists when freeing space (#24740) 2020-07-27 23:09:29 -07:00
Robo
76e3ee6fe6 fix: loading dedicated/shared worker scripts over custom protocol (#20625)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-07-27 18:48:37 -07:00
Samuel Attard
4829b0f816 fix: disable rosetta as Electron does not run under rosetta (#24670) 2020-07-27 13:26:04 -07:00
Samuel Attard
a612fb00c8 chore: update symbol server URL (#24709) 2020-07-27 13:25:39 -07:00
Electron Bot
2f02a469f4 chore: bump node to v12.18.3 (master) (#24707)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-07-27 09:12:29 -07:00
Charles Kerr
b39a5b71fe chore: add trop annotations to release notes. (#24672)
Trop annotations are in the form of "(Also in 7.3, 8, 9)" with links to
the sibling branches.

Previously seen in b43e601b83 but is now
free of optional chaining and nullish coalescing, to run on Node < 14 :)
2020-07-27 10:01:41 -05:00
Electron Bot
91f5837344 Bump v11.0.0-nightly.20200727 2020-07-27 07:31:39 -07:00
Electron Bot
5fc354df73 Bump v11.0.0-nightly.20200724 2020-07-24 07:32:19 -07:00
Shelley Vohr
ee31611e84 fix: missing base::CommandLine include (#24718) 2020-07-24 00:08:04 -07:00
Jeremy Rose
071c5930b9 refactor: ginify InAppPurchase (#24674) 2020-07-23 14:55:41 -07:00
Samuel Attard
5cfe956fe1 fix: ensure that errors thrown in the context bridge are created in the correct context (#24534) 2020-07-23 14:32:38 -07:00
Samuel Attard
b500294c1d feat: add worldSafe flag for executeJS results (#24114)
* feat: add worldSafe flag for executeJS results

* chore: do not log warning for webContents.executeJS

* Apply suggestions from code review

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

* chore: apply PR feedback

* chore: split logic a bit

* chore: allow primitives through the world safe checl

* chore: clean up per PR feedback

* chore: flip boolean logic

* chore: update per PR feedback

* chore: fix typo

* chore: fix spec

Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2020-07-23 14:32:20 -07:00
szTheory
3b250b649b docs: fix role typos in startSpeaking/stopSpeaking (#24550) 2020-07-23 13:59:24 -07:00
Samuel Attard
2162eeb609 build: fix linting issue in docs (#24710) 2020-07-23 12:27:00 -07:00
Charles Kerr
00fc8066de docs: clarify use of pageRanges when printing. (#24694)
Fixes #24684 -- the docs are slightly unclear about how pageRanges
should be used, e.g. whether the page indices are zero-based or
one-based.
2020-07-23 12:03:52 -07:00
Electron Bot
6b1de271a7 Bump v11.0.0-nightly.20200723 2020-07-23 07:33:04 -07:00
Raghav Bhutra
ec838cfc11 docs: add Documentation for Open Recent Menu-item (#24374) 2020-07-22 19:22:01 -07:00
Gellert Hegyi
6fa3406912 fix: remove corner mask override to increase window resize performance (#24642)
* fix: remove corner mask override to increase window resize performance

* uses corner mask only for vibrant view
2020-07-23 09:54:32 +09:00
Felix Rieseberg
b57f5086ef fix: Allow VoiceOver to navigate "back into" web contents (#24655)
* fix: Use ElectronAdaptedContentView

* fix: Actually, nevermind, it's "BrowserAccessebilityCocoa"

* chore: Remove things instead of manually keeping them
2020-07-22 15:29:01 -07:00
Jeremy Rose
734753dd7a refactor: ginify NativeTheme (#24673) 2020-07-22 11:01:30 -07:00
Electron Bot
10bf50e1fd Bump v11.0.0-nightly.20200722 2020-07-22 07:32:34 -07:00
Samuel Attard
5795e59352 build: upload x-compiled arm mksnapshot only on linux (#24680)
* build: upload x-compiled arm mksnapshot only on linux

* Update upload.py
2020-07-22 01:45:18 -07:00
Electron Bot
2fb14f53fe chore: bump chromium to 1a093e6a0cb5e72ba78990fe39824 (master) (#24575) 2020-07-21 22:34:34 -07:00
Samuel Attard
d4a4269989 perf: do not convert object keys in ctx bridge as they are always primitives (#24671)
* perf: do not convert object keys in ctx bridge as they are always primitives

* Update shell/renderer/api/electron_api_context_bridge.cc

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2020-07-21 15:26:16 -07:00
Charles Kerr
cd805544d1 docs: update the URL for catapult's trace viewer. (#24654)
The old URL points to an archived repo. The updated URL points to the
catapult's landing page for trace-viewer.
2020-07-21 15:00:31 -07:00
Electron Bot
95dd81bd68 Bump v11.0.0-nightly.20200721 2020-07-21 09:40:26 -07:00
Samuel Attard
a76e74ac3f Revert "chore: add Trop annotations to release notes. (#24644)"
This reverts commit b43e601b83.
2020-07-21 09:38:28 -07:00
szTheory
74e7f2773f doc: fix typos in role values (#24552) 2020-07-21 08:59:20 -07:00
Charles Kerr
b43e601b83 chore: add Trop annotations to release notes. (#24644)
* chore: add Trop annotations to release notes.

Trop annotations are in the form of "(Also in 7.3, 8, 9)"
with links to the sibling branches.
2020-07-21 10:32:36 -05:00
Samuel Attard
589968f9fc build: add arm64 macOS publish jobs (#24652)
* build: add arm64 macOS publish jobs

* update ci-release-build to run new arm64 macOS publish jobs

* fixup circleci config validate issues

Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-07-21 07:55:00 -04:00
Cheng Zhao
a12602dbc3 docs: document missing permission types (#24660) 2020-07-21 07:49:37 -04:00
Erick Zhao
77eea08a54 docs: update webContents.send link in ipcMain docs (#24245) 2020-07-21 14:47:10 +09:00
Kriti Singh
86b441d599 docs: add descriptions for types of permissions (#23781) 2020-07-21 14:29:32 +09:00
Paul Frazee
509740c357 fix: Close protocol response streams when aborted (#24014) 2020-07-21 09:51:38 +09:00
Shelley Vohr
bcba4baa85 fix: use Node.js isolate setup logic in bindings (#24579)
* fix: use Node.js isolate setup logic in bindings

* Flags should be more process-specific

* Remove redundant isolate function setting

* Remove old SetFatalErrorHandler call
2020-07-20 12:41:52 -07:00
Jeremy Rose
f0953902db refactor: clean up Session with CleanedUpAtExit (#24603) 2020-07-20 12:13:33 -07:00
Jeremy Rose
e5cb22b7f9 refactor: use gin::Arguments in WebContents (#24604) 2020-07-20 11:07:02 -07:00
Samuel Attard
682f78b9a8 test: add more auto updater tests for squirrel.mac (#24611) 2020-07-20 09:51:33 -07:00
Electron Bot
8f5280a821 Bump v11.0.0-nightly.20200720 2020-07-20 08:32:04 -07:00
Milan Burda
fa1323d6cd feat: add child-process-gone event to app (#24367) 2020-07-20 10:34:16 -04:00
Samuel Attard
f146a164af build: add apple silicon support (#24545)
* chore: add patches to prevent installation of non-arm pip packages

* chore: add patches for apple-silicon

* build: add apple silicon build

* ci: add testing of new arm binary

* chore: remove / update for upstreamed patches

* Skip content tracing on macos on arm

* build: ensure that spec native modules are rebuilt for arm64 on apple-silicon

* chore: fix patches

* chore: fix broken patch

* chore: fix arm64 DCHECK

* build: add MAS arm64 build

* build: disable arm2 tests

* chore: update patches

* build: actually build MAS version of apple silicon app

Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-07-17 09:08:44 -07:00
Electron Bot
6f53457a17 Bump v11.0.0-nightly.20200717 2020-07-17 08:33:03 -07:00
Jeremy Rose
45551f6bf2 refactor: WebContents::From returns pointer (#24605) 2020-07-16 16:16:05 -07:00
Samuel Attard
14bbc07f1e build: free up space on macOS VM in background (#24607) 2020-07-16 14:20:42 -07:00
Samuel Attard
3806f4cb64 build: stop the macOS checkout step early if the src cache already exists (#24606) 2020-07-16 14:16:02 -07:00
Jeremy Rose
eb7c04c7c4 refactor: stop using isolate() in WebContents (#24602) 2020-07-16 14:11:44 -07:00
Samuel Attard
f649e604be build: tsify asar and move to webpack js2c pipeline (#24495)
* build: tsify asar and move to webpack js2c pipeline

* build: use the webpack provider for fs-wrapper
2020-07-16 11:38:31 -07:00
Samuel Attard
4c3da359fc build: convert touch-bar to typescript (#24511) 2020-07-16 11:37:38 -07:00
Shelley Vohr
3f37ff87d2 fix: chrome://accessibility not loading (#24437) 2020-07-16 10:14:54 -07:00
Electron Bot
24fb498fd3 Bump v11.0.0-nightly.20200716 2020-07-16 08:32:31 -07:00
Samuel Attard
d8be385d4b fix: do not register the node ESM loader in renderer processes (#24301) 2020-07-16 08:09:07 -07:00
Jeremy Rose
e4180b3a3a refactor: rename InspectableWebContents{Impl =>} (#24543) 2020-07-15 11:27:42 -07:00
Yoshietru Nagata
eb550c57d3 docs: fix wrong description in breaking-changes.md (#24477) 2020-07-15 09:31:54 -07:00
Electron Bot
cdd5bb1eba Bump v11.0.0-nightly.20200715 2020-07-15 08:31:25 -07:00
Electron Bot
9c8cdd63fd chore: bump chromium to ab0f6deadb33bb3a0201cd2d21602 (master) (#24539)
* chore: bump chromium in DEPS to 0ee01724797ab0f6deadb33bb3a0201cd2d21602

* Update patches

* Separate pdf/pdf_ppapi.h into its own target.

https://chromium-review.googlesource.com/c/chromium/src/+/2292458

* [ozone/x11] Removed DesktopWindowTreeHostX11 and its DnD client.

https://chromium-review.googlesource.com/c/chromium/src/+/2279199

* Move front_end html_entrypoints to data_deps

https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2292282

Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-07-14 20:07:42 -07:00
Samuel Attard
3f54f240bd perf: pass primitives directly through the context bridge, avoids copying (#24531) 2020-07-14 18:38:54 -07:00
Samuel Attard
36900df7d9 build: free up more space on the mac VM (#24549) 2020-07-14 18:34:38 -07:00
Shelley Vohr
ce87a7e69f fix: broken --trace-sync-io flag in Node.js (#24529) 2020-07-14 10:47:20 -07:00
Shelley Vohr
990a6f8b6c fix: Node.js cpu and heap profiling (#24528)
* fix: Node.js cpu and heap profiling

* chore: emable more now-working Node.js specs
2020-07-14 11:56:57 -04:00
Electron Bot
e002f7315d Bump v11.0.0-nightly.20200714 2020-07-14 08:31:14 -07:00
Electron Bot
8bbdc224ac chore: bump chromium to 6d130075d378a64187360ba4e7820 (master) (#24256)
* chore: bump chromium in DEPS to 7fb9778894d73378bff51087ce869ea5aa6e5d5d

* chore: bump chromium in DEPS to 83da426e53d423f0530fc23433b6d2c4d0548442

* update patches

* remove chromeos-only TtsControllerDelegate

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2255314

* SharedUserScriptMaster -> SharedUserScriptManager

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2258357

* avoid deprecated DISALLOW_COPY_AND_ASSIGN

https://groups.google.com/a/chromium.org/forum/#!msg/cxx/qwH2hxaEjac/TUKq6eqfCwAJ

* chore: bump chromium in DEPS to b2eaf9ff4e6b03267bf279583ea20ceb2b25e9d0

* update patches

* rename GetHighContrastColorScheme -> GetPlatformHighContrastColorScheme

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2250224

* remove vulkan info collection

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2252818

* add max_xcode_version build var

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2264867

* add missing headers

* chore: bump chromium in DEPS to cded18ca1138f7e8efc904f077ddcca34f0135cf

* update patches

* add empty floc blocklist to BrowserProcessImpl

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2240873

* chore: bump chromium in DEPS to f06602226cd80bf677b2ce013a94a2fb7f6ac58d

* chore: bump chromium in DEPS to 747aa4bfc74fc6cf7f08ee72624cd69ae41ae28d

* chore: bump chromium in DEPS to 31c0105e50fcc4e94de33e5c8602c755ace4a32b

* chore: update patches

* Reland "[base] Stop including check.h, notreached.h, etc. in logging.h"

https://chromium-review.googlesource.com/c/chromium/src/+/2264297

* X11 and Ozone: make sure gfx::AcceleratedWidget to be uint32_t

https://chromium-review.googlesource.com/c/chromium/src/+/2260554

* Move zygote from //services/service_manager back to //content

https://chromium-review.googlesource.com/c/chromium/src/+/2252466

* chore: update v8 patches

* [XProto] Remove usage of Shape extension

https://chromium-review.googlesource.com/c/chromium/src/+/2262113

* fixup! add empty floc blocklist to BrowserProcessImpl

* Require macOS 10.15.1 sdk

https://chromium-review.googlesource.com/c/chromium/src/+/2238504

* Use newer Xcode version 11.5.0

* update src cache

* chore: bump chromium in DEPS to 60a9883e35db3f6f91916f0878e88e1849c17b11

* chore: update patches

* Reland "Reland "New toolchain for Windows 10 19041 SDK""

https://chromium-review.googlesource.com/c/chromium/src/+/2255527

* update patches

* Convert raw NonClientFrameViews to unique_ptrs

https://chromium-review.googlesource.com/c/chromium/src/+/2240417

* [printing] Move PrintHostMsg_DidPreviewDocument_Params to print.mojom

https://chromium-review.googlesource.com/c/chromium/src/+/2257035

* chore: bump chromium in DEPS to 12c233c2a85bfa28fb279f390121ba681e52a71b

* chore: update patches

* Removing oppressive language for the directory chrome/browser/apps

https://chromium-review.googlesource.com/c/chromium/src/+/2269822

* Inclusion: rename SpellcheckLanguageBlacklistPolicyHandler

https://chromium-review.googlesource.com/c/chromium/src/+/2267646

* Clean up duplicate WebContents "is fullscreen" functions

https://chromium-review.googlesource.com/c/chromium/src/+/2275148

* Adds icon loading service with sandbox for Windows.

https://chromium-review.googlesource.com/c/chromium/src/+/1987273

* No more Vulkan info collection for UMA on Windows

https://chromium-review.googlesource.com/c/chromium/src/+/2252818

* fix lint

* chore: update buildflag conditions

* chore: bump chromium in DEPS to a837d4c4230ace4f10b2768728f4044b7995dfa5

* update hunspell files

* chore: update patches

* Make content::FileSelectListener a RefCounted

https://chromium-review.googlesource.com/c/chromium/src/+/2275338

* fix build failures on MAS

* update patches

* fixup! Reland "[base] Stop including check.h, notreached.h, etc. in logging.h"

* fix build on windows

* Check for GDI exhaustion if window creation fails

https://chromium-review.googlesource.com/c/chromium/src/+/2244124

* chore: bump chromium in DEPS to 2c9b2a73be4ef9ec22d8b6da8e174cb80753f125

* chore: update patches

* Network Service: Move DeleteCookiePredicate into public folder

https://chromium-review.googlesource.com/c/chromium/src/+/2264186

* chore: bump chromium in DEPS to fa2606299bcc02c362528d26b5dcf8c8a0db0735

* chore: bump chromium in DEPS to d9c235d1227204dbae3708daae851573a3566b94

* chore: bump chromium in DEPS to 2f82c284243c035f49a747fd1ead6c44b4b31093

* chore: update patches

* Move creating the LayerTreeSettings into blink.

https://chromium-review.googlesource.com/c/chromium/src/+/2267720

* chore: bump chromium in DEPS to 914112f1d9af9e4974059dc403da62699a55550f

* update patches

* chore: bump chromium in DEPS to e0bc1ffae6393fc543a2da94c88167df75859b36

* refactor: match upstream print preview handling (#24452)

* update patches

* chore: bump chromium in DEPS to 0881423156abe084164b51ab58ce93a8bd380524

* update patches

* update patches

* give a type to pendingPromise

* chore: bump chromium in DEPS to 11a8c1534b16d130075d378a64187360ba4e7820

* update patches

* 2272609: Move //services/service_manager/sandbox to //sandbox/policy.

https://chromium-review.googlesource.com/c/chromium/src/+/2272609

* update patches

* fixup! 2272609: Move //services/service_manager/sandbox to //sandbox/policy.

* fixup! 2272609: Move //services/service_manager/sandbox to //sandbox/policy.

* 2264511: Cookies: Update SetCanonicalCookie to return CookieAccessResult

https://chromium-review.googlesource.com/c/chromium/src/+/2264511

* chore: fix setAlwaysOnTop test

The window must be visible for state to be updated properly.

* Revert "Migrate modules/desktop_capture and modules/video_capture to webrtc::Mutex."

https://webrtc-review.googlesource.com/c/src/+/179080

* update patches

Co-authored-by: Andy Locascio <andy@slack-corp.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
Co-authored-by: Electron Bot <anonymous@electronjs.org>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
2020-07-13 21:13:34 -04:00
Jeremy Rose
36bd940bc3 refactor: ginify NativeImage (#24486) 2020-07-13 14:44:12 -07:00
Shelley Vohr
f0a0e10bd1 fix: use default NSVisualEffectState (#24471) 2020-07-13 12:40:37 -07:00
Samuel Attard
b02748e607 build: ensure that electron/lib/browser can only use browser or common imports (#24512)
* build: ensure that electron/lib/browser can only use browser or common imports

* chore: fix linting
2020-07-13 09:58:49 -07:00
Jeremy Rose
1719f073c1 chore: remove some unused code in the net module (#24488) 2020-07-13 09:36:20 -07:00
Shelley Vohr
91cdedfea9 fix: default to simplex for printing DuplexMode (#24489) 2020-07-13 11:31:39 -04:00
Samuel Attard
cbb47570bd build: run the JS linter on the build folder (#24513) 2020-07-10 14:48:50 -07:00
Sam Saccone
e18f508e66 docs: fix incorrect formatting in browser docs. (#24473)
Address incorrect typing for isEnabled. The root cause of this was due
to missing backticks which caused the docs parser to think that the
return type of the `isEnabled` function was null, where it was supposed
to be a boolean type.

The side effect of this was that the generated typescript typings were
incorrect for this function.

Fixes #24409
2020-07-10 13:57:29 -07:00
Shelley Vohr
6c4017ff45 fix: always callback error with invalid print settings (#24476) 2020-07-10 09:42:22 -07:00
Samuel Attard
5737fda154 build: tsify worker/init (#24497) 2020-07-09 20:32:37 -07:00
Samuel Attard
8847517798 build: tsify isolated_renderer/init (#24496) 2020-07-09 20:32:26 -07:00
Samuel Attard
c9aa68e32c build: update octicons (#24492) 2020-07-09 18:12:18 -07:00
Samuel Attard
42f716bbc6 build: update in-range dependencies (#24480) 2020-07-09 16:32:21 -07:00
Samuel Attard
ef9addcb92 build: fix linting issue in spec-main 2020-07-09 11:25:43 -07:00
Samuel Attard
dcb56923cb build: update @types packages and fs-extra (#24475) 2020-07-09 10:57:50 -07:00
Samuel Attard
eb6616e4e9 build: update to standard 14 (#24479) 2020-07-09 10:18:49 -07:00
Jeremy Rose
9bd0fc5348 refactor: ginify BrowserView (#23578) 2020-07-09 08:48:39 -07:00
432 changed files with 8960 additions and 5864 deletions

View File

@@ -37,10 +37,18 @@ parameters:
type: boolean
default: false
run-osx-publish-arm64:
type: boolean
default: false
run-mas-publish:
type: boolean
default: false
run-mas-publish-arm64:
type: boolean
default: false
run-linux-publish:
type: boolean
default: false
@@ -77,12 +85,17 @@ machine-linux-2xlarge: &machine-linux-2xlarge
machine-mac: &machine-mac
macos:
xcode: "11.1.0"
xcode: "11.5.0"
machine-mac-large: &machine-mac-large
resource_class: large
macos:
xcode: "11.1.0"
xcode: "11.5.0"
machine-mac-large-arm: &machine-mac-large-arm
resource_class: large
macos:
xcode: "12.0.0-UA"
# Build configurations options.
env-testing-build: &env-testing-build
@@ -123,6 +136,10 @@ env-arm: &env-arm
BUILD_NATIVE_MKSNAPSHOT: 1
TARGET_ARCH: arm
env-apple-silicon: &env-apple-silicon
GN_EXTRA_ARGS: 'target_cpu = "arm64"'
TARGET_ARCH: arm64
env-arm64: &env-arm64
GN_EXTRA_ARGS: 'target_cpu = "arm64" fatal_linker_warnings = false enable_linux_installer = false'
MKSNAPSHOT_TOOLCHAIN: //build/toolchain/linux:clang_arm64
@@ -133,6 +150,11 @@ env-mas: &env-mas
GN_EXTRA_ARGS: 'is_mas_build = true'
MAS_BUILD: 'true'
env-mas-apple-silicon: &env-mas-apple-silicon
GN_EXTRA_ARGS: 'target_cpu = "arm64" is_mas_build = true'
MAS_BUILD: 'true'
TARGET_ARCH: arm64
# Misc build configuration options.
env-enable-sccache: &env-enable-sccache
USE_SCCACHE: true
@@ -312,14 +334,25 @@ step-get-more-space-on-mac: &step-get-more-space-on-mac
name: Free up space on MacOS
command: |
if [ "`uname`" == "Darwin" ]; then
sudo rm -rf /Library/Developer/CoreSimulator
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform
sudo mkdir -p $TMPDIR/del-target
tmpify() {
if [ -d "$1" ]; then
sudo mv $1 $TMPDIR/del-target/$(echo $1|shasum -a 256|head -n1|cut -d " " -f1)
fi
}
tmpify /Library/Developer/CoreSimulator
tmpify $(xcode-select -p)/Platforms/AppleTVOS.platform
tmpify $(xcode-select -p)/Platforms/iPhoneOS.platform
tmpify $(xcode-select -p)/Platforms/WatchOS.platform
tmpify $(xcode-select -p)/Platforms/WatchSimulator.platform
tmpify $(xcode-select -p)/Platforms/AppleTVSimulator.platform
tmpify $(xcode-select -p)/Platforms/iPhoneSimulator.platform
tmpify ~/.rubies
tmpify ~/Library/Caches/Homebrew
tmpify /usr/local/Homebrew
sudo rm -rf $TMPDIR/del-target
fi
background: true
# On macOS delete all .git directories under src/ expect for
# third_party/angle/ because of build time generation of file
@@ -712,8 +745,6 @@ 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
@@ -742,9 +773,13 @@ step-maybe-cross-arch-snapshot: &step-maybe-cross-arch-snapshot
elif [ "$TARGET_ARCH" == "arm64" ]; then
export MKSNAPSHOT_PATH="clang_x64_v8_arm64"
fi
cp "out/Default/$MKSNAPSHOT_PATH/mksnapshot" out/Default
cp "out/Default/$MKSNAPSHOT_PATH/libffmpeg.so" out/Default
cp "out/Default/$MKSNAPSHOT_PATH/mksnapshot" out/Default
cp "out/Default/$MKSNAPSHOT_PATH/v8_context_snapshot_generator" out/Default
if [ "`uname`" == "Linux" ]; then
cp "out/Default/$MKSNAPSHOT_PATH/libffmpeg.so" out/Default
elif [ "`uname`" == "Darwin" ]; then
cp "out/Default/$MKSNAPSHOT_PATH/libffmpeg.dylib" out/Default
fi
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --create-snapshot-only
mkdir cross-arch-snapshots
cp out/Default-mksnapshot-test/*.bin cross-arch-snapshots
@@ -763,8 +798,13 @@ step-maybe-trigger-arm-test: &step-maybe-trigger-arm-test
# Only run for non-fork prs
if [ "$TRIGGER_ARM_TEST" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then
#Trigger VSTS job, passing along CircleCI job number and branch to build
echo "Triggering electron-$TARGET_ARCH-testing build on VSTS"
node electron/script/release/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
if [ "`uname`" == "Darwin" ]; then
echo "Triggering electron-arm2-testing build on Azure DevOps"
node electron/script/release/ci-release-build.js --job=electron-arm2-testing --ci=DevOps --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
else
echo "Triggering electron-$TARGET_ARCH-testing build on VSTS"
node electron/script/release/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
fi
fi
step-maybe-generate-typescript-defs: &step-maybe-generate-typescript-defs
@@ -812,8 +852,13 @@ step-touch-sync-done: &step-touch-sync-done
step-maybe-restore-src-cache: &step-maybe-restore-src-cache
restore_cache:
keys:
- v7-src-cache-{{ checksum "src/electron/.depshash" }}
- v8-src-cache-{{ checksum "src/electron/.depshash" }}
name: Restoring src cache
step-maybe-restore-src-cache-marker: &step-maybe-restore-src-cache-marker
restore_cache:
keys:
- v1-src-cache-marker-{{ checksum "src/electron/.depshash" }}
name: Restoring src cache marker
# Restore exact or closest git cache based on the hash of DEPS and .circle-sync-done
# If the src cache was restored above then this will match an empty cache
@@ -890,9 +935,18 @@ step-minimize-workspace-size-from-checkout: &step-minimize-workspace-size-from-c
step-save-src-cache: &step-save-src-cache
save_cache:
paths:
- /portal
key: v7-src-cache-{{ checksum "/portal/src/electron/.depshash" }}
- /var/portal
key: v8-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
name: Persisting src cache
step-make-src-cache-marker: &step-make-src-cache-marker
run:
name: Making src cache marker
command: touch .src-cache-marker
step-save-src-cache-marker: &step-save-src-cache-marker
save_cache:
paths:
- .src-cache-marker
key: v1-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
# Check for doc only change
step-check-for-doc-only-change: &step-check-for-doc-only-change
@@ -998,7 +1052,8 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache
- *step-generate-deps-hash
- *step-touch-sync-done
- maybe-restore-portaled-src-cache
- maybe-restore-portaled-src-cache:
halt-if-successful: true
- *step-maybe-restore-git-cache
- *step-set-git-cache-path
# This sync call only runs if .circle-sync-done is an EMPTY file
@@ -1019,10 +1074,12 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache
- run:
name: Move src folder to the cross-OS portal
command: |
sudo mkdir -p /portal
sudo chown -R $(id -u):$(id -g) /portal
mv ./src /portal
sudo mkdir -p /var/portal
sudo chown -R $(id -u):$(id -g) /var/portal
mv ./src /var/portal
- *step-save-src-cache
- *step-make-src-cache-marker
- *step-save-src-cache-marker
steps-electron-gn-check: &steps-electron-gn-check
steps:
@@ -1043,8 +1100,8 @@ steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-cha
- *step-depot-tools-get
- *step-depot-tools-add-to-path
- *step-restore-brew-cache
- *step-get-more-space-on-mac
- *step-install-gnutar-on-mac
- *step-get-more-space-on-mac
- *step-generate-deps-hash
- *step-touch-sync-done
- maybe-restore-portaled-src-cache
@@ -1226,20 +1283,34 @@ chromium-upgrade-branches: &chromium-upgrade-branches
# Command Aliases
commands:
maybe-restore-portaled-src-cache:
parameters:
halt-if-successful:
type: boolean
default: false
steps:
- run:
name: Prepare for cross-OS sync restore
command: |
sudo mkdir -p /portal
sudo chown -R $(id -u):$(id -g) /portal
sudo mkdir -p /var/portal
sudo chown -R $(id -u):$(id -g) /var/portal
- when:
condition: << parameters.halt-if-successful >>
steps:
- *step-maybe-restore-src-cache-marker
- run:
name: Halt the job early if the src cache exists
command: |
if [ -f ".src-cache-marker" ]; then
circleci-agent step halt
fi
- *step-maybe-restore-src-cache
- run:
name: Fix the src cache restore point on macOS
command: |
if [ -d "/portal/src" ]; then
if [ -d "/var/portal/src" ]; then
echo Relocating Cache
rm -rf src
mv /portal/src ./
mv /var/portal/src ./
fi
checkout-from-cache:
steps:
@@ -1702,7 +1773,7 @@ jobs:
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: false
checkout: true
@@ -1714,7 +1785,7 @@ jobs:
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: true
checkout: false
@@ -1766,7 +1837,7 @@ jobs:
<<: *env-32bit-release
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: false
checkout: true
@@ -1780,7 +1851,7 @@ jobs:
<<: *env-32bit-release
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: true
checkout: false
@@ -1833,7 +1904,7 @@ jobs:
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-publish:
- electron-publish:
attach: false
checkout: true
@@ -1847,7 +1918,7 @@ jobs:
<<: *env-32bit-release
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: true
checkout: false
@@ -1907,7 +1978,7 @@ jobs:
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-publish:
- electron-publish:
attach: false
checkout: true
@@ -1920,11 +1991,11 @@ jobs:
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: true
checkout: false
osx-testing:
osx-testing-x64:
<<: *machine-mac-large
environment:
<<: *env-mac-large
@@ -1939,22 +2010,14 @@ jobs:
checkout-and-assume-cache: true
attach: false
osx-testing-gn-check:
osx-testing-x64-gn-check:
<<: *machine-mac
environment:
<<: *env-machine-mac
<<: *env-testing-build
<<: *steps-electron-gn-check
osx-chromedriver:
<<: *machine-mac
environment:
<<: *env-machine-mac
<<: *env-release-build
<<: *env-send-slack-notifications
<<: *steps-chromedriver-build
osx-release:
osx-release-x64:
<<: *machine-mac-large
environment:
<<: *env-mac-large
@@ -1968,7 +2031,7 @@ jobs:
checkout-and-assume-cache: true
attach: false
osx-publish:
osx-publish-x64:
<<: *machine-mac-large
environment:
<<: *env-mac-large-release
@@ -1977,11 +2040,25 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: false
checkout: true
osx-publish-skip-checkout:
osx-publish-arm64:
<<: *machine-mac-large-arm
environment:
<<: *env-mac-large-release
<<: *env-release-build
<<: *env-enable-sccache
<<: *env-apple-silicon
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
attach: false
checkout: true
osx-publish-x64-skip-checkout:
<<: *machine-mac-large
environment:
<<: *env-mac-large-release
@@ -1989,11 +2066,40 @@ jobs:
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: true
checkout: false
mas-testing:
osx-publish-arm64-skip-checkout:
<<: *machine-mac-large-arm
environment:
<<: *env-mac-large-release
<<: *env-release-build
<<: *env-enable-sccache
<<: *env-apple-silicon
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
attach: true
checkout: false
osx-testing-arm64:
<<: *machine-mac-large-arm
environment:
<<: *env-mac-large
<<: *env-testing-build
<<: *env-ninja-status
<<: *env-macos-build
<<: *env-apple-silicon
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
steps:
- electron-build:
persist: true
checkout: false
checkout-and-assume-cache: true
attach: false
mas-testing-x64:
<<: *machine-mac-large
environment:
<<: *env-mac-large
@@ -2009,7 +2115,7 @@ jobs:
checkout-and-assume-cache: true
attach: false
mas-testing-gn-check:
mas-testing-x64-gn-check:
<<: *machine-mac
environment:
<<: *env-machine-mac
@@ -2042,11 +2148,25 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: false
checkout: true
mas-publish-skip-checkout:
mas-publish-arm64:
<<: *machine-mac-large-arm
environment:
<<: *env-mac-large-release
<<: *env-mas-apple-silicon
<<: *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-publish:
attach: false
checkout: true
mas-publish-x64-skip-checkout:
<<: *machine-mac-large
environment:
<<: *env-mac-large-release
@@ -2055,10 +2175,39 @@ jobs:
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
- electron-publish:
attach: true
checkout: false
mas-publish-arm64-skip-checkout:
<<: *machine-mac-large-arm
environment:
<<: *env-mac-large-release
<<: *env-mas-apple-silicon
<<: *env-release-build
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
steps:
- electron-publish:
attach: true
checkout: false
mas-testing-arm64:
<<: *machine-mac-large-arm
environment:
<<: *env-mac-large
<<: *env-testing-build
<<: *env-ninja-status
<<: *env-macos-build
<<: *env-mas-apple-silicon
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
steps:
- electron-build:
persist: true
checkout: false
checkout-and-assume-cache: true
attach: false
# Layer 3: Tests.
linux-x64-unittests:
<<: *machine-linux-2xlarge
@@ -2182,7 +2331,7 @@ jobs:
<<: *env-send-slack-notifications
<<: *steps-verify-ffmpeg
osx-testing-tests:
osx-testing-x64-tests:
<<: *machine-mac-large
environment:
<<: *env-mac-large
@@ -2190,7 +2339,7 @@ jobs:
parallelism: 2
<<: *steps-tests
osx-release-tests:
osx-release-x64-tests:
<<: *machine-mac-large
environment:
<<: *env-mac-large
@@ -2205,7 +2354,7 @@ jobs:
<<: *env-send-slack-notifications
<<: *steps-verify-ffmpeg
mas-testing-tests:
mas-testing-x64-tests:
<<: *machine-mac-large
environment:
<<: *env-mac-large
@@ -2269,7 +2418,7 @@ jobs:
steps:
- *step-maybe-notify-slack-success
osx-release-summary:
osx-release-x64-summary:
<<: *machine-mac
environment:
<<: *env-machine-mac
@@ -2334,7 +2483,7 @@ workflows:
publish-osx:
when: << pipeline.parameters.run-osx-publish >>
jobs:
- osx-publish:
- osx-publish-x64:
context: release-env
publish-mas:
@@ -2343,14 +2492,32 @@ workflows:
- mas-publish:
context: release-env
publish-osx-arm64:
when: << pipeline.parameters.run-osx-publish-arm64 >>
jobs:
- osx-publish-arm64:
context: release-env
publish-mas-arm64:
when: << pipeline.parameters.run-mas-publish-arm64 >>
jobs:
- mas-publish-arm64:
context: release-env
publish-macos:
when: << pipeline.parameters.run-macos-publish >>
jobs:
- mac-checkout
- osx-publish-skip-checkout:
- osx-publish-x64-skip-checkout:
requires:
- mac-checkout
- mas-publish-skip-checkout:
- mas-publish-x64-skip-checkout:
requires:
- mac-checkout
- osx-publish-arm64-skip-checkout:
requires:
- mac-checkout
- mas-publish-arm64-skip-checkout:
requires:
- mac-checkout
@@ -2405,29 +2572,37 @@ workflows:
- mac-checkout-fast
- mac-checkout-and-save-cache
- osx-testing:
- osx-testing-x64:
requires:
- mac-checkout-and-save-cache
- osx-testing-gn-check:
- osx-testing-x64-gn-check:
requires:
- mac-checkout-fast
- osx-testing-tests:
- osx-testing-x64-tests:
requires:
- osx-testing
- osx-testing-x64
- mas-testing:
- osx-testing-arm64:
requires:
- mac-checkout-and-save-cache
- mas-testing-gn-check:
- mas-testing-x64:
requires:
- mac-checkout-and-save-cache
- mas-testing-x64-gn-check:
requires:
- mac-checkout-fast
- mas-testing-tests:
- mas-testing-x64-tests:
requires:
- mas-testing
- mas-testing-x64
- mas-testing-arm64:
requires:
- mac-checkout-and-save-cache
nightly-linux-release-test:
triggers:
@@ -2491,19 +2666,19 @@ workflows:
- mac-checkout-fast
- mac-checkout-and-save-cache
- osx-release:
- osx-release-x64:
requires:
- mac-checkout-and-save-cache
- osx-release-tests:
- osx-release-x64-tests:
requires:
- osx-release
- osx-release-x64
- osx-verify-ffmpeg:
requires:
- osx-release
- osx-release-summary:
- osx-release-x64
- osx-release-x64-summary:
requires:
- osx-release
- osx-release-tests
- osx-release-x64
- osx-release-x64-tests
- osx-verify-ffmpeg
- mas-release:

View File

@@ -28,10 +28,12 @@
},
"globals": {
"standardScheme": "readonly",
"globalThis": "readonly",
"BUILDFLAG": "readonly",
"ENABLE_DESKTOP_CAPTURER": "readonly",
"ENABLE_REMOTE_MODULE": "readonly",
"ENABLE_VIEWS_API": "readonly"
"ENABLE_VIEWS_API": "readonly",
"BigInt": "readonly"
},
"overrides": [
{

View File

@@ -94,6 +94,15 @@ npm_action("build_electron_definitions") {
outputs = [ "$target_gen_dir/tsc/typings/electron.d.ts" ]
}
webpack_build("electron_asar_bundle") {
deps = [ ":build_electron_definitions" ]
inputs = auto_filenames.asar_bundle_deps
config_file = "//electron/build/webpack/webpack.config.asar.js"
out_file = "$target_gen_dir/js2c/asar_bundle.js"
}
webpack_build("electron_browser_bundle") {
deps = [ ":build_electron_definitions" ]
@@ -139,25 +148,18 @@ webpack_build("electron_isolated_renderer_bundle") {
out_file = "$target_gen_dir/js2c/isolated_bundle.js"
}
copy("electron_js2c_copy") {
sources = [
"lib/common/asar.js",
"lib/common/asar_init.js",
]
outputs = [ "$target_gen_dir/js2c/{{source_file_part}}" ]
}
action("electron_js2c") {
deps = [
":electron_asar_bundle",
":electron_browser_bundle",
":electron_isolated_renderer_bundle",
":electron_js2c_copy",
":electron_renderer_bundle",
":electron_sandboxed_renderer_bundle",
":electron_worker_bundle",
]
webpack_sources = [
sources = [
"$target_gen_dir/js2c/asar_bundle.js",
"$target_gen_dir/js2c/browser_init.js",
"$target_gen_dir/js2c/isolated_bundle.js",
"$target_gen_dir/js2c/renderer_init.js",
@@ -165,11 +167,6 @@ action("electron_js2c") {
"$target_gen_dir/js2c/worker_init.js",
]
sources = webpack_sources + [
"$target_gen_dir/js2c/asar.js",
"$target_gen_dir/js2c/asar_init.js",
]
inputs = sources + [ "//third_party/electron_node/tools/js2c.py" ]
outputs = [ "$root_gen_dir/electron_natives.cc" ]
@@ -468,7 +465,7 @@ source_set("electron_lib") {
deps += [ "//third_party/crashpad/crashpad/client" ]
}
libs = [
frameworks = [
"AVFoundation.framework",
"Carbon.framework",
"LocalAuthentication.framework",
@@ -495,7 +492,7 @@ source_set("electron_lib") {
"shell/common/crash_keys.h",
]
} else {
libs += [
frameworks += [
"Squirrel.framework",
"ReactiveObjC.framework",
"Mantle.framework",
@@ -661,6 +658,7 @@ source_set("electron_lib") {
deps += [
"//components/pdf/browser",
"//components/pdf/renderer",
"//pdf:pdf_ppapi",
]
sources += [
"shell/browser/electron_pdf_web_contents_helper_client.cc",
@@ -805,10 +803,10 @@ if (is_mac) {
include_dirs = [ "." ]
sources = filenames.framework_sources
libs = []
frameworks = []
if (enable_osr) {
libs += [ "IOSurface.framework" ]
frameworks += [ "IOSurface.framework" ]
}
ldflags = [
@@ -917,7 +915,7 @@ if (is_mac) {
output_name = electron_login_helper_name
sources = filenames.login_helper_sources
include_dirs = [ "." ]
libs = [ "AppKit.framework" ]
frameworks = [ "AppKit.framework" ]
info_plist = "shell/app/resources/mac/loginhelper-Info.plist"
extra_substitutions =
[ "ELECTRON_BUNDLE_ID=$electron_mac_bundle_id.loginhelper" ]

9
DEPS
View File

@@ -8,14 +8,15 @@ gclient_gn_args = [
'checkout_pgo_profiles',
'checkout_oculus_sdk',
'checkout_openxr',
'checkout_google_benchmark'
'checkout_google_benchmark',
'mac_xcode_version',
]
vars = {
'chromium_version':
'9ae03ef8f7d4f6ac663f725bcfe70311987652f3',
'93a6ebbe22f1a093e6a0cb5e72ba78990fe39824',
'node_version':
'v12.18.2',
'v12.18.3',
'nan_version':
'2c4ee8a32a299eada3cd6e468bbd0a473bfea96d',
'squirrel.mac_version':
@@ -54,6 +55,8 @@ vars = {
# Python "requests" module is used for releases only.
'checkout_requests': False,
'mac_xcode_version': 'default',
# To allow running hooks without parsing the DEPS tree
'process_deps': True,

View File

@@ -1 +1 @@
11.0.0-nightly.20200709
11.0.0-nightly.20200731

View File

@@ -29,7 +29,7 @@
version: 1.0.{build}
build_cloud: electron-16-core
image: vs2019bt-16.4.0
image: vs2019bt-16.6.2
environment:
GIT_CACHE_PATH: C:\Users\electron\libcc_cache
ELECTRON_OUT_DIR: Default
@@ -61,6 +61,7 @@ build_script:
- git config --global core.longpaths true
- cd ..
- mkdir src
- update_depot_tools.bat
- ps: Move-Item $env:APPVEYOR_BUILD_FOLDER -Destination src\electron
- ps: $env:CHROMIUM_BUILDTOOLS_PATH="$pwd\src\buildtools"
- ps: $env:SCCACHE_PATH="$pwd\src\electron\external_binaries\sccache.exe"

104
azure-pipelines-arm.yml Normal file
View File

@@ -0,0 +1,104 @@
steps:
- task: CopyFiles@2
displayName: 'Copy Files to: src/electron'
inputs:
TargetFolder: src/electron
- bash: |
cd src/electron
node script/yarn.js install --frozen-lockfile
displayName: 'Yarn install'
- bash: |
export ZIP_DEST=$PWD/src/out/Default
echo "##vso[task.setvariable variable=ZIP_DEST]$ZIP_DEST"
mkdir -p $ZIP_DEST
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=dist.zip --dest=$ZIP_DEST
cd $ZIP_DEST
unzip -o dist.zip
displayName: 'Download and unzip dist files for test'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
export FFMPEG_ZIP_DEST=$PWD/src/out/ffmpeg
mkdir -p $FFMPEG_ZIP_DEST
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=ffmpeg.zip --dest=$FFMPEG_ZIP_DEST
cd $FFMPEG_ZIP_DEST
unzip -o ffmpeg.zip
displayName: 'Download and unzip ffmpeg for test'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
export NODE_HEADERS_DEST=$PWD/src/out/Default/gen
mkdir -p $NODE_HEADERS_DEST
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=node_headers.tar.gz --dest=$NODE_HEADERS_DEST
cd $NODE_HEADERS_DEST
tar xzf node_headers.tar.gz
displayName: 'Download and untar node header files for test'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
export CROSS_ARCH_SNAPSHOTS=$PWD/src/out/Default/cross-arch-snapshots
mkdir -p $CROSS_ARCH_SNAPSHOTS
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/snapshot_blob.bin --dest=$CROSS_ARCH_SNAPSHOTS
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/v8_context_snapshot.bin --dest=$CROSS_ARCH_SNAPSHOTS
displayName: 'Download cross arch snapshot files'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
export NATIVE_UNITTESTS_DEST=$PWD/src/out/Default
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=shell_browser_ui_unittests --dest=$NATIVE_UNITTESTS_DEST
chmod +x $NATIVE_UNITTESTS_DEST/shell_browser_ui_unittests
displayName: 'Download native unittest executables'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
cd src
export ELECTRON_OUT_DIR=Default
set npm_config_arch=arm64
(cd electron && node script/yarn test -- --enable-logging)
displayName: 'Run Electron tests'
timeoutInMinutes: 20
env:
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
IGNORE_YARN_INSTALL_ERROR: 1
ELECTRON_TEST_RESULTS_DIR: junit
- bash: |
cd src
python electron/script/verify-ffmpeg.py --source-root "$PWD" --build-dir out/Default --ffmpeg-path out/ffmpeg
displayName: Verify non proprietary ffmpeg
timeoutInMinutes: 5
condition: succeededOrFailed()
- bash: |
cd src
echo Verify cross arch snapshot
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --snapshot-files-dir $PWD/out/Default/cross-arch-snapshots
displayName: Verify cross arch snapshot
timeoutInMinutes: 5
condition: succeededOrFailed()
- task: PublishTestResults@2
displayName: 'Publish Test Results'
inputs:
testResultsFiles: '*.xml'
searchFolder: '$(System.DefaultWorkingDirectory)/src/junit/'
condition: succeededOrFailed()
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
displayName: 'Clean Agent Directories'
condition: always()

View File

@@ -1,2 +1,2 @@
process.env.PRINT_WEBPACK_GRAPH = true
require('./run-compiler')
process.env.PRINT_WEBPACK_GRAPH = true;
require('./run-compiler');

View File

@@ -1,25 +1,25 @@
const fs = require('fs');
const path = require('path')
const webpack = require('webpack')
const path = require('path');
const webpack = require('webpack');
const configPath = process.argv[2]
const outPath = path.resolve(process.argv[3])
const config = require(configPath)
const configPath = process.argv[2];
const outPath = path.resolve(process.argv[3]);
const config = require(configPath);
config.output = {
path: path.dirname(outPath),
filename: path.basename(outPath)
}
};
const { wrapInitWithProfilingTimeout } = config;
delete config.wrapInitWithProfilingTimeout;
webpack(config, (err, stats) => {
if (err) {
console.error(err)
process.exit(1)
console.error(err);
process.exit(1);
} else if (stats.hasErrors()) {
console.error(stats.toString('normal'))
process.exit(1)
console.error(stats.toString('normal'));
process.exit(1);
} else {
if (wrapInitWithProfilingTimeout) {
const contents = fs.readFileSync(outPath, 'utf8');
@@ -33,6 +33,6 @@ if ((globalThis.process || binding.process).argv.includes("--profile-electron-in
}`;
fs.writeFileSync(outPath, newContents);
}
process.exit(0)
process.exit(0);
}
})
});

View File

@@ -0,0 +1,5 @@
module.exports = require('./webpack.config.base')({
target: 'asar',
alwaysHasNode: true,
targetDeletesNodeGlobals: true
});

View File

@@ -1,39 +1,39 @@
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const electronRoot = path.resolve(__dirname, '../..')
const electronRoot = path.resolve(__dirname, '../..');
const onlyPrintingGraph = !!process.env.PRINT_WEBPACK_GRAPH
const onlyPrintingGraph = !!process.env.PRINT_WEBPACK_GRAPH;
class AccessDependenciesPlugin {
apply(compiler) {
apply (compiler) {
// Only hook into webpack when we are printing the dependency graph
if (!onlyPrintingGraph) return
if (!onlyPrintingGraph) return;
compiler.hooks.compilation.tap('AccessDependenciesPlugin', compilation => {
compilation.hooks.finishModules.tap('AccessDependenciesPlugin', modules => {
const filePaths = modules.map(m => m.resource).filter(p => p).map(p => path.relative(electronRoot, p))
console.info(JSON.stringify(filePaths))
})
})
const filePaths = modules.map(m => m.resource).filter(p => p).map(p => path.relative(electronRoot, p));
console.info(JSON.stringify(filePaths));
});
});
}
}
const defines = {
BUILDFLAG: onlyPrintingGraph ? '(a => a)' : ''
}
};
const buildFlagsPrefix = '--buildflags='
const buildFlagsPrefix = '--buildflags=';
const buildFlagArg = process.argv.find(arg => arg.startsWith(buildFlagsPrefix));
if (buildFlagArg) {
const buildFlagPath = buildFlagArg.substr(buildFlagsPrefix.length)
const buildFlagPath = buildFlagArg.substr(buildFlagsPrefix.length);
const flagFile = fs.readFileSync(buildFlagPath, 'utf8')
const flagFile = fs.readFileSync(buildFlagPath, 'utf8');
for (const line of flagFile.split(/(\r\n|\r|\n)/g)) {
const flagMatch = line.match(/#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/)
const flagMatch = line.match(/#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/);
if (flagMatch) {
const [, flagName, flagValue] = flagMatch;
defines[flagName] = JSON.stringify(Boolean(parseInt(flagValue, 10)));
@@ -41,27 +41,27 @@ if (buildFlagArg) {
}
}
const ignoredModules = []
const ignoredModules = [];
if (defines['ENABLE_DESKTOP_CAPTURER'] === 'false') {
if (defines.ENABLE_DESKTOP_CAPTURER === 'false') {
ignoredModules.push(
'@electron/internal/browser/desktop-capturer',
'@electron/internal/browser/api/desktop-capturer',
'@electron/internal/renderer/api/desktop-capturer'
)
);
}
if (defines['ENABLE_REMOTE_MODULE'] === 'false') {
if (defines.ENABLE_REMOTE_MODULE === 'false') {
ignoredModules.push(
'@electron/internal/browser/remote/server',
'@electron/internal/renderer/api/remote'
)
);
}
if (defines['ENABLE_VIEWS_API'] === 'false') {
if (defines.ENABLE_VIEWS_API === 'false') {
ignoredModules.push(
'@electron/internal/browser/api/views/image-view.js'
)
);
}
module.exports = ({
@@ -71,11 +71,13 @@ module.exports = ({
target,
wrapInitWithProfilingTimeout
}) => {
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts')
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts');
if (!fs.existsSync(entry)) {
entry = path.resolve(electronRoot, 'lib', target, 'init.js')
entry = path.resolve(electronRoot, 'lib', target, 'init.js');
}
const electronAPIFile = path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts');
return ({
mode: 'development',
devtool: false,
@@ -88,16 +90,19 @@ module.exports = ({
resolve: {
alias: {
'@electron/internal': path.resolve(electronRoot, 'lib'),
'electron': path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts'),
electron$: electronAPIFile,
'electron/main$': electronAPIFile,
'electron/renderer$': electronAPIFile,
'electron/common$': electronAPIFile,
// Force timers to resolve to our dependency that doesn't use window.postMessage
'timers': path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js')
timers: path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js')
},
extensions: ['.ts', '.js']
},
module: {
rules: [{
test: (moduleName) => !onlyPrintingGraph && ignoredModules.includes(moduleName),
loader: 'null-loader',
loader: 'null-loader'
}, {
test: /\.ts$/,
loader: 'ts-loader',
@@ -106,7 +111,7 @@ module.exports = ({
transpileOnly: onlyPrintingGraph,
ignoreDiagnostics: [
// File '{0}' is not under 'rootDir' '{1}'.
6059,
6059
]
}
}]
@@ -116,7 +121,7 @@ module.exports = ({
__filename: false,
// We provide our own "timers" import above, any usage of setImmediate inside
// one of our renderer bundles should import it from the 'timers' package
setImmediate: false,
setImmediate: false
},
optimization: {
minimize: true,
@@ -124,24 +129,24 @@ module.exports = ({
new TerserPlugin({
terserOptions: {
keep_classnames: true,
keep_fnames: true,
},
}),
],
keep_fnames: true
}
})
]
},
plugins: [
new AccessDependenciesPlugin(),
...(targetDeletesNodeGlobals ? [
new webpack.ProvidePlugin({
process: ['@electron/internal/renderer/webpack-provider', 'process'],
global: ['@electron/internal/renderer/webpack-provider', '_global'],
Buffer: ['@electron/internal/renderer/webpack-provider', 'Buffer'],
process: ['@electron/internal/common/webpack-provider', 'process'],
global: ['@electron/internal/common/webpack-provider', '_global'],
Buffer: ['@electron/internal/common/webpack-provider', 'Buffer']
})
] : []),
new webpack.ProvidePlugin({
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise'],
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise']
}),
new webpack.DefinePlugin(defines),
new webpack.DefinePlugin(defines)
]
})
}
});
};

View File

@@ -1,4 +1,4 @@
module.exports = require('./webpack.config.base')({
target: 'browser',
alwaysHasNode: true
})
});

View File

@@ -1,4 +1,4 @@
module.exports = require('./webpack.config.base')({
target: 'isolated_renderer',
alwaysHasNode: false
})
});

View File

@@ -3,4 +3,4 @@ module.exports = require('./webpack.config.base')({
alwaysHasNode: true,
targetDeletesNodeGlobals: true,
wrapInitWithProfilingTimeout: true
})
});

View File

@@ -1,5 +1,5 @@
module.exports = require('./webpack.config.base')({
target: 'sandboxed_renderer',
alwaysHasNode: false,
wrapInitWithProfilingTimeout: true,
})
wrapInitWithProfilingTimeout: true
});

View File

@@ -3,4 +3,4 @@ module.exports = require('./webpack.config.base')({
loadElectronFromAlternateTarget: 'renderer',
alwaysHasNode: true,
targetDeletesNodeGlobals: true
})
});

View File

@@ -32,6 +32,8 @@ PATHS_TO_SKIP = [
# On Linux, we don't use crashpad, but this binary is still built for some
# reason. Exclude it from the zip.
'./crashpad_handler',
'resources/inspector', #Skipping because these are outputs that we don't need
]
def skip_path(dep, dist_zip, target_cpu):

View File

@@ -12,6 +12,8 @@ import("//third_party/widevine/cdm/widevine.gni")
static_library("chrome") {
visibility = [ "//electron:electron_lib" ]
sources = [
"//chrome/browser/accessibility/accessibility_ui.cc",
"//chrome/browser/accessibility/accessibility_ui.h",
"//chrome/browser/browser_process.cc",
"//chrome/browser/browser_process.h",
"//chrome/browser/crash_upload_list/crash_upload_list_crashpad.cc",
@@ -62,18 +64,20 @@ static_library("chrome") {
"//extensions/browser/app_window/size_constraints.cc",
"//extensions/browser/app_window/size_constraints.h",
]
public_deps = [
"//chrome/common",
"//chrome/common:version_header",
"//components/keyed_service/content",
"//components/paint_preview/buildflags",
"//components/proxy_config",
"//components/security_state/content",
"//content/public/browser",
]
deps = [
"//chrome/browser:resource_prefetch_predictor_proto",
"//chrome/services/speech:buildflags",
"//components/feature_engagement:buildflags",
"//components/optimization_guide/proto:optimization_guide_proto",
]
@@ -93,6 +97,14 @@ static_library("chrome") {
]
}
if (is_win) {
sources += [
"//chrome/browser/win/icon_reader_service.cc",
"//chrome/browser/win/icon_reader_service.h",
]
public_deps += [ "//chrome/services/util_win:lib" ]
}
if (enable_desktop_capturer) {
sources += [
"//chrome/browser/media/webrtc/desktop_media_list.h",
@@ -153,13 +165,6 @@ static_library("chrome") {
}
}
if (enable_tts) {
sources += [
"//chrome/browser/speech/tts_controller_delegate_impl.cc",
"//chrome/browser/speech/tts_controller_delegate_impl.h",
]
}
if (enable_widevine) {
sources += [
"//chrome/renderer/media/chrome_key_systems.cc",
@@ -266,7 +271,7 @@ static_library("chrome") {
source_set("plugins") {
sources = []
deps = []
libs = []
frameworks = []
# browser side
sources += [
@@ -300,7 +305,7 @@ source_set("plugins") {
"//chrome/browser/renderer_host/pepper/monitor_finder_mac.h",
"//chrome/browser/renderer_host/pepper/monitor_finder_mac.mm",
]
libs += [ "CoreGraphics.framework" ]
frameworks += [ "CoreGraphics.framework" ]
}
if (is_linux) {
deps += [ "//components/services/font/public/cpp" ]
@@ -364,8 +369,8 @@ source_set("chrome_spellchecker") {
"//chrome/browser/spellchecker/spellcheck_factory.h",
"//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc",
"//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h",
"//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc",
"//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.h",
"//chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc",
"//chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.h",
"//chrome/browser/spellchecker/spellcheck_language_policy_handler.cc",
"//chrome/browser/spellchecker/spellcheck_language_policy_handler.h",
"//chrome/browser/spellchecker/spellcheck_service.cc",

View File

@@ -53,31 +53,31 @@
<nav>
<div class="linkcol">
<a class="hero-link" target="_blank" href="https://electronjs.org/blog">
<span class="octicon hero-octicon octicon-gist" aria-hidden="true"></span>
<span class="octicon hero-octicon octicon-code-square-24" aria-hidden="true"></span>
<h4>Blog</h4>
</a>
</div>
<div class="linkcol">
<a class="hero-link" target="_blank" href="https://github.com/electron/electron">
<span class="octicon hero-octicon octicon-mark-github" aria-hidden="true"></span>
<span class="octicon hero-octicon octicon-mark-github-16" aria-hidden="true"></span>
<h4>Repository</h4>
</a>
</div>
<div class="linkcol">
<a class="hero-link" target="_blank" href="https://electronjs.org/docs">
<span class="octicon hero-octicon octicon-gear" aria-hidden="true"></span>
<span class="octicon hero-octicon octicon-book-24" aria-hidden="true"></span>
<h4>Docs</h4>
</a>
</div>
<div class="linkcol">
<a class="hero-link" target="_blank" href="https://github.com/electron/electron-api-demos">
<span class="octicon hero-octicon octicon-star" aria-hidden="true"></span>
<span class="octicon hero-octicon octicon-star-fill-24" aria-hidden="true"></span>
<h4>API Demos</h4>
</a>
</div>
<div class="linkcol">
<a class="hero-link" target="_blank" href="https://electronforge.io">
<span class="octicon hero-octicon octicon-gift" aria-hidden="true"></span>
<span class="octicon hero-octicon octicon-gift-24" aria-hidden="true"></span>
<h4>Forge</h4>
</a>
</div>

View File

@@ -360,7 +360,7 @@ page.
Emitted whenever there is a GPU info update.
### Event: 'gpu-process-crashed'
### Event: 'gpu-process-crashed' _Deprecated_
Returns:
@@ -369,6 +369,11 @@ Returns:
Emitted when the GPU process crashes or is killed.
**Deprecated:** This event is superceded by the `child-process-gone` event
which contains more information about why the child process dissapeared. It
isn't always because it crashed. The `killed` boolean can be replaced by
checking `reason === 'killed'` when you switch to that event.
### Event: 'renderer-process-crashed' _Deprecated_
Returns:
@@ -403,6 +408,36 @@ Returns:
Emitted when the renderer process unexpectedly dissapears. This is normally
because it was crashed or killed.
#### Event: 'child-process-gone'
Returns:
* `event` Event
* `details` Object
* `type` String - Process type. One of the following values:
* `Utility`
* `Zygote`
* `Sandbox helper`
* `GPU`
* `Pepper Plugin`
* `Pepper Plugin Broker`
* `Unknown`
* `reason` String - The reason the child process is gone. Possible values:
* `clean-exit` - Process exited with an exit code of zero
* `abnormal-exit` - Process exited with a non-zero exit code
* `killed` - Process was sent a SIGTERM or otherwise killed externally
* `crashed` - Process crashed
* `oom` - Process ran out of memory
* `launch-failure` - Process never successfully launched
* `integrity-failure` - Windows code integrity checks failed
* `exitCode` Number - The exit code for the process
(e.g. status from waitpid if on posix, from GetExitCodeProcess on Windows).
* `name` String (optional) - The name of the process. i.e. for plugins it might be Flash.
Examples for utility: `Audio Service`, `Content Decryption Module Service`, `Network Service`, `Video Capture`, etc.
Emitted when the child process unexpectedly dissapears. This is normally
because it was crashed or killed. It does not include renderer processes.
### Event: 'accessibility-support-changed' _macOS_ _Windows_
Returns:
@@ -990,6 +1025,7 @@ if (!gotTheLock) {
// Create myWindow, load the rest of the app, etc...
app.whenReady().then(() => {
myWindow = createWindow()
})
}
```
@@ -1105,8 +1141,10 @@ For `infoType` equal to `complete`:
For `infoType` equal to `basic`:
Promise is fulfilled with `Object` containing fewer attributes than when requested with `complete`. Here's an example of basic response:
```js
{ auxAttributes:
{ amdSwitchable: true,
{
auxAttributes:
{
amdSwitchable: true,
canSupportThreadedTextureMailbox: false,
directComposition: false,
directRendering: true,
@@ -1119,12 +1157,14 @@ For `infoType` equal to `basic`:
sandboxed: false,
softwareRendering: false,
supportsOverlays: false,
videoDecodeAcceleratorFlags: 0 },
gpuDevice:
[ { active: true, deviceId: 26657, vendorId: 4098 },
{ active: false, deviceId: 3366, vendorId: 32902 } ],
machineModelName: 'MacBookPro',
machineModelVersion: '11.5' }
videoDecodeAcceleratorFlags: 0
},
gpuDevice:
[{ active: true, deviceId: 26657, vendorId: 4098 },
{ active: false, deviceId: 3366, vendorId: 32902 }],
machineModelName: 'MacBookPro',
machineModelVersion: '11.5'
}
```
Using `basic` should be preferred if only basic information like `vendorId` or `driverId` is needed.
@@ -1176,6 +1216,13 @@ Returns `Object`:
should restore the state from the previous session. This indicates that the
app should restore the windows that were open the last time the app was
closed. This setting is not available on [MAS builds][mas-builds].
* `executableWillLaunchAtLogin` Boolean _Windows_ - `true` if app is set to open at login and its run key is not deactivated. This differs from `openAtLogin` as it ignores the `args` option, this property will be true if the given executable would be launched at login with **any** arguments.
* `launchItems` Object[] _Windows_
* `name` String _Windows_ - name value of a registry entry.
* `path` String _Windows_ - The executable to an app that corresponds to a registry entry.
* `args` String[] _Windows_ - the command-line arguments to pass to the executable.
* `scope` String _Windows_ - one of `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
* `enabled` Boolean _Windows_ - `true` if the app registry key is startup approved and therfore shows as `enabled` in Task Manager and Windows settings.
### `app.setLoginItemSettings(settings)` _macOS_ _Windows_
@@ -1191,7 +1238,9 @@ Returns `Object`:
* `args` String[] (optional) _Windows_ - The command-line arguments to pass to
the executable. Defaults to an empty array. Take care to wrap paths in
quotes.
* `enabled` Boolean (optional) _Windows_ - `true` will change the startup approved registry key and `enable / disable` the App in Task Manager and Windows Settings.
Defaults to `true`.
* `name` String (optional) _Windows_ - value name to write into registry. Defaults to the app's AppUserModelId().
Set the app's login item settings.
To work with Electron's `autoUpdater` on Windows, which uses [Squirrel][Squirrel-Windows],

View File

@@ -104,7 +104,7 @@ The `autoUpdater` object has the following methods:
* `options` Object
* `url` String
* `headers` Record<String, String> (optional) _macOS_ - HTTP request headers.
* `serverType` String (optional) _macOS_ - Either `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
* `serverType` String (optional) _macOS_ - Can be `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
README for more information.
Sets the `url` and initialize the auto updater.

View File

@@ -28,25 +28,6 @@ view.webContents.loadURL('https://electronjs.org')
* `options` Object (optional)
* `webPreferences` Object (optional) - See [BrowserWindow](browser-window.md).
### Static Methods
#### `BrowserView.getAllViews()`
Returns `BrowserView[]` - An array of all opened BrowserViews.
#### `BrowserView.fromWebContents(webContents)`
* `webContents` [WebContents](web-contents.md)
Returns `BrowserView | null` - The BrowserView that owns the given `webContents`
or `null` if the contents are not owned by a BrowserView.
#### `BrowserView.fromId(id)`
* `id` Integer
Returns `BrowserView` - The view with the given `id`.
### Instance Properties
Objects created with `new BrowserView` have the following properties:
@@ -55,10 +36,6 @@ Objects created with `new BrowserView` have the following properties:
A [`WebContents`](web-contents.md) object owned by this view.
#### `view.id` _Experimental_
A `Integer` representing the unique ID of the view.
### Instance Methods
Objects created with `new BrowserView` have the following instance methods:

View File

@@ -38,7 +38,7 @@ the window after this event will have no visual flash:
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ show: false })
const win = new BrowserWindow({ show: false })
win.once('ready-to-show', () => {
win.show()
})
@@ -60,7 +60,7 @@ immediately, and use a `backgroundColor` close to your app's background:
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ backgroundColor: '#2e2c29' })
const win = new BrowserWindow({ backgroundColor: '#2e2c29' })
win.loadURL('https://github.com')
```
@@ -74,8 +74,8 @@ By using `parent` option, you can create child windows:
```javascript
const { BrowserWindow } = require('electron')
let top = new BrowserWindow()
let child = new BrowserWindow({ parent: top })
const top = new BrowserWindow()
const child = new BrowserWindow({ parent: top })
child.show()
top.show()
```
@@ -90,7 +90,7 @@ window, you have to set both `parent` and `modal` options:
```javascript
const { BrowserWindow } = require('electron')
let child = new BrowserWindow({ parent: top, modal: true, show: false })
const child = new BrowserWindow({ parent: top, modal: true, show: false })
child.loadURL('https://github.com')
child.once('ready-to-show', () => {
child.show()
@@ -348,6 +348,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
You can access this context in the dev tools by selecting the
'Electron Isolated Context' entry in the combo box at the top of the
Console tab.
* `worldSafeExecuteJavaScript` Boolean (optional) - If true, values returned from `webFrame.executeJavaScript` will be sanitized to ensure JS values
can't unsafely cross between worlds when using `contextIsolation`. The default
is `false`. In Electron 12, the default will be changed to `true`. _Deprecated_
* `nativeWindowOpen` Boolean (optional) - Whether to use native
`window.open()`. Defaults to `false`. Child windows will always have node
integration disabled unless `nodeIntegrationInSubFrames` is true. **Note:** This option is currently
@@ -597,7 +600,7 @@ e.g. `APPCOMMAND_BROWSER_BACKWARD` is emitted as `browser-backward`.
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.on('app-command', (e, cmd) => {
// Navigate the window back when the user hits their mouse back button
if (cmd === 'browser-backward' && win.webContents.canGoBack()) {
@@ -772,7 +775,7 @@ To check if a DevTools extension is installed you can run the following:
```javascript
const { BrowserWindow } = require('electron')
let installed = BrowserWindow.getDevToolsExtensions().hasOwnProperty('devtron')
const installed = 'devtron' in BrowserWindow.getDevToolsExtensions()
console.log(installed)
```
@@ -789,7 +792,7 @@ Objects created with `new BrowserWindow` have the following properties:
```javascript
const { BrowserWindow } = require('electron')
// In this example `win` is our instance
let win = new BrowserWindow({ width: 800, height: 600 })
const win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL('https://github.com')
```
@@ -1123,7 +1126,7 @@ Disable or enable the window.
#### `win.isEnabled()`
Returns Boolean - whether the window is enabled.
Returns `Boolean` - whether the window is enabled.
#### `win.setSize(width, height[, animate])`
@@ -1314,9 +1317,9 @@ a HTML-rendered toolbar. For example:
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
let toolbarRect = document.getElementById('toolbar').getBoundingClientRect()
const toolbarRect = document.getElementById('toolbar').getBoundingClientRect()
win.setSheetOffset(toolbarRect.height)
```
@@ -1440,7 +1443,7 @@ Node's [`url.format`](https://nodejs.org/api/url.html#url_url_format_urlobject)
method:
```javascript
let url = require('url').format({
const url = require('url').format({
protocol: 'file',
slashes: true,
pathname: require('path').join(__dirname, 'index.html')

View File

@@ -83,4 +83,4 @@ Returns `Promise<Object>` - Resolves with an object containing the `value` and `
Get the maximum usage across processes of trace buffer as a percentage of the
full state.
[trace viewer]: https://github.com/catapult-project/catapult/blob/master/tracing
[trace viewer]: https://chromium.googlesource.com/catapult/+/HEAD/tracing/README.md

View File

@@ -156,10 +156,16 @@ parameters in a renderer process will not result in those parameters being sent
with crashes that occur in other renderer processes or in the main process.
**Note:** Parameters have limits on the length of the keys and values. Key
names must be no longer than 39 bytes, and values must be no longer than 127
names must be no longer than 39 bytes, and values must be no longer than 20320
bytes. Keys with names longer than the maximum will be silently ignored. Key
values longer than the maximum length will be truncated.
**Note:** On linux values that are longer than 127 bytes will be chunked into
multiple keys, each 127 bytes in length. E.g. `addExtraParameter('foo', 'a'.repeat(130))`
will result in two chunked keys `foo__1` and `foo__2`, the first will contain
the first 127 bytes and the second will contain the remaining 3 bytes. On
your crash reporting backend you should stitch together keys in this format.
### `crashReporter.removeExtraParameter(key)`
* `key` String - Parameter key, must be no longer than 39 bytes.

View File

@@ -9,7 +9,7 @@ runtime that allows interacting with pages and instrumenting them.
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
try {
win.webContents.debugger.attach('1.1')

View File

@@ -11,7 +11,7 @@ control the download item.
```javascript
// In the main process.
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.webContents.session.on('will-download', (event, item, webContents) => {
// Set the save path, making Electron not to prompt a save dialog.
item.setSavePath('/tmp/save.pdf')

View File

@@ -15,7 +15,7 @@ To create a frameless window, you need to set `frame` to `false` in
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 600, frame: false })
const win = new BrowserWindow({ width: 800, height: 600, frame: false })
win.show()
```
@@ -33,7 +33,7 @@ Results in a hidden title bar and a full size content window, yet the title bar
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ titleBarStyle: 'hidden' })
const win = new BrowserWindow({ titleBarStyle: 'hidden' })
win.show()
```
@@ -43,7 +43,7 @@ Results in a hidden title bar with an alternative look where the traffic light b
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ titleBarStyle: 'hiddenInset' })
const win = new BrowserWindow({ titleBarStyle: 'hiddenInset' })
win.show()
```
@@ -58,7 +58,7 @@ This option is only applicable for frameless windows.
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ titleBarStyle: 'customButtonsOnHover', frame: false })
const win = new BrowserWindow({ titleBarStyle: 'customButtonsOnHover', frame: false })
win.show()
```
@@ -69,7 +69,7 @@ window transparent:
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ transparent: true, frame: false })
const win = new BrowserWindow({ transparent: true, frame: false })
win.show()
```
@@ -100,7 +100,7 @@ API:
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.setIgnoreMouseEvents(true)
```
@@ -112,8 +112,8 @@ optional parameter can be used to forward mouse move messages to the web page,
allowing events such as `mouseleave` to be emitted:
```javascript
let win = require('electron').remote.getCurrentWindow()
let el = document.getElementById('clickThroughElement')
const win = require('electron').remote.getCurrentWindow()
const el = document.getElementById('clickThroughElement')
el.addEventListener('mouseenter', () => {
win.setIgnoreMouseEvents(true, { forward: true })
})

View File

@@ -148,4 +148,4 @@ found in the [`ipc-main-invoke-event`](structures/ipc-main-invoke-event.md)
structure docs.
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
[web-contents-send]: web-contents.md#contentssendchannel-arg1-arg2-
[web-contents-send]: web-contents.md#contentssendchannel-args

View File

@@ -195,8 +195,8 @@ const template = [
{
label: 'Speech',
submenu: [
{ role: 'startspeaking' },
{ role: 'stopspeaking' }
{ role: 'startSpeaking' },
{ role: 'stopSpeaking' }
]
}
] : [
@@ -211,12 +211,12 @@ const template = [
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forcereload' },
{ role: 'toggledevtools' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetzoom' },
{ role: 'zoomin' },
{ role: 'zoomout' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' }
]

View File

@@ -17,7 +17,7 @@ renderer process:
```javascript
const { BrowserWindow } = require('electron').remote
let win = new BrowserWindow({ width: 800, height: 600 })
const win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL('https://github.com')
```

View File

@@ -33,8 +33,8 @@ const { app, BrowserWindow, screen } = require('electron')
let win
app.whenReady().then(() => {
let displays = screen.getAllDisplays()
let externalDisplay = displays.find((display) => {
const displays = screen.getAllDisplays()
const externalDisplay = displays.find((display) => {
return display.bounds.x !== 0 || display.bounds.y !== 0
})

View File

@@ -12,7 +12,7 @@ property of [`WebContents`](web-contents.md), or from the `session` module.
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 600 })
const win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL('http://github.com')
const ses = win.webContents.session
@@ -332,7 +332,7 @@ verify proc.
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.webContents.session.setCertificateVerifyProc((request, callback) => {
const { hostname } = request
@@ -348,8 +348,16 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
* `handler` Function | null
* `webContents` [WebContents](web-contents.md) - WebContents requesting the permission. Please note that if the request comes from a subframe you should use `requestingUrl` to check the request origin.
* `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex',
'pointerLock', 'fullscreen', 'openExternal'.
* `permission` String - The type of requested permission.
* `media` - Request access to media devices such as camera, microphone and speakers.
* `mediaKeySystem` - Request access to DRM protected content.
* `geolocation` - Request access to user's current location.
* `notifications` - Request notification creation and the ability to display them in the user's system tray.
* `midi` - Request MIDI access in the `webmidi` API.
* `midiSysex` - Request the use of system exclusive messages in the `webmidi` API.
* `pointerLock` - Request to directly interpret mouse movements as an input method. Click [here](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API) to know more.
* `fullscreen` - Request for the app to enter fullscreen mode.
* `openExternal` - Request to open links in external applications.
* `callback` Function
* `permissionGranted` Boolean - Allow or deny the permission.
* `details` Object - Some properties are only available on certain permission types.
@@ -655,7 +663,7 @@ const path = require('path')
app.whenReady().then(() => {
const protocol = session.fromPartition('some-partition').protocol
protocol.registerFileProtocol('atom', (request, callback) => {
let url = request.url.substr(7)
const url = request.url.substr(7)
callback({ path: path.normalize(`${__dirname}/${url}`) })
}, (error) => {
if (error) console.error('Failed to register protocol')

View File

@@ -1,4 +1,4 @@
# KeyboardEvent Object extends `Event`
# KeyboardEvent Object
* `ctrlKey` Boolean (optional) - whether the Control key was used in an accelerator to trigger the Event
* `metaKey` Boolean (optional) - whether a meta key was used in an accelerator to trigger the Event

View File

@@ -41,7 +41,7 @@ An example TraceConfig that roughly matches what Chrome DevTools records:
'disabled-by-default-v8.cpu_profiler',
'disabled-by-default-v8.cpu_profiler.hires'
],
excluded_categories: [ '*' ]
excluded_categories: ['*']
}
```

View File

@@ -209,7 +209,7 @@ not (transparent windows won't work correctly when DWM composition is disabled):
```javascript
const { BrowserWindow, systemPreferences } = require('electron')
let browserOptions = { width: 1000, height: 800 }
const browserOptions = { width: 1000, height: 800 }
// Make the window transparent only if the platform supports it.
if (process.platform !== 'win32' || systemPreferences.isAeroGlassEnabled()) {
@@ -218,7 +218,7 @@ if (process.platform !== 'win32' || systemPreferences.isAeroGlassEnabled()) {
}
// Create the window.
let win = new BrowserWindow(browserOptions)
const win = new BrowserWindow(browserOptions)
// Navigate.
if (browserOptions.transparent) {

View File

@@ -41,6 +41,10 @@ the button in the touch bar.
A `NativeImage` representing the button's current icon. Changing this value immediately updates the button
in the touch bar.
#### `touchBarButton.iconPosition`
A `String` - Can be `left`, `right` or `overlay`. Defaults to `overlay`.
#### `touchBarButton.enabled`
A `Boolean` representing whether the button is in an enabled state.

View File

@@ -49,3 +49,7 @@ updates the control in the touch bar. Updating deep properties inside this array
An `Integer` representing the currently selected segment. Changing this value immediately updates the control
in the touch bar. User interaction with the touch bar will update this value automatically.
#### `touchBarSegmentedControl.mode`
A `String` representing the current selection mode of the control. Can be `single`, `multiple` or `buttons`.

View File

@@ -11,3 +11,11 @@ Process: [Main](../tutorial/application-architecture.md#main-and-renderer-proces
* `small` - Small space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceSmall`. This is the default.
* `large` - Large space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceLarge`.
* `flexible` - Take up all available space. Maps to `NSTouchBarItemIdentifierFlexibleSpace`.
### Instance Properties
The following properties are available on instances of `TouchBarSpacer`:
#### `touchBarSpacer.size`
A `String` representing the size of the spacer. Can be `small`, `large` or `flexible`.

View File

@@ -12,10 +12,10 @@ the [`BrowserWindow`](browser-window.md) object. An example of accessing the
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 1500 })
const win = new BrowserWindow({ width: 800, height: 1500 })
win.loadURL('http://github.com')
let contents = win.webContents
const contents = win.webContents
console.log(contents)
```
@@ -418,7 +418,7 @@ To only prevent the menu shortcuts, use
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 600 })
const win = new BrowserWindow({ width: 800, height: 600 })
win.webContents.on('before-input-event', (event, input) => {
// For example, only enable application menu keyboard shortcuts when
@@ -665,7 +665,7 @@ app.whenReady().then(() => {
win = new BrowserWindow({ width: 800, height: 600 })
win.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
event.preventDefault()
let result = deviceList.find((device) => {
const result = deviceList.find((device) => {
return device.deviceName === 'test'
})
if (!result) {
@@ -691,7 +691,7 @@ buffer.
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ webPreferences: { offscreen: true } })
const win = new BrowserWindow({ webPreferences: { offscreen: true } })
win.webContents.on('paint', (event, dirty, image) => {
// updateBitmap(dirty, image.getBitmap())
})
@@ -907,7 +907,7 @@ Returns `String` - The URL of the current web page.
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 600 })
const win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL('http://github.com').then(() => {
const currentURL = win.webContents.getURL()
console.log(currentURL)
@@ -1311,6 +1311,8 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
* `success` Boolean - Indicates success of the print call.
* `failureReason` String - Error description called back if the print fails.
When a custom `pageSize` is passed, Chromium attempts to validate platform specific minumum values for `width_microns` and `height_microns`. Width and height must both be minimum 353 microns but may be higher on some operating systems.
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.
@@ -1319,7 +1321,14 @@ Use `page-break-before: always;` CSS style to force to print to a new page.
Example usage:
```js
const options = { silent: true, deviceName: 'My-Printer' }
const options = {
silent: true,
deviceName: 'My-Printer',
pageRanges: {
from: 0,
to: 1
}
}
win.webContents.print(options, (success, errorType) => {
if (!success) console.log(errorType)
})
@@ -1334,13 +1343,12 @@ win.webContents.print(options, (success, errorType) => {
* `landscape` Boolean (optional) - `true` for landscape, `false` for portrait.
* `marginsType` Integer (optional) - Specifies the type of margins to use. Uses 0 for
default margin, 1 for no margin, and 2 for minimum margin.
and `width` in microns.
* `scaleFactor` Number (optional) - The scale factor of the web page. Can range from 0 to 100.
* `pageRanges` Record<string, number> (optional) - The page range to print.
* `from` Number - the first page to print.
* `to` Number - the last page to print (inclusive).
* `from` Number - zero-based index of the first page to print.
* `to` Number - zero-based index of the last page to print (inclusive).
* `pageSize` String | Size (optional) - Specify page size of the generated PDF. Can be `A3`,
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height`
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height` and `width` in microns.
* `printBackground` Boolean (optional) - Whether to print CSS backgrounds.
* `printSelectionOnly` Boolean (optional) - Whether to print selection only.
@@ -1371,19 +1379,22 @@ An example of `webContents.printToPDF`:
```javascript
const { BrowserWindow } = require('electron')
const fs = require('fs')
const path = require('path')
const os = require('os')
let win = new BrowserWindow({ width: 800, height: 600 })
const win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL('http://github.com')
win.webContents.on('did-finish-load', () => {
// Use default printing options
win.webContents.printToPDF({}).then(data => {
fs.writeFile('/tmp/print.pdf', data, (error) => {
const pdfPath = path.join(os.homedir(), 'Desktop', 'temp.pdf')
fs.writeFile(pdfPath, data, (error) => {
if (error) throw error
console.log('Write PDF successfully.')
console.log(`Wrote PDF successfully to ${pdfPath}`)
})
}).catch(error => {
console.log(error)
console.log(`Failed to write PDF to ${pdfPath}: `, error)
})
})
```
@@ -1397,7 +1408,7 @@ creation:
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.webContents.on('devtools-opened', () => {
win.webContents.addWorkSpace(__dirname)
})
@@ -1718,7 +1729,7 @@ Returns `Promise<void>` - resolves if the page is saved.
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.loadURL('https://github.com')

View File

@@ -561,8 +561,8 @@ Stops any `findInPage` request for the `webview` with the provided `action`.
* `collate` Boolean (optional) - Whether the web page should be collated.
* `copies` Number (optional) - The number of copies of the web page to print.
* `pageRanges` Record<string, number> (optional) - The page range to print.
* `from` Number - the start page.
* `to` Number - the end page.
* `from` Number - zero-based index of the first page to print.
* `to` Number - zero-based index of the last page to print (inclusive).
* `duplexMode` String (optional) - Set the duplex mode of the printed web page. Can be `simplex`, `shortEdge`, or `longEdge`.
* `dpi` Record<string, number> (optional)
* `horizontal` Number (optional) - The horizontal dpi.
@@ -816,7 +816,7 @@ const { shell } = require('electron')
const webview = document.querySelector('webview')
webview.addEventListener('new-window', async (e) => {
const protocol = require('url').parse(e.url).protocol
const protocol = (new URL(e.url)).protocol
if (protocol === 'http:' || protocol === 'https:') {
await shell.openExternal(e.url)
}

View File

@@ -94,6 +94,6 @@ mainWindow.webContents.on('new-window', (event, url, frameName, disposition, opt
```javascript
// renderer process (mainWindow)
let modal = window.open('', 'modal')
const modal = window.open('', 'modal')
modal.document.write('<h1>Hello</h1>')
```

View File

@@ -266,7 +266,7 @@ const getGuestForWebContents = (webContentsId, contents) => {
throw new Error(`Invalid webContentsId: ${webContentsId}`)
}
if (guest.hostWebContents !== contents) {
throw new Error(`Access denied to webContents`)
throw new Error('Access denied to webContents')
}
return guest
}
@@ -322,7 +322,7 @@ powerMonitor.querySystemIdleState(threshold, callback)
const idleState = powerMonitor.getSystemIdleState(threshold)
```
### API Changed: `powerMonitor.querySystemIdleTime` is now `powerMonitor.getSystemIdleState`
### API Changed: `powerMonitor.querySystemIdleTime` is now `powerMonitor.getSystemIdleTime`
```js
// Removed in Electron 7.0
@@ -610,11 +610,11 @@ const { memory } = metrics[0] // Deprecated property
```js
// Deprecated
let optionsA = { webPreferences: { blinkFeatures: '' } }
let windowA = new BrowserWindow(optionsA)
const optionsA = { webPreferences: { blinkFeatures: '' } }
const windowA = new BrowserWindow(optionsA)
// Replace with
let optionsB = { webPreferences: { enableBlinkFeatures: '' } }
let windowB = new BrowserWindow(optionsB)
const optionsB = { webPreferences: { enableBlinkFeatures: '' } }
const windowB = new BrowserWindow(optionsB)
// Deprecated
window.on('app-command', (e, cmd) => {
@@ -785,11 +785,11 @@ The following list includes the breaking API changes made in Electron 2.0.
```js
// Deprecated
let optionsA = { titleBarStyle: 'hidden-inset' }
let windowA = new BrowserWindow(optionsA)
const optionsA = { titleBarStyle: 'hidden-inset' }
const windowA = new BrowserWindow(optionsA)
// Replace with
let optionsB = { titleBarStyle: 'hiddenInset' }
let windowB = new BrowserWindow(optionsB)
const optionsB = { titleBarStyle: 'hiddenInset' }
const windowB = new BrowserWindow(optionsB)
```
### `menu`

View File

@@ -15,7 +15,7 @@ calls, and other compiler optimizations. The only workaround is to build an
unoptimized local build.
The official symbol server URL for Electron is
https://electron-symbols.githubapp.com.
https://symbols.electronjs.org.
You cannot visit this URL directly, you must add it to the symbol path of your
debugging tool. In the examples below, a local cache directory is used to avoid
repeatedly fetching the PDB from the server. Replace `c:\code\symbols` with an
@@ -30,7 +30,7 @@ directory on your computer, if you'd prefer a different location for downloaded
symbols):
```powershell
SRV*c:\code\symbols\*https://electron-symbols.githubapp.com
SRV*c:\code\symbols\*https://symbols.electronjs.org
```
Set this string as `_NT_SYMBOL_PATH` in the environment, using the Windbg menus,
@@ -38,7 +38,7 @@ or by typing the `.sympath` command. If you would like to get symbols from
Microsoft's symbol server as well, you should list that first:
```powershell
SRV*c:\code\symbols\*https://msdl.microsoft.com/download/symbols;SRV*c:\code\symbols\*https://electron-symbols.githubapp.com
SRV*c:\code\symbols\*https://msdl.microsoft.com/download/symbols;SRV*c:\code\symbols\*https://symbols.electronjs.org
```
## Using the symbol server in Visual Studio

View File

@@ -107,7 +107,7 @@ To solve this, you can turn off node integration in Electron:
```javascript
// In the main process.
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: false
}
@@ -155,7 +155,7 @@ To achieve this goal, set the background in the constructor for [BrowserWindow][
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
const win = new BrowserWindow({
backgroundColor: '#fff'
})
```

View File

@@ -15,7 +15,7 @@ can open them programmatically by calling the `openDevTools()` API on the
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.webContents.openDevTools()
```

View File

@@ -70,7 +70,7 @@ fs.readdirSync('/path/to/example.asar')
Use a module from the archive:
```javascript
require('/path/to/example.asar/dir/module.js')
require('./path/to/example.asar/dir/module.js')
```
You can also display a web page in an `asar` archive with `BrowserWindow`:

View File

@@ -9,9 +9,9 @@ const childProcess = require('child_process')
const electronPath = require('electron')
// spawn the process
let env = { /* ... */ }
let stdio = ['inherit', 'inherit', 'inherit', 'ipc']
let appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
const env = { /* ... */ }
const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
// listen for IPC messages from the app
appProcess.on('message', (msg) => {
@@ -50,7 +50,7 @@ class TestDriver {
// handle rpc responses
this.process.on('message', (message) => {
// pop the handler
let rpcCall = this.rpcCalls[message.msgId]
const rpcCall = this.rpcCalls[message.msgId]
if (!rpcCall) return
this.rpcCalls[message.msgId] = null
// reject/resolve
@@ -70,7 +70,7 @@ class TestDriver {
// to use: driver.rpc('method', 1, 2, 3).then(...)
async rpc (cmd, ...args) {
// send rpc request
let msgId = this.rpcCalls.length
const msgId = this.rpcCalls.length
this.process.send({ msgId, cmd, args })
return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))
}
@@ -92,10 +92,10 @@ async function onMessage ({ msgId, cmd, args }) {
let method = METHODS[cmd]
if (!method) method = () => new Error('Invalid method: ' + cmd)
try {
let resolve = await method(...args)
const resolve = await method(...args)
process.send({ msgId, resolve })
} catch (err) {
let reject = {
const reject = {
message: err.message,
stack: err.stack,
name: err.name
@@ -119,7 +119,7 @@ Then, in your test suite, you can use your test-driver as follows:
const test = require('ava')
const electronPath = require('electron')
let app = new TestDriver({
const app = new TestDriver({
path: electronPath,
args: ['./app'],
env: {

View File

@@ -96,7 +96,7 @@ const { app, BrowserWindow } = require('electron')
function createWindow () {
// Create the browser window.
let win = new BrowserWindow({
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {

View File

@@ -37,18 +37,18 @@ inAppPurchase.on('transactions-updated', (event, transactions) => {
// Check each transaction.
transactions.forEach(function (transaction) {
let payment = transaction.payment
const payment = transaction.payment
switch (transaction.transactionState) {
case 'purchasing':
console.log(`Purchasing ${payment.productIdentifier}...`)
break
case 'purchased':
case 'purchased': {
console.log(`${payment.productIdentifier} purchased.`)
// Get the receipt url.
let receiptURL = inAppPurchase.getReceiptURL()
const receiptURL = inAppPurchase.getReceiptURL()
console.log(`Receipt URL: ${receiptURL}`)
@@ -62,6 +62,8 @@ inAppPurchase.on('transactions-updated', (event, transactions) => {
inAppPurchase.finishTransactionByDate(transaction.transactionDate)
break
}
case 'failed':
console.log(`Failed to purchase ${payment.productIdentifier}.`)
@@ -105,8 +107,8 @@ inAppPurchase.getProducts(PRODUCT_IDS).then(products => {
})
// Ask the user which product he/she wants to purchase.
let selectedProduct = products[0]
let selectedQuantity = 1
const selectedProduct = products[0]
const selectedQuantity = 1
// Purchase the selected product.
inAppPurchase.purchaseProduct(selectedProduct.productIdentifier, selectedQuantity).then(isProductValid => {

View File

@@ -10,7 +10,7 @@ so the `nodeIntegrationInWorker` option should be set to `true` in
`webPreferences`.
```javascript
let win = new BrowserWindow({
const win = new BrowserWindow({
webPreferences: {
nodeIntegrationInWorker: true
}
@@ -44,7 +44,7 @@ loads no native modules after the Web Workers get started.
process.dlopen = () => {
throw new Error('Load native module is not safe')
}
let worker = new Worker('script.js')
const worker = new Worker('script.js')
```
[web-workers]: https://developer.mozilla.org/en/docs/Web/API/Web_Workers_API/Using_web_workers

View File

@@ -10,7 +10,7 @@ you want to show Notifications in the main process please check out the
[Notification](../api/notification.md) module.
```javascript
let myNotification = new Notification('Title', {
const myNotification = new Notification('Title', {
body: 'Lorem Ipsum Dolor Sit Amet'
})

View File

@@ -39,6 +39,29 @@ will be started with the path of the file added as a command line argument.
## macOS Notes
### Adding the Recent Documents list to the application menu:
![macOS Recent Documents menu item][menu-item-image]
You can add menu items to access and clear recent documents by adding the following code snippet to your menu's template.
```json
{
"submenu":[
{
"label":"Open Recent",
"role":"recentdocuments",
"submenu":[
{
"label":"Clear Recent",
"role":"clearrecentdocuments"
}
]
}
]
}
```
When a file is requested from the recent documents menu, the `open-file` event
of `app` module will be emitted for it.
@@ -47,3 +70,4 @@ of `app` module will be emitted for it.
[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-macos-windows
[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-macos-windows
[app-registration]: https://msdn.microsoft.com/en-us/library/cc144104(VS.85).aspx
[menu-item-image]: https://user-images.githubusercontent.com/3168941/33003655-ea601c3a-cd70-11e7-97fa-7c062149cfb1.png

View File

@@ -42,7 +42,7 @@ app.commandLine.appendSwitch('ppapi-flash-path', path.join(__dirname, pluginName
app.commandLine.appendSwitch('ppapi-flash-version', '17.0.0.169')
app.whenReady().then(() => {
let win = new BrowserWindow({
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {

View File

@@ -146,7 +146,7 @@ const options = {
}
}
let client = webdriverio.remote(options)
const client = webdriverio.remote(options)
client
.init()

View File

@@ -149,7 +149,7 @@ To set the overlay icon for a window, you can use the
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.setOverlayIcon('path/to/overlay.png', 'Description for overlay')
```
@@ -168,7 +168,7 @@ To flash the BrowserWindow taskbar button, you can use the
```javascript
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
const win = new BrowserWindow()
win.once('focus', () => win.flashFrame(false))
win.flashFrame(true)
```

View File

@@ -54,6 +54,7 @@ template("electron_extra_paks") {
])
output = "${invoker.output_dir}/resources.pak"
sources = [
"$root_gen_dir/chrome/dev_ui_browser_resources.pak",
"$root_gen_dir/components/components_resources.pak",
"$root_gen_dir/content/browser/resources/media/media_internals_resources.pak",
"$root_gen_dir/content/browser/tracing/tracing_resources.pak",
@@ -67,6 +68,7 @@ template("electron_extra_paks") {
"$target_gen_dir/electron_resources.pak",
]
deps = [
"//chrome/browser:dev_ui_browser_resources",
"//components/resources",
"//content:content_resources",
"//content:dev_ui_content_resources",

View File

@@ -75,4 +75,9 @@
<message name="IDS_ACCEPT_LANGUAGES" use_name_for_id="true">
en-US,en
</message>
<if expr="is_win">
<message name="IDS_UTILITY_PROCESS_UTILITY_WIN_NAME" desc="The name of the utility process used to handle Windows utility operations.">
Windows Utilities
</message>
</if>
</grit-part>

View File

@@ -174,7 +174,7 @@ auto_filenames = {
]
isolated_bundle_deps = [
"lib/isolated_renderer/init.js",
"lib/isolated_renderer/init.ts",
"lib/renderer/web-view/web-view-constants.ts",
"lib/renderer/web-view/web-view-element.ts",
"package.json",
@@ -217,7 +217,7 @@ auto_filenames = {
"lib/browser/api/screen.ts",
"lib/browser/api/session.ts",
"lib/browser/api/system-preferences.ts",
"lib/browser/api/touch-bar.js",
"lib/browser/api/touch-bar.ts",
"lib/browser/api/tray.ts",
"lib/browser/api/view.ts",
"lib/browser/api/views/image-view.ts",
@@ -238,7 +238,6 @@ auto_filenames = {
"lib/browser/remote/objects-registry.ts",
"lib/browser/remote/server.ts",
"lib/browser/rpc-server.ts",
"lib/browser/utils.ts",
"lib/common/api/clipboard.ts",
"lib/common/api/deprecate.ts",
"lib/common/api/module-list.ts",
@@ -273,6 +272,7 @@ auto_filenames = {
"lib/common/type-utils.ts",
"lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/common/webpack-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.ts",
"lib/renderer/api/desktop-capturer.ts",
@@ -294,7 +294,6 @@ 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/webpack-provider.ts",
"lib/renderer/window-setup.ts",
"package.json",
"tsconfig.electron.json",
@@ -315,6 +314,7 @@ auto_filenames = {
"lib/common/reset-search-paths.ts",
"lib/common/type-utils.ts",
"lib/common/webpack-globals-provider.ts",
"lib/common/webpack-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.ts",
"lib/renderer/api/desktop-capturer.ts",
@@ -326,8 +326,18 @@ auto_filenames = {
"lib/renderer/ipc-renderer-internal-utils.ts",
"lib/renderer/ipc-renderer-internal.ts",
"lib/renderer/remote/callbacks-registry.ts",
"lib/renderer/webpack-provider.ts",
"lib/worker/init.js",
"lib/worker/init.ts",
"package.json",
"tsconfig.electron.json",
"tsconfig.json",
"typings/internal-ambient.d.ts",
"typings/internal-electron.d.ts",
]
asar_bundle_deps = [
"lib/asar/fs-wrapper.ts",
"lib/asar/init.ts",
"lib/common/webpack-provider.ts",
"package.json",
"tsconfig.electron.json",
"tsconfig.json",

View File

@@ -14,11 +14,11 @@ filenames = {
default_app_octicon_sources = [
"node_modules/@primer/octicons/build/build.css",
"node_modules/@primer/octicons/build/svg/gear.svg",
"node_modules/@primer/octicons/build/svg/gift.svg",
"node_modules/@primer/octicons/build/svg/gist.svg",
"node_modules/@primer/octicons/build/svg/mark-github.svg",
"node_modules/@primer/octicons/build/svg/star.svg",
"node_modules/@primer/octicons/build/svg/book-24.svg",
"node_modules/@primer/octicons/build/svg/code-square-24.svg",
"node_modules/@primer/octicons/build/svg/gift-24.svg",
"node_modules/@primer/octicons/build/svg/mark-github-16.svg",
"node_modules/@primer/octicons/build/svg/star-fill-24.svg",
]
lib_sources = [
@@ -81,7 +81,6 @@ filenames = {
"shell/browser/api/electron_api_native_theme.h",
"shell/browser/api/electron_api_native_theme_mac.mm",
"shell/browser/api/electron_api_net.cc",
"shell/browser/api/electron_api_net.h",
"shell/browser/api/electron_api_net_log.cc",
"shell/browser/api/electron_api_net_log.h",
"shell/browser/api/electron_api_notification.cc",
@@ -357,8 +356,6 @@ filenames = {
"shell/browser/ui/inspectable_web_contents.cc",
"shell/browser/ui/inspectable_web_contents.h",
"shell/browser/ui/inspectable_web_contents_delegate.h",
"shell/browser/ui/inspectable_web_contents_impl.cc",
"shell/browser/ui/inspectable_web_contents_impl.h",
"shell/browser/ui/inspectable_web_contents_view.h",
"shell/browser/ui/inspectable_web_contents_view_delegate.cc",
"shell/browser/ui/inspectable_web_contents_view_delegate.h",
@@ -401,6 +398,8 @@ filenames = {
"shell/browser/ui/views/submenu_button.h",
"shell/browser/ui/views/win_frame_view.cc",
"shell/browser/ui/views/win_frame_view.h",
"shell/browser/ui/webui/accessibility_ui.cc",
"shell/browser/ui/webui/accessibility_ui.h",
"shell/browser/ui/win/dialog_thread.cc",
"shell/browser/ui/win/dialog_thread.h",
"shell/browser/ui/win/electron_desktop_native_widget_aura.cc",

View File

@@ -10,6 +10,7 @@ hunspell_dictionaries = [
"//third_party/hunspell_dictionaries/en-AU-9-0.bdic",
"//third_party/hunspell_dictionaries/en-CA-9-0.bdic",
"//third_party/hunspell_dictionaries/en-GB-9-0.bdic",
"//third_party/hunspell_dictionaries/en-GB-oxendict-9-0.bdic",
"//third_party/hunspell_dictionaries/en-US-9-0.bdic",
"//third_party/hunspell_dictionaries/es-ES-3-0.bdic",
"//third_party/hunspell_dictionaries/et-EE-3-0.bdic",

805
lib/asar/fs-wrapper.ts Normal file
View File

@@ -0,0 +1,805 @@
import { Buffer } from 'buffer';
import * as path from 'path';
import * as util from 'util';
const asar = process._linkedBinding('electron_common_asar');
const v8Util = process._linkedBinding('electron_common_v8_util');
const Module = require('module');
const Promise: PromiseConstructor = global.Promise as any;
const envNoAsar = process.env.ELECTRON_NO_ASAR &&
process.type !== 'browser' &&
process.type !== 'renderer';
const isAsarDisabled = () => process.noAsar || envNoAsar;
const internalBinding = (process as any).internalBinding;
delete (process as any).internalBinding;
const nextTick = (functionToCall: Function, args: any[] = []) => {
process.nextTick(() => functionToCall(...args));
};
// Cache asar archive objects.
const cachedArchives = new Map<string, NodeJS.AsarArchive>();
const getOrCreateArchive = (archivePath: string) => {
const isCached = cachedArchives.has(archivePath);
if (isCached) {
return cachedArchives.get(archivePath);
}
const newArchive = asar.createArchive(archivePath);
if (!newArchive) return null;
cachedArchives.set(archivePath, newArchive);
return newArchive;
};
// Separate asar package's path from full path.
const splitPath = (archivePathOrBuffer: string | Buffer) => {
// Shortcut for disabled asar.
if (isAsarDisabled()) return { isAsar: <const>false };
// Check for a bad argument type.
let archivePath = archivePathOrBuffer;
if (Buffer.isBuffer(archivePathOrBuffer)) {
archivePath = archivePathOrBuffer.toString();
}
if (typeof archivePath !== 'string') return { isAsar: <const>false };
return asar.splitPath(path.normalize(archivePath));
};
// Convert asar archive's Stats object to fs's Stats object.
let nextInode = 0;
const uid = process.getuid != null ? process.getuid() : 0;
const gid = process.getgid != null ? process.getgid() : 0;
const fakeTime = new Date();
const asarStatsToFsStats = function (stats: NodeJS.AsarFileStat) {
const { Stats, constants } = require('fs');
let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR;
if (stats.isFile) {
mode ^= constants.S_IFREG;
} else if (stats.isDirectory) {
mode ^= constants.S_IFDIR;
} else if (stats.isLink) {
mode ^= constants.S_IFLNK;
}
return new Stats(
1, // dev
mode, // mode
1, // nlink
uid,
gid,
0, // rdev
undefined, // blksize
++nextInode, // ino
stats.size,
undefined, // blocks,
fakeTime.getTime(), // atim_msec
fakeTime.getTime(), // mtim_msec
fakeTime.getTime(), // ctim_msec
fakeTime.getTime() // birthtim_msec
);
};
const enum AsarError {
NOT_FOUND = 'NOT_FOUND',
NOT_DIR = 'NOT_DIR',
NO_ACCESS = 'NO_ACCESS',
INVALID_ARCHIVE = 'INVALID_ARCHIVE'
}
type AsarErrorObject = Error & { code?: string, errno?: number };
const createError = (errorType: AsarError, { asarPath, filePath }: { asarPath?: string, filePath?: string } = {}) => {
let error: AsarErrorObject;
switch (errorType) {
case AsarError.NOT_FOUND:
error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`);
error.code = 'ENOENT';
error.errno = -2;
break;
case AsarError.NOT_DIR:
error = new Error('ENOTDIR, not a directory');
error.code = 'ENOTDIR';
error.errno = -20;
break;
case AsarError.NO_ACCESS:
error = new Error(`EACCES: permission denied, access '${filePath}'`);
error.code = 'EACCES';
error.errno = -13;
break;
case AsarError.INVALID_ARCHIVE:
error = new Error(`Invalid package ${asarPath}`);
break;
default:
throw new Error(`Invalid error type "${errorType}" passed to createError.`);
}
return error;
};
const overrideAPISync = function (module: Record<string, any>, name: string, pathArgumentIndex?: number | null, fromAsync: boolean = false) {
if (pathArgumentIndex == null) pathArgumentIndex = 0;
const old = module[name];
const func = function (this: any, ...args: any[]) {
const pathArgument = args[pathArgumentIndex!];
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return old.apply(this, args);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
const newPath = archive.copyFileOut(filePath);
if (!newPath) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
args[pathArgumentIndex!] = newPath;
return old.apply(this, args);
};
if (fromAsync) {
return func;
}
module[name] = func;
};
const overrideAPI = function (module: Record<string, any>, name: string, pathArgumentIndex?: number | null) {
if (pathArgumentIndex == null) pathArgumentIndex = 0;
const old = module[name];
module[name] = function (this: any, ...args: any[]) {
const pathArgument = args[pathArgumentIndex!];
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return old.apply(this, args);
const { asarPath, filePath } = pathInfo;
const callback = args[args.length - 1];
if (typeof callback !== 'function') {
return overrideAPISync(module, name, pathArgumentIndex!, true)!.apply(this, args);
}
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const newPath = archive.copyFileOut(filePath);
if (!newPath) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
args[pathArgumentIndex!] = newPath;
return old.apply(this, args);
};
if (old[util.promisify.custom]) {
module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex);
}
if (module.promises && module.promises[name]) {
module.promises[name] = makePromiseFunction(module.promises[name], pathArgumentIndex);
}
};
const makePromiseFunction = function (orig: Function, pathArgumentIndex: number) {
return function (this: any, ...args: any[]) {
const pathArgument = args[pathArgumentIndex];
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return orig.apply(this, args);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
return Promise.reject(createError(AsarError.INVALID_ARCHIVE, { asarPath }));
}
const newPath = archive.copyFileOut(filePath);
if (!newPath) {
return Promise.reject(createError(AsarError.NOT_FOUND, { asarPath, filePath }));
}
args[pathArgumentIndex] = newPath;
return orig.apply(this, args);
};
};
// Override fs APIs.
export const wrapFsWithAsar = (fs: Record<string, any>) => {
const logFDs: Record<string, number> = {};
const logASARAccess = (asarPath: string, filePath: string, offset: number) => {
if (!process.env.ELECTRON_LOG_ASAR_READS) return;
if (!logFDs[asarPath]) {
const path = require('path');
const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt`;
const logPath = path.join(require('os').tmpdir(), logFilename);
logFDs[asarPath] = fs.openSync(logPath, 'a');
}
fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`);
};
const { lstatSync } = fs;
fs.lstatSync = (pathArgument: string, options: any) => {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return lstatSync(pathArgument, options);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
const stats = archive.stat(filePath);
if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
return asarStatsToFsStats(stats);
};
const { lstat } = fs;
fs.lstat = function (pathArgument: string, options: any, callback: any) {
const pathInfo = splitPath(pathArgument);
if (typeof options === 'function') {
callback = options;
options = {};
}
if (!pathInfo.isAsar) return lstat(pathArgument, options, callback);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const stats = archive.stat(filePath);
if (!stats) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
const fsStats = asarStatsToFsStats(stats);
nextTick(callback, [null, fsStats]);
};
fs.promises.lstat = util.promisify(fs.lstat);
const { statSync } = fs;
fs.statSync = (pathArgument: string, options: any) => {
const { isAsar } = splitPath(pathArgument);
if (!isAsar) return statSync(pathArgument, options);
// Do not distinguish links for now.
return fs.lstatSync(pathArgument, options);
};
const { stat } = fs;
fs.stat = (pathArgument: string, options: any, callback: any) => {
const { isAsar } = splitPath(pathArgument);
if (typeof options === 'function') {
callback = options;
options = {};
}
if (!isAsar) return stat(pathArgument, options, callback);
// Do not distinguish links for now.
process.nextTick(() => fs.lstat(pathArgument, options, callback));
};
fs.promises.stat = util.promisify(fs.stat);
const wrapRealpathSync = function (realpathSync: Function) {
return function (this: any, pathArgument: string, options: any) {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return realpathSync.apply(this, arguments);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
}
const fileRealPath = archive.realpath(filePath);
if (fileRealPath === false) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
}
return path.join(realpathSync(asarPath, options), fileRealPath);
};
};
const { realpathSync } = fs;
fs.realpathSync = wrapRealpathSync(realpathSync);
fs.realpathSync.native = wrapRealpathSync(realpathSync.native);
const wrapRealpath = function (realpath: Function) {
return function (this: any, pathArgument: string, options: any, callback: any) {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return realpath.apply(this, arguments);
const { asarPath, filePath } = pathInfo;
if (arguments.length < 3) {
callback = options;
options = {};
}
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const fileRealPath = archive.realpath(filePath);
if (fileRealPath === false) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
realpath(asarPath, options, (error: Error | null, archiveRealPath: string) => {
if (error === null) {
const fullPath = path.join(archiveRealPath, fileRealPath);
callback(null, fullPath);
} else {
callback(error);
}
});
};
};
const { realpath } = fs;
fs.realpath = wrapRealpath(realpath);
fs.realpath.native = wrapRealpath(realpath.native);
fs.promises.realpath = util.promisify(fs.realpath.native);
const { exists } = fs;
fs.exists = (pathArgument: string, callback: any) => {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return exists(pathArgument, callback);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const pathExists = (archive.stat(filePath) !== false);
nextTick(callback, [pathExists]);
};
fs.exists[util.promisify.custom] = (pathArgument: string) => {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return exists[util.promisify.custom](pathArgument);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
return Promise.reject(error);
}
return Promise.resolve(archive.stat(filePath) !== false);
};
const { existsSync } = fs;
fs.existsSync = (pathArgument: string) => {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return existsSync(pathArgument);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) return false;
return archive.stat(filePath) !== false;
};
const { access } = fs;
fs.access = function (pathArgument: string, mode: any, callback: any) {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return access.apply(this, arguments);
const { asarPath, filePath } = pathInfo;
if (typeof mode === 'function') {
callback = mode;
mode = fs.constants.F_OK;
}
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const info = archive.getFileInfo(filePath);
if (!info) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.access(realPath, mode, callback);
}
const stats = archive.stat(filePath);
if (!stats) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
if (mode & fs.constants.W_OK) {
const error = createError(AsarError.NO_ACCESS, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
nextTick(callback);
};
fs.promises.access = util.promisify(fs.access);
const { accessSync } = fs;
fs.accessSync = function (pathArgument: string, mode: any) {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return accessSync.apply(this, arguments);
const { asarPath, filePath } = pathInfo;
if (mode == null) mode = fs.constants.F_OK;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
}
const info = archive.getFileInfo(filePath);
if (!info) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
}
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.accessSync(realPath, mode);
}
const stats = archive.stat(filePath);
if (!stats) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
}
if (mode & fs.constants.W_OK) {
throw createError(AsarError.NO_ACCESS, { asarPath, filePath });
}
};
const { readFile } = fs;
fs.readFile = function (pathArgument: string, options: any, callback: any) {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return readFile.apply(this, arguments);
const { asarPath, filePath } = pathInfo;
if (typeof options === 'function') {
callback = options;
options = { encoding: null };
} else if (typeof options === 'string') {
options = { encoding: options };
} else if (options === null || options === undefined) {
options = { encoding: null };
} else if (typeof options !== 'object') {
throw new TypeError('Bad arguments');
}
const { encoding } = options;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const info = archive.getFileInfo(filePath);
if (!info) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
if (info.size === 0) {
nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]);
return;
}
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.readFile(realPath, options, callback);
}
const buffer = Buffer.alloc(info.size);
const fd = archive.getFd();
if (!(fd >= 0)) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
logASARAccess(asarPath, filePath, info.offset);
fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => {
callback(error, encoding ? buffer.toString(encoding) : buffer);
});
};
fs.promises.readFile = util.promisify(fs.readFile);
const { readFileSync } = fs;
fs.readFileSync = function (pathArgument: string, options: any) {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return readFileSync.apply(this, arguments);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
const info = archive.getFileInfo(filePath);
if (!info) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
if (info.size === 0) return (options) ? '' : Buffer.alloc(0);
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.readFileSync(realPath, options);
}
if (!options) {
options = { encoding: null };
} else if (typeof options === 'string') {
options = { encoding: options };
} else if (typeof options !== 'object') {
throw new TypeError('Bad arguments');
}
const { encoding } = options;
const buffer = Buffer.alloc(info.size);
const fd = archive.getFd();
if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
logASARAccess(asarPath, filePath, info.offset);
fs.readSync(fd, buffer, 0, info.size, info.offset);
return (encoding) ? buffer.toString(encoding) : buffer;
};
const { readdir } = fs;
fs.readdir = function (pathArgument: string, options: { encoding?: string | null; withFileTypes?: boolean } = {}, callback?: Function) {
const pathInfo = splitPath(pathArgument);
if (typeof options === 'function') {
callback = options;
options = {};
}
if (!pathInfo.isAsar) return readdir.apply(this, arguments);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback!, [error]);
return;
}
const files = archive.readdir(filePath);
if (!files) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback!, [error]);
return;
}
if (options.withFileTypes) {
const dirents = [];
for (const file of files) {
const stats = archive.stat(file);
if (!stats) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath: file });
nextTick(callback!, [error]);
return;
}
if (stats.isFile) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
} else if (stats.isDirectory) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
} else if (stats.isLink) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
}
}
nextTick(callback!, [null, dirents]);
return;
}
nextTick(callback!, [null, files]);
};
fs.promises.readdir = util.promisify(fs.readdir);
const { readdirSync } = fs;
fs.readdirSync = function (pathArgument: string, options: { encoding: BufferEncoding | null; withFileTypes?: false } | BufferEncoding | null) {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return readdirSync.apply(this, arguments);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
}
const files = archive.readdir(filePath);
if (!files) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
}
if (options && (options as any).withFileTypes) {
const dirents = [];
for (const file of files) {
const stats = archive.stat(file);
if (!stats) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath: file });
}
if (stats.isFile) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
} else if (stats.isDirectory) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
} else if (stats.isLink) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
}
}
return dirents;
}
return files;
};
const { internalModuleReadJSON } = internalBinding('fs');
internalBinding('fs').internalModuleReadJSON = (pathArgument: string) => {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return internalModuleReadJSON(pathArgument);
const { asarPath, filePath } = pathInfo;
const archive = getOrCreateArchive(asarPath);
if (!archive) return [];
const info = archive.getFileInfo(filePath);
if (!info) return [];
if (info.size === 0) return ['', false];
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.readFileSync(realPath, { encoding: 'utf8' });
}
const buffer = Buffer.alloc(info.size);
const fd = archive.getFd();
if (!(fd >= 0)) return [];
logASARAccess(asarPath, filePath, info.offset);
fs.readSync(fd, buffer, 0, info.size, info.offset);
const str = buffer.toString('utf8');
return [str, str.length > 0];
};
const { internalModuleStat } = internalBinding('fs');
internalBinding('fs').internalModuleStat = (pathArgument: string) => {
const pathInfo = splitPath(pathArgument);
if (!pathInfo.isAsar) return internalModuleStat(pathArgument);
const { asarPath, filePath } = pathInfo;
// -ENOENT
const archive = getOrCreateArchive(asarPath);
if (!archive) return -34;
// -ENOENT
const stats = archive.stat(filePath);
if (!stats) return -34;
return (stats.isDirectory) ? 1 : 0;
};
// Calling mkdir for directory inside asar archive should throw ENOTDIR
// error, but on Windows it throws ENOENT.
if (process.platform === 'win32') {
const { mkdir } = fs;
fs.mkdir = (pathArgument: string, options: any, callback: any) => {
if (typeof options === 'function') {
callback = options;
options = {};
}
const pathInfo = splitPath(pathArgument);
if (pathInfo.isAsar && pathInfo.filePath.length > 0) {
const error = createError(AsarError.NOT_DIR);
nextTick(callback, [error]);
return;
}
mkdir(pathArgument, options, callback);
};
fs.promises.mkdir = util.promisify(fs.mkdir);
const { mkdirSync } = fs;
fs.mkdirSync = function (pathArgument: string, options: any) {
const pathInfo = splitPath(pathArgument);
if (pathInfo.isAsar && pathInfo.filePath.length) throw createError(AsarError.NOT_DIR);
return mkdirSync(pathArgument, options);
};
}
function invokeWithNoAsar (func: Function) {
return function (this: any) {
const processNoAsarOriginalValue = process.noAsar;
process.noAsar = true;
try {
return func.apply(this, arguments);
} finally {
process.noAsar = processNoAsarOriginalValue;
}
};
}
// Strictly implementing the flags of fs.copyFile is hard, just do a simple
// implementation for now. Doing 2 copies won't spend much time more as OS
// has filesystem caching.
overrideAPI(fs, 'copyFile');
overrideAPISync(fs, 'copyFileSync');
overrideAPI(fs, 'open');
overrideAPISync(process, 'dlopen', 1);
overrideAPISync(Module._extensions, '.node', 1);
overrideAPISync(fs, 'openSync');
const overrideChildProcess = (childProcess: Record<string, any>) => {
// Executing a command string containing a path to an asar archive
// confuses `childProcess.execFile`, which is internally called by
// `childProcess.{exec,execSync}`, causing Electron to consider the full
// command as a single path to an archive.
const { exec, execSync } = childProcess;
childProcess.exec = invokeWithNoAsar(exec);
childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]);
childProcess.execSync = invokeWithNoAsar(execSync);
overrideAPI(childProcess, 'execFile');
overrideAPISync(childProcess, 'execFileSync');
};
// Lazily override the child_process APIs only when child_process is
// fetched the first time. We will eagerly override the child_process APIs
// when this env var is set so that stack traces generated inside node unit
// tests will match. This env var will only slow things down in users apps
// and should not be used.
if (process.env.ELECTRON_EAGER_ASAR_HOOK_FOR_TESTING) {
overrideChildProcess(require('child_process'));
} else {
const originalModuleLoad = Module._load;
Module._load = (request: string, ...args: any[]) => {
const loadResult = originalModuleLoad(request, ...args);
if (request === 'child_process') {
if (!v8Util.getHiddenValue(loadResult, 'asar-ready')) {
v8Util.setHiddenValue(loadResult, 'asar-ready', true);
// Just to make it obvious what we are dealing with here
const childProcess = loadResult;
overrideChildProcess(childProcess);
}
}
return loadResult;
};
}
};

3
lib/asar/init.ts Normal file
View File

@@ -0,0 +1,3 @@
import { wrapFsWithAsar } from './fs-wrapper';
wrapFsWithAsar(require('fs'));

View File

@@ -0,0 +1,21 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/renderer"
],
"patterns": [
"./*",
"../*",
"@electron/internal/isolated_renderer/*",
"@electron/internal/renderer/*",
"@electron/internal/sandboxed_worker/*",
"@electron/internal/worker/*"
]
}
]
}
}

View File

@@ -1,41 +1,36 @@
import * as fs from 'fs';
import * as path from 'path';
import { deprecate, Menu } from 'electron';
import { EventEmitter } from 'events';
import { deprecate, Menu } from 'electron/main';
const bindings = process._linkedBinding('electron_browser_app');
const commandLine = process._linkedBinding('electron_common_command_line');
const { app, App } = bindings;
const { app } = bindings;
// Only one app object permitted.
export default app;
let dockMenu: Electron.Menu | null = null;
// App is an EventEmitter.
Object.setPrototypeOf(App.prototype, EventEmitter.prototype);
EventEmitter.call(app as any);
// Properties.
const nativeASGetter = app.isAccessibilitySupportEnabled;
const nativeASSetter = app.setAccessibilitySupportEnabled;
Object.defineProperty(App.prototype, 'accessibilitySupportEnabled', {
Object.defineProperty(app, 'accessibilitySupportEnabled', {
get: () => nativeASGetter.call(app),
set: (enabled) => nativeASSetter.call(app, enabled)
});
const nativeBCGetter = app.getBadgeCount;
const nativeBCSetter = app.setBadgeCount;
Object.defineProperty(App.prototype, 'badgeCount', {
Object.defineProperty(app, 'badgeCount', {
get: () => nativeBCGetter.call(app),
set: (count) => nativeBCSetter.call(app, count)
});
const nativeNGetter = app.getName;
const nativeNSetter = app.setName;
Object.defineProperty(App.prototype, 'name', {
Object.defineProperty(app, 'name', {
get: () => nativeNGetter.call(app),
set: (name) => nativeNSetter.call(app, name)
});
@@ -60,7 +55,7 @@ Object.defineProperty(app, 'applicationMenu', {
}
});
App.prototype.isPackaged = (() => {
(app as any).isPackaged = (() => {
const execFile = path.basename(process.execPath).toLowerCase();
if (process.platform === 'win32') {
return execFile !== 'electron.exe';
@@ -137,4 +132,4 @@ for (const name of events) {
// Deprecate allowRendererProcessReuse but only if they set it to false, no need to log if
// they are setting it to true
deprecate.removeProperty(app, 'allowRendererProcessReuse', [false]);
deprecate.removeProperty({ __proto__: app } as any, 'allowRendererProcessReuse', [false]);

View File

@@ -1,8 +1,3 @@
import { EventEmitter } from 'events';
const { autoUpdater, AutoUpdater } = process._linkedBinding('electron_browser_auto_updater');
// AutoUpdater is an EventEmitter.
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
EventEmitter.call(autoUpdater);
const { autoUpdater } = process._linkedBinding('electron_browser_auto_updater');
export default autoUpdater;

View File

@@ -1,6 +1,6 @@
import { app } from 'electron';
import { app } from 'electron/main';
import { EventEmitter } from 'events';
import * as squirrelUpdate from './squirrel-update-win';
import * as squirrelUpdate from '@electron/internal/browser/api/auto-updater/squirrel-update-win';
class AutoUpdater extends EventEmitter {
updateAvailable: boolean = false;

View File

@@ -1,5 +1,5 @@
import { EventEmitter } from 'events';
import type { BaseWindow as TLWT } from 'electron';
import type { BaseWindow as TLWT } from 'electron/main';
const { BaseWindow } = process._linkedBinding('electron_browser_base_window') as { BaseWindow: typeof TLWT };
Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);

View File

@@ -1,15 +1,3 @@
import { EventEmitter } from 'events';
const { BrowserView } = process._linkedBinding('electron_browser_browser_view');
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
BrowserView.fromWebContents = (webContents: Electron.WebContents) => {
for (const view of BrowserView.getAllViews()) {
if (view.webContents.equal(webContents)) return view;
}
return null;
};
export default BrowserView;

View File

@@ -1,5 +1,5 @@
import { BaseWindow, WebContents, Event, BrowserView, TouchBar } from 'electron';
import type { BrowserWindow as BWT } from 'electron';
import { BaseWindow, WebContents, Event, BrowserView, TouchBar } from 'electron/main';
import type { BrowserWindow as BWT } from 'electron/main';
const { BrowserWindow } = process._linkedBinding('electron_browser_window') as { BrowserWindow: typeof BWT };
Object.setPrototypeOf(BrowserWindow.prototype, BaseWindow.prototype);

View File

@@ -1,4 +1,4 @@
import { app, deprecate } from 'electron';
import { app, deprecate } from 'electron/main';
const binding = process._linkedBinding('electron_browser_crash_reporter');

View File

@@ -1,5 +1,5 @@
import { app, BrowserWindow } from 'electron';
import { OpenDialogOptions, OpenDialogReturnValue, MessageBoxOptions, SaveDialogOptions, SaveDialogReturnValue, MessageBoxReturnValue, CertificateTrustDialogOptions } from 'electron/main';
import { app, BrowserWindow } from 'electron/main';
import type { OpenDialogOptions, OpenDialogReturnValue, MessageBoxOptions, SaveDialogOptions, SaveDialogReturnValue, MessageBoxReturnValue, CertificateTrustDialogOptions } from 'electron/main';
const dialogBinding = process._linkedBinding('electron_browser_dialog');
const DialogType = {

View File

@@ -3,11 +3,7 @@ import { EventEmitter } from 'events';
let _inAppPurchase;
if (process.platform === 'darwin') {
const { inAppPurchase, InAppPurchase } = process._linkedBinding('electron_browser_in_app_purchase');
// inAppPurchase is an EventEmitter.
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype);
EventEmitter.call(inAppPurchase);
const { inAppPurchase } = process._linkedBinding('electron_browser_in_app_purchase');
_inAppPurchase = inAppPurchase;
} else {

View File

@@ -1,4 +1,4 @@
import { app, BrowserWindow, WebContents, MenuItemConstructorOptions } from 'electron';
import { app, BrowserWindow, WebContents, MenuItemConstructorOptions } from 'electron/main';
const isMac = process.platform === 'darwin';
const isWindows = process.platform === 'win32';

View File

@@ -1,5 +1,5 @@
import * as roles from './menu-item-roles';
import { Menu, Event, BrowserWindow, WebContents } from 'electron';
import * as roles from '@electron/internal/browser/api/menu-item-roles';
import { Menu, Event, BrowserWindow, WebContents } from 'electron/main';
let nextCommandId = 0;

View File

@@ -1,5 +1,5 @@
import { BaseWindow, MenuItem, webContents, Menu as MenuType, BrowserWindow, MenuItemConstructorOptions } from 'electron';
import { sortMenuItems } from './menu-utils';
import { BaseWindow, MenuItem, webContents, Menu as MenuType, BrowserWindow, MenuItemConstructorOptions } from 'electron/main';
import { sortMenuItems } from '@electron/internal/browser/api/menu-utils';
const v8Util = process._linkedBinding('electron_common_v8_util');
const bindings = process._linkedBinding('electron_browser_menu');

View File

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

View File

@@ -1,6 +1,6 @@
// TODO(deepak1556): Deprecate and remove standalone netLog module,
// it is now a property of session module.
import { app, session } from 'electron';
import { app, session } from 'electron/main';
const startLogging: typeof session.defaultSession.netLog.startLogging = async (path, options) => {
if (!app.isReady()) return;

View File

@@ -1,10 +1,8 @@
import * as url from 'url';
import { Readable, Writable } from 'stream';
import { app } from 'electron';
import { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
import { app } from 'electron/main';
import type { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
const {
net,
Net,
isValidHeaderName,
isValidHeaderValue,
createURLLoader
@@ -134,6 +132,7 @@ class SlurpStream extends Writable {
this._data = Buffer.concat([this._data, chunk]);
callback();
}
data () { return this._data; }
}
@@ -271,7 +270,7 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
return urlLoaderOptions;
}
class ClientRequest extends Writable implements Electron.ClientRequest {
export class ClientRequest extends Writable implements Electron.ClientRequest {
_started: boolean = false;
_firstWrite: boolean = false;
_aborted: boolean = false;
@@ -494,10 +493,6 @@ class ClientRequest extends Writable implements Electron.ClientRequest {
}
}
Net.prototype.request = function (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
export function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
return new ClientRequest(options, callback);
};
net.ClientRequest = ClientRequest;
module.exports = net;
}

View File

@@ -1,5 +1,5 @@
import { EventEmitter } from 'events';
import { app } from 'electron';
import { app } from 'electron/main';
const {
createPowerMonitor,

View File

@@ -1,4 +1,4 @@
import { app, session } from 'electron';
import { app, session } from 'electron/main';
// Global protocol APIs.
const protocol = process._linkedBinding('electron_browser_protocol');

View File

@@ -1,10 +1,20 @@
'use strict';
const { createScreen } = process._linkedBinding('electron_common_screen');
import { createLazyInstance } from '../utils';
const { EventEmitter } = require('events');
const { Screen, createScreen } = process._linkedBinding('electron_common_screen');
let _screen: Electron.Screen;
// Screen is an EventEmitter.
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
module.exports = createLazyInstance(createScreen, Screen, true);
// We can't call createScreen until after app.on('ready'), but this module
// exposes an instance created by createScreen. In order to avoid
// side-effecting and calling createScreen upon import of this module, instead
// we export a proxy which lazily calls createScreen on first access.
export default new Proxy({}, {
get: (target, prop) => {
if (_screen === undefined) {
_screen = createScreen();
}
const v = (_screen as any)[prop];
if (typeof v === 'function') {
return v.bind(_screen);
}
return v;
}
});

View File

@@ -1,15 +1,10 @@
import { EventEmitter } from 'events';
import { deprecate } from 'electron';
const { systemPreferences, SystemPreferences } = process._linkedBinding('electron_browser_system_preferences');
// SystemPreferences is an EventEmitter.
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
EventEmitter.call(systemPreferences);
import { deprecate } from 'electron/main';
const { systemPreferences } = process._linkedBinding('electron_browser_system_preferences');
if ('getAppLevelAppearance' in systemPreferences) {
const nativeALAGetter = systemPreferences.getAppLevelAppearance;
const nativeALASetter = systemPreferences.setAppLevelAppearance;
Object.defineProperty(SystemPreferences.prototype, 'appLevelAppearance', {
Object.defineProperty(systemPreferences, 'appLevelAppearance', {
get: () => nativeALAGetter.call(systemPreferences),
set: (appearance) => nativeALASetter.call(systemPreferences, appearance)
});
@@ -17,25 +12,25 @@ if ('getAppLevelAppearance' in systemPreferences) {
if ('getEffectiveAppearance' in systemPreferences) {
const nativeEAGetter = systemPreferences.getAppLevelAppearance;
Object.defineProperty(SystemPreferences.prototype, 'effectiveAppearance', {
Object.defineProperty(systemPreferences, 'effectiveAppearance', {
get: () => nativeEAGetter.call(systemPreferences)
});
}
SystemPreferences.prototype.isDarkMode = deprecate.moveAPI(
SystemPreferences.prototype.isDarkMode,
systemPreferences.isDarkMode = deprecate.moveAPI(
systemPreferences.isDarkMode,
'systemPreferences.isDarkMode()',
'nativeTheme.shouldUseDarkColors'
);
SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI(
SystemPreferences.prototype.isInvertedColorScheme,
systemPreferences.isInvertedColorScheme = deprecate.moveAPI(
systemPreferences.isInvertedColorScheme,
'systemPreferences.isInvertedColorScheme()',
'nativeTheme.shouldUseInvertedColorScheme'
);
SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI(
SystemPreferences.prototype.isHighContrastColorScheme,
systemPreferences.isHighContrastColorScheme = deprecate.moveAPI(
systemPreferences.isHighContrastColorScheme,
'systemPreferences.isHighContrastColorScheme()',
'nativeTheme.shouldUseHighContrastColors'
);
module.exports = systemPreferences;
export default systemPreferences;

View File

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

View File

@@ -0,0 +1,459 @@
import { EventEmitter } from 'events';
let nextItemID = 1;
const hiddenProperties = Symbol('hidden touch bar props');
const extendConstructHook = (target: any, hook: Function) => {
const existingHook = target._hook;
target._hook = function () {
hook.call(this);
if (existingHook) existingHook.call(this);
};
};
const ImmutableProperty = <T extends TouchBarItem<any>>(def: (config: T extends TouchBarItem<infer C> ? C : never, setInternalProp: <K extends keyof T>(k: K, v: T[K]) => void) => any) => (target: T, propertyKey: keyof T) => {
extendConstructHook(target as any, function (this: T) {
(this as any)[hiddenProperties][propertyKey] = def((this as any)._config, (k, v) => {
(this as any)[hiddenProperties][k] = v;
});
});
Object.defineProperty(target, propertyKey, {
get: function () {
return (this as any)[hiddenProperties][propertyKey];
},
set: function () {
throw new Error(`Cannot override property ${name}`);
},
enumerable: true,
configurable: false
});
};
const LiveProperty = <T extends TouchBarItem<any>>(def: (config: T extends TouchBarItem<infer C> ? C : never) => any, onMutate?: (self: T, newValue: any) => void) => (target: T, propertyKey: keyof T) => {
extendConstructHook(target as any, function (this: T) {
(this as any)[hiddenProperties][propertyKey] = def((this as any)._config);
if (onMutate) onMutate((this as any), (this as any)[hiddenProperties][propertyKey]);
});
Object.defineProperty(target, propertyKey, {
get: function () {
return this[hiddenProperties][propertyKey];
},
set: function (value) {
if (onMutate) onMutate((this as any), value);
this[hiddenProperties][propertyKey] = value;
this.emit('change', this);
},
enumerable: true
});
};
abstract class TouchBarItem<ConfigType> extends EventEmitter {
@ImmutableProperty(() => `${nextItemID++}`) id!: string;
abstract type: string;
abstract onInteraction: Function | null;
child?: TouchBar;
private _parents: { id: string; type: string }[] = [];
private _config!: ConfigType;
constructor (config: ConfigType) {
super();
this._config = this._config || config || {} as any;
(this as any)[hiddenProperties] = {};
const hook = (this as any)._hook;
if (hook) hook.call(this);
delete (this as any)._hook;
}
public _addParent (item: TouchBarItem<any>) {
const existing = this._parents.some(test => test.id === item.id);
if (!existing) {
this._parents.push({
id: item.id,
type: item.type
});
}
}
public _removeParent (item: TouchBarItem<any>) {
this._parents = this._parents.filter(test => test.id !== item.id);
}
}
class TouchBarButton extends TouchBarItem<Electron.TouchBarButtonConstructorOptions> implements Electron.TouchBarButton {
@ImmutableProperty(() => 'button')
type!: string;
@LiveProperty<TouchBarButton>(config => config.label)
label!: string;
@LiveProperty<TouchBarButton>(config => config.accessibilityLabel)
accessibilityLabel!: string;
@LiveProperty<TouchBarButton>(config => config.backgroundColor)
backgroundColor!: string;
@LiveProperty<TouchBarButton>(config => config.icon)
icon!: Electron.NativeImage;
@LiveProperty<TouchBarButton>(config => config.iconPosition)
iconPosition!: Electron.TouchBarButton['iconPosition'];
@LiveProperty<TouchBarButton>(config => typeof config.enabled !== 'boolean' ? true : config.enabled)
enabled!: boolean;
@ImmutableProperty<TouchBarButton>(({ click: onClick }) => typeof onClick === 'function' ? () => onClick() : null)
onInteraction!: Function | null;
}
class TouchBarColorPicker extends TouchBarItem<Electron.TouchBarColorPickerConstructorOptions> implements Electron.TouchBarColorPicker {
@ImmutableProperty(() => 'colorpicker')
type!: string;
@LiveProperty<TouchBarColorPicker>(config => config.availableColors)
availableColors!: string[];
@LiveProperty<TouchBarColorPicker>(config => config.selectedColor)
selectedColor!: string;
@ImmutableProperty<TouchBarColorPicker>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { color: string }) => {
setInternalProp('selectedColor', details.color);
onChange(details.color);
} : null)
onInteraction!: Function | null;
}
class TouchBarGroup extends TouchBarItem<Electron.TouchBarGroupConstructorOptions> implements Electron.TouchBarGroup {
@ImmutableProperty(() => 'group')
type!: string;
@LiveProperty<TouchBarGroup>(config => config.items instanceof TouchBar ? config.items : new TouchBar(config.items), (self, newChild: TouchBar) => {
if (self.child) {
for (const item of self.child.orderedItems) {
item._removeParent(self);
}
}
for (const item of newChild.orderedItems) {
item._addParent(item);
}
})
child!: TouchBar;
onInteraction = null;
}
class TouchBarLabel extends TouchBarItem<Electron.TouchBarLabelConstructorOptions> implements Electron.TouchBarLabel {
@ImmutableProperty(() => 'label')
type!: string;
@LiveProperty<TouchBarLabel>(config => config.label)
label!: string;
@LiveProperty<TouchBarLabel>(config => config.accessibilityLabel)
accessibilityLabel!: string;
@LiveProperty<TouchBarLabel>(config => config.textColor)
textColor!: string;
onInteraction = null;
}
class TouchBarPopover extends TouchBarItem<Electron.TouchBarPopoverConstructorOptions> implements Electron.TouchBarPopover {
@ImmutableProperty(() => 'popover')
type!: string;
@LiveProperty<TouchBarPopover>(config => config.label)
label!: string;
@LiveProperty<TouchBarPopover>(config => config.icon)
icon!: Electron.NativeImage;
@LiveProperty<TouchBarPopover>(config => config.showCloseButton)
showCloseButton!: boolean;
@LiveProperty<TouchBarPopover>(config => config.items instanceof TouchBar ? config.items : new TouchBar(config.items), (self, newChild: TouchBar) => {
if (self.child) {
for (const item of self.child.orderedItems) {
item._removeParent(self);
}
}
for (const item of newChild.orderedItems) {
item._addParent(item);
}
})
child!: TouchBar;
onInteraction = null;
}
class TouchBarSlider extends TouchBarItem<Electron.TouchBarSliderConstructorOptions> implements Electron.TouchBarSlider {
@ImmutableProperty(() => 'slider')
type!: string;
@LiveProperty<TouchBarSlider>(config => config.label)
label!: string;
@LiveProperty<TouchBarSlider>(config => config.minValue)
minValue!: number;
@LiveProperty<TouchBarSlider>(config => config.maxValue)
maxValue!: number;
@LiveProperty<TouchBarSlider>(config => config.value)
value!: number;
@ImmutableProperty<TouchBarSlider>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { value: number }) => {
setInternalProp('value', details.value);
onChange(details.value);
} : null)
onInteraction!: Function | null;
}
class TouchBarSpacer extends TouchBarItem<Electron.TouchBarSpacerConstructorOptions> implements Electron.TouchBarSpacer {
@ImmutableProperty(() => 'spacer')
type!: string;
@ImmutableProperty<TouchBarSpacer>(config => config.size)
size!: Electron.TouchBarSpacer['size'];
onInteraction = null;
}
class TouchBarSegmentedControl extends TouchBarItem<Electron.TouchBarSegmentedControlConstructorOptions> implements Electron.TouchBarSegmentedControl {
@ImmutableProperty(() => 'segmented_control')
type!: string;
@LiveProperty<TouchBarSegmentedControl>(config => config.segmentStyle)
segmentStyle!: Electron.TouchBarSegmentedControl['segmentStyle'];
@LiveProperty<TouchBarSegmentedControl>(config => config.segments || [])
segments!: Electron.SegmentedControlSegment[];
@LiveProperty<TouchBarSegmentedControl>(config => config.selectedIndex)
selectedIndex!: number;
@LiveProperty<TouchBarSegmentedControl>(config => config.mode)
mode!: Electron.TouchBarSegmentedControl['mode'];
@ImmutableProperty<TouchBarSegmentedControl>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { selectedIndex: number, isSelected: boolean }) => {
setInternalProp('selectedIndex', details.selectedIndex);
onChange(details.selectedIndex, details.isSelected);
} : null)
onInteraction!: Function | null;
}
class TouchBarScrubber extends TouchBarItem<Electron.TouchBarScrubberConstructorOptions> implements Electron.TouchBarScrubber {
@ImmutableProperty(() => 'scrubber')
type!: string;
@LiveProperty<TouchBarScrubber>(config => config.items)
items!: Electron.ScrubberItem[];
@LiveProperty<TouchBarScrubber>(config => config.selectedStyle || null)
selectedStyle!: Electron.TouchBarScrubber['selectedStyle'];
@LiveProperty<TouchBarScrubber>(config => config.overlayStyle || null)
overlayStyle!: Electron.TouchBarScrubber['overlayStyle'];
@LiveProperty<TouchBarScrubber>(config => config.showArrowButtons || false)
showArrowButtons!: boolean;
@LiveProperty<TouchBarScrubber>(config => config.mode || 'free')
mode!: Electron.TouchBarScrubber['mode'];
@LiveProperty<TouchBarScrubber>(config => typeof config.continuous === 'undefined' ? true : config.continuous)
continuous!: boolean;
@ImmutableProperty<TouchBarScrubber>(({ select: onSelect, highlight: onHighlight }) => typeof onSelect === 'function' || typeof onHighlight === 'function' ? (details: { type: 'select'; selectedIndex: number } | { type: 'highlight'; highlightedIndex: number }) => {
if (details.type === 'select') {
if (onSelect) onSelect(details.selectedIndex);
} else {
if (onHighlight) onHighlight(details.highlightedIndex);
}
} : null)
onInteraction!: Function | null;
}
class TouchBarOtherItemsProxy extends TouchBarItem<null> implements Electron.TouchBarOtherItemsProxy {
@ImmutableProperty(() => 'other_items_proxy') type!: string;
onInteraction = null;
}
const escapeItemSymbol = Symbol('escape item');
class TouchBar extends EventEmitter implements Electron.TouchBar {
// Bind a touch bar to a window
static _setOnWindow (touchBar: TouchBar | Electron.TouchBarConstructorOptions['items'], window: Electron.BrowserWindow) {
if (window._touchBar != null) {
window._touchBar._removeFromWindow(window);
}
if (!touchBar) {
window._setTouchBarItems([]);
return;
}
if (Array.isArray(touchBar)) {
touchBar = new TouchBar({ items: touchBar });
}
touchBar._addToWindow(window);
}
private windowListeners: Record<number, Function> = {};
private items: Record<string, TouchBarItem<any>> = {};
orderedItems: TouchBarItem<any>[] = [];
constructor (options: Electron.TouchBarConstructorOptions) {
super();
if (options == null) {
throw new Error('Must specify options object as first argument');
}
let { items, escapeItem } = options;
if (!Array.isArray(items)) {
items = [];
}
this.windowListeners = {};
this.items = {};
this.escapeItem = (escapeItem as any) || null;
const registerItem = (item: TouchBarItem<any>) => {
this.items[item.id] = item;
item.on('change', this.changeListener);
if (item.child instanceof TouchBar) {
item.child.orderedItems.forEach(registerItem);
}
};
let hasOtherItemsProxy = false;
const idSet = new Set();
items.forEach((item) => {
if (!(item instanceof TouchBarItem)) {
throw new Error('Each item must be an instance of TouchBarItem');
}
if (item.type === 'other_items_proxy') {
if (!hasOtherItemsProxy) {
hasOtherItemsProxy = true;
} else {
throw new Error('Must only have one OtherItemsProxy per TouchBar');
}
}
if (!idSet.has(item.id)) {
idSet.add(item.id);
} else {
throw new Error('Cannot add a single instance of TouchBarItem multiple times in a TouchBar');
}
});
// register in separate loop after all items are validated
for (const item of (items as TouchBarItem<any>[])) {
this.orderedItems.push(item);
registerItem(item);
}
}
private changeListener = (item: TouchBarItem<any>) => {
this.emit('change', item.id, item.type);
};
private [escapeItemSymbol]: TouchBarItem<unknown> | null = null;
set escapeItem (item: TouchBarItem<unknown> | null) {
if (item != null && !(item instanceof TouchBarItem)) {
throw new Error('Escape item must be an instance of TouchBarItem');
}
const escapeItem = this.escapeItem;
if (escapeItem) {
escapeItem.removeListener('change', this.changeListener);
}
this[escapeItemSymbol] = item;
if (this.escapeItem != null) {
this.escapeItem.on('change', this.changeListener);
}
this.emit('escape-item-change', item);
}
get escapeItem (): TouchBarItem<unknown> | null {
return this[escapeItemSymbol];
}
_addToWindow (window: Electron.BrowserWindow) {
const { id } = window;
// Already added to window
if (Object.prototype.hasOwnProperty.call(this.windowListeners, id)) return;
window._touchBar = this;
const changeListener = (itemID: string) => {
window._refreshTouchBarItem(itemID);
};
this.on('change', changeListener);
const escapeItemListener = (item: Electron.TouchBarItemType | null) => {
window._setEscapeTouchBarItem(item != null ? item : {});
};
this.on('escape-item-change', escapeItemListener);
const interactionListener = (_: any, itemID: string, details: any) => {
let item = this.items[itemID];
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
item = this.escapeItem;
}
if (item != null && item.onInteraction != null) {
item.onInteraction(details);
}
};
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];
const unregisterItems = (items: TouchBarItem<any>[]) => {
for (const item of items) {
item.removeListener('change', this.changeListener);
if (item.child instanceof TouchBar) {
unregisterItems(item.child.orderedItems);
}
}
};
unregisterItems(this.orderedItems);
if (this.escapeItem) {
this.escapeItem.removeListener('change', this.changeListener);
}
};
window.once('closed', removeListeners);
this.windowListeners[id] = removeListeners;
window._setTouchBarItems(this.orderedItems);
escapeItemListener(this.escapeItem);
}
_removeFromWindow (window: Electron.BrowserWindow) {
const removeListeners = this.windowListeners[window.id];
if (removeListeners != null) removeListeners();
}
static TouchBarButton = TouchBarButton;
static TouchBarColorPicker = TouchBarColorPicker;
static TouchBarGroup = TouchBarGroup;
static TouchBarLabel = TouchBarLabel;
static TouchBarPopover = TouchBarPopover;
static TouchBarSlider = TouchBarSlider;
static TouchBarSpacer = TouchBarSpacer;
static TouchBarSegmentedControl = TouchBarSegmentedControl;
static TouchBarScrubber = TouchBarScrubber;
static TouchBarOtherItemsProxy = TouchBarOtherItemsProxy;
}
export default TouchBar;

View File

@@ -1,4 +1,4 @@
import { View } from 'electron';
import { View } from 'electron/main';
const { ImageView } = process._linkedBinding('electron_browser_image_view');

View File

@@ -1,4 +1,4 @@
import { View } from 'electron';
import { View } from 'electron/main';
const { WebContentsView } = process._linkedBinding('electron_browser_web_contents_view');

View File

@@ -1,15 +1,14 @@
import { app, ipcMain, session, deprecate } from 'electron';
import type { MenuItem, MenuItemConstructorOptions, WebContentsInternal } from 'electron';
import { app, ipcMain, session, deprecate } from 'electron/main';
import type { MenuItem, MenuItemConstructorOptions } from 'electron/main';
import * as url from 'url';
import * as path from 'path';
import { internalWindowOpen } from '../guest-window-manager';
import { NavigationController } from '../navigation-controller';
import { ipcMainInternal } from '../ipc-main-internal';
import * as ipcMainUtils from '../ipc-main-internal-utils';
import { parseFeatures } from '../../common/parse-features-string';
import { MessagePortMain } from '../message-port-main';
import { EventEmitter } from 'events';
import { internalWindowOpen } from '@electron/internal/browser/guest-window-manager';
import { NavigationController } from '@electron/internal/browser/navigation-controller';
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
import { parseFeatures } from '@electron/internal/common/parse-features-string';
import { MessagePortMain } from '@electron/internal/browser/message-port-main';
// session is not used here, the purpose is to make sure session is initalized
// before the webContents module.
@@ -72,6 +71,19 @@ const PDFPageSizes: Record<string, MediaSize> = {
}
};
// The minimum micron size Chromium accepts is that where:
// Per printing/units.h:
// * kMicronsPerInch - Length of an inch in 0.001mm unit.
// * kPointsPerInch - Length of an inch in CSS's 1pt unit.
//
// Formula: (kPointsPerInch / kMicronsPerInch) * size >= 1
//
// Practically, this means microns need to be > 352 microns.
// We therefore need to verify this or it will silently fail.
const isValidCustomPageSize = (width: number, height: number) => {
return [width, height].every(x => x > 352);
};
// Default printing setting
const defaultPrintingSetting = {
// Customizable.
@@ -109,9 +121,7 @@ const defaultPrintingSetting = {
// JavaScript implementations of WebContents.
const binding = process._linkedBinding('electron_browser_web_contents');
const { WebContents } = binding as { WebContents: { prototype: WebContentsInternal } };
Object.setPrototypeOf(WebContents.prototype, EventEmitter.prototype);
const { WebContents } = binding as { WebContents: { prototype: Electron.WebContentsInternal } };
WebContents.prototype.send = function (channel, ...args) {
if (typeof channel !== 'string') {
@@ -190,7 +200,7 @@ for (const method of webFrameMethods) {
};
}
const waitTillCanExecuteJavaScript = async (webContents: WebContentsInternal) => {
const waitTillCanExecuteJavaScript = async (webContents: Electron.WebContentsInternal) => {
if (webContents.getURL() && !webContents.isLoadingMainFrame()) return;
return new Promise((resolve) => {
@@ -212,7 +222,9 @@ WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (worldId
};
// Translate the options of printToPDF.
WebContents.prototype.printToPDF = function (options) {
let pendingPromise: Promise<any> | undefined;
WebContents.prototype.printToPDF = async function (options) {
const printSettings = {
...defaultPrintingSetting,
requestID: getNextId()
@@ -315,13 +327,20 @@ WebContents.prototype.printToPDF = function (options) {
const error = new Error('height and width properties are required for pageSize');
return Promise.reject(error);
}
// Dimensions in Microns
// 1 meter = 10^6 microns
// Dimensions in Microns - 1 meter = 10^6 microns
const height = Math.ceil(pageSize.height);
const width = Math.ceil(pageSize.width);
if (!isValidCustomPageSize(width, height)) {
const error = new Error('height and width properties must be minimum 352 microns.');
return Promise.reject(error);
}
printSettings.mediaSize = {
name: 'CUSTOM',
custom_display_name: 'Custom',
height_microns: Math.ceil(pageSize.height),
width_microns: Math.ceil(pageSize.width)
height_microns: height,
width_microns: width
};
} else if (Object.prototype.hasOwnProperty.call(PDFPageSizes, pageSize)) {
printSettings.mediaSize = PDFPageSizes[pageSize];
@@ -338,7 +357,12 @@ WebContents.prototype.printToPDF = function (options) {
// PrinterType enum from //printing/print_job_constants.h
printSettings.printerType = 2;
if (this._printToPDF) {
return this._printToPDF(printSettings);
if (pendingPromise) {
pendingPromise = pendingPromise.then(() => this._printToPDF(printSettings));
} else {
pendingPromise = this._printToPDF(printSettings);
}
return pendingPromise;
} else {
const error = new Error('Printing feature is disabled');
return Promise.reject(error);
@@ -356,12 +380,19 @@ WebContents.prototype.print = function (options = {}, callback) {
if (!pageSize.height || !pageSize.width) {
throw new Error('height and width properties are required for pageSize');
}
// Dimensions in Microns - 1 meter = 10^6 microns
const height = Math.ceil(pageSize.height);
const width = Math.ceil(pageSize.width);
if (!isValidCustomPageSize(width, height)) {
throw new Error('height and width properties must be minimum 352 microns.');
}
(options as any).mediaSize = {
name: 'CUSTOM',
custom_display_name: 'Custom',
height_microns: Math.ceil(pageSize.height),
width_microns: Math.ceil(pageSize.width)
height_microns: height,
width_microns: width
};
} else if (PDFPageSizes[pageSize]) {
(options as any).mediaSize = PDFPageSizes[pageSize];
@@ -456,7 +487,7 @@ WebContents.prototype._init = function () {
this.setMaxListeners(0);
// Dispatch IPC messages to the ipc module.
this.on('-ipc-message' as any, function (this: WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
this.on('-ipc-message' as any, function (this: Electron.WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
if (internal) {
addReplyInternalToEvent(event);
ipcMainInternal.emit(channel, event, ...args);
@@ -481,7 +512,7 @@ WebContents.prototype._init = function () {
}
});
this.on('-ipc-message-sync' as any, function (this: WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
this.on('-ipc-message-sync' as any, function (this: Electron.WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
addReturnValueToEvent(event);
if (internal) {
addReplyInternalToEvent(event);
@@ -519,7 +550,7 @@ WebContents.prototype._init = function () {
});
// The devtools requests the webContents to reload.
this.on('devtools-reload-page', function (this: WebContentsInternal) {
this.on('devtools-reload-page', function (this: Electron.WebContentsInternal) {
this.reload();
});
@@ -542,7 +573,7 @@ WebContents.prototype._init = function () {
// Create a new browser window for the native implementation of
// "window.open", used in sandbox and nativeWindowOpen mode.
this.on('-add-new-contents' as any, (event: any, webContents: WebContentsInternal, disposition: string,
this.on('-add-new-contents' as any, (event: any, webContents: Electron.WebContentsInternal, disposition: string,
userGesture: boolean, left: number, top: number, width: number, height: number, url: string, frameName: string,
referrer: string, rawFeatures: string, postData: string) => {
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&

View File

@@ -2,7 +2,7 @@
// BrowserWindow-based extensions stuff to the new native-backed extensions
// API.
import { app, session, BrowserWindow, deprecate } from 'electron';
import { app, session, BrowserWindow, deprecate } from 'electron/main';
app.whenReady().then(function () {
const addExtension = function (srcDirectory: string) {

View File

@@ -1,4 +1,5 @@
import { shell, Menu } from 'electron';
import { Menu } from 'electron/main';
import { shell } from 'electron/common';
const v8Util = process._linkedBinding('electron_common_v8_util');

View File

@@ -1,4 +1,4 @@
import { dialog, Menu } from 'electron';
import { dialog, Menu } from 'electron/main';
import * as fs from 'fs';
import * as url from 'url';

View File

@@ -1,5 +1,5 @@
import { EventEmitter } from 'events';
import { IpcMainInvokeEvent } from 'electron';
import { IpcMainInvokeEvent } from 'electron/main';
export class IpcMainImpl extends EventEmitter {
private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map();

View File

@@ -1,4 +1,4 @@
import { ipcMainInternal } from './ipc-main-internal';
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
import type { WebContents, LoadURLOptions } from 'electron/main';
import { EventEmitter } from 'events';

View File

@@ -1,6 +1,6 @@
'use strict';
import { WebContents } from 'electron';
import { WebContents } from 'electron/main';
const v8Util = process._linkedBinding('electron_common_v8_util');

View File

@@ -1,14 +1,13 @@
import * as electron from 'electron';
import * as electron from 'electron/main';
import { EventEmitter } from 'events';
import objectsRegistry from './objects-registry';
import { ipcMainInternal } from '../ipc-main-internal';
import { isPromise, isSerializableObject, deserialize, serialize } from '../../common/type-utils';
import type { MetaTypeFromRenderer, ObjectMember, MetaType, ObjProtoDescriptor } from '../../common/remote/types';
import objectsRegistry from '@electron/internal/browser/remote/objects-registry';
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
import { isPromise, isSerializableObject, deserialize, serialize } from '@electron/internal/common/type-utils';
import type { MetaTypeFromRenderer, ObjectMember, MetaType, ObjProtoDescriptor } from '@electron/internal/common/remote/types';
const v8Util = process._linkedBinding('electron_common_v8_util');
const eventBinding = process._linkedBinding('electron_browser_event');
const features = process._linkedBinding('electron_common_features');
const { NativeImage } = process._linkedBinding('electron_common_native_image');
if (!features.isRemoteModuleEnabled()) {
throw new Error('remote module is disabled');
@@ -102,7 +101,7 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v
// Recognize certain types of objects.
if (value instanceof Buffer) {
type = 'buffer';
} else if (value instanceof NativeImage) {
} else if (value && value.constructor && value.constructor.name === 'NativeImage') {
type = 'nativeimage';
} else if (Array.isArray(value)) {
type = 'array';

View File

@@ -1,24 +1,24 @@
import * as electron from 'electron';
import { app } from 'electron/main';
import type { IpcMainInvokeEvent, WebContents } from 'electron/main';
import { clipboard, crashReporter } from 'electron/common';
import * as fs from 'fs';
import { ipcMainInternal } from './ipc-main-internal';
import * as ipcMainUtils from './ipc-main-internal-utils';
import * as guestViewManager from './guest-view-manager';
import * as typeUtils from '../common/type-utils';
import { IpcMainInvokeEvent } from 'electron/main';
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
import * as guestViewManager from '@electron/internal/browser/guest-view-manager';
import * as typeUtils from '@electron/internal/common/type-utils';
const eventBinding = process._linkedBinding('electron_browser_event');
const clipboard = process._linkedBinding('electron_common_clipboard');
const emitCustomEvent = function (contents: electron.WebContents, eventName: string, ...args: any[]) {
const emitCustomEvent = function (contents: WebContents, eventName: string, ...args: any[]) {
const event = eventBinding.createWithSender(contents);
electron.app.emit(eventName, event, contents, ...args);
app.emit(eventName, event, contents, ...args);
contents.emit(eventName, event, ...args);
return event;
};
const logStack = function (contents: electron.WebContents, code: string, stack: string) {
const logStack = function (contents: WebContents, code: string, stack: string) {
if (stack) {
console.warn(`WebContents (${contents.id}): ${code}`, stack);
}
@@ -54,7 +54,7 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_CLIPBOARD_SYNC', function (event: IpcM
throw new Error(`Invalid method: ${method}`);
}
return typeUtils.serialize((electron.clipboard as any)[method](...typeUtils.deserialize(args)));
return typeUtils.serialize((clipboard as any)[method](...typeUtils.deserialize(args)));
});
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
@@ -118,21 +118,21 @@ ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event: ElectronIn
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_LAST_CRASH_REPORT', () => {
return electron.crashReporter.getLastCrashReport();
return crashReporter.getLastCrashReport();
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOADED_REPORTS', () => {
return electron.crashReporter.getUploadedReports();
return crashReporter.getUploadedReports();
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOAD_TO_SERVER', () => {
return electron.crashReporter.getUploadToServer();
return crashReporter.getUploadToServer();
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_SET_UPLOAD_TO_SERVER', (event: IpcMainInvokeEvent, uploadToServer: boolean) => {
return electron.crashReporter.setUploadToServer(uploadToServer);
return crashReporter.setUploadToServer(uploadToServer);
});
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_CRASHES_DIRECTORY', () => {
return electron.crashReporter.getCrashesDirectory();
return crashReporter.getCrashesDirectory();
});

View File

@@ -1,38 +0,0 @@
import { EventEmitter } from 'events';
/**
* Creates a lazy instance of modules that can't be required before the
* app 'ready' event by returning a proxy object to mitigate side effects
* on 'require'
*
* @param {Function} creator - Function that creates a new module instance
* @param {Object} holder - the object holding the module prototype
* @param {Boolean} isEventEmitter - whether or not the module is an EventEmitter
* @returns {Object} - a proxy object for the
*/
export function createLazyInstance (
creator: Function,
holder: Object,
isEventEmitter: Boolean
) {
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);
}
// 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](...args);
};
}
return module;
}

View File

@@ -1,783 +0,0 @@
'use strict';
(function () {
const asar = process._linkedBinding('electron_common_asar');
const v8Util = process._linkedBinding('electron_common_v8_util');
const { Buffer } = require('buffer');
const Module = require('module');
const path = require('path');
const util = require('util');
const Promise = global.Promise;
const envNoAsar = process.env.ELECTRON_NO_ASAR &&
process.type !== 'browser' &&
process.type !== 'renderer';
const isAsarDisabled = () => process.noAsar || envNoAsar;
const internalBinding = process.internalBinding;
delete process.internalBinding;
/**
* @param {!Function} functionToCall
* @param {!Array|undefined} args
*/
const nextTick = (functionToCall, args = []) => {
process.nextTick(() => functionToCall(...args));
};
// Cache asar archive objects.
const cachedArchives = new Map();
const getOrCreateArchive = archivePath => {
const isCached = cachedArchives.has(archivePath);
if (isCached) {
return cachedArchives.get(archivePath);
}
const newArchive = asar.createArchive(archivePath);
if (!newArchive) return null;
cachedArchives.set(archivePath, newArchive);
return newArchive;
};
// Separate asar package's path from full path.
const splitPath = archivePathOrBuffer => {
// Shortcut for disabled asar.
if (isAsarDisabled()) return { isAsar: false };
// Check for a bad argument type.
let archivePath = archivePathOrBuffer;
if (Buffer.isBuffer(archivePathOrBuffer)) {
archivePath = archivePathOrBuffer.toString();
}
if (typeof archivePath !== 'string') return { isAsar: false };
return asar.splitPath(path.normalize(archivePath));
};
// Convert asar archive's Stats object to fs's Stats object.
let nextInode = 0;
const uid = process.getuid != null ? process.getuid() : 0;
const gid = process.getgid != null ? process.getgid() : 0;
const fakeTime = new Date();
const msec = (date) => (date || fakeTime).getTime();
const asarStatsToFsStats = function (stats) {
const { Stats, constants } = require('fs');
let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR;
if (stats.isFile) {
mode ^= constants.S_IFREG;
} else if (stats.isDirectory) {
mode ^= constants.S_IFDIR;
} else if (stats.isLink) {
mode ^= constants.S_IFLNK;
}
return new Stats(
1, // dev
mode, // mode
1, // nlink
uid,
gid,
0, // rdev
undefined, // blksize
++nextInode, // ino
stats.size,
undefined, // blocks,
msec(stats.atime), // atim_msec
msec(stats.mtime), // mtim_msec
msec(stats.ctime), // ctim_msec
msec(stats.birthtime) // birthtim_msec
);
};
const AsarError = {
NOT_FOUND: 'NOT_FOUND',
NOT_DIR: 'NOT_DIR',
NO_ACCESS: 'NO_ACCESS',
INVALID_ARCHIVE: 'INVALID_ARCHIVE'
};
const createError = (errorType, { asarPath, filePath } = {}) => {
let error;
switch (errorType) {
case AsarError.NOT_FOUND:
error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`);
error.code = 'ENOENT';
error.errno = -2;
break;
case AsarError.NOT_DIR:
error = new Error('ENOTDIR, not a directory');
error.code = 'ENOTDIR';
error.errno = -20;
break;
case AsarError.NO_ACCESS:
error = new Error(`EACCES: permission denied, access '${filePath}'`);
error.code = 'EACCES';
error.errno = -13;
break;
case AsarError.INVALID_ARCHIVE:
error = new Error(`Invalid package ${asarPath}`);
break;
default:
throw new Error(`Invalid error type "${errorType}" passed to createError.`);
}
return error;
};
const overrideAPISync = function (module, name, pathArgumentIndex, fromAsync) {
if (pathArgumentIndex == null) pathArgumentIndex = 0;
const old = module[name];
const func = function () {
const pathArgument = arguments[pathArgumentIndex];
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return old.apply(this, arguments);
const archive = getOrCreateArchive(asarPath);
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
const newPath = archive.copyFileOut(filePath);
if (!newPath) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
arguments[pathArgumentIndex] = newPath;
return old.apply(this, arguments);
};
if (fromAsync) {
return func;
}
module[name] = func;
};
const overrideAPI = function (module, name, pathArgumentIndex) {
if (pathArgumentIndex == null) pathArgumentIndex = 0;
const old = module[name];
module[name] = function () {
const pathArgument = arguments[pathArgumentIndex];
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return old.apply(this, arguments);
const callback = arguments[arguments.length - 1];
if (typeof callback !== 'function') {
return overrideAPISync(module, name, pathArgumentIndex, true).apply(this, arguments);
}
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const newPath = archive.copyFileOut(filePath);
if (!newPath) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
arguments[pathArgumentIndex] = newPath;
return old.apply(this, arguments);
};
if (old[util.promisify.custom]) {
module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex);
}
if (module.promises && module.promises[name]) {
module.promises[name] = makePromiseFunction(module.promises[name], pathArgumentIndex);
}
};
const makePromiseFunction = function (orig, pathArgumentIndex) {
return function (...args) {
const pathArgument = args[pathArgumentIndex];
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return orig.apply(this, args);
const archive = getOrCreateArchive(asarPath);
if (!archive) {
return Promise.reject(createError(AsarError.INVALID_ARCHIVE, { asarPath }));
}
const newPath = archive.copyFileOut(filePath);
if (!newPath) {
return Promise.reject(createError(AsarError.NOT_FOUND, { asarPath, filePath }));
}
args[pathArgumentIndex] = newPath;
return orig.apply(this, args);
};
};
// Override fs APIs.
exports.wrapFsWithAsar = fs => {
const logFDs = {};
const logASARAccess = (asarPath, filePath, offset) => {
if (!process.env.ELECTRON_LOG_ASAR_READS) return;
if (!logFDs[asarPath]) {
const path = require('path');
const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt`;
const logPath = path.join(require('os').tmpdir(), logFilename);
logFDs[asarPath] = fs.openSync(logPath, 'a');
}
fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`);
};
const { lstatSync } = fs;
fs.lstatSync = (pathArgument, options) => {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return lstatSync(pathArgument, options);
const archive = getOrCreateArchive(asarPath);
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
const stats = archive.stat(filePath);
if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
return asarStatsToFsStats(stats);
};
const { lstat } = fs;
fs.lstat = function (pathArgument, options, callback) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (typeof options === 'function') {
callback = options;
options = {};
}
if (!isAsar) return lstat(pathArgument, options, callback);
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const stats = archive.stat(filePath);
if (!stats) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
const fsStats = asarStatsToFsStats(stats);
nextTick(callback, [null, fsStats]);
};
fs.promises.lstat = util.promisify(fs.lstat);
const { statSync } = fs;
fs.statSync = (pathArgument, options) => {
const { isAsar } = splitPath(pathArgument);
if (!isAsar) return statSync(pathArgument, options);
// Do not distinguish links for now.
return fs.lstatSync(pathArgument, options);
};
const { stat } = fs;
fs.stat = (pathArgument, options, callback) => {
const { isAsar } = splitPath(pathArgument);
if (typeof options === 'function') {
callback = options;
options = {};
}
if (!isAsar) return stat(pathArgument, options, callback);
// Do not distinguish links for now.
process.nextTick(() => fs.lstat(pathArgument, options, callback));
};
fs.promises.stat = util.promisify(fs.stat);
const wrapRealpathSync = function (realpathSync) {
return function (pathArgument, options) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return realpathSync.apply(this, arguments);
const archive = getOrCreateArchive(asarPath);
if (!archive) {
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
}
const fileRealPath = archive.realpath(filePath);
if (fileRealPath === false) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
}
return path.join(realpathSync(asarPath, options), fileRealPath);
};
};
const { realpathSync } = fs;
fs.realpathSync = wrapRealpathSync(realpathSync);
fs.realpathSync.native = wrapRealpathSync(realpathSync.native);
const wrapRealpath = function (realpath) {
return function (pathArgument, options, callback) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return realpath.apply(this, arguments);
if (arguments.length < 3) {
callback = options;
options = {};
}
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const fileRealPath = archive.realpath(filePath);
if (fileRealPath === false) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
realpath(asarPath, options, (error, archiveRealPath) => {
if (error === null) {
const fullPath = path.join(archiveRealPath, fileRealPath);
callback(null, fullPath);
} else {
callback(error);
}
});
};
};
const { realpath } = fs;
fs.realpath = wrapRealpath(realpath);
fs.realpath.native = wrapRealpath(realpath.native);
fs.promises.realpath = util.promisify(fs.realpath.native);
const { exists } = fs;
fs.exists = (pathArgument, callback) => {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return exists(pathArgument, callback);
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const pathExists = (archive.stat(filePath) !== false);
nextTick(callback, [pathExists]);
};
fs.exists[util.promisify.custom] = pathArgument => {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return exists[util.promisify.custom](pathArgument);
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
return Promise.reject(error);
}
return Promise.resolve(archive.stat(filePath) !== false);
};
const { existsSync } = fs;
fs.existsSync = pathArgument => {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return existsSync(pathArgument);
const archive = getOrCreateArchive(asarPath);
if (!archive) return false;
return archive.stat(filePath) !== false;
};
const { access } = fs;
fs.access = function (pathArgument, mode, callback) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return access.apply(this, arguments);
if (typeof mode === 'function') {
callback = mode;
mode = fs.constants.F_OK;
}
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const info = archive.getFileInfo(filePath);
if (!info) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.access(realPath, mode, callback);
}
const stats = archive.stat(filePath);
if (!stats) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
if (mode & fs.constants.W_OK) {
const error = createError(AsarError.NO_ACCESS, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
nextTick(callback);
};
fs.promises.access = util.promisify(fs.access);
const { accessSync } = fs;
fs.accessSync = function (pathArgument, mode) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return accessSync.apply(this, arguments);
if (mode == null) mode = fs.constants.F_OK;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
}
const info = archive.getFileInfo(filePath);
if (!info) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
}
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.accessSync(realPath, mode);
}
const stats = archive.stat(filePath);
if (!stats) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
}
if (mode & fs.constants.W_OK) {
throw createError(AsarError.NO_ACCESS, { asarPath, filePath });
}
};
const { readFile } = fs;
fs.readFile = function (pathArgument, options, callback) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return readFile.apply(this, arguments);
if (typeof options === 'function') {
callback = options;
options = { encoding: null };
} else if (typeof options === 'string') {
options = { encoding: options };
} else if (options === null || options === undefined) {
options = { encoding: null };
} else if (typeof options !== 'object') {
throw new TypeError('Bad arguments');
}
const { encoding } = options;
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const info = archive.getFileInfo(filePath);
if (!info) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
if (info.size === 0) {
nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]);
return;
}
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.readFile(realPath, options, callback);
}
const buffer = Buffer.alloc(info.size);
const fd = archive.getFd();
if (!(fd >= 0)) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
logASARAccess(asarPath, filePath, info.offset);
fs.read(fd, buffer, 0, info.size, info.offset, error => {
callback(error, encoding ? buffer.toString(encoding) : buffer);
});
};
fs.promises.readFile = util.promisify(fs.readFile);
const { readFileSync } = fs;
fs.readFileSync = function (pathArgument, options) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return readFileSync.apply(this, arguments);
const archive = getOrCreateArchive(asarPath);
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
const info = archive.getFileInfo(filePath);
if (!info) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
if (info.size === 0) return (options) ? '' : Buffer.alloc(0);
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.readFileSync(realPath, options);
}
if (!options) {
options = { encoding: null };
} else if (typeof options === 'string') {
options = { encoding: options };
} else if (typeof options !== 'object') {
throw new TypeError('Bad arguments');
}
const { encoding } = options;
const buffer = Buffer.alloc(info.size);
const fd = archive.getFd();
if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
logASARAccess(asarPath, filePath, info.offset);
fs.readSync(fd, buffer, 0, info.size, info.offset);
return (encoding) ? buffer.toString(encoding) : buffer;
};
const { readdir } = fs;
fs.readdir = function (pathArgument, options = {}, callback) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (typeof options === 'function') {
callback = options;
options = {};
}
if (!isAsar) return readdir.apply(this, arguments);
const archive = getOrCreateArchive(asarPath);
if (!archive) {
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
nextTick(callback, [error]);
return;
}
const files = archive.readdir(filePath);
if (!files) {
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
nextTick(callback, [error]);
return;
}
if (options.withFileTypes) {
const dirents = [];
for (const file of files) {
const stats = archive.stat(file);
if (stats.isFile) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
} else if (stats.isDirectory) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
} else if (stats.isLink) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
}
}
nextTick(callback, [null, dirents]);
return;
}
nextTick(callback, [null, files]);
};
fs.promises.readdir = util.promisify(fs.readdir);
const { readdirSync } = fs;
fs.readdirSync = function (pathArgument, options = {}) {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return readdirSync.apply(this, arguments);
const archive = getOrCreateArchive(asarPath);
if (!archive) {
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
}
const files = archive.readdir(filePath);
if (!files) {
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
}
if (options.withFileTypes) {
const dirents = [];
for (const file of files) {
const stats = archive.stat(file);
if (stats.isFile) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
} else if (stats.isDirectory) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
} else if (stats.isLink) {
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
}
}
return dirents;
}
return files;
};
const { internalModuleReadJSON } = internalBinding('fs');
internalBinding('fs').internalModuleReadJSON = pathArgument => {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return internalModuleReadJSON(pathArgument);
const archive = getOrCreateArchive(asarPath);
if (!archive) return;
const info = archive.getFileInfo(filePath);
if (!info) return;
if (info.size === 0) return '';
if (info.unpacked) {
const realPath = archive.copyFileOut(filePath);
return fs.readFileSync(realPath, { encoding: 'utf8' });
}
const buffer = Buffer.alloc(info.size);
const fd = archive.getFd();
if (!(fd >= 0)) return;
logASARAccess(asarPath, filePath, info.offset);
fs.readSync(fd, buffer, 0, info.size, info.offset);
return buffer.toString('utf8');
};
const { internalModuleStat } = internalBinding('fs');
internalBinding('fs').internalModuleStat = pathArgument => {
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
if (!isAsar) return internalModuleStat(pathArgument);
// -ENOENT
const archive = getOrCreateArchive(asarPath);
if (!archive) return -34;
// -ENOENT
const stats = archive.stat(filePath);
if (!stats) return -34;
return (stats.isDirectory) ? 1 : 0;
};
// Calling mkdir for directory inside asar archive should throw ENOTDIR
// error, but on Windows it throws ENOENT.
if (process.platform === 'win32') {
const { mkdir } = fs;
fs.mkdir = (pathArgument, options, callback) => {
if (typeof options === 'function') {
callback = options;
options = {};
}
const { isAsar, filePath } = splitPath(pathArgument);
if (isAsar && filePath.length > 0) {
const error = createError(AsarError.NOT_DIR);
nextTick(callback, [error]);
return;
}
mkdir(pathArgument, options, callback);
};
fs.promises.mkdir = util.promisify(fs.mkdir);
const { mkdirSync } = fs;
fs.mkdirSync = function (pathArgument, options) {
const { isAsar, filePath } = splitPath(pathArgument);
if (isAsar && filePath.length) throw createError(AsarError.NOT_DIR);
return mkdirSync(pathArgument, options);
};
}
function invokeWithNoAsar (func) {
return function () {
const processNoAsarOriginalValue = process.noAsar;
process.noAsar = true;
try {
return func.apply(this, arguments);
} finally {
process.noAsar = processNoAsarOriginalValue;
}
};
}
// Strictly implementing the flags of fs.copyFile is hard, just do a simple
// implementation for now. Doing 2 copies won't spend much time more as OS
// has filesystem caching.
overrideAPI(fs, 'copyFile');
overrideAPISync(fs, 'copyFileSync');
overrideAPI(fs, 'open');
overrideAPISync(process, 'dlopen', 1);
overrideAPISync(Module._extensions, '.node', 1);
overrideAPISync(fs, 'openSync');
const overrideChildProcess = (childProcess) => {
// Executing a command string containing a path to an asar archive
// confuses `childProcess.execFile`, which is internally called by
// `childProcess.{exec,execSync}`, causing Electron to consider the full
// command as a single path to an archive.
const { exec, execSync } = childProcess;
childProcess.exec = invokeWithNoAsar(exec);
childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]);
childProcess.execSync = invokeWithNoAsar(execSync);
overrideAPI(childProcess, 'execFile');
overrideAPISync(childProcess, 'execFileSync');
};
// Lazily override the child_process APIs only when child_process is
// fetched the first time. We will eagerly override the child_process APIs
// when this env var is set so that stack traces generated inside node unit
// tests will match. This env var will only slow things down in users apps
// and should not be used.
if (process.env.ELECTRON_EAGER_ASAR_HOOK_FOR_TESTING) {
overrideChildProcess(require('child_process'));
} else {
const originalModuleLoad = Module._load;
Module._load = (request, ...args) => {
const loadResult = originalModuleLoad(request, ...args);
if (request === 'child_process') {
if (!v8Util.getHiddenValue(loadResult, 'asar-ready')) {
v8Util.setHiddenValue(loadResult, 'asar-ready', true);
// Just to make it obvious what we are dealing with here
const childProcess = loadResult;
overrideChildProcess(childProcess);
}
}
return loadResult;
};
}
};
})();

View File

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

View File

@@ -1,4 +1,4 @@
const { nativeImage, NativeImage } = process._linkedBinding('electron_common_native_image');
const { nativeImage } = process._linkedBinding('electron_common_native_image');
export function isPromise (val: any) {
return (
@@ -80,7 +80,7 @@ function deserializeNativeImage (value: any) {
}
export function serialize (value: any): any {
if (value instanceof NativeImage) {
if (value && value.constructor && value.constructor.name === 'NativeImage') {
return serializeNativeImage(value);
} if (Array.isArray(value)) {
return value.map(serialize);

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