mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
475 Commits
same-party
...
7-3-x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdaaee0218 | ||
|
|
3f2d5c217a | ||
|
|
ed88a0b4c2 | ||
|
|
28ed26dcb9 | ||
|
|
15f73decbf | ||
|
|
7345b90301 | ||
|
|
2a147f2951 | ||
|
|
2a56f72930 | ||
|
|
b91829394a | ||
|
|
5a0b619fc0 | ||
|
|
2a309753aa | ||
|
|
b7e1d5aca2 | ||
|
|
b786c25402 | ||
|
|
fc922cad4a | ||
|
|
ca0241e005 | ||
|
|
96357efc1e | ||
|
|
31101c7f93 | ||
|
|
6ca5c971bd | ||
|
|
3fd6c85427 | ||
|
|
298bcc864f | ||
|
|
ef805d7d3b | ||
|
|
341c5698ec | ||
|
|
5f93e88902 | ||
|
|
092706c43c | ||
|
|
13ed82a7d5 | ||
|
|
3b062b84b2 | ||
|
|
bcd0250866 | ||
|
|
f83f7760bb | ||
|
|
c77c902cc7 | ||
|
|
cbaaf6a34e | ||
|
|
4fb7b33b2b | ||
|
|
c2485e1181 | ||
|
|
cb71ab3222 | ||
|
|
3e6ee023a5 | ||
|
|
418049f70c | ||
|
|
3674762c36 | ||
|
|
669fd2bf88 | ||
|
|
91dfa1e5b2 | ||
|
|
bc8fc0d406 | ||
|
|
2db889b539 | ||
|
|
b181077654 | ||
|
|
60bdb56673 | ||
|
|
12cbca449c | ||
|
|
3a568ab070 | ||
|
|
bef1325a46 | ||
|
|
7deaef0be8 | ||
|
|
66ee5b8d7d | ||
|
|
fa81cd7621 | ||
|
|
8f502de1dc | ||
|
|
13d2090a54 | ||
|
|
ac7ec6012c | ||
|
|
e90a928a14 | ||
|
|
24db46d072 | ||
|
|
df2a249a6c | ||
|
|
5990d3404f | ||
|
|
d87c8a8183 | ||
|
|
512b9f2536 | ||
|
|
66b407ee22 | ||
|
|
cb2248e1f9 | ||
|
|
7fc14a8987 | ||
|
|
49e8c84c07 | ||
|
|
d2edf5899b | ||
|
|
22ebf4c142 | ||
|
|
7046b3c409 | ||
|
|
f450985d49 | ||
|
|
7de1fdddeb | ||
|
|
6f6ca442e6 | ||
|
|
2362fa0a0d | ||
|
|
35a4db080e | ||
|
|
c7f1319711 | ||
|
|
5b3267c400 | ||
|
|
0552e0d5de | ||
|
|
c87b474496 | ||
|
|
69683def0d | ||
|
|
8148b76efa | ||
|
|
7dfcb5ef04 | ||
|
|
0b3bf1e556 | ||
|
|
fd529ac30a | ||
|
|
3909001a00 | ||
|
|
039be2e407 | ||
|
|
fb6f60460b | ||
|
|
ca11175780 | ||
|
|
f45e2d2075 | ||
|
|
653576448b | ||
|
|
3dd4ac9ddc | ||
|
|
154d306e79 | ||
|
|
08f156cf61 | ||
|
|
a9bead22f6 | ||
|
|
0301ae9c77 | ||
|
|
959e80cc53 | ||
|
|
97010bf947 | ||
|
|
372c5b9518 | ||
|
|
4dc2c2800c | ||
|
|
ba1b75c218 | ||
|
|
5dac4531ca | ||
|
|
3cb788a030 | ||
|
|
9984dd9017 | ||
|
|
7efa4ccfb3 | ||
|
|
3bf5ad22f4 | ||
|
|
6e94fb3f9f | ||
|
|
9c92d87e2f | ||
|
|
8dcf7fc0b9 | ||
|
|
c7fd17a723 | ||
|
|
b68f8999ca | ||
|
|
35cb95b3d8 | ||
|
|
3b3cc0215a | ||
|
|
3cb2484184 | ||
|
|
bafcac7913 | ||
|
|
6a0c701a43 | ||
|
|
5ed3bd8724 | ||
|
|
779e3250c0 | ||
|
|
4c5197fe6e | ||
|
|
72a75889de | ||
|
|
242658102a | ||
|
|
7586295378 | ||
|
|
e7fce15152 | ||
|
|
456c9f09a2 | ||
|
|
f229d12056 | ||
|
|
4a2d94d031 | ||
|
|
df09e278a1 | ||
|
|
10f65979ff | ||
|
|
cac3884d75 | ||
|
|
b9734edfd1 | ||
|
|
ace3216392 | ||
|
|
12cb11f99b | ||
|
|
c16c4c25ed | ||
|
|
8e30e21ee6 | ||
|
|
a80f7fc81f | ||
|
|
83e7b00e7c | ||
|
|
eabb872345 | ||
|
|
b12090a129 | ||
|
|
0bf12e4e76 | ||
|
|
411b9f2ae7 | ||
|
|
a632f1f1b7 | ||
|
|
2543517443 | ||
|
|
5736df41c8 | ||
|
|
fbbe8afc3e | ||
|
|
6fdd055642 | ||
|
|
0f082ecd5c | ||
|
|
f29a600fd8 | ||
|
|
1bde50636f | ||
|
|
7d739a3608 | ||
|
|
8d3f0f47c1 | ||
|
|
dfd338d8bf | ||
|
|
dd242f4b28 | ||
|
|
4c61f62149 | ||
|
|
fd654afae9 | ||
|
|
cadddfec23 | ||
|
|
0e8aa5c252 | ||
|
|
edac24c9be | ||
|
|
0251f5aa93 | ||
|
|
cccd3778f8 | ||
|
|
680f3432ae | ||
|
|
420fb46121 | ||
|
|
6ef605eaa2 | ||
|
|
d7247bc77d | ||
|
|
5e6e118595 | ||
|
|
b1f19970e7 | ||
|
|
c30ecc38d0 | ||
|
|
14d9b8a8b4 | ||
|
|
f0d872947d | ||
|
|
40a212e132 | ||
|
|
cb9b3b29af | ||
|
|
da65c881e2 | ||
|
|
aa5e51dbe0 | ||
|
|
b5d38bfff0 | ||
|
|
a3054443f4 | ||
|
|
1f631ca337 | ||
|
|
e7576019e9 | ||
|
|
77448c75c6 | ||
|
|
538ebabf7e | ||
|
|
d17dfabfcb | ||
|
|
b6cb396893 | ||
|
|
4da56797c3 | ||
|
|
570e4f6a34 | ||
|
|
8c5a2c0a4a | ||
|
|
1c1e9fc7a7 | ||
|
|
0a3a0def25 | ||
|
|
ae9ee76e56 | ||
|
|
7908013a49 | ||
|
|
c7bb1d0300 | ||
|
|
4cf67fea52 | ||
|
|
2c29a4e93d | ||
|
|
04234f41f2 | ||
|
|
0799c8721c | ||
|
|
442174da0f | ||
|
|
ef94a79cac | ||
|
|
38afcff63a | ||
|
|
7ddfffb723 | ||
|
|
f8e601adaf | ||
|
|
dd27520637 | ||
|
|
85c07d0715 | ||
|
|
4dac36d111 | ||
|
|
ae27218296 | ||
|
|
c92efa4ec5 | ||
|
|
cbf499b18a | ||
|
|
7b1117e186 | ||
|
|
3bd3d9440a | ||
|
|
704cdd3a2b | ||
|
|
c56e5abd8c | ||
|
|
ec61041d41 | ||
|
|
32b51bce75 | ||
|
|
e0576ef11a | ||
|
|
a889ec7957 | ||
|
|
bef0dd868b | ||
|
|
3d74cacefe | ||
|
|
0b1248c8e6 | ||
|
|
0569c929a1 | ||
|
|
2dc42971ce | ||
|
|
577d0483e9 | ||
|
|
a3fde67056 | ||
|
|
79aebdffb7 | ||
|
|
fc800837bd | ||
|
|
58469efd1c | ||
|
|
80c36602fd | ||
|
|
f910704838 | ||
|
|
f17dc63c16 | ||
|
|
04b0d61323 | ||
|
|
544b4c5209 | ||
|
|
b35f37ea5a | ||
|
|
7f0bbd27b1 | ||
|
|
3b557bcf2e | ||
|
|
ef2f0580d9 | ||
|
|
425d2fa6f1 | ||
|
|
1409056baf | ||
|
|
72cb493023 | ||
|
|
1e7f26e5fd | ||
|
|
383e75796a | ||
|
|
1edfffae25 | ||
|
|
96e7b443fc | ||
|
|
8f728af13a | ||
|
|
281b0741f7 | ||
|
|
38c43ab48f | ||
|
|
d1d3d1bd1f | ||
|
|
89a7bdb566 | ||
|
|
09d59f2b83 | ||
|
|
ebec22045d | ||
|
|
a2c4000b84 | ||
|
|
9010838f21 | ||
|
|
6fe40ca797 | ||
|
|
101b971f36 | ||
|
|
6156254886 | ||
|
|
d0e4bd3fc9 | ||
|
|
3e5213b4e9 | ||
|
|
7ba8855ac1 | ||
|
|
c3e0ae9646 | ||
|
|
4b456bf3cb | ||
|
|
2a4bdfff98 | ||
|
|
c4baee0dab | ||
|
|
15688c6d6e | ||
|
|
aa4d7f1799 | ||
|
|
0476eb67ab | ||
|
|
69aebc05e5 | ||
|
|
98d06f6e5c | ||
|
|
7d321a90aa | ||
|
|
cfddc0a125 | ||
|
|
ade70463c1 | ||
|
|
4cb983ed40 | ||
|
|
79b3fcb2ab | ||
|
|
4cfa7be79d | ||
|
|
67bcc2a972 | ||
|
|
449bdcfda2 | ||
|
|
e70adc8f83 | ||
|
|
a6ae1ca755 | ||
|
|
b6fb8d3a63 | ||
|
|
5462a2c197 | ||
|
|
de428e9a7a | ||
|
|
fbe36e2365 | ||
|
|
5ac12c9e44 | ||
|
|
8d11391cb4 | ||
|
|
e82f453f71 | ||
|
|
abc187d651 | ||
|
|
913ef7890f | ||
|
|
bcb24bcdd1 | ||
|
|
d7ce600503 | ||
|
|
d16f29f7fc | ||
|
|
3b77af7ac3 | ||
|
|
0a241479cb | ||
|
|
1c8177c542 | ||
|
|
3f148fc3aa | ||
|
|
dd2310df48 | ||
|
|
9bc81be9d0 | ||
|
|
dc90c1153e | ||
|
|
7beb7b9a12 | ||
|
|
b6198b1648 | ||
|
|
16ff7b17a0 | ||
|
|
0d8be47c0f | ||
|
|
c4ab79e7ae | ||
|
|
49d91c7fc8 | ||
|
|
2b84b1ea43 | ||
|
|
4bb6f57657 | ||
|
|
db47a846da | ||
|
|
a3c951b14b | ||
|
|
c5abb17569 | ||
|
|
1b0ba71973 | ||
|
|
8dd004235b | ||
|
|
55955c3d79 | ||
|
|
5b33e9d9b7 | ||
|
|
6871b448ad | ||
|
|
7a1b3c5f2d | ||
|
|
2e8349c520 | ||
|
|
d63150502b | ||
|
|
daa61688b1 | ||
|
|
0a3b0391f4 | ||
|
|
1b66c03e41 | ||
|
|
92362da082 | ||
|
|
16e468137f | ||
|
|
b27a34eb91 | ||
|
|
18176b48b4 | ||
|
|
317f0cec68 | ||
|
|
49009236d9 | ||
|
|
976c7d122a | ||
|
|
ef548b6592 | ||
|
|
37c1df6ce1 | ||
|
|
d9068b7175 | ||
|
|
cac0a4fe3c | ||
|
|
e6807b387c | ||
|
|
f6f73a5a78 | ||
|
|
7bad919bee | ||
|
|
667db6fa67 | ||
|
|
022ee3793b | ||
|
|
5c0e8c5fd3 | ||
|
|
961436f300 | ||
|
|
e2c7e4b1ad | ||
|
|
d12b0eefe0 | ||
|
|
8dfb8c5589 | ||
|
|
57bb08e084 | ||
|
|
03e0905519 | ||
|
|
b6717a6644 | ||
|
|
41b32cf5d9 | ||
|
|
176f4728d7 | ||
|
|
0de78019c8 | ||
|
|
1926169c91 | ||
|
|
25b3ee29cf | ||
|
|
2228e5ac65 | ||
|
|
92cace3998 | ||
|
|
7dc858be02 | ||
|
|
7ffa127f0f | ||
|
|
f423312090 | ||
|
|
4cacd15730 | ||
|
|
37eeaf32ad | ||
|
|
b97c3aca2d | ||
|
|
2e07877c28 | ||
|
|
d8bd7654c6 | ||
|
|
f979e0cfbc | ||
|
|
1582824d33 | ||
|
|
71009fd61e | ||
|
|
055b5d274e | ||
|
|
1841742586 | ||
|
|
3841ac9b6d | ||
|
|
ed1c2a47f1 | ||
|
|
bcc4f67784 | ||
|
|
771d4dc374 | ||
|
|
b1fb7c7bfb | ||
|
|
725dac35ab | ||
|
|
6e32da3798 | ||
|
|
32ac551ca5 | ||
|
|
b90ac55d2d | ||
|
|
e797639d6d | ||
|
|
a31d041f03 | ||
|
|
6b70f0e8e6 | ||
|
|
b1357ab12b | ||
|
|
ffdec7a661 | ||
|
|
50822bc8d4 | ||
|
|
85164223cb | ||
|
|
5cb6ba2883 | ||
|
|
e23030b69e | ||
|
|
d417bbdcfd | ||
|
|
e6f92f3f30 | ||
|
|
3b59f12495 | ||
|
|
3083693e67 | ||
|
|
2a2f7e7090 | ||
|
|
f4c697d7a1 | ||
|
|
944690e79b | ||
|
|
ae303ade7d | ||
|
|
19162f4bac | ||
|
|
ee53f8cef6 | ||
|
|
4463715714 | ||
|
|
f6894da715 | ||
|
|
1657908fa8 | ||
|
|
26d059b3ea | ||
|
|
49aaa2fe96 | ||
|
|
0a9b201c34 | ||
|
|
e17cd837ef | ||
|
|
981208b66c | ||
|
|
77af675511 | ||
|
|
a92ee9443d | ||
|
|
23a52c02a3 | ||
|
|
8536f62826 | ||
|
|
312f14ee88 | ||
|
|
1f660c67c6 | ||
|
|
325bdfaf23 | ||
|
|
e3dcdba2db | ||
|
|
6b5a2c550a | ||
|
|
21e702a563 | ||
|
|
f79cfae3c2 | ||
|
|
51ffe25f45 | ||
|
|
0a0d7a8202 | ||
|
|
421490c6b3 | ||
|
|
068e12d909 | ||
|
|
d251642c83 | ||
|
|
27ceeb5e94 | ||
|
|
4ac616b681 | ||
|
|
fc677b7cf6 | ||
|
|
d25e511fc0 | ||
|
|
b4ba2f6d55 | ||
|
|
f967d73a13 | ||
|
|
c923aa2fde | ||
|
|
dec2078e54 | ||
|
|
2d16034854 | ||
|
|
4ce431829f | ||
|
|
430b355258 | ||
|
|
638ca05702 | ||
|
|
9e7cce3d2b | ||
|
|
c7a3142bab | ||
|
|
b6f5e26eab | ||
|
|
3330647834 | ||
|
|
bb8c7dfc7e | ||
|
|
4dea193854 | ||
|
|
080c2c9218 | ||
|
|
182f63d3a3 | ||
|
|
d3e0c461f6 | ||
|
|
b09a1c7607 | ||
|
|
7cc1efd2e6 | ||
|
|
324096aa37 | ||
|
|
d780a16d78 | ||
|
|
005ef96ac0 | ||
|
|
4f74be084a | ||
|
|
b7c2188d09 | ||
|
|
192326c7c5 | ||
|
|
1af064a144 | ||
|
|
6061fcce40 | ||
|
|
e68a9486da | ||
|
|
dd8bcb67ba | ||
|
|
27661d8e7b | ||
|
|
94ddcc4742 | ||
|
|
2515087322 | ||
|
|
36a5ba0d4d | ||
|
|
241b2a98af | ||
|
|
9b2e3d1d0b | ||
|
|
67cb05b435 | ||
|
|
3f5222b047 | ||
|
|
28e7e6492d | ||
|
|
23f7f63405 | ||
|
|
99eaa0d1bb | ||
|
|
543dbaf412 | ||
|
|
5901293628 | ||
|
|
30543cec74 | ||
|
|
66aeeac8df | ||
|
|
b03bc4de83 | ||
|
|
601c3f97ea | ||
|
|
d01a8eaa39 | ||
|
|
54e1c11b2b | ||
|
|
c1ad66b104 | ||
|
|
a905259646 | ||
|
|
356045ff57 | ||
|
|
a9f69bf732 | ||
|
|
3e181b8efc | ||
|
|
171c76e3cb | ||
|
|
0299f69fd1 | ||
|
|
0bdb7b164c | ||
|
|
ea9cfc127c | ||
|
|
0afb1a8188 | ||
|
|
f154dbda40 | ||
|
|
c3013d2e9f | ||
|
|
3272564eac | ||
|
|
a0f4632461 | ||
|
|
8ddf837d49 | ||
|
|
9822953d31 | ||
|
|
c6061cce41 | ||
|
|
a9602682c5 | ||
|
|
b7e5213afc | ||
|
|
9b70d9aa68 | ||
|
|
d9bfac32e3 | ||
|
|
9e815afc02 |
@@ -1,3 +1,46 @@
|
||||
version: 2.1
|
||||
|
||||
parameters:
|
||||
upload-to-s3:
|
||||
type: string
|
||||
default: '1'
|
||||
|
||||
run-lint:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
run-build-linux:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
run-build-mac:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
run-linux-x64-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-linux-ia32-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-linux-arm-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-linux-arm64-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-osx-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-mas-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
# The config expects the following environment variables to be set:
|
||||
# - "SLACK_WEBHOOK" Slack hook URL to send notifications.
|
||||
#
|
||||
@@ -41,6 +84,7 @@ env-release-build: &env-release-build
|
||||
GN_CONFIG: //electron/build/args/release.gn
|
||||
STRIP_BINARIES: true
|
||||
GENERATE_SYMBOLS: true
|
||||
CHECK_DIST_MANIFEST: '1'
|
||||
|
||||
env-headless-testing: &env-headless-testing
|
||||
DISPLAY: ':99.0'
|
||||
@@ -87,17 +131,32 @@ env-enable-sccache: &env-enable-sccache
|
||||
env-send-slack-notifications: &env-send-slack-notifications
|
||||
NOTIFY_SLACK: true
|
||||
|
||||
env-global: &env-global
|
||||
ELECTRON_OUT_DIR: Default
|
||||
|
||||
env-linux-medium: &env-linux-medium
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 3
|
||||
|
||||
env-linux-2xlarge: &env-linux-2xlarge
|
||||
NUMBER_OF_NINJA_PROCESSES: 18
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 34
|
||||
|
||||
env-linux-2xlarge-release: &env-linux-2xlarge-release
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 16
|
||||
|
||||
env-machine-mac: &env-machine-mac
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 6
|
||||
|
||||
env-mac-large: &env-mac-large
|
||||
NUMBER_OF_NINJA_PROCESSES: 10
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 18
|
||||
|
||||
env-mac-large-release: &env-mac-large-release
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 8
|
||||
|
||||
env-disable-crash-reporter-tests: &env-disable-crash-reporter-tests
|
||||
DISABLE_CRASH_REPORTER_TESTS: true
|
||||
@@ -158,7 +217,25 @@ step-gclient-sync: &step-gclient-sync
|
||||
$GCLIENT_EXTRA_ARGS \
|
||||
"$CIRCLE_REPOSITORY_URL"
|
||||
|
||||
gclient sync --with_branch_heads --with_tags
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 gclient sync --with_branch_heads --with_tags
|
||||
# Re-export all the patches to check if there were changes.
|
||||
python src/electron/script/export_all_patches.py src/electron/patches/config.json
|
||||
cd src/electron
|
||||
git update-index --refresh || true
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
# There are changes to the patches. Make a git commit with the updated patches
|
||||
git add patches
|
||||
GIT_COMMITTER_NAME="Electron Bot" GIT_COMMITTER_EMAIL="anonymous@electronjs.org" git commit -m "update patches" --author="Electron Bot <anonymous@electronjs.org>"
|
||||
# Export it
|
||||
mkdir -p ../../patches
|
||||
git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch
|
||||
echo
|
||||
echo "======================================================================"
|
||||
echo "There were changes to the patches when applying."
|
||||
echo "Check the CI artifacts for a patch you can apply to fix it."
|
||||
echo "======================================================================"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
step-setup-env-for-build: &step-setup-env-for-build
|
||||
@@ -174,7 +251,7 @@ step-setup-env-for-build: &step-setup-env-for-build
|
||||
echo 'export SCCACHE_PATH="'"$SCCACHE_PATH"'"' >> $BASH_ENV
|
||||
if [ "$CIRCLE_PR_NUMBER" != "" ]; then
|
||||
#if building a fork set readonly access to sccache
|
||||
echo 'export SCCACHE_BUCKET="electronjs-sccache"' >> $BASH_ENV
|
||||
echo 'export SCCACHE_BUCKET="electronjs-sccache-ci"' >> $BASH_ENV
|
||||
echo 'export SCCACHE_TWO_TIER=true' >> $BASH_ENV
|
||||
fi
|
||||
fi
|
||||
@@ -186,6 +263,13 @@ step-restore-brew-cache: &step-restore-brew-cache
|
||||
keys:
|
||||
- v1-brew-cache-{{ arch }}
|
||||
|
||||
step-save-brew-cache: &step-save-brew-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- /usr/local/Homebrew
|
||||
key: v1-brew-cache-{{ arch }}
|
||||
name: Persisting brew cache
|
||||
|
||||
step-get-more-space-on-mac: &step-get-more-space-on-mac
|
||||
run:
|
||||
name: Free up space on MacOS
|
||||
@@ -236,7 +320,7 @@ step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
cd src/electron
|
||||
./script/codesign/import-testing-cert-ci.sh
|
||||
./script/codesign/generate-identity.sh
|
||||
fi
|
||||
|
||||
step-install-gnutar-on-mac: &step-install-gnutar-on-mac
|
||||
@@ -278,9 +362,18 @@ step-maybe-electron-dist-strip: &step-maybe-electron-dist-strip
|
||||
run:
|
||||
name: Strip electron binaries
|
||||
command: |
|
||||
if [ "$STRIP_BINARIES" == "true" ] && [ "`uname`" != "Darwin" ]; then
|
||||
if [ "$STRIP_BINARIES" == "true" ] && [ "`uname`" == "Linux" ]; then
|
||||
if [ x"$TARGET_ARCH" == x ]; then
|
||||
target_cpu=x64
|
||||
elif [ "$TARGET_ARCH" == "ia32" ]; then
|
||||
target_cpu=x86
|
||||
else
|
||||
target_cpu="$TARGET_ARCH"
|
||||
fi
|
||||
cd src
|
||||
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH"
|
||||
electron/script/copy-debug-symbols.py --target-cpu="$target_cpu" --out-dir=out/Default/debug --compress
|
||||
electron/script/strip-binaries.py --target-cpu="$target_cpu"
|
||||
electron/script/add-debug-link.py --target-cpu="$target_cpu" --debug-dir=out/Default/debug
|
||||
fi
|
||||
|
||||
step-electron-dist-build: &step-electron-dist-build
|
||||
@@ -323,7 +416,9 @@ step-electron-chromedriver-build: &step-electron-chromedriver-build
|
||||
command: |
|
||||
cd src
|
||||
ninja -C out/Default chrome/test/chromedriver -j $NUMBER_OF_NINJA_PROCESSES
|
||||
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver
|
||||
if [ "`uname`" == "Linux" ]; then
|
||||
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver
|
||||
fi
|
||||
ninja -C out/Default electron:electron_chromedriver_zip
|
||||
|
||||
step-electron-chromedriver-store: &step-electron-chromedriver-store
|
||||
@@ -443,13 +538,17 @@ step-mksnapshot-build: &step-mksnapshot-build
|
||||
name: mksnapshot build
|
||||
command: |
|
||||
cd src
|
||||
ninja -C out/Default electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES
|
||||
if [ "`uname`" != "Darwin" ]; then
|
||||
if [ "$TARGET_ARCH" == "arm" ]; then
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/mksnapshot
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/v8_context_snapshot_generator
|
||||
elif [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x64_v8_arm64/mksnapshot
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x64_v8_arm64/v8_context_snapshot_generator
|
||||
else
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/mksnapshot
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/v8_context_snapshot_generator
|
||||
fi
|
||||
fi
|
||||
ninja -C out/Default electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
@@ -466,6 +565,8 @@ step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
|
||||
if [ "$GENERATE_SYMBOLS" == "true" ]; then
|
||||
cd src
|
||||
ninja -C out/Default electron:electron_symbols
|
||||
cd out/Default/breakpad_symbols
|
||||
find . -name \*.sym -print0 | xargs -0 npx @sentry/cli@1.51.1 difutil bundle-sources
|
||||
fi
|
||||
|
||||
step-maybe-zip-symbols: &step-maybe-zip-symbols
|
||||
@@ -535,6 +636,93 @@ step-ninja-summary: &step-ninja-summary
|
||||
command: |
|
||||
python depot_tools/post_build_ninja_summary.py -C src/out/Default
|
||||
|
||||
# Checkout Steps
|
||||
step-generate-deps-hash: &step-generate-deps-hash
|
||||
run:
|
||||
name: Generate DEPS Hash
|
||||
command: node src/electron/script/generate-deps-hash.js
|
||||
|
||||
step-touch-sync-done: &step-touch-sync-done
|
||||
run:
|
||||
name: Touch Sync Done
|
||||
command: touch src/electron/.circle-sync-done
|
||||
|
||||
# Restore exact src cache based on the hash of DEPS and patches/*
|
||||
# If no cache is matched EXACTLY then the .circle-sync-done file is empty
|
||||
# If a cache is matched EXACTLY then the .circle-sync-done file contains "done"
|
||||
step-maybe-restore-src-cache: &step-maybe-restore-src-cache
|
||||
restore_cache:
|
||||
paths:
|
||||
- ./src
|
||||
keys:
|
||||
- v5-src-cache-{{ arch }}-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache
|
||||
|
||||
# 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
|
||||
# If the src cache was not restored above then this will match a close git cache
|
||||
step-maybe-restore-git-cache: &step-maybe-restore-git-cache
|
||||
restore_cache:
|
||||
paths:
|
||||
- ~/.gclient-cache
|
||||
keys:
|
||||
- v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
- v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }}
|
||||
name: Conditionally restoring git cache
|
||||
|
||||
step-set-git-cache-path: &step-set-git-cache-path
|
||||
run:
|
||||
name: Set GIT_CACHE_PATH to make gclient to use the cache
|
||||
command: |
|
||||
# CircleCI does not support interpolation when setting environment variables.
|
||||
# https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-shell-command
|
||||
echo 'export GIT_CACHE_PATH="$HOME/.gclient-cache"' >> $BASH_ENV
|
||||
|
||||
# Persist the git cache based on the hash of DEPS and .circle-sync-done
|
||||
# If the src cache was restored above then this will persist an empty cache
|
||||
step-save-git-cache: &step-save-git-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- ~/.gclient-cache
|
||||
key: v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
name: Persisting git cache
|
||||
|
||||
step-run-electron-only-hooks: &step-run-electron-only-hooks
|
||||
run:
|
||||
name: Run Electron Only Hooks
|
||||
command: gclient runhooks --spec="solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]"
|
||||
|
||||
step-generate-deps-hash-cleanly: &step-generate-deps-hash-cleanly
|
||||
run:
|
||||
name: Generate DEPS Hash
|
||||
command: (cd src/electron && git checkout .) && node src/electron/script/generate-deps-hash.js
|
||||
|
||||
# Mark the sync as done for future cache saving
|
||||
step-mark-sync-done: &step-mark-sync-done
|
||||
run:
|
||||
name: Mark Sync Done
|
||||
command: echo DONE > src/electron/.circle-sync-done
|
||||
|
||||
# Minimize the size of the cache
|
||||
step-minimize-workspace-size-from-checkout: &step-minimize-workspace-size-from-checkout
|
||||
run:
|
||||
name: Remove some unused data to avoid storing it in the workspace/cache
|
||||
command: |
|
||||
rm -rf src/android_webview
|
||||
rm -rf src/ios
|
||||
rm -rf src/third_party/blink/web_tests
|
||||
rm -rf src/third_party/blink/perf_tests
|
||||
rm -rf src/third_party/hunspell_dictionaries
|
||||
rm -rf src/third_party/WebKit/LayoutTests
|
||||
|
||||
# Save the src cache based on the deps hash
|
||||
step-save-src-cache: &step-save-src-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- ./src
|
||||
key: v5-src-cache-{{ arch }}-{{ checksum "src/electron/.depshash" }}
|
||||
name: Persisting src cache
|
||||
|
||||
# Lists of steps.
|
||||
steps-lint: &steps-lint
|
||||
steps:
|
||||
@@ -551,7 +739,7 @@ steps-lint: &steps-lint
|
||||
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
|
||||
gn_version="$(curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
|
||||
|
||||
cipd ensure -ensure-file - -root . <<-CIPD
|
||||
cipd ensure -ensure-file - -root . \<<-CIPD
|
||||
\$ServiceURL https://chrome-infra-packages.appspot.com/
|
||||
@Subdir src/buildtools/linux64
|
||||
gn/gn/linux-amd64 $gn_version
|
||||
@@ -580,7 +768,7 @@ steps-lint: &steps-lint
|
||||
node script/yarn install --frozen-lockfile
|
||||
node script/yarn lint
|
||||
|
||||
steps-checkout: &steps-checkout
|
||||
steps-checkout-fast: &steps-checkout-fast
|
||||
steps:
|
||||
- *step-checkout-electron
|
||||
- *step-depot-tools-get
|
||||
@@ -589,88 +777,61 @@ steps-checkout: &steps-checkout
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-install-gnutar-on-mac
|
||||
|
||||
- run:
|
||||
name: Generate DEPS Hash
|
||||
command: node src/electron/script/generate-deps-hash.js
|
||||
- run:
|
||||
name: Touch Sync Done
|
||||
command: touch src/electron/.circle-sync-done
|
||||
# Restore exact src cache based on the hash of DEPS and patches/*
|
||||
# If no cache is matched EXACTLY then the .circle-sync-done file is empty
|
||||
# If a cache is matched EXACTLY then the .circle-sync-done file contains "done"
|
||||
- restore_cache:
|
||||
paths:
|
||||
- ./src
|
||||
keys:
|
||||
- v5-src-cache-{{ arch }}-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache
|
||||
# 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
|
||||
# If the src cache was not restored above then this will match a close git cache
|
||||
- restore_cache:
|
||||
paths:
|
||||
- ~/.gclient-cache
|
||||
keys:
|
||||
- v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
- v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }}
|
||||
name: Conditionally restoring git cache
|
||||
- run:
|
||||
name: Set GIT_CACHE_PATH to make gclient to use the cache
|
||||
command: |
|
||||
# CircleCI does not support interpolation when setting environment variables.
|
||||
# https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-shell-command
|
||||
echo 'export GIT_CACHE_PATH="$HOME/.gclient-cache"' >> $BASH_ENV
|
||||
- *step-generate-deps-hash
|
||||
- *step-touch-sync-done
|
||||
- *step-maybe-restore-src-cache
|
||||
- *step-maybe-restore-git-cache
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
# Persist the git cache based on the hash of DEPS and .circle-sync-done
|
||||
# If the src cache was restored above then this will persist an empty cache
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.gclient-cache
|
||||
key: v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
name: Persisting git cache
|
||||
- store_artifacts:
|
||||
path: patches
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- run:
|
||||
name: Wipe Electron
|
||||
command: rm -rf src/electron
|
||||
- *step-checkout-electron
|
||||
- run:
|
||||
name: Run Electron Only Hooks
|
||||
command: gclient runhooks --spec="solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]"
|
||||
- run:
|
||||
name: Generate DEPS Hash
|
||||
command: (cd src/electron && git checkout .) && node src/electron/script/generate-deps-hash.js
|
||||
# Mark the sync as done for future cache saving
|
||||
- run:
|
||||
name: Mark Sync Done
|
||||
command: echo DONE > src/electron/.circle-sync-done
|
||||
# Minimize the size of the cache
|
||||
- run:
|
||||
name: Remove some unused data to avoid storing it in the workspace/cache
|
||||
command: |
|
||||
rm -rf src/android_webview
|
||||
rm -rf src/ios
|
||||
rm -rf src/third_party/blink/web_tests
|
||||
rm -rf src/third_party/blink/perf_tests
|
||||
rm -rf src/third_party/hunspell_dictionaries
|
||||
rm -rf src/third_party/WebKit/LayoutTests
|
||||
# Save the src cache based on the deps hash
|
||||
- save_cache:
|
||||
paths:
|
||||
- ./src
|
||||
key: v5-src-cache-{{ arch }}-{{ checksum "src/electron/.depshash" }}
|
||||
name: Persisting src cache
|
||||
- save_cache:
|
||||
paths:
|
||||
- /usr/local/Homebrew
|
||||
key: v1-brew-cache-{{ arch }}
|
||||
name: Persisting brew cache
|
||||
- *step-run-electron-only-hooks
|
||||
- *step-generate-deps-hash-cleanly
|
||||
- *step-mark-sync-done
|
||||
- *step-minimize-workspace-size-from-checkout
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- depot_tools
|
||||
- src
|
||||
|
||||
steps-checkout-and-save-cache: &steps-checkout-and-save-cache
|
||||
steps:
|
||||
- *step-checkout-electron
|
||||
- *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-generate-deps-hash
|
||||
- *step-touch-sync-done
|
||||
- *step-maybe-restore-src-cache
|
||||
- *step-maybe-restore-git-cache
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
- store_artifacts:
|
||||
path: patches
|
||||
- *step-save-git-cache
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- run:
|
||||
name: Wipe Electron
|
||||
command: rm -rf src/electron
|
||||
- *step-checkout-electron
|
||||
- *step-run-electron-only-hooks
|
||||
- *step-generate-deps-hash-cleanly
|
||||
- *step-mark-sync-done
|
||||
- *step-minimize-workspace-size-from-checkout
|
||||
- *step-save-src-cache
|
||||
- *step-save-brew-cache
|
||||
|
||||
steps-electron-gn-check: &steps-electron-gn-check
|
||||
steps:
|
||||
- attach_workspace:
|
||||
@@ -707,9 +868,11 @@ steps-electron-build-for-tests: &steps-electron-build-for-tests
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-setup-env-for-build
|
||||
- *step-restore-brew-cache
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-install-npm-deps-on-mac
|
||||
- *step-fix-sync-on-mac
|
||||
- *step-gn-gen-default
|
||||
- *step-delete-git-directories
|
||||
|
||||
# Electron app
|
||||
- *step-electron-build
|
||||
@@ -880,7 +1043,6 @@ steps-tests: &steps-tests
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
||||
command: |
|
||||
cd src
|
||||
export ELECTRON_OUT_DIR=Default
|
||||
(cd electron && node script/yarn test -- --ci --enable-logging)
|
||||
- run:
|
||||
name: Check test results existence
|
||||
@@ -913,7 +1075,6 @@ steps-test-nan: &steps-test-nan
|
||||
name: Run Nan Tests
|
||||
command: |
|
||||
cd src
|
||||
export ELECTRON_OUT_DIR=Default
|
||||
node electron/script/nan-spec-runner.js
|
||||
|
||||
steps-test-node: &steps-test-node
|
||||
@@ -928,7 +1089,6 @@ steps-test-node: &steps-test-node
|
||||
name: Run Node Tests
|
||||
command: |
|
||||
cd src
|
||||
export ELECTRON_OUT_DIR=Default
|
||||
node electron/script/node-spec-runner.js junit
|
||||
- store_test_results:
|
||||
path: src/junit
|
||||
@@ -937,7 +1097,6 @@ chromium-upgrade-branches: &chromium-upgrade-branches
|
||||
/chromium\-upgrade\/[0-9]+/
|
||||
|
||||
# List of all jobs.
|
||||
version: 2
|
||||
jobs:
|
||||
# Layer 0: Lint. Standalone.
|
||||
lint:
|
||||
@@ -947,33 +1106,47 @@ jobs:
|
||||
<<: *steps-lint
|
||||
|
||||
# Layer 1: Checkout.
|
||||
linux-checkout:
|
||||
linux-checkout-fast:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-checkout
|
||||
<<: *steps-checkout-fast
|
||||
|
||||
linux-checkout-and-save-cache:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-checkout-and-save-cache
|
||||
|
||||
linux-checkout-for-native-tests:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_pyyaml=True'
|
||||
<<: *steps-checkout
|
||||
<<: *steps-checkout-fast
|
||||
|
||||
linux-checkout-for-native-tests-with-no-patches:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=apply_patches=False --custom-var=checkout_pyyaml=True'
|
||||
<<: *steps-checkout
|
||||
<<: *steps-checkout-fast
|
||||
|
||||
mac-checkout:
|
||||
mac-checkout-fast:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
|
||||
<<: *steps-checkout
|
||||
<<: *steps-checkout-fast
|
||||
|
||||
mac-checkout-and-save-cache:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
|
||||
<<: *steps-checkout-and-save-cache
|
||||
|
||||
# Layer 2: Builds.
|
||||
linux-x64-debug:
|
||||
@@ -1040,9 +1213,11 @@ jobs:
|
||||
linux-x64-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-linux-2xlarge-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
linux-ia32-debug:
|
||||
@@ -1089,10 +1264,12 @@ jobs:
|
||||
linux-ia32-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-linux-2xlarge-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-ia32
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
linux-arm-debug:
|
||||
@@ -1140,10 +1317,12 @@ jobs:
|
||||
linux-arm-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
linux-arm64-debug:
|
||||
@@ -1207,10 +1386,12 @@ jobs:
|
||||
linux-arm64-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm64
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
osx-testing:
|
||||
@@ -1222,6 +1403,15 @@ jobs:
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
|
||||
osx-debug:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
<<: *env-debug-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
|
||||
osx-debug-gn-check:
|
||||
<<: *machine-mac
|
||||
environment:
|
||||
@@ -1257,9 +1447,11 @@ jobs:
|
||||
osx-publish:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
mas-testing:
|
||||
@@ -1272,6 +1464,16 @@ jobs:
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
|
||||
mas-debug:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
<<: *env-mas
|
||||
<<: *env-debug-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
|
||||
mas-debug-gn-check:
|
||||
<<: *machine-mac
|
||||
environment:
|
||||
@@ -1310,10 +1512,12 @@ jobs:
|
||||
mas-publish:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-mas
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
# Layer 3: Tests.
|
||||
@@ -1569,29 +1773,75 @@ jobs:
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
# The publish workflows below each contain one job so that they are
|
||||
# compatible with how sudowoodo works today. If these workflows are
|
||||
# changed to have multiple jobs, then scripts/release/ci-release-build.js
|
||||
# will need to be updated and there will most likely need to be changes to
|
||||
# sudowoodo
|
||||
|
||||
publish-x64-linux:
|
||||
when: << pipeline.parameters.run-linux-x64-publish >>
|
||||
jobs:
|
||||
- linux-x64-publish:
|
||||
context: release-env
|
||||
|
||||
publish-ia32-linux:
|
||||
when: << pipeline.parameters.run-linux-ia32-publish >>
|
||||
jobs:
|
||||
- linux-ia32-publish:
|
||||
context: release-env
|
||||
|
||||
publish-arm-linux:
|
||||
when: << pipeline.parameters.run-linux-arm-publish >>
|
||||
jobs:
|
||||
- linux-arm-publish:
|
||||
context: release-env
|
||||
|
||||
publish-arm64-linux:
|
||||
when: << pipeline.parameters.run-linux-arm64-publish >>
|
||||
jobs:
|
||||
- linux-arm64-publish:
|
||||
context: release-env
|
||||
|
||||
publish-osx:
|
||||
when: << pipeline.parameters.run-osx-publish >>
|
||||
jobs:
|
||||
- osx-publish:
|
||||
context: release-env
|
||||
|
||||
publish-mas:
|
||||
when: << pipeline.parameters.run-mas-publish >>
|
||||
jobs:
|
||||
- mas-publish:
|
||||
context: release-env
|
||||
|
||||
lint:
|
||||
when: << pipeline.parameters.run-lint >>
|
||||
jobs:
|
||||
- lint
|
||||
|
||||
build-linux:
|
||||
when: << pipeline.parameters.run-build-linux >>
|
||||
jobs:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-checkout-and-save-cache
|
||||
|
||||
- linux-x64-debug:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-debug-gn-check:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-testing:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-testing-no-run-as-node:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-testing-gn-check:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-testing-tests:
|
||||
requires:
|
||||
- linux-x64-testing
|
||||
@@ -1604,10 +1854,10 @@ workflows:
|
||||
|
||||
- linux-ia32-debug:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-ia32-testing:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-ia32-testing-tests:
|
||||
requires:
|
||||
- linux-ia32-testing
|
||||
@@ -1620,37 +1870,45 @@ workflows:
|
||||
|
||||
- linux-arm-debug:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm-testing:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
|
||||
- linux-arm64-debug:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-debug-gn-check:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-testing:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-testing-gn-check:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
|
||||
build-mac:
|
||||
when: << pipeline.parameters.run-build-mac >>
|
||||
jobs:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- mac-checkout-and-save-cache
|
||||
|
||||
- osx-testing:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
|
||||
- osx-debug:
|
||||
requires:
|
||||
- mac-checkout-fast
|
||||
|
||||
- osx-debug-gn-check:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
|
||||
- osx-testing-gn-check:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
|
||||
- osx-testing-tests:
|
||||
requires:
|
||||
@@ -1658,14 +1916,19 @@ workflows:
|
||||
|
||||
- mas-testing:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
|
||||
- mas-debug:
|
||||
requires:
|
||||
- mac-checkout-fast
|
||||
|
||||
- mas-debug-gn-check:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
|
||||
- mas-testing-gn-check:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
|
||||
- mas-testing-tests:
|
||||
requires:
|
||||
@@ -1681,11 +1944,11 @@ workflows:
|
||||
- master
|
||||
- *chromium-upgrade-branches
|
||||
jobs:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
|
||||
- linux-x64-release:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-release-tests:
|
||||
requires:
|
||||
- linux-x64-release
|
||||
@@ -1697,7 +1960,7 @@ workflows:
|
||||
- linux-x64-release
|
||||
- linux-x64-chromedriver:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-release-summary:
|
||||
requires:
|
||||
- linux-x64-release
|
||||
@@ -1707,7 +1970,7 @@ workflows:
|
||||
|
||||
- linux-ia32-release:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-ia32-release-tests:
|
||||
requires:
|
||||
- linux-ia32-release
|
||||
@@ -1719,7 +1982,7 @@ workflows:
|
||||
- linux-ia32-release
|
||||
- linux-ia32-chromedriver:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-ia32-release-summary:
|
||||
requires:
|
||||
- linux-ia32-release
|
||||
@@ -1729,10 +1992,10 @@ workflows:
|
||||
|
||||
- linux-arm-release:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm-chromedriver:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm-release-summary:
|
||||
requires:
|
||||
- linux-arm-release
|
||||
@@ -1741,10 +2004,10 @@ workflows:
|
||||
|
||||
- linux-arm64-release:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-chromedriver:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-release-summary:
|
||||
requires:
|
||||
- linux-arm64-release
|
||||
@@ -1760,11 +2023,11 @@ workflows:
|
||||
- master
|
||||
- *chromium-upgrade-branches
|
||||
jobs:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
|
||||
- osx-release:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- osx-release-tests:
|
||||
requires:
|
||||
- osx-release
|
||||
@@ -1776,7 +2039,7 @@ workflows:
|
||||
- osx-release
|
||||
- osx-chromedriver:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- osx-release-summary:
|
||||
requires:
|
||||
- osx-release
|
||||
@@ -1786,7 +2049,7 @@ workflows:
|
||||
|
||||
- mas-release:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- mas-release-tests:
|
||||
requires:
|
||||
- mas-release
|
||||
@@ -1798,7 +2061,7 @@ workflows:
|
||||
- mas-release
|
||||
- mas-chromedriver:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- mas-release-summary:
|
||||
requires:
|
||||
- mas-release
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
"browser": true
|
||||
},
|
||||
"rules": {
|
||||
"semi": ["error", "always"],
|
||||
"no-var": "error",
|
||||
"no-unused-vars": 0,
|
||||
"no-global-assign": 0,
|
||||
"guard-for-in": 2,
|
||||
"@typescript-eslint/no-unused-vars": ["error", {
|
||||
"vars": "all",
|
||||
"args": "after-used",
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
||||
# `git apply` and friends don't understand CRLF, even on windows. Force those
|
||||
# files to be checked out with LF endings even if core.autocrlf is true.
|
||||
*.patch text eol=lf
|
||||
patches/**/.patches merge=union
|
||||
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -11,6 +11,7 @@
|
||||
|
||||
# Upgrades WG
|
||||
/patches/ @electron/wg-upgrades
|
||||
DEPS @electron/wg-upgrades
|
||||
|
||||
# Docs & Tooling WG
|
||||
/default_app/ @electron/wg-docs-tools
|
||||
|
||||
46
BUILD.gn
46
BUILD.gn
@@ -26,6 +26,7 @@ if (is_mac) {
|
||||
import("//third_party/icu/config.gni")
|
||||
import("//ui/gl/features.gni")
|
||||
import("//v8/gni/v8.gni")
|
||||
import("build/rules.gni")
|
||||
}
|
||||
|
||||
if (is_linux) {
|
||||
@@ -300,14 +301,6 @@ source_set("manifests") {
|
||||
"//printing/buildflags",
|
||||
"//services/service_manager/public/cpp",
|
||||
]
|
||||
|
||||
if (enable_basic_printing) {
|
||||
deps += [ "//components/services/pdf_compositor/public/cpp:manifest" ]
|
||||
}
|
||||
|
||||
if (enable_print_preview) {
|
||||
deps += [ "//chrome/services/printing/public/cpp:manifest" ]
|
||||
}
|
||||
}
|
||||
|
||||
npm_action("electron_version_args") {
|
||||
@@ -354,8 +347,11 @@ source_set("electron_lib") {
|
||||
"//base:base_static",
|
||||
"//base/allocator:buildflags",
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//chrome/services/printing/public/mojom",
|
||||
"//components/certificate_transparency",
|
||||
"//components/net_log",
|
||||
"//components/network_hints/common",
|
||||
"//components/network_hints/renderer",
|
||||
"//components/network_session_configurator/common",
|
||||
"//components/prefs",
|
||||
"//components/spellcheck/renderer",
|
||||
@@ -371,7 +367,7 @@ source_set("electron_lib") {
|
||||
"//device/bluetooth/public/cpp",
|
||||
"//gin",
|
||||
"//media/capture/mojom:video_capture",
|
||||
"//media/mojo/interfaces",
|
||||
"//media/mojo/mojom",
|
||||
"//net:extras",
|
||||
"//net:net_resources",
|
||||
"//net:net_with_v8",
|
||||
@@ -384,7 +380,7 @@ source_set("electron_lib") {
|
||||
"//services/device/public/mojom",
|
||||
"//services/proxy_resolver:lib",
|
||||
"//services/video_capture/public/mojom:constants",
|
||||
"//services/viz/privileged/interfaces/compositing",
|
||||
"//services/viz/privileged/mojom/compositing",
|
||||
"//skia",
|
||||
"//third_party/blink/public:blink",
|
||||
"//third_party/boringssl",
|
||||
@@ -579,15 +575,16 @@ source_set("electron_lib") {
|
||||
]
|
||||
deps += [
|
||||
"//components/viz/service",
|
||||
"//services/viz/public/interfaces",
|
||||
"//services/viz/public/mojom",
|
||||
"//ui/compositor",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_desktop_capturer) {
|
||||
if (is_component_build && is_win) {
|
||||
if (is_component_build && !is_linux) {
|
||||
# On windows the implementation relies on unexported
|
||||
# DxgiDuplicatorController class.
|
||||
# DxgiDuplicatorController class. On macOS the implementation
|
||||
# relies on unexported webrtc::GetWindowOwnerPid method.
|
||||
deps += [ "//third_party/webrtc/modules/desktop_capture" ]
|
||||
}
|
||||
sources += [
|
||||
@@ -659,6 +656,12 @@ if (is_mac) {
|
||||
electron_framework_version = "A"
|
||||
electron_version = read_file("ELECTRON_VERSION", "trim string")
|
||||
|
||||
mac_xib_bundle_data("electron_xibs") {
|
||||
sources = [
|
||||
"shell/common/resources/mac/MainMenu.xib",
|
||||
]
|
||||
}
|
||||
|
||||
bundle_data("electron_framework_resources") {
|
||||
public_deps = [
|
||||
":packed_resources",
|
||||
@@ -773,6 +776,7 @@ if (is_mac) {
|
||||
":electron_framework_libraries",
|
||||
":electron_framework_resources",
|
||||
":electron_swiftshader_library",
|
||||
":electron_xibs",
|
||||
]
|
||||
if (!is_mas_build) {
|
||||
deps += [ ":electron_crashpad_helper" ]
|
||||
@@ -1285,12 +1289,18 @@ dist_zip("electron_chromedriver_zip") {
|
||||
]
|
||||
}
|
||||
|
||||
mksnapshot_deps = [
|
||||
":licenses",
|
||||
"//tools/v8_context_snapshot:v8_context_snapshot_generator($v8_snapshot_toolchain)",
|
||||
"//v8:mksnapshot($v8_snapshot_toolchain)",
|
||||
]
|
||||
|
||||
group("electron_mksnapshot") {
|
||||
public_deps = mksnapshot_deps
|
||||
}
|
||||
|
||||
dist_zip("electron_mksnapshot_zip") {
|
||||
data_deps = [
|
||||
"//v8:mksnapshot($v8_snapshot_toolchain)",
|
||||
"//tools/v8_context_snapshot:v8_context_snapshot_generator",
|
||||
":licenses",
|
||||
]
|
||||
data_deps = mksnapshot_deps
|
||||
outputs = [
|
||||
"$root_build_dir/mksnapshot.zip",
|
||||
]
|
||||
|
||||
13
DEPS
13
DEPS
@@ -5,14 +5,15 @@ gclient_gn_args = [
|
||||
'checkout_android_native_support',
|
||||
'checkout_libaom',
|
||||
'checkout_nacl',
|
||||
'checkout_oculus_sdk'
|
||||
'checkout_oculus_sdk',
|
||||
'checkout_openxr'
|
||||
]
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'9eecb7a9f652bbf84f6437b49c70922b65b38bf3',
|
||||
'78.0.3904.130',
|
||||
'node_version':
|
||||
'v12.6.0',
|
||||
'v12.8.1',
|
||||
'nan_version':
|
||||
'2ee313aaca52e2b478965ac50eb5082520380d1b',
|
||||
|
||||
@@ -60,6 +61,8 @@ vars = {
|
||||
True,
|
||||
'checkout_oculus_sdk':
|
||||
False,
|
||||
'checkout_openxr':
|
||||
False,
|
||||
'build_with_chromium':
|
||||
True,
|
||||
'checkout_android':
|
||||
@@ -111,7 +114,7 @@ hooks = [
|
||||
'pattern': 'src/electron/script/update-external-binaries.py',
|
||||
'condition': 'download_external_binaries',
|
||||
'action': [
|
||||
'python',
|
||||
'python3',
|
||||
'src/electron/script/update-external-binaries.py',
|
||||
],
|
||||
},
|
||||
@@ -149,3 +152,5 @@ hooks = [
|
||||
recursedeps = [
|
||||
'src',
|
||||
]
|
||||
|
||||
# Touch DEPS to bust cache
|
||||
|
||||
@@ -1 +1 @@
|
||||
7.0.0-nightly.20190731
|
||||
7.3.3
|
||||
10
appveyor.yml
10
appveyor.yml
@@ -60,7 +60,7 @@ build_script:
|
||||
- ps: $env:SCCACHE_PATH="$pwd\src\electron\external_binaries\sccache.exe"
|
||||
- ps: >-
|
||||
if ($env:GN_CONFIG -eq 'release') {
|
||||
$env:GCLIENT_EXTRA_ARGS="--custom-var=checkout_boto=True --custom-var=checkout_requests=True"
|
||||
$env:GCLIENT_EXTRA_ARGS="$env:GCLIENT_EXTRA_ARGS --custom-var=checkout_boto=True --custom-var=checkout_requests=True"
|
||||
} else {
|
||||
$env:NINJA_STATUS="[%r processes, %f/%t @ %o/s : %es] "
|
||||
}
|
||||
@@ -89,6 +89,10 @@ build_script:
|
||||
- appveyor PushArtifact out/Default/dist.zip
|
||||
- appveyor PushArtifact out/Default/chromedriver.zip
|
||||
- appveyor PushArtifact out/ffmpeg/ffmpeg.zip
|
||||
- 7z a node_headers.zip out\Default\gen\node_headers
|
||||
- appveyor PushArtifact node_headers.zip
|
||||
- appveyor PushArtifact out/Default/mksnapshot.zip
|
||||
- appveyor PushArtifact out/Default/electron.lib
|
||||
- ps: >-
|
||||
if ($env:GN_CONFIG -eq 'release') {
|
||||
# Needed for msdia140.dll on 64-bit windows
|
||||
@@ -105,7 +109,7 @@ test_script:
|
||||
# Workaround for https://github.com/appveyor/ci/issues/2420
|
||||
- set "PATH=%PATH%;C:\Program Files\Git\mingw64\libexec\git-core"
|
||||
- ps: >-
|
||||
if ((-Not (Test-Path Env:\ELECTRON_RELEASE)) -And ($env:GN_CONFIG -in "testing", "release")) {
|
||||
if ((-Not (Test-Path Env:\TEST_WOA)) -And (-Not (Test-Path Env:\ELECTRON_RELEASE)) -And ($env:GN_CONFIG -in "testing", "release")) {
|
||||
$env:RUN_TESTS="true"
|
||||
}
|
||||
- ps: >-
|
||||
@@ -133,4 +137,6 @@ deploy_script:
|
||||
Write-Output "Uploading Electron release distribution to github releases"
|
||||
& python script\release\uploaders\upload.py
|
||||
}
|
||||
} elseif (Test-Path Env:\TEST_WOA) {
|
||||
node script/release/ci-release-build.js --job=electron-woa-testing --ci=VSTS --armTest --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
|
||||
}
|
||||
|
||||
85
azure-pipelines-woa.yml
Normal file
85
azure-pipelines-woa.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
steps:
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy Files to: src\electron'
|
||||
inputs:
|
||||
TargetFolder: src\electron
|
||||
|
||||
- script: |
|
||||
cd src\electron
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
displayName: 'Yarn install'
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$pwd\dist.zip"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/dist.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -osrc\out\Default -y $localArtifactPath
|
||||
displayName: 'Download and extract dist.zip for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$pwd\ffmpeg.zip"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/ffmpeg.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -osrc\out\ffmpeg $localArtifactPath
|
||||
displayName: 'Download and extract ffmpeg.zip for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$pwd\src\node_headers.zip"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/node_headers.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
cd src
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y node_headers.zip
|
||||
displayName: 'Download node headers for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$pwd\src\out\Default\electron.lib"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/electron.lib"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
displayName: 'Download electron.lib for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
New-Item src\out\Default\gen\node_headers\Release -Type directory
|
||||
Copy-Item -path src\out\Default\electron.lib -destination src\out\Default\gen\node_headers\Release\node.lib
|
||||
displayName: 'Setup node headers'
|
||||
|
||||
- script: |
|
||||
cd src
|
||||
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
set npm_config_arch=arm64
|
||||
cd electron
|
||||
node script/yarn test -- --ci --enable-logging --verbose
|
||||
displayName: 'Run Electron tests'
|
||||
env:
|
||||
ELECTRON_OUT_DIR: Default
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Test Results'
|
||||
inputs:
|
||||
testResultsFiles: '*.xml'
|
||||
searchFolder: '$(System.DefaultWorkingDirectory)/src/junit/'
|
||||
condition: always()
|
||||
|
||||
- script: |
|
||||
cd src
|
||||
echo "Verifying non proprietary ffmpeg"
|
||||
python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
|
||||
displayName: 'Verify ffmpeg'
|
||||
|
||||
- powershell: |
|
||||
Get-Process | Where Name –Like "electron*" | Stop-Process
|
||||
Get-Process | Where Name –Like "MicrosoftEdge*" | Stop-Process
|
||||
Get-Process | Where Name –Like "msedge*" | Stop-Process
|
||||
displayName: 'Kill processes left running from last test run'
|
||||
condition: always()
|
||||
@@ -6,6 +6,8 @@ is_official_build = false
|
||||
dcheck_always_on = true
|
||||
symbol_level = 1
|
||||
|
||||
strip_absolute_paths_from_debug_symbols = false
|
||||
|
||||
# This may be guarded behind is_chrome_branded alongside
|
||||
# proprietary_codecs https://webrtc-review.googlesource.com/c/src/+/36321,
|
||||
# explicitly override here to build OpenH264 encoder/FFmpeg decoder.
|
||||
|
||||
57
build/rules.gni
Normal file
57
build/rules.gni
Normal file
@@ -0,0 +1,57 @@
|
||||
import("//build/config/mac/mac_sdk.gni")
|
||||
|
||||
# This is imported from /ios becuase this functionality was moved
|
||||
# after Chromium stopped using xib files for macOS menu functionality
|
||||
# See https://chromium-review.googlesource.com/c/chromium/src/+/1648695
|
||||
import("//build/config/ios/rules.gni")
|
||||
|
||||
# Template is copied here from Chromium but was removed in
|
||||
# https://chromium-review.googlesource.com/c/chromium/src/+/1637981
|
||||
# Template to compile and package Mac XIB files as bundle data.
|
||||
# Arguments
|
||||
# sources:
|
||||
# list of string, sources to comiple
|
||||
# output_path:
|
||||
# (optional) string, the path to use for the outputs list in the
|
||||
# bundle_data step. If unspecified, defaults to bundle_resources_dir.
|
||||
template("mac_xib_bundle_data") {
|
||||
_target_name = target_name
|
||||
_compile_target_name = _target_name + "_compile_ibtool"
|
||||
|
||||
compile_ib_files(_compile_target_name) {
|
||||
forward_variables_from(invoker, [ "testonly" ])
|
||||
visibility = [ ":$_target_name" ]
|
||||
sources = invoker.sources
|
||||
output_extension = "nib"
|
||||
ibtool_flags = [
|
||||
"--minimum-deployment-target",
|
||||
mac_deployment_target,
|
||||
|
||||
# TODO(rsesek): Enable this once all the bots are on Xcode 7+.
|
||||
# "--target-device",
|
||||
# "mac",
|
||||
]
|
||||
}
|
||||
|
||||
bundle_data(_target_name) {
|
||||
forward_variables_from(invoker,
|
||||
[
|
||||
"testonly",
|
||||
"visibility",
|
||||
])
|
||||
|
||||
public_deps = [
|
||||
":$_compile_target_name",
|
||||
]
|
||||
sources = get_target_outputs(":$_compile_target_name")
|
||||
|
||||
_output_path = "{{bundle_resources_dir}}"
|
||||
if (defined(invoker.output_path)) {
|
||||
_output_path = invoker.output_path
|
||||
}
|
||||
|
||||
outputs = [
|
||||
"$_output_path/{{source_file_part}}",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,10 @@ module.exports = ({
|
||||
global: ['@electron/internal/renderer/webpack-provider', '_global'],
|
||||
Buffer: ['@electron/internal/renderer/webpack-provider', 'Buffer'],
|
||||
})
|
||||
] : [])
|
||||
] : []),
|
||||
new webpack.ProvidePlugin({
|
||||
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise'],
|
||||
}),
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ PATHS_TO_SKIP = [
|
||||
'./libVkICD_mock_', #Skipping because these are outputs that we don't need
|
||||
'./VkICD_mock_', #Skipping because these are outputs that we don't need
|
||||
|
||||
# Skipping because its an output of create_bundle from //build/config/mac/rules.gni
|
||||
# that we don't need
|
||||
'Electron.dSYM',
|
||||
|
||||
# //chrome/browser:resources depends on this via
|
||||
# //chrome/browser/resources/ssl/ssl_error_assistant, but we don't need to
|
||||
# ship it.
|
||||
@@ -51,14 +55,13 @@ def main(argv):
|
||||
with open(runtime_deps) as f:
|
||||
for dep in f.readlines():
|
||||
dep = dep.strip()
|
||||
dist_files.add(dep)
|
||||
if not skip_path(dep, dist_zip, target_cpu):
|
||||
dist_files.add(dep)
|
||||
if sys.platform == 'darwin':
|
||||
execute(['zip', '-r', '-y', dist_zip] + list(dist_files))
|
||||
else:
|
||||
with zipfile.ZipFile(dist_zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as z:
|
||||
for dep in dist_files:
|
||||
if skip_path(dep, dist_zip, target_cpu):
|
||||
continue
|
||||
if os.path.isdir(dep):
|
||||
for root, dirs, files in os.walk(dep):
|
||||
for file in files:
|
||||
|
||||
@@ -18,6 +18,8 @@ buildflag_header("buildflags") {
|
||||
"ENABLE_TTS=$enable_tts",
|
||||
"ENABLE_COLOR_CHOOSER=$enable_color_chooser",
|
||||
"ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions",
|
||||
"ENABLE_PICTURE_IN_PICTURE=$enable_picture_in_picture",
|
||||
"ENABLE_MEDIA_KEY_OVERRIDES=$enable_media_key_overrides",
|
||||
"OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ declare_args() {
|
||||
|
||||
enable_color_chooser = true
|
||||
|
||||
enable_picture_in_picture = true
|
||||
|
||||
enable_media_key_overrides = true
|
||||
|
||||
# Provide a fake location provider for mocking
|
||||
# the geolocation responses. Disable it if you
|
||||
# need to test with chromium's location provider.
|
||||
|
||||
@@ -37,6 +37,12 @@ static_library("chrome") {
|
||||
"//chrome/browser/net/proxy_config_monitor.h",
|
||||
"//chrome/browser/net/proxy_service_factory.cc",
|
||||
"//chrome/browser/net/proxy_service_factory.h",
|
||||
"//chrome/browser/predictors/preconnect_manager.cc",
|
||||
"//chrome/browser/predictors/preconnect_manager.h",
|
||||
"//chrome/browser/predictors/proxy_lookup_client_impl.cc",
|
||||
"//chrome/browser/predictors/proxy_lookup_client_impl.h",
|
||||
"//chrome/browser/predictors/resolve_host_client_impl.cc",
|
||||
"//chrome/browser/predictors/resolve_host_client_impl.h",
|
||||
"//chrome/browser/ssl/security_state_tab_helper.cc",
|
||||
"//chrome/browser/ssl/security_state_tab_helper.h",
|
||||
"//chrome/browser/ui/autofill/popup_view_common.cc",
|
||||
@@ -55,6 +61,7 @@ static_library("chrome") {
|
||||
"//content/public/browser",
|
||||
]
|
||||
deps = [
|
||||
"//chrome/browser:resource_prefetch_predictor_proto",
|
||||
"//components/feature_engagement:buildflags",
|
||||
]
|
||||
|
||||
@@ -126,10 +133,6 @@ static_library("chrome") {
|
||||
sources += [
|
||||
"//chrome/browser/speech/tts_controller_delegate_impl.cc",
|
||||
"//chrome/browser/speech/tts_controller_delegate_impl.h",
|
||||
"//chrome/browser/speech/tts_message_filter.cc",
|
||||
"//chrome/browser/speech/tts_message_filter.h",
|
||||
"//chrome/renderer/tts_dispatcher.cc",
|
||||
"//chrome/renderer/tts_dispatcher.h",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -159,17 +162,21 @@ static_library("chrome") {
|
||||
"//chrome/browser/printing/printer_query.h",
|
||||
"//chrome/browser/printing/printing_message_filter.cc",
|
||||
"//chrome/browser/printing/printing_message_filter.h",
|
||||
"//chrome/browser/printing/printing_service.cc",
|
||||
"//chrome/browser/printing/printing_service.h",
|
||||
]
|
||||
|
||||
public_deps += [
|
||||
"//chrome/services/printing:lib",
|
||||
"//components/printing/browser",
|
||||
"//components/printing/renderer",
|
||||
"//components/services/pdf_compositor/public/cpp:factory",
|
||||
"//components/services/pdf_compositor",
|
||||
"//components/services/pdf_compositor/public/cpp",
|
||||
"//components/services/pdf_compositor/public/mojom",
|
||||
]
|
||||
|
||||
deps += [
|
||||
"//components/printing/common",
|
||||
"//components/services/pdf_compositor",
|
||||
"//printing",
|
||||
]
|
||||
|
||||
@@ -182,4 +189,30 @@ static_library("chrome") {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (enable_picture_in_picture) {
|
||||
sources += [
|
||||
"//chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc",
|
||||
"//chrome/browser/picture_in_picture/picture_in_picture_window_manager.h",
|
||||
"//chrome/browser/ui/views/overlay/back_to_tab_image_button.cc",
|
||||
"//chrome/browser/ui/views/overlay/back_to_tab_image_button.h",
|
||||
"//chrome/browser/ui/views/overlay/close_image_button.cc",
|
||||
"//chrome/browser/ui/views/overlay/close_image_button.h",
|
||||
"//chrome/browser/ui/views/overlay/overlay_window_views.cc",
|
||||
"//chrome/browser/ui/views/overlay/overlay_window_views.h",
|
||||
"//chrome/browser/ui/views/overlay/playback_image_button.cc",
|
||||
"//chrome/browser/ui/views/overlay/playback_image_button.h",
|
||||
"//chrome/browser/ui/views/overlay/resize_handle_button.cc",
|
||||
"//chrome/browser/ui/views/overlay/resize_handle_button.h",
|
||||
"//chrome/browser/ui/views/overlay/skip_ad_label_button.cc",
|
||||
"//chrome/browser/ui/views/overlay/skip_ad_label_button.h",
|
||||
"//chrome/browser/ui/views/overlay/track_image_button.cc",
|
||||
"//chrome/browser/ui/views/overlay/track_image_button.h",
|
||||
]
|
||||
|
||||
deps += [
|
||||
"//chrome/app/vector_icons",
|
||||
"//components/vector_icons:vector_icons",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ component("pepper_flash") {
|
||||
"//chrome/browser/renderer_host/pepper/monitor_finder_mac.h",
|
||||
"//chrome/browser/renderer_host/pepper/monitor_finder_mac.mm",
|
||||
]
|
||||
libs = [ "CoreGraphics.framework" ]
|
||||
}
|
||||
if (is_linux) {
|
||||
deps += [ "//components/services/font/public/cpp" ]
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron'
|
||||
import * as path from 'path'
|
||||
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron';
|
||||
import * as path from 'path';
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
app.quit()
|
||||
})
|
||||
app.quit();
|
||||
});
|
||||
|
||||
function decorateURL (url: string) {
|
||||
// safely add `?utm_source=default_app
|
||||
const parsedUrl = new URL(url)
|
||||
parsedUrl.searchParams.append('utm_source', 'default_app')
|
||||
return parsedUrl.toString()
|
||||
const parsedUrl = new URL(url);
|
||||
parsedUrl.searchParams.append('utm_source', 'default_app');
|
||||
return parsedUrl.toString();
|
||||
}
|
||||
|
||||
// Find the shortest path to the electron binary
|
||||
const absoluteElectronPath = process.execPath
|
||||
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath)
|
||||
const absoluteElectronPath = process.execPath;
|
||||
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath);
|
||||
const electronPath = absoluteElectronPath.length < relativeElectronPath.length
|
||||
? absoluteElectronPath
|
||||
: relativeElectronPath
|
||||
: relativeElectronPath;
|
||||
|
||||
const indexPath = path.resolve(app.getAppPath(), 'index.html')
|
||||
const indexPath = path.resolve(app.getAppPath(), 'index.html');
|
||||
|
||||
function isTrustedSender (webContents: Electron.WebContents) {
|
||||
if (webContents !== (mainWindow && mainWindow.webContents)) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
const parsedUrl = new URL(webContents.getURL())
|
||||
const parsedUrl = new URL(webContents.getURL());
|
||||
const urlPath = process.platform === 'win32'
|
||||
// Strip the prefixed "/" that occurs on windows
|
||||
? path.resolve(parsedUrl.pathname.substr(1))
|
||||
: parsedUrl.pathname
|
||||
return parsedUrl.protocol === 'file:' && urlPath === indexPath
|
||||
: parsedUrl.pathname;
|
||||
return parsedUrl.protocol === 'file:' && urlPath === indexPath;
|
||||
}
|
||||
|
||||
ipcMain.handle('bootstrap', (event) => {
|
||||
return isTrustedSender(event.sender) ? electronPath : null
|
||||
})
|
||||
return isTrustedSender(event.sender) ? electronPath : null;
|
||||
});
|
||||
|
||||
async function createWindow () {
|
||||
await app.whenReady()
|
||||
await app.whenReady();
|
||||
|
||||
const options: Electron.BrowserWindowConstructorOptions = {
|
||||
width: 900,
|
||||
@@ -57,46 +57,46 @@ async function createWindow () {
|
||||
},
|
||||
useContentSize: true,
|
||||
show: false
|
||||
}
|
||||
};
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
options.icon = path.join(__dirname, 'icon.png')
|
||||
options.icon = path.join(__dirname, 'icon.png');
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(options)
|
||||
mainWindow.on('ready-to-show', () => mainWindow!.show())
|
||||
mainWindow = new BrowserWindow(options);
|
||||
mainWindow.on('ready-to-show', () => mainWindow!.show());
|
||||
|
||||
mainWindow.webContents.on('new-window', (event, url) => {
|
||||
event.preventDefault()
|
||||
shell.openExternal(decorateURL(url))
|
||||
})
|
||||
event.preventDefault();
|
||||
shell.openExternal(decorateURL(url));
|
||||
});
|
||||
|
||||
mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, done) => {
|
||||
const parsedUrl = new URL(webContents.getURL())
|
||||
const parsedUrl = new URL(webContents.getURL());
|
||||
|
||||
const options: Electron.MessageBoxOptions = {
|
||||
title: 'Permission Request',
|
||||
message: `Allow '${parsedUrl.origin}' to access '${permission}'?`,
|
||||
buttons: ['OK', 'Cancel'],
|
||||
cancelId: 1
|
||||
}
|
||||
};
|
||||
|
||||
dialog.showMessageBox(mainWindow!, options).then(({ response }) => {
|
||||
done(response === 0)
|
||||
})
|
||||
})
|
||||
done(response === 0);
|
||||
});
|
||||
});
|
||||
|
||||
return mainWindow
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
export const loadURL = async (appUrl: string) => {
|
||||
mainWindow = await createWindow()
|
||||
mainWindow.loadURL(appUrl)
|
||||
mainWindow.focus()
|
||||
}
|
||||
mainWindow = await createWindow();
|
||||
mainWindow.loadURL(appUrl);
|
||||
mainWindow.focus();
|
||||
};
|
||||
|
||||
export const loadFile = async (appPath: string) => {
|
||||
mainWindow = await createWindow()
|
||||
mainWindow.loadFile(appPath)
|
||||
mainWindow.focus()
|
||||
}
|
||||
mainWindow = await createWindow();
|
||||
mainWindow.loadFile(appPath);
|
||||
mainWindow.focus();
|
||||
};
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
<head>
|
||||
<title>Electron</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self'" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-6PH54BfkNq/EMMhUY7nhHf3c+AxloOwfy7hWyT01CM8='; style-src 'self'; img-src 'self'; connect-src 'self'" />
|
||||
<link href="./styles.css" type="text/css" rel="stylesheet" />
|
||||
<link href="./octicon/build.css" type="text/css" rel="stylesheet" />
|
||||
<script defer src="./index.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -84,6 +83,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<script>
|
||||
window.electronDefaultApp.initialize()
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,30 +0,0 @@
|
||||
async function getOcticonSvg (name: string) {
|
||||
try {
|
||||
const response = await fetch(`octicon/${name}.svg`)
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = await response.text()
|
||||
return div
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSVG (element: HTMLSpanElement) {
|
||||
for (const cssClass of element.classList) {
|
||||
if (cssClass.startsWith('octicon-')) {
|
||||
const icon = await getOcticonSvg(cssClass.substr(8))
|
||||
if (icon) {
|
||||
for (const elemClass of element.classList) {
|
||||
icon.classList.add(elemClass)
|
||||
}
|
||||
element.before(icon)
|
||||
element.remove()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) {
|
||||
loadSVG(element)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { app, dialog } from 'electron'
|
||||
import { app, dialog } from 'electron';
|
||||
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as url from 'url'
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
type DefaultAppOptions = {
|
||||
file: null | string;
|
||||
@@ -14,10 +14,10 @@ type DefaultAppOptions = {
|
||||
modules: string[];
|
||||
}
|
||||
|
||||
const Module = require('module')
|
||||
const Module = require('module');
|
||||
|
||||
// Parse command line options.
|
||||
const argv = process.argv.slice(1)
|
||||
const argv = process.argv.slice(1);
|
||||
|
||||
const option: DefaultAppOptions = {
|
||||
file: null,
|
||||
@@ -27,50 +27,50 @@ const option: DefaultAppOptions = {
|
||||
interactive: false,
|
||||
abi: false,
|
||||
modules: []
|
||||
}
|
||||
};
|
||||
|
||||
let nextArgIsRequire = false
|
||||
let nextArgIsRequire = false;
|
||||
|
||||
for (const arg of argv) {
|
||||
if (nextArgIsRequire) {
|
||||
option.modules.push(arg)
|
||||
nextArgIsRequire = false
|
||||
continue
|
||||
option.modules.push(arg);
|
||||
nextArgIsRequire = false;
|
||||
continue;
|
||||
} else if (arg === '--version' || arg === '-v') {
|
||||
option.version = true
|
||||
break
|
||||
option.version = true;
|
||||
break;
|
||||
} else if (arg.match(/^--app=/)) {
|
||||
option.file = arg.split('=')[1]
|
||||
break
|
||||
option.file = arg.split('=')[1];
|
||||
break;
|
||||
} else if (arg === '--interactive' || arg === '-i' || arg === '-repl') {
|
||||
option.interactive = true
|
||||
option.interactive = true;
|
||||
} else if (arg === '--test-type=webdriver') {
|
||||
option.webdriver = true
|
||||
option.webdriver = true;
|
||||
} else if (arg === '--require' || arg === '-r') {
|
||||
nextArgIsRequire = true
|
||||
continue
|
||||
nextArgIsRequire = true;
|
||||
continue;
|
||||
} else if (arg === '--abi' || arg === '-a') {
|
||||
option.abi = true
|
||||
continue
|
||||
option.abi = true;
|
||||
continue;
|
||||
} else if (arg === '--no-help') {
|
||||
option.noHelp = true
|
||||
continue
|
||||
option.noHelp = true;
|
||||
continue;
|
||||
} else if (arg[0] === '-') {
|
||||
continue
|
||||
continue;
|
||||
} else {
|
||||
option.file = arg
|
||||
break
|
||||
option.file = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextArgIsRequire) {
|
||||
console.error('Invalid Usage: --require [file]\n\n"file" is required')
|
||||
process.exit(1)
|
||||
console.error('Invalid Usage: --require [file]\n\n"file" is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Set up preload modules
|
||||
if (option.modules.length > 0) {
|
||||
Module._preloadModules(option.modules)
|
||||
Module._preloadModules(option.modules);
|
||||
}
|
||||
|
||||
function loadApplicationPackage (packagePath: string) {
|
||||
@@ -79,102 +79,102 @@ function loadApplicationPackage (packagePath: string) {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
value: true
|
||||
})
|
||||
});
|
||||
|
||||
try {
|
||||
// Override app name and version.
|
||||
packagePath = path.resolve(packagePath)
|
||||
const packageJsonPath = path.join(packagePath, 'package.json')
|
||||
let appPath
|
||||
packagePath = path.resolve(packagePath);
|
||||
const packageJsonPath = path.join(packagePath, 'package.json');
|
||||
let appPath;
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
let packageJson
|
||||
let packageJson;
|
||||
try {
|
||||
packageJson = require(packageJsonPath)
|
||||
packageJson = require(packageJsonPath);
|
||||
} catch (e) {
|
||||
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`)
|
||||
return
|
||||
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packageJson.version) {
|
||||
app.setVersion(packageJson.version)
|
||||
app.setVersion(packageJson.version);
|
||||
}
|
||||
if (packageJson.productName) {
|
||||
app.name = packageJson.productName
|
||||
app.name = packageJson.productName;
|
||||
} else if (packageJson.name) {
|
||||
app.name = packageJson.name
|
||||
app.name = packageJson.name;
|
||||
}
|
||||
appPath = packagePath
|
||||
appPath = packagePath;
|
||||
}
|
||||
|
||||
try {
|
||||
const filePath = Module._resolveFilename(packagePath, module, true)
|
||||
app._setDefaultAppPaths(appPath || path.dirname(filePath))
|
||||
const filePath = Module._resolveFilename(packagePath, module, true);
|
||||
app._setDefaultAppPaths(appPath || path.dirname(filePath));
|
||||
} catch (e) {
|
||||
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`)
|
||||
return
|
||||
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the app.
|
||||
Module._load(packagePath, module, true)
|
||||
Module._load(packagePath, module, true);
|
||||
} catch (e) {
|
||||
console.error('App threw an error during load')
|
||||
console.error(e.stack || e)
|
||||
throw e
|
||||
console.error('App threw an error during load');
|
||||
console.error(e.stack || e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function showErrorMessage (message: string) {
|
||||
app.focus()
|
||||
dialog.showErrorBox('Error launching app', message)
|
||||
process.exit(1)
|
||||
app.focus();
|
||||
dialog.showErrorBox('Error launching app', message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function loadApplicationByURL (appUrl: string) {
|
||||
const { loadURL } = await import('./default_app')
|
||||
loadURL(appUrl)
|
||||
const { loadURL } = await import('./default_app');
|
||||
loadURL(appUrl);
|
||||
}
|
||||
|
||||
async function loadApplicationByFile (appPath: string) {
|
||||
const { loadFile } = await import('./default_app')
|
||||
loadFile(appPath)
|
||||
const { loadFile } = await import('./default_app');
|
||||
loadFile(appPath);
|
||||
}
|
||||
|
||||
function startRepl () {
|
||||
if (process.platform === 'win32') {
|
||||
console.error('Electron REPL not currently supported on Windows')
|
||||
process.exit(1)
|
||||
console.error('Electron REPL not currently supported on Windows');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// prevent quitting
|
||||
app.on('window-all-closed', () => {})
|
||||
app.on('window-all-closed', () => {});
|
||||
|
||||
const repl = require('repl')
|
||||
const repl = require('repl');
|
||||
repl.start('> ').on('exit', () => {
|
||||
process.exit(0)
|
||||
})
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
// start the default app.
|
||||
if (option.file && !option.webdriver) {
|
||||
const file = option.file
|
||||
const protocol = url.parse(file).protocol
|
||||
const extension = path.extname(file)
|
||||
const file = option.file;
|
||||
const protocol = url.parse(file).protocol;
|
||||
const extension = path.extname(file);
|
||||
if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') {
|
||||
loadApplicationByURL(file)
|
||||
loadApplicationByURL(file);
|
||||
} else if (extension === '.html' || extension === '.htm') {
|
||||
loadApplicationByFile(path.resolve(file))
|
||||
loadApplicationByFile(path.resolve(file));
|
||||
} else {
|
||||
loadApplicationPackage(file)
|
||||
loadApplicationPackage(file);
|
||||
}
|
||||
} else if (option.version) {
|
||||
console.log('v' + process.versions.electron)
|
||||
process.exit(0)
|
||||
console.log('v' + process.versions.electron);
|
||||
process.exit(0);
|
||||
} else if (option.abi) {
|
||||
console.log(process.versions.modules)
|
||||
process.exit(0)
|
||||
console.log(process.versions.modules);
|
||||
process.exit(0);
|
||||
} else if (option.interactive) {
|
||||
startRepl()
|
||||
startRepl();
|
||||
} else {
|
||||
if (!option.noHelp) {
|
||||
const welcomeMessage = `
|
||||
@@ -192,10 +192,10 @@ Options:
|
||||
-i, --interactive Open a REPL to the main process.
|
||||
-r, --require Module to preload (option can be repeated).
|
||||
-v, --version Print the version.
|
||||
-a, --abi Print the Node ABI version.`
|
||||
-a, --abi Print the Node ABI version.`;
|
||||
|
||||
console.log(welcomeMessage)
|
||||
console.log(welcomeMessage);
|
||||
}
|
||||
|
||||
loadApplicationByFile('index.html')
|
||||
loadApplicationByFile('index.html');
|
||||
}
|
||||
|
||||
@@ -1,20 +1,53 @@
|
||||
import { ipcRenderer } from 'electron'
|
||||
import { ipcRenderer, contextBridge } from 'electron';
|
||||
|
||||
async function getOcticonSvg (name: string) {
|
||||
try {
|
||||
const response = await fetch(`octicon/${name}.svg`);
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = await response.text();
|
||||
return div;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSVG (element: HTMLSpanElement) {
|
||||
for (const cssClass of element.classList) {
|
||||
if (cssClass.startsWith('octicon-')) {
|
||||
const icon = await getOcticonSvg(cssClass.substr(8));
|
||||
if (icon) {
|
||||
for (const elemClass of element.classList) {
|
||||
icon.classList.add(elemClass);
|
||||
}
|
||||
element.before(icon);
|
||||
element.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initialize () {
|
||||
const electronPath = await ipcRenderer.invoke('bootstrap')
|
||||
const electronPath = await ipcRenderer.invoke('bootstrap');
|
||||
|
||||
function replaceText (selector: string, text: string) {
|
||||
const element = document.querySelector<HTMLElement>(selector)
|
||||
const element = document.querySelector<HTMLElement>(selector);
|
||||
if (element) {
|
||||
element.innerText = text
|
||||
element.innerText = text;
|
||||
}
|
||||
}
|
||||
|
||||
replaceText('.electron-version', `Electron v${process.versions.electron}`)
|
||||
replaceText('.chrome-version', `Chromium v${process.versions.chrome}`)
|
||||
replaceText('.node-version', `Node v${process.versions.node}`)
|
||||
replaceText('.v8-version', `v8 v${process.versions.v8}`)
|
||||
replaceText('.command-example', `${electronPath} path-to-app`)
|
||||
replaceText('.electron-version', `Electron v${process.versions.electron}`);
|
||||
replaceText('.chrome-version', `Chromium v${process.versions.chrome}`);
|
||||
replaceText('.node-version', `Node v${process.versions.node}`);
|
||||
replaceText('.v8-version', `v8 v${process.versions.v8}`);
|
||||
replaceText('.command-example', `${electronPath} path-to-app`);
|
||||
|
||||
for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) {
|
||||
loadSVG(element);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initialize)
|
||||
contextBridge.exposeInMainWorld('electronDefaultApp', {
|
||||
initialize
|
||||
});
|
||||
|
||||
@@ -39,6 +39,7 @@ an issue:
|
||||
* [Using Electron's APIs](tutorial/application-architecture.md#using-electron-apis)
|
||||
* [Using Node.js APIs](tutorial/application-architecture.md#using-nodejs-apis)
|
||||
* [Using Native Node.js Modules](tutorial/using-native-node-modules.md)
|
||||
* [Performance Strategies](tutorial/performance.md)
|
||||
* Adding Features to Your App
|
||||
* [Notifications](tutorial/notifications.md)
|
||||
* [Recent Documents](tutorial/recent-documents.md)
|
||||
|
||||
@@ -32,7 +32,7 @@ In most cases, you should do everything in the `ready` event handler.
|
||||
|
||||
Returns:
|
||||
|
||||
* `launchInfo` Object _macOS_
|
||||
* `launchInfo` unknown _macOS_
|
||||
|
||||
Emitted when Electron has finished initializing. On macOS, `launchInfo` holds
|
||||
the `userInfo` of the `NSUserNotification` that was used to open the application,
|
||||
@@ -146,7 +146,7 @@ Returns:
|
||||
* `event` Event
|
||||
* `type` String - A string identifying the activity. Maps to
|
||||
[`NSUserActivity.activityType`][activity-type].
|
||||
* `userInfo` Object - Contains app-specific state stored by the activity on
|
||||
* `userInfo` unknown - Contains app-specific state stored by the activity on
|
||||
another device.
|
||||
|
||||
Emitted during [Handoff][handoff] when an activity from a different device wants
|
||||
@@ -189,7 +189,7 @@ Returns:
|
||||
* `event` Event
|
||||
* `type` String - A string identifying the activity. Maps to
|
||||
[`NSUserActivity.activityType`][activity-type].
|
||||
* `userInfo` Object - Contains app-specific state stored by the activity.
|
||||
* `userInfo` unknown - Contains app-specific state stored by the activity.
|
||||
|
||||
Emitted during [Handoff][handoff] after an activity from this device was successfully
|
||||
resumed on another one.
|
||||
@@ -201,7 +201,7 @@ Returns:
|
||||
* `event` Event
|
||||
* `type` String - A string identifying the activity. Maps to
|
||||
[`NSUserActivity.activityType`][activity-type].
|
||||
* `userInfo` Object - Contains app-specific state stored by the activity.
|
||||
* `userInfo` unknown - Contains app-specific state stored by the activity.
|
||||
|
||||
Emitted when [Handoff][handoff] is about to be resumed on another device. If you need to update the state to be transferred, you should call `event.preventDefault()` immediately, construct a new `userInfo` dictionary and call `app.updateCurrentActiviy()` in a timely manner. Otherwise, the operation will fail and `continue-activity-error` will be called.
|
||||
|
||||
@@ -314,10 +314,8 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `request` Object
|
||||
* `method` String
|
||||
* `authenticationResponseDetails` Object
|
||||
* `url` URL
|
||||
* `referrer` URL
|
||||
* `authInfo` Object
|
||||
* `isProxy` Boolean
|
||||
* `scheme` String
|
||||
@@ -325,8 +323,8 @@ Returns:
|
||||
* `port` Integer
|
||||
* `realm` String
|
||||
* `callback` Function
|
||||
* `username` String
|
||||
* `password` String
|
||||
* `username` String (optional)
|
||||
* `password` String (optional)
|
||||
|
||||
Emitted when `webContents` wants to do basic auth.
|
||||
|
||||
@@ -337,12 +335,16 @@ should prevent the default behavior with `event.preventDefault()` and call
|
||||
```javascript
|
||||
const { app } = require('electron')
|
||||
|
||||
app.on('login', (event, webContents, request, authInfo, callback) => {
|
||||
app.on('login', (event, webContents, details, authInfo, callback) => {
|
||||
event.preventDefault()
|
||||
callback('username', 'secret')
|
||||
})
|
||||
```
|
||||
|
||||
If `callback` is called without a username or password, the authentication
|
||||
request will be cancelled and the authentication error will be returned to the
|
||||
page.
|
||||
|
||||
### Event: 'gpu-info-update'
|
||||
|
||||
Emitted whenever there is a GPU info update.
|
||||
@@ -582,7 +584,7 @@ them.
|
||||
|
||||
Sets or creates a directory your app's logs which can then be manipulated with `app.getPath()` or `app.setPath(pathName, newPath)`.
|
||||
|
||||
Calling `app.setAppLogsPath()` without a `path` parameter will result in this directory being set to `/Library/Logs/YourAppName` on _macOS_, and inside the `userData` directory on _Linux_ and _Windows_.
|
||||
Calling `app.setAppLogsPath()` without a `path` parameter will result in this directory being set to `~/Library/Logs/YourAppName` on _macOS_, and inside the `userData` directory on _Linux_ and _Windows_.
|
||||
|
||||
### `app.getAppPath()`
|
||||
|
||||
@@ -614,6 +616,8 @@ Returns `String` - The current application directory.
|
||||
Returns `String` - A path to a special directory or file associated with `name`. On
|
||||
failure, an `Error` is thrown.
|
||||
|
||||
If `app.getPath('logs')` is called without called `app.setAppLogsPath()` being called first, a default log directory will be created equivalent to calling `app.setAppLogsPath()` without a `path` parameter.
|
||||
|
||||
### `app.getFileIcon(path[, options])`
|
||||
|
||||
* `path` String
|
||||
@@ -707,34 +711,34 @@ Clears the recent documents list.
|
||||
|
||||
### `app.setAsDefaultProtocolClient(protocol[, path, args])`
|
||||
|
||||
* `protocol` String - The name of your protocol, without `://`. If you want your
|
||||
app to handle `electron://` links, call this method with `electron` as the
|
||||
parameter.
|
||||
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
|
||||
* `args` String[] (optional) _Windows_ - Defaults to an empty array
|
||||
* `protocol` String - The name of your protocol, without `://`. For example,
|
||||
if you want your app to handle `electron://` links, call this method with
|
||||
`electron` as the parameter.
|
||||
* `path` String (optional) _Windows_ - The path to the Electron executable.
|
||||
Defaults to `process.execPath`
|
||||
* `args` String[] (optional) _Windows_ - Arguments passed to the executable.
|
||||
Defaults to an empty array
|
||||
|
||||
Returns `Boolean` - Whether the call succeeded.
|
||||
|
||||
This method sets the current executable as the default handler for a protocol
|
||||
(aka URI scheme). It allows you to integrate your app deeper into the operating
|
||||
system. Once registered, all links with `your-protocol://` will be opened with
|
||||
the current executable. The whole link, including protocol, will be passed to
|
||||
your application as a parameter.
|
||||
|
||||
On Windows, you can provide optional parameters path, the path to your executable,
|
||||
and args, an array of arguments to be passed to your executable when it launches.
|
||||
Sets the current executable as the default handler for a protocol (aka URI
|
||||
scheme). It allows you to integrate your app deeper into the operating system.
|
||||
Once registered, all links with `your-protocol://` will be opened with the
|
||||
current executable. The whole link, including protocol, will be passed to your
|
||||
application as a parameter.
|
||||
|
||||
**Note:** On macOS, you can only register protocols that have been added to
|
||||
your app's `info.plist`, which can not be modified at runtime. You can however
|
||||
change the file with a simple text editor or script during build time.
|
||||
Please refer to [Apple's documentation][CFBundleURLTypes] for details.
|
||||
your app's `info.plist`, which cannot be modified at runtime. However, you can
|
||||
change the file during build time via [Electron Forge][electron-forge],
|
||||
[Electron Packager][electron-packager], or by editing `info.plist` with a text
|
||||
editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details.
|
||||
|
||||
**Note:** In a Windows Store environment (when packaged as an `appx`) this API
|
||||
will return `true` for all calls but the registry key it sets won't be accessible
|
||||
by other applications. In order to register your Windows Store application
|
||||
as a default protocol handler you must [declare the protocol in your manifest](https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol).
|
||||
|
||||
The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally.
|
||||
The API uses the Windows Registry and `LSSetDefaultHandlerForURLScheme` internally.
|
||||
|
||||
### `app.removeAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_
|
||||
|
||||
@@ -753,10 +757,8 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler.
|
||||
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
|
||||
* `args` String[] (optional) _Windows_ - Defaults to an empty array
|
||||
|
||||
Returns `Boolean`
|
||||
|
||||
This method checks if the current executable is the default handler for a protocol
|
||||
(aka URI scheme). If so, it will return true. Otherwise, it will return false.
|
||||
Returns `Boolean` - Whether the current executable is the default handler for a
|
||||
protocol (aka URI scheme).
|
||||
|
||||
**Note:** On macOS, you can use this method to check if the app has been
|
||||
registered as the default protocol handler for a protocol. You can also verify
|
||||
@@ -764,7 +766,7 @@ this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the
|
||||
macOS machine. Please refer to
|
||||
[Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details.
|
||||
|
||||
The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally.
|
||||
The API uses the Windows Registry and `LSCopyDefaultHandlerForURLScheme` internally.
|
||||
|
||||
### `app.setUserTasks(tasks)` _Windows_
|
||||
|
||||
@@ -949,7 +951,7 @@ allow multiple instances of the application to once again run side by side.
|
||||
|
||||
* `type` String - Uniquely identifies the activity. Maps to
|
||||
[`NSUserActivity.activityType`][activity-type].
|
||||
* `userInfo` Object - App-specific state to store for use by another device.
|
||||
* `userInfo` any - App-specific state to store for use by another device.
|
||||
* `webpageURL` String (optional) - The webpage to load in a browser if no suitable app is
|
||||
installed on the resuming device. The scheme must be `http` or `https`.
|
||||
|
||||
@@ -972,7 +974,7 @@ Marks the current [Handoff][handoff] user activity as inactive without invalidat
|
||||
|
||||
* `type` String - Uniquely identifies the activity. Maps to
|
||||
[`NSUserActivity.activityType`][activity-type].
|
||||
* `userInfo` Object - App-specific state to store for use by another device.
|
||||
* `userInfo` any - App-specific state to store for use by another device.
|
||||
|
||||
Updates the current activity if its type matches `type`, merging the entries from
|
||||
`userInfo` into its current `userInfo` dictionary.
|
||||
@@ -1182,8 +1184,9 @@ Show the app's about panel options. These options can be overridden with `app.se
|
||||
* `website` String (optional) _Linux_ - The app's website.
|
||||
* `iconPath` String (optional) _Linux_ - Path to the app's icon. Will be shown as 64x64 pixels while retaining aspect ratio.
|
||||
|
||||
Set the about panel options. This will override the values defined in the app's
|
||||
`.plist` file on MacOS. See the [Apple docs][about-panel-options] for more details. On Linux, values must be set in order to be shown; there are no defaults.
|
||||
Set the about panel options. This will override the values defined in the app's `.plist` file on MacOS. See the [Apple docs][about-panel-options] for more details. On Linux, values must be set in order to be shown; there are no defaults.
|
||||
|
||||
If you do not set `credits` but still wish to surface them in your app, AppKit will look for a file named "Credits.html", "Credits.rtf", and "Credits.rtfd", in that order, in the bundle returned by the NSBundle class method main. The first file found is used, and if none is found, the info area is left blank. See Apple [documentation](https://developer.apple.com/documentation/appkit/nsaboutpaneloptioncredits?language=objc) for more information.
|
||||
|
||||
### `app.isEmojiPanelSupported()`
|
||||
|
||||
@@ -1304,6 +1307,8 @@ A `Boolean` property that returns `true` if the app is packaged, `false` otherw
|
||||
[dock-menu]:https://developer.apple.com/macos/human-interface-guidelines/menus/dock-menus/
|
||||
[tasks]:https://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks
|
||||
[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx
|
||||
[electron-forge]: https://www.electronforge.io/
|
||||
[electron-packager]: https://github.com/electron/electron-packager
|
||||
[CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115
|
||||
[LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme
|
||||
[handoff]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html
|
||||
|
||||
@@ -103,7 +103,7 @@ The `autoUpdater` object has the following methods:
|
||||
|
||||
* `options` Object
|
||||
* `url` String
|
||||
* `headers` Object (optional) _macOS_ - HTTP request headers.
|
||||
* `headers` Record<String, String> (optional) _macOS_ - HTTP request headers.
|
||||
* `serverType` String (optional) _macOS_ - Either `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
|
||||
README for more information.
|
||||
|
||||
|
||||
@@ -68,6 +68,40 @@ webFrame.setIsolatedWorldInfo(
|
||||
|
||||
This property was removed in Chromium 77, and as such is no longer available.
|
||||
|
||||
### `webkitdirectory` attribute for `<input type="file"/>`
|
||||

|
||||
The `webkitdirectory` property on HTML file inputs allows them to select folders.
|
||||
Previous versions of Electron had an incorrect implementation where the `event.target.files`
|
||||
of the input returned a `FileList` that returned one `File` corresponding to the selected folder.
|
||||

|
||||
As of Electron 7, that `FileList` is now list of all files contained within
|
||||
the folder, similarly to Chrome, Firefox, and Edge
|
||||
([link to MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory)).
|
||||

|
||||
As an illustration, take a folder with this structure:
|
||||
```console
|
||||
folder
|
||||
├── file1
|
||||
├── file2
|
||||
└── file3
|
||||
```
|
||||

|
||||
In Electron <=6, this would return a `FileList` with a `File` object for:
|
||||
```console
|
||||
path/to/folder
|
||||
```
|
||||

|
||||
In Electron 7, this now returns a `FileList` with a `File` object for:
|
||||
```console
|
||||
/path/to/folder/file3
|
||||
/path/to/folder/file2
|
||||
/path/to/folder/file1
|
||||
```
|
||||

|
||||
Note that `webkitdirectory` no longer exposes the path to the selected folder.
|
||||
If you require the path to the selected folder rather than the folder contents,
|
||||
see the `dialog.showOpenDialog` API ([link](https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowopendialogbrowserwindow-options)).
|
||||
|
||||
## Planned Breaking API Changes (6.0)
|
||||
|
||||
### `win.setMenu(null)`
|
||||
@@ -197,7 +231,7 @@ A new API, `protocol.registerSchemesAsPrivileged` has been added and should be u
|
||||
### webFrame Isolated World APIs
|
||||
|
||||
```js
|
||||
// Deprecated
|
||||
// Removed in Electron 7.0
|
||||
webFrame.setIsolatedWorldContentSecurityPolicy(worldId, csp)
|
||||
webFrame.setIsolatedWorldHumanReadableName(worldId, name)
|
||||
webFrame.setIsolatedWorldSecurityOrigin(worldId, securityOrigin)
|
||||
|
||||
@@ -51,6 +51,9 @@ This event is usually emitted after the `did-finish-load` event, but for
|
||||
pages with many remote resources, it may be emitted before the `did-finish-load`
|
||||
event.
|
||||
|
||||
Please note that using this event implies that the renderer will be considered "visible" and
|
||||
paint even though `show` is false. This event will never fire if you use `paintWhenInitiallyHidden: false`
|
||||
|
||||
## Setting `backgroundColor`
|
||||
|
||||
For a complex app, the `ready-to-show` event could be emitted too late, making
|
||||
@@ -184,6 +187,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
leave it undefined so the executable's icon will be used.
|
||||
* `show` Boolean (optional) - Whether window should be shown when created. Default is
|
||||
`true`.
|
||||
* `paintWhenInitiallyHidden` Boolean (optional) - Whether the renderer should be active when `show` is `false` and it has just been created. In order for `document.visibilityState` to work correctly on first load with `show: false` you should set this to `false`. Setting this to `false` will cause the `ready-to-show` event to not fire. Default is `true`.
|
||||
* `frame` Boolean (optional) - Specify `false` to create a
|
||||
[Frameless Window](frameless-window.md). Default is `true`.
|
||||
* `parent` BrowserWindow (optional) - Specify parent window. Default is `null`.
|
||||
@@ -269,8 +273,6 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
OS-level sandbox and disabling the Node.js engine. This is not the same as
|
||||
the `nodeIntegration` option and the APIs available to the preload script
|
||||
are more limited. Read more about the option [here](sandbox-option.md).
|
||||
**Note:** This option is currently experimental and may change or be
|
||||
removed in future Electron releases.
|
||||
* `enableRemoteModule` Boolean (optional) - Whether to enable the [`remote`](remote.md) module.
|
||||
Default is `true`.
|
||||
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the
|
||||
@@ -379,6 +381,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
* `disableHtmlFullscreenWindowResize` Boolean (optional) - Whether to
|
||||
prevent the window from resizing when entering HTML Fullscreen. Default
|
||||
is `false`.
|
||||
* `enableWebSQL` Boolean (optional) - Whether to enable the [WebSQL api](https://www.w3.org/TR/webdatabase/).
|
||||
Default is `true`.
|
||||
|
||||
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
||||
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
||||
@@ -485,6 +489,9 @@ Emitted when the window is hidden.
|
||||
Emitted when the web page has been rendered (while not being shown) and window can be displayed without
|
||||
a visual flash.
|
||||
|
||||
Please note that using this event implies that the renderer will be considered "visible" and
|
||||
paint even though `show` is false. This event will never fire if you use `paintWhenInitiallyHidden: false`
|
||||
|
||||
#### Event: 'maximize'
|
||||
|
||||
Emitted when window is maximized.
|
||||
@@ -506,7 +513,7 @@ Emitted when the window is restored from a minimized state.
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `newBounds` [`Rectangle`](structures/rectangle.md) - Size the window is being resized to.
|
||||
* `newBounds` [Rectangle](structures/rectangle.md) - Size the window is being resized to.
|
||||
|
||||
Emitted before the window is resized. Calling `event.preventDefault()` will prevent the window from being resized.
|
||||
|
||||
@@ -521,7 +528,7 @@ Emitted after the window has been resized.
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `newBounds` [`Rectangle`](structures/rectangle.md) - Location the window is being moved to.
|
||||
* `newBounds` [Rectangle](structures/rectangle.md) - Location the window is being moved to.
|
||||
|
||||
Emitted before the window is moved. Calling `event.preventDefault()` will prevent the window from being moved.
|
||||
|
||||
@@ -614,6 +621,12 @@ Returns:
|
||||
|
||||
Emitted on 3-finger swipe. Possible directions are `up`, `right`, `down`, `left`.
|
||||
|
||||
The method underlying this event is built to handle older macOS-style trackpad swiping,
|
||||
where the content on the screen doesn't move with the swipe. Most macOS trackpads are not
|
||||
configured to allow this kind of swiping anymore, so in order for it to emit properly the
|
||||
'Swipe between pages' preference in `System Preferences > Trackpad > More Gestures` must be
|
||||
set to 'Swipe with two or three fingers'.
|
||||
|
||||
#### Event: 'rotate-gesture' _macOS_
|
||||
|
||||
Returns:
|
||||
@@ -691,7 +704,7 @@ is emitted.
|
||||
|
||||
#### `BrowserWindow.getExtensions()`
|
||||
|
||||
Returns `Object` - The keys are the extension names and each value is
|
||||
Returns `Record<String, ExtensionInfo>` - The keys are the extension names and each value is
|
||||
an Object containing `name` and `version` properties.
|
||||
|
||||
**Note:** This API cannot be called before the `ready` event of the `app` module
|
||||
@@ -724,7 +737,7 @@ is emitted.
|
||||
|
||||
#### `BrowserWindow.getDevToolsExtensions()`
|
||||
|
||||
Returns `Object` - The keys are the extension names and each value is
|
||||
Returns `Record<string, ExtensionInfo>` - The keys are the extension names and each value is
|
||||
an Object containing `name` and `version` properties.
|
||||
|
||||
To check if a DevTools extension is installed you can run the following:
|
||||
@@ -1079,15 +1092,11 @@ Returns `Integer[]` - Contains the window's maximum width and height.
|
||||
|
||||
* `resizable` Boolean
|
||||
|
||||
Sets whether the window can be manually resized by user.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Sets whether the window can be manually resized by the user.
|
||||
|
||||
#### `win.isResizable()`
|
||||
|
||||
Returns `Boolean` - Whether the window can be manually resized by user.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Returns `Boolean` - Whether the window can be manually resized by the user.
|
||||
|
||||
#### `win.setMovable(movable)` _macOS_ _Windows_
|
||||
|
||||
@@ -1095,41 +1104,29 @@ Returns `Boolean` - Whether the window can be manually resized by user.
|
||||
|
||||
Sets whether the window can be moved by user. On Linux does nothing.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.isMovable()` _macOS_ _Windows_
|
||||
|
||||
Returns `Boolean` - Whether the window can be moved by user.
|
||||
|
||||
On Linux always returns `true`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setMinimizable(minimizable)` _macOS_ _Windows_
|
||||
|
||||
* `minimizable` Boolean
|
||||
|
||||
Sets whether the window can be manually minimized by user. On Linux does
|
||||
nothing.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Sets whether the window can be manually minimized by user. On Linux does nothing.
|
||||
|
||||
#### `win.isMinimizable()` _macOS_ _Windows_
|
||||
|
||||
Returns `Boolean` - Whether the window can be manually minimized by user
|
||||
Returns `Boolean` - Whether the window can be manually minimized by the user.
|
||||
|
||||
On Linux always returns `true`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setMaximizable(maximizable)` _macOS_ _Windows_
|
||||
|
||||
* `maximizable` Boolean
|
||||
|
||||
Sets whether the window can be manually maximized by user. On Linux does
|
||||
nothing.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Sets whether the window can be manually maximized by user. On Linux does nothing.
|
||||
|
||||
#### `win.isMaximizable()` _macOS_ _Windows_
|
||||
|
||||
@@ -1137,23 +1134,15 @@ Returns `Boolean` - Whether the window can be manually maximized by user.
|
||||
|
||||
On Linux always returns `true`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setFullScreenable(fullscreenable)`
|
||||
|
||||
* `fullscreenable` Boolean
|
||||
|
||||
Sets whether the maximize/zoom window button toggles fullscreen mode or
|
||||
maximizes the window.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Sets whether the maximize/zoom window button toggles fullscreen mode or maximizes the window.
|
||||
|
||||
#### `win.isFullScreenable()`
|
||||
|
||||
Returns `Boolean` - Whether the maximize/zoom window button toggles fullscreen mode or
|
||||
maximizes the window.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Returns `Boolean` - Whether the maximize/zoom window button toggles fullscreen mode or maximizes the window.
|
||||
|
||||
#### `win.setClosable(closable)` _macOS_ _Windows_
|
||||
|
||||
@@ -1161,16 +1150,12 @@ maximizes the window.
|
||||
|
||||
Sets whether the window can be manually closed by user. On Linux does nothing.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.isClosable()` _macOS_ _Windows_
|
||||
|
||||
Returns `Boolean` - Whether the window can be manually closed by user.
|
||||
|
||||
On Linux always returns `true`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setAlwaysOnTop(flag[, level][, relativeLevel])`
|
||||
|
||||
* `flag` Boolean
|
||||
@@ -1381,7 +1366,7 @@ win.loadURL('http://localhost:8000/post', {
|
||||
|
||||
* `filePath` String
|
||||
* `options` Object (optional)
|
||||
* `query` Object (optional) - Passed to `url.format()`.
|
||||
* `query` Record<String, String> (optional) - Passed to `url.format()`.
|
||||
* `search` String (optional) - Passed to `url.format()`.
|
||||
* `hash` String (optional) - Passed to `url.format()`.
|
||||
|
||||
@@ -1437,29 +1422,27 @@ screen readers
|
||||
Sets a 16 x 16 pixel overlay onto the current taskbar icon, usually used to
|
||||
convey some sort of application status or to passively notify the user.
|
||||
|
||||
#### `win.setHasShadow(hasShadow)` _macOS_
|
||||
#### `win.setHasShadow(hasShadow)`
|
||||
|
||||
* `hasShadow` Boolean
|
||||
|
||||
Sets whether the window should have a shadow. On Windows and Linux does
|
||||
nothing.
|
||||
Sets whether the window should have a shadow.
|
||||
|
||||
#### `win.hasShadow()` _macOS_
|
||||
#### `win.hasShadow()`
|
||||
|
||||
Returns `Boolean` - Whether the window has a shadow.
|
||||
|
||||
On Windows and Linux always returns
|
||||
`true`.
|
||||
|
||||
#### `win.setOpacity(opacity)` _Windows_ _macOS_
|
||||
|
||||
* `opacity` Number - between 0.0 (fully transparent) and 1.0 (fully opaque)
|
||||
|
||||
Sets the opacity of the window. On Linux does nothing.
|
||||
Sets the opacity of the window. On Linux, does nothing. Out of bound number
|
||||
values are clamped to the [0, 1] range.
|
||||
|
||||
#### `win.getOpacity()` _Windows_ _macOS_
|
||||
#### `win.getOpacity()`
|
||||
|
||||
Returns `Number` - between 0.0 (fully transparent) and 1.0 (fully opaque)
|
||||
Returns `Number` - between 0.0 (fully transparent) and 1.0 (fully opaque). On
|
||||
Linux, always returns 1.
|
||||
|
||||
#### `win.setShape(rects)` _Windows_ _Linux_ _Experimental_
|
||||
|
||||
@@ -1548,7 +1531,7 @@ Same as `webContents.showDefinitionForSelection()`.
|
||||
|
||||
#### `win.setIcon(icon)` _Windows_ _Linux_
|
||||
|
||||
* `icon` [NativeImage](native-image.md)
|
||||
* `icon` [NativeImage](native-image.md) | String
|
||||
|
||||
Changes window icon.
|
||||
|
||||
@@ -1567,23 +1550,17 @@ This cannot be called when `titleBarStyle` is set to `customButtonsOnHover`.
|
||||
Sets whether the window menu bar should hide itself automatically. Once set the
|
||||
menu bar will only show when users press the single `Alt` key.
|
||||
|
||||
If the menu bar is already visible, calling `setAutoHideMenuBar(true)` won't
|
||||
hide it immediately.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
If the menu bar is already visible, calling `setAutoHideMenuBar(true)` won't hide it immediately.
|
||||
|
||||
#### `win.isMenuBarAutoHide()`
|
||||
|
||||
Returns `Boolean` - Whether menu bar automatically hides itself.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setMenuBarVisibility(visible)` _Windows_ _Linux_
|
||||
|
||||
* `visible` Boolean
|
||||
|
||||
Sets whether the menu bar should be visible. If the menu bar is auto-hide, users
|
||||
can still bring up the menu bar by pressing the single `Alt` key.
|
||||
Sets whether the menu bar should be visible. If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key.
|
||||
|
||||
#### `win.isMenuBarVisible()`
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ Disables the disk cache for HTTP requests.
|
||||
|
||||
Disable HTTP/2 and SPDY/3.1 protocols.
|
||||
|
||||
### --disable-ntlm-v2
|
||||
|
||||
Disables NTLM v2 for posix platforms, no effect elsewhere.
|
||||
|
||||
## --lang
|
||||
|
||||
Set a custom locale.
|
||||
|
||||
@@ -16,12 +16,15 @@ following properties:
|
||||
method.
|
||||
* `url` String (optional) - The request URL. Must be provided in the absolute
|
||||
form with the protocol scheme specified as http or https.
|
||||
* `session` Object (optional) - The [`Session`](session.md) instance with
|
||||
* `session` Session (optional) - The [`Session`](session.md) instance with
|
||||
which the request is associated.
|
||||
* `partition` String (optional) - The name of the [`partition`](session.md)
|
||||
with which the request is associated. Defaults to the empty string. The
|
||||
`session` option prevails on `partition`. Thus if a `session` is explicitly
|
||||
specified, `partition` is ignored.
|
||||
* `useSessionCookies` Boolean (optional) - Whether to send cookies with this
|
||||
request from the provided session. This will make the `net` request's
|
||||
cookie behavior match a `fetch` request. Default is `false`.
|
||||
* `protocol` String (optional) - The protocol scheme in the form 'scheme:'.
|
||||
Currently supported values are 'http:' or 'https:'. Defaults to 'http:'.
|
||||
* `host` String (optional) - The server host provided as a concatenation of
|
||||
@@ -32,8 +35,8 @@ the hostname and the port number 'hostname:port'.
|
||||
* `redirect` String (optional) - The redirect mode for this request. Should be
|
||||
one of `follow`, `error` or `manual`. Defaults to `follow`. When mode is `error`,
|
||||
any redirection will be aborted. When mode is `manual` the redirection will be
|
||||
deferred until [`request.followRedirect`](#requestfollowredirect) is invoked. Listen for the [`redirect`](#event-redirect) event in
|
||||
this mode to get more details about the redirect request.
|
||||
cancelled unless [`request.followRedirect`](#requestfollowredirect) is invoked
|
||||
synchronously during the [`redirect`](#event-redirect) event.
|
||||
|
||||
`options` properties such as `protocol`, `host`, `hostname`, `port` and `path`
|
||||
strictly follow the Node.js model as described in the
|
||||
@@ -70,8 +73,8 @@ Returns:
|
||||
* `port` Integer
|
||||
* `realm` String
|
||||
* `callback` Function
|
||||
* `username` String
|
||||
* `password` String
|
||||
* `username` String (optional)
|
||||
* `password` String (optional)
|
||||
|
||||
Emitted when an authenticating proxy is asking for user credentials.
|
||||
|
||||
@@ -134,10 +137,13 @@ Returns:
|
||||
* `statusCode` Integer
|
||||
* `method` String
|
||||
* `redirectUrl` String
|
||||
* `responseHeaders` Object
|
||||
* `responseHeaders` Record<String, String[]>
|
||||
|
||||
Emitted when there is redirection and the mode is `manual`. Calling
|
||||
[`request.followRedirect`](#requestfollowredirect) will continue with the redirection.
|
||||
Emitted when the server returns a redirect response (e.g. 301 Moved
|
||||
Permanently). Calling [`request.followRedirect`](#requestfollowredirect) will
|
||||
continue with the redirection. If this event is handled,
|
||||
[`request.followRedirect`](#requestfollowredirect) must be called
|
||||
**synchronously**, otherwise the request will be cancelled.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
@@ -158,7 +164,7 @@ internally buffered inside Electron process memory.
|
||||
#### `request.setHeader(name, value)`
|
||||
|
||||
* `name` String - An extra HTTP header name.
|
||||
* `value` Object - An extra HTTP header value.
|
||||
* `value` String - An extra HTTP header value.
|
||||
|
||||
Adds an extra HTTP header. The header name will be issued as-is without
|
||||
lowercasing. It can be called only before first write. Calling this method after
|
||||
@@ -169,7 +175,7 @@ the first write will throw an error. If the passed value is not a `String`, its
|
||||
|
||||
* `name` String - Specify an extra header name.
|
||||
|
||||
Returns `Object` - The value of a previously set extra header name.
|
||||
Returns `String` - The value of a previously set extra header name.
|
||||
|
||||
#### `request.removeHeader(name)`
|
||||
|
||||
@@ -214,7 +220,8 @@ response object,it will emit the `aborted` event.
|
||||
|
||||
#### `request.followRedirect()`
|
||||
|
||||
Continues any deferred redirection request when the redirection mode is `manual`.
|
||||
Continues any pending redirection. Can only be called during a `'redirect'`
|
||||
event.
|
||||
|
||||
#### `request.getUploadProgress()`
|
||||
|
||||
|
||||
@@ -4,18 +4,12 @@
|
||||
|
||||
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
The following example shows how to write a string to the clipboard:
|
||||
|
||||
```javascript
|
||||
const { clipboard } = require('electron')
|
||||
clipboard.writeText('Example String')
|
||||
```
|
||||
|
||||
On Linux, there is also a `selection` clipboard. To manipulate it
|
||||
you need to pass `selection` to each method:
|
||||
|
||||
```javascript
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
clipboard.writeText('Example String', 'selection')
|
||||
console.log(clipboard.readText('selection'))
|
||||
```
|
||||
@@ -28,56 +22,106 @@ The `clipboard` module has the following methods:
|
||||
|
||||
### `clipboard.readText([type])`
|
||||
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Returns `String` - The content in the clipboard as plain text.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
clipboard.writeText('hello i am a bit of text!')
|
||||
|
||||
const text = clipboard.readText()
|
||||
console.log(text)
|
||||
// hello i am a bit of text!'
|
||||
```
|
||||
|
||||
### `clipboard.writeText(text[, type])`
|
||||
|
||||
* `text` String
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Writes the `text` into the clipboard as plain text.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const text = 'hello i am a bit of text!'
|
||||
clipboard.writeText(text)
|
||||
```
|
||||
|
||||
### `clipboard.readHTML([type])`
|
||||
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Returns `String` - The content in the clipboard as markup.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
clipboard.writeHTML('<b>Hi</b>')
|
||||
const html = clipboard.readHTML()
|
||||
|
||||
console.log(html)
|
||||
// <meta charset='utf-8'><b>Hi</b>
|
||||
```
|
||||
|
||||
### `clipboard.writeHTML(markup[, type])`
|
||||
|
||||
* `markup` String
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Writes `markup` to the clipboard.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
clipboard.writeHTML('<b>Hi</b')
|
||||
```
|
||||
|
||||
### `clipboard.readImage([type])`
|
||||
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Returns [`NativeImage`](native-image.md) - The image content in the clipboard.
|
||||
|
||||
### `clipboard.writeImage(image[, type])`
|
||||
|
||||
* `image` [NativeImage](native-image.md)
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Writes `image` to the clipboard.
|
||||
|
||||
### `clipboard.readRTF([type])`
|
||||
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Returns `String` - The content in the clipboard as RTF.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
clipboard.writeRTF('{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}')
|
||||
|
||||
const rtf = clipboard.readRTF()
|
||||
console.log(rtf)
|
||||
// {\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}
|
||||
```
|
||||
|
||||
### `clipboard.writeRTF(text[, type])`
|
||||
|
||||
* `text` String
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Writes the `text` into the clipboard in RTF.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const rtf = '{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}'
|
||||
clipboard.writeRTF(rtf)
|
||||
```
|
||||
|
||||
### `clipboard.readBookmark()` _macOS_ _Windows_
|
||||
|
||||
Returns `Object`:
|
||||
@@ -93,7 +137,7 @@ bookmark is unavailable.
|
||||
|
||||
* `title` String
|
||||
* `url` String
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Writes the `title` and `url` into the clipboard as a bookmark.
|
||||
|
||||
@@ -102,7 +146,9 @@ you can use `clipboard.write` to write both a bookmark and fallback text to the
|
||||
clipboard.
|
||||
|
||||
```js
|
||||
clipboard.write({
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
clipboard.writeBookmark({
|
||||
text: 'https://electronjs.org',
|
||||
bookmark: 'Electron Homepage'
|
||||
})
|
||||
@@ -110,39 +156,50 @@ clipboard.write({
|
||||
|
||||
### `clipboard.readFindText()` _macOS_
|
||||
|
||||
Returns `String` - The text on the find pasteboard. This method uses synchronous
|
||||
IPC when called from the renderer process. The cached value is reread from the
|
||||
find pasteboard whenever the application is activated.
|
||||
Returns `String` - The text on the find pasteboard, which is the pasteboard that holds information about the current state of the active application’s find panel.
|
||||
|
||||
This method uses synchronous IPC when called from the renderer process.
|
||||
The cached value is reread from the find pasteboard whenever the application is activated.
|
||||
|
||||
### `clipboard.writeFindText(text)` _macOS_
|
||||
|
||||
* `text` String
|
||||
|
||||
Writes the `text` into the find pasteboard as plain text. This method uses
|
||||
synchronous IPC when called from the renderer process.
|
||||
Writes the `text` into the find pasteboard (the pasteboard that holds information about the current state of the active application’s find panel) as plain text. This method uses synchronous IPC when called from the renderer process.
|
||||
|
||||
### `clipboard.clear([type])`
|
||||
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Clears the clipboard content.
|
||||
|
||||
### `clipboard.availableFormats([type])`
|
||||
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Returns `String[]` - An array of supported formats for the clipboard `type`.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const formats = clipboard.availableFormats()
|
||||
console.log(formats)
|
||||
// [ 'text/plain', 'text/html' ]
|
||||
```
|
||||
|
||||
### `clipboard.has(format[, type])` _Experimental_
|
||||
|
||||
* `format` String
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Returns `Boolean` - Whether the clipboard supports the specified `format`.
|
||||
|
||||
```javascript
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
console.log(clipboard.has('<p>selection</p>'))
|
||||
|
||||
const hasFormat = clipboard.has('<p>selection</p>')
|
||||
console.log(hasFormat)
|
||||
// 'true' or 'false
|
||||
```
|
||||
|
||||
### `clipboard.read(format)` _Experimental_
|
||||
@@ -157,14 +214,33 @@ Returns `String` - Reads `format` type from the clipboard.
|
||||
|
||||
Returns `Buffer` - Reads `format` type from the clipboard.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const buffer = Buffer.from('this is binary', 'utf8')
|
||||
clipboard.writeBuffer('public.utf8-plain-text', buffer)
|
||||
|
||||
const ret = clipboard.readBuffer('public.utf8-plain-text')
|
||||
|
||||
console.log(buffer.equals(out))
|
||||
// true
|
||||
```
|
||||
|
||||
### `clipboard.writeBuffer(format, buffer[, type])` _Experimental_
|
||||
|
||||
* `format` String
|
||||
* `buffer` Buffer
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Writes the `buffer` into the clipboard as `format`.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const buffer = Buffer.from('writeBuffer', 'utf8')
|
||||
clipboard.writeBuffer('public.utf8-plain-text', buffer)
|
||||
```
|
||||
|
||||
### `clipboard.write(data[, type])`
|
||||
|
||||
* `data` Object
|
||||
@@ -173,10 +249,29 @@ Writes the `buffer` into the clipboard as `format`.
|
||||
* `image` [NativeImage](native-image.md) (optional)
|
||||
* `rtf` String (optional)
|
||||
* `bookmark` String (optional) - The title of the URL at `text`.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`. `selection` is only available on Linux.
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
```javascript
|
||||
const { clipboard } = require('electron')
|
||||
clipboard.write({ text: 'test', html: '<b>test</b>' })
|
||||
```
|
||||
Writes `data` to the clipboard.
|
||||
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
clipboard.write({
|
||||
text: 'test',
|
||||
html: '<b>Hi</b>',
|
||||
rtf: '{\\rtf1\\utf8 text}',
|
||||
bookmark: 'a title'
|
||||
})
|
||||
|
||||
console.log(clipboard.readText())
|
||||
// 'test'
|
||||
|
||||
console.log(clipboard.readHTML())
|
||||
// <meta charset='utf-8'><b>Hi</b>
|
||||
|
||||
console.log(clipboard.readRTF())
|
||||
// '{\\rtf1\\utf8 text}'
|
||||
|
||||
console.log(clipboard.readBookmark())
|
||||
// { title: 'a title', url: 'test' }
|
||||
```
|
||||
|
||||
@@ -74,6 +74,9 @@ will be returned in the promise.
|
||||
|
||||
Returns `Promise<Object>` - Resolves with an object containing the `value` and `percentage` of trace buffer maximum usage
|
||||
|
||||
* `value` Number
|
||||
* `percentage` Number
|
||||
|
||||
Get the maximum usage across processes of trace buffer as a percentage of the
|
||||
full state.
|
||||
|
||||
|
||||
112
docs/api/context-bridge.md
Normal file
112
docs/api/context-bridge.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# contextBridge
|
||||
|
||||
> Create a safe, bi-directional, synchronous bridge across isolated contexts
|
||||
|
||||
Process: [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
An example of exposing an API to a renderer from an isolated preload script is given below:
|
||||
|
||||
```javascript
|
||||
// Preload (Isolated World)
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld(
|
||||
'electron',
|
||||
{
|
||||
doThing: () => ipcRenderer.send('do-a-thing')
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Renderer (Main World)
|
||||
|
||||
window.electron.doThing()
|
||||
```
|
||||
|
||||
## Glossary
|
||||
|
||||
### Main World
|
||||
|
||||
The "Main World" is the JavaScript context that your main renderer code runs in. By default, the
|
||||
page you load in your renderer executes code in this world.
|
||||
|
||||
### Isolated World
|
||||
|
||||
When `contextIsolation` is enabled in your `webPreferences`, your `preload` scripts run in an
|
||||
"Isolated World". You can read more about context isolation and what it affects in the
|
||||
[security](../tutorial/security.md#3-enable-context-isolation-for-remote-content) docs.
|
||||
|
||||
## Methods
|
||||
|
||||
The `contextBridge` module has the following methods:
|
||||
|
||||
### `contextBridge.exposeInMainWorld(apiKey, api)` _Experimental_
|
||||
|
||||
* `apiKey` String - The key to inject the API onto `window` with. The API will be accessible on `window[apiKey]`.
|
||||
* `api` Record<String, any> - Your API object, more information on what this API can be and how it works is available below.
|
||||
|
||||
## Usage
|
||||
|
||||
### API Objects
|
||||
|
||||
The `api` object provided to [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api-experimental) must be an object
|
||||
whose keys are strings and values are a `Function`, `String`, `Number`, `Array`, `Boolean`, or another nested object that meets the same conditions.
|
||||
|
||||
`Function` values are proxied to the other context and all other values are **copied** and **frozen**. Any data / primitives sent in
|
||||
the API object become immutable and updates on either side of the bridge do not result in an update on the other side.
|
||||
|
||||
An example of a complex API object is shown below:
|
||||
|
||||
```javascript
|
||||
const { contextBridge } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld(
|
||||
'electron',
|
||||
{
|
||||
doThing: () => ipcRenderer.send('do-a-thing'),
|
||||
myPromises: [Promise.resolve(), Promise.reject(new Error('whoops'))],
|
||||
anAsyncFunction: async () => 123,
|
||||
data: {
|
||||
myFlags: ['a', 'b', 'c'],
|
||||
bootTime: 1234
|
||||
},
|
||||
nestedAPI: {
|
||||
evenDeeper: {
|
||||
youCanDoThisAsMuchAsYouWant: {
|
||||
fn: () => ({
|
||||
returnData: 123
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### API Functions
|
||||
|
||||
`Function` values that you bind through the `contextBridge` are proxied through Electron to ensure that contexts remain isolated. This
|
||||
results in some key limitations that we've outlined below.
|
||||
|
||||
#### Parameter / Error / Return Type support
|
||||
|
||||
Because parameters, errors and return values are **copied** when they are sent over the bridge, there are only certain types that can be used.
|
||||
At a high level, if the type you want to use can be serialized and deserialized into the same object it will work. A table of type support
|
||||
has been included below for completeness:
|
||||
|
||||
| Type | Complexity | Parameter Support | Return Value Support | Limitations |
|
||||
| ---- | ---------- | ----------------- | -------------------- | ----------- |
|
||||
| `String` | Simple | ✅ | ✅ | N/A |
|
||||
| `Number` | Simple | ✅ | ✅ | N/A |
|
||||
| `Boolean` | Simple | ✅ | ✅ | N/A |
|
||||
| `Object` | Complex | ✅ | ✅ | Keys must be supported using only "Simple" types in this table. Values must be supported in this table. Prototype modifications are dropped. Sending custom classes will copy values but not the prototype. |
|
||||
| `Array` | Complex | ✅ | ✅ | Same limitations as the `Object` type |
|
||||
| `Error` | Complex | ✅ | ✅ | Errors that are thrown are also copied, this can result in the message and stack trace of the error changing slightly due to being thrown in a different context |
|
||||
| `Promise` | Complex | ✅ | ✅ | Promises are only proxied if they are the return value or exact parameter. Promises nested in arrays or objects will be dropped. |
|
||||
| `Function` | Complex | ✅ | ✅ | Prototype modifications are dropped. Sending classes or constructors will not work. |
|
||||
| [Cloneable Types](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) | Simple | ✅ | ✅ | See the linked document on cloneable types |
|
||||
| `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped |
|
||||
|
||||
|
||||
If the type you care about is not in the above table, it is probably not supported.
|
||||
@@ -48,7 +48,7 @@ The `crashReporter` module has the following methods:
|
||||
* `productName` String (optional) - Defaults to `app.name`.
|
||||
* `uploadToServer` Boolean (optional) - Whether crash reports should be sent to the server. Default is `true`.
|
||||
* `ignoreSystemCrashHandler` Boolean (optional) - Default is `false`.
|
||||
* `extra` Object (optional) - An object you can define that will be sent along with the
|
||||
* `extra` Record<String, String> (optional) - An object you can define that will be sent along with the
|
||||
report. Only string properties are sent correctly. Nested objects are not
|
||||
supported. When using Windows, the property names and values must be fewer than 64 characters.
|
||||
* `crashesDirectory` String (optional) - Directory to store the crash reports temporarily (only used when the crash reporter is started via `process.crashReporter.start`).
|
||||
|
||||
@@ -50,7 +50,7 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `method` String - Method name.
|
||||
* `params` Object - Event parameters defined by the 'parameters'
|
||||
* `params` unknown - Event parameters defined by the 'parameters'
|
||||
attribute in the remote debugging protocol.
|
||||
|
||||
Emitted whenever the debugging target issues an instrumentation event.
|
||||
@@ -78,7 +78,7 @@ Detaches the debugger from the `webContents`.
|
||||
|
||||
* `method` String - Method name, should be one of the methods defined by the
|
||||
[remote debugging protocol][rdp].
|
||||
* `commandParams` Object (optional) - JSON object with request parameters.
|
||||
* `commandParams` any (optional) - JSON object with request parameters.
|
||||
|
||||
Returns `Promise<any>` - A promise that resolves with the response defined by
|
||||
the 'returns' attribute of the command description in the remote debugging protocol
|
||||
|
||||
@@ -52,6 +52,8 @@ The `dialog` module has the following methods:
|
||||
boxes.
|
||||
* `securityScopedBookmarks` Boolean (optional) _macOS_ _mas_ - Create [security scoped bookmarks](https://developer.apple.com/library/content/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW16) when packaged for the Mac App Store.
|
||||
|
||||
Returns `String[] | undefined`, the file paths chosen by the user; if the dialog is cancelled it returns `undefined`.
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
The `filters` specifies an array of file types that can be displayed or
|
||||
@@ -116,7 +118,7 @@ Returns `Promise<Object>` - Resolve with an object containing the following:
|
||||
|
||||
* `canceled` Boolean - whether or not the dialog was canceled.
|
||||
* `filePaths` String[] - An array of file paths chosen by the user. If the dialog is cancelled this will be an empty array.
|
||||
* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated.
|
||||
* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated. (For return values, see [table here](#bookmarks-array).)
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
@@ -198,7 +200,7 @@ The `filters` specifies an array of file types that can be displayed, see
|
||||
Returns `Promise<Object>` - Resolve with an object containing the following:
|
||||
* `canceled` Boolean - whether or not the dialog was canceled.
|
||||
* `filePath` String (optional) - If the dialog is canceled, this will be `undefined`.
|
||||
* `bookmark` String (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present.
|
||||
* `bookmark` String (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present. (For return values, see [table here](#bookmarks-array).)
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
@@ -227,7 +229,7 @@ expanding and collapsing the dialog.
|
||||
include a checkbox with the given label.
|
||||
* `checkboxChecked` Boolean (optional) - Initial checked state of the
|
||||
checkbox. `false` by default.
|
||||
* `icon` [NativeImage](native-image.md) (optional)
|
||||
* `icon` ([NativeImage](native-image.md) | String) (optional)
|
||||
* `cancelId` Integer (optional) - The index of the button to be used to cancel the dialog, via
|
||||
the `Esc` key. By default this is assigned to the first button with "cancel" or "no" as the
|
||||
label. If no such labeled buttons exist and this option is not set, `0` will be used as the
|
||||
@@ -252,6 +254,7 @@ Shows a message box, it will block the process until the message box is closed.
|
||||
It returns the index of the clicked button.
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
If the `browserWindow` is not shown, the dialog will not be attached to it. In that case it will be displayed as an independent window.
|
||||
|
||||
### `dialog.showMessageBox([browserWindow, ]options)`
|
||||
|
||||
@@ -333,6 +336,17 @@ On Windows the options are more limited, due to the Win32 APIs used:
|
||||
* The `browserWindow` argument is ignored since it is not possible to make
|
||||
this confirmation dialog modal.
|
||||
|
||||
## Bookmarks array
|
||||
|
||||
`showOpenDialog`, `showOpenDialogSync`, `showSaveDialog`, and `showSaveDialogSync` will return a `bookmarks` array.
|
||||
|
||||
| Build Type | securityScopedBookmarks boolean | Return Type | Return Value |
|
||||
|------------|---------------------------------|:-----------:|--------------------------------|
|
||||
| macOS mas | True | Success | `['LONGBOOKMARKSTRING']` |
|
||||
| macOS mas | True | Error | `['']` (array of empty string) |
|
||||
| macOS mas | False | NA | `[]` (empty array) |
|
||||
| non mas | any | NA | `[]` (empty array) |
|
||||
|
||||
## Sheets
|
||||
|
||||
On macOS, dialogs are presented as sheets attached to a window if you provide
|
||||
|
||||
@@ -18,6 +18,8 @@ app.dock.bounce()
|
||||
* `type` String (optional) - Can be `critical` or `informational`. The default is
|
||||
`informational`
|
||||
|
||||
Returns `Integer` - an ID representing the request.
|
||||
|
||||
When `critical` is passed, the dock icon will bounce until either the
|
||||
application becomes active or the request is canceled.
|
||||
|
||||
@@ -25,7 +27,7 @@ When `informational` is passed, the dock icon will bounce for one second.
|
||||
However, the request remains active until either the application becomes active
|
||||
or the request is canceled.
|
||||
|
||||
Returns `Integer` an ID representing the request.
|
||||
**Nota Bene:** This method can only be used while the app is not focused; when the app is focused it will return -1.
|
||||
|
||||
#### `dock.cancelBounce(id)` _macOS_
|
||||
|
||||
|
||||
@@ -123,3 +123,14 @@ the one downloaded by `npm install`. Usage:
|
||||
```sh
|
||||
export ELECTRON_OVERRIDE_DIST_PATH=/Users/username/projects/electron/out/Debug
|
||||
```
|
||||
|
||||
## Set By Electron
|
||||
|
||||
Electron sets some variables in your environment at runtime.
|
||||
|
||||
### `ORIGINAL_XDG_CURRENT_DESKTOP`
|
||||
|
||||
This variable is set to the value of `XDG_CURRENT_DESKTOP` that your application
|
||||
originally launched with. Electron sometimes modifies the value of `XDG_CURRENT_DESKTOP`
|
||||
to affect other logic within Chromium so if you want access to the _original_ value
|
||||
you should look up this environment variable instead.
|
||||
|
||||
@@ -51,7 +51,7 @@ A `String` representing the HTTP status message.
|
||||
|
||||
#### `response.headers`
|
||||
|
||||
An `Object` representing the response HTTP headers. The `headers` object is
|
||||
An `Record<string, string[]>` representing the response HTTP headers. The `headers` object is
|
||||
formatted as follows:
|
||||
|
||||
* All header names are lowercased.
|
||||
|
||||
@@ -77,6 +77,7 @@ only the next time a message is sent to `channel`, after which it is removed.
|
||||
|
||||
* `channel` String
|
||||
* `listener` Function
|
||||
* `...args` any[]
|
||||
|
||||
Removes the specified `listener` from the listener array for the specified
|
||||
`channel`.
|
||||
@@ -90,7 +91,7 @@ Removes listeners of the specified `channel`.
|
||||
### `ipcMain.handle(channel, listener)`
|
||||
|
||||
* `channel` String
|
||||
* `listener` Function<Promise> | Function<any>
|
||||
* `listener` Function<Promise<void> | any>
|
||||
* `event` IpcMainInvokeEvent
|
||||
* `...args` any[]
|
||||
|
||||
@@ -122,7 +123,7 @@ WebContents is the source of the invoke request.
|
||||
### `ipcMain.handleOnce(channel, listener)`
|
||||
|
||||
* `channel` String
|
||||
* `listener` Function<Promise> | Function<any>
|
||||
* `listener` Function<Promise<void> | any>
|
||||
* `event` IpcMainInvokeEvent
|
||||
* `...args` any[]
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ See [`Menu`](menu.md) for examples.
|
||||
* `menuItem` MenuItem
|
||||
* `browserWindow` [BrowserWindow](browser-window.md)
|
||||
* `event` [KeyboardEvent](structures/keyboard-event.md)
|
||||
* `role` String (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteandmatchstyle`, `delete`, `selectall`, `reload`, `forcereload`, `toggledevtools`, `resetzoom`, `zoomin`, `zoomout`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideothers`, `unhide`, `quit`, `startspeaking`, `stopspeaking`, `close`, `minimize`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu` or `windowMenu` - Define the action of the menu item, when specified the
|
||||
* `role` String (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, `startSpeaking`, `stopSpeaking`, `close`, `minimize`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu` - Define the action of the menu item, when specified the
|
||||
`click` property will be ignored. See [roles](#roles).
|
||||
* `type` String (optional) - Can be `normal`, `separator`, `submenu`, `checkbox` or
|
||||
`radio`.
|
||||
@@ -69,6 +69,7 @@ a `type`.
|
||||
The `role` property can have following values:
|
||||
|
||||
* `undo`
|
||||
* `about` - Trigger a native about panel (custom message box on Window, which does not provide its own).
|
||||
* `redo`
|
||||
* `cut`
|
||||
* `copy`
|
||||
@@ -82,7 +83,7 @@ The `role` property can have following values:
|
||||
* `reload` - Reload the current window.
|
||||
* `forceReload` - Reload the current window ignoring the cache.
|
||||
* `toggleDevTools` - Toggle developer tools in the current window.
|
||||
* `toggleFullScreen` - Toggle full screen mode on the current window.
|
||||
* `togglefullscreen` - Toggle full screen mode on the current window.
|
||||
* `resetZoom` - Reset the focused page's zoom level to the original size.
|
||||
* `zoomIn` - Zoom in the focused page by 10%.
|
||||
* `zoomOut` - Zoom out the focused page by 10%.
|
||||
@@ -94,7 +95,6 @@ The `role` property can have following values:
|
||||
The following additional roles are available on _macOS_:
|
||||
|
||||
* `appMenu` - Whole default "App" menu (About, Services, etc.)
|
||||
* `about` - Map to the `orderFrontStandardAboutPanel` action.
|
||||
* `hide` - Map to the `hide` action.
|
||||
* `hideOthers` - Map to the `hideOtherApplications` action.
|
||||
* `unhide` - Map to the `unhideAllApplications` action.
|
||||
@@ -152,7 +152,7 @@ A `String` indicating the type of the item. Can be `normal`, `separator`, `subme
|
||||
|
||||
#### `menuItem.role`
|
||||
|
||||
A `String` (optional) indicating the item's role, if set. Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteandmatchstyle`, `delete`, `selectall`, `reload`, `forcereload`, `toggledevtools`, `resetzoom`, `zoomin`, `zoomout`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideothers`, `unhide`, `quit`, `startspeaking`, `stopspeaking`, `close`, `minimize`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu` or `windowMenu`
|
||||
A `String` (optional) indicating the item's role, if set. Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, `startSpeaking`, `stopSpeaking`, `close`, `minimize`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu`
|
||||
|
||||
#### `menuItem.accelerator`
|
||||
|
||||
|
||||
@@ -328,9 +328,9 @@ can be called on empty images.
|
||||
|
||||
[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer
|
||||
|
||||
## Properties
|
||||
### Instance Properties
|
||||
|
||||
### `nativeImage.isMacTemplateImage` _macOS_
|
||||
#### `nativeImage.isMacTemplateImage` _macOS_
|
||||
|
||||
A `Boolean` property that determines whether the image is considered a [template image](https://developer.apple.com/documentation/appkit/nsimage/1520017-template).
|
||||
|
||||
|
||||
66
docs/api/native-theme.md
Normal file
66
docs/api/native-theme.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# nativeTheme
|
||||
|
||||
> Read and respond to changes in Chromium's native color theme.
|
||||
|
||||
Process: [Main](../glossary.md#main-process)
|
||||
|
||||
## Events
|
||||
|
||||
The `nativeTheme` module emits the following events:
|
||||
|
||||
### Event: 'updated'
|
||||
|
||||
Emitted when something in the underlying NativeTheme has changed. This normally
|
||||
means that either the value of `shouldUseDarkColors`,
|
||||
`shouldUseHighContrastColors` or `shouldUseInvertedColorScheme` has changed.
|
||||
You will have to check them to determine which one has changed.
|
||||
|
||||
## Properties
|
||||
|
||||
The `nativeTheme` module has the following properties:
|
||||
|
||||
### `nativeTheme.shouldUseDarkColors` _Readonly_
|
||||
|
||||
A `Boolean` for if the OS / Chromium currently has a dark mode enabled or is
|
||||
being instructed to show a dark-style UI. If you want to modify this value you
|
||||
should use `themeSource` below.
|
||||
|
||||
### `nativeTheme.themeSource`
|
||||
|
||||
A `String` property that can be `system`, `light` or `dark`. It is used to override and supercede
|
||||
the value that Chromium has chosen to use internally.
|
||||
|
||||
Setting this property to `system` will remove the override and
|
||||
everything will be reset to the OS default. By default `themeSource` is `system`.
|
||||
|
||||
Settings this property to `dark` will have the following effects:
|
||||
* `nativeTheme.shouldUseDarkColors` will be `true` when accessed
|
||||
* Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the dark UI.
|
||||
* Any UI the OS renders on macOS including menus, window frames, etc. will use the dark UI.
|
||||
* The [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) CSS query will match `dark` mode.
|
||||
* The `updated` event will be emitted
|
||||
|
||||
Settings this property to `light` will have the following effects:
|
||||
* `nativeTheme.shouldUseDarkColors` will be `false` when accessed
|
||||
* Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the light UI.
|
||||
* Any UI the OS renders on macOS including menus, window frames, etc. will use the light UI.
|
||||
* The [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) CSS query will match `light` mode.
|
||||
* The `updated` event will be emitted
|
||||
|
||||
The usage of this property should align with a classic "dark mode" state machine in your application
|
||||
where the user has three options.
|
||||
* `Follow OS` --> `themeSource = 'system'`
|
||||
* `Dark Mode` --> `themeSource = 'dark'`
|
||||
* `Light Mode` --> `themeSource = 'light'`
|
||||
|
||||
Your application should then always use `shouldUseDarkColors` to determine what CSS to apply.
|
||||
|
||||
### `nativeTheme.shouldUseHighContrastColors` _macOS_ _Windows_ _Readonly_
|
||||
|
||||
A `Boolean` for if the OS / Chromium currently has high-contrast mode enabled
|
||||
or is being instructed to show a high-constrast UI.
|
||||
|
||||
### `nativeTheme.shouldUseInvertedColorScheme` _macOS_ _Windows_ _Readonly_
|
||||
|
||||
A `Boolean` for if the OS / Chromium currently has an inverted color scheme
|
||||
or is being instructed to use an inverted color scheme.
|
||||
@@ -54,7 +54,7 @@ The `net` module has the following methods:
|
||||
|
||||
### `net.request(options)`
|
||||
|
||||
* `options` (Object | String) - The `ClientRequest` constructor options.
|
||||
* `options` (ClientRequestConstructorOptions | String) - The `ClientRequest` constructor options.
|
||||
|
||||
Returns [`ClientRequest`](./client-request.md)
|
||||
|
||||
|
||||
@@ -217,11 +217,15 @@ that all statistics are reported in Kilobytes.
|
||||
|
||||
Returns `String` - The version of the host operating system.
|
||||
|
||||
Examples:
|
||||
Example:
|
||||
|
||||
* `macOS` -> `10.13.6`
|
||||
* `Windows` -> `10.0.17763`
|
||||
* `Linux` -> `4.15.0-45-generic`
|
||||
```js
|
||||
const version = process.getSystemVersion()
|
||||
console.log(version)
|
||||
// On macOS -> '10.13.6'
|
||||
// On Windows -> '10.0.17763'
|
||||
// On Linux -> '4.15.0-45-generic'
|
||||
```
|
||||
|
||||
**Note:** It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`.
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ should be called with either a `String` or an object that has the `data`,
|
||||
* `redirectRequest` Object
|
||||
* `url` String
|
||||
* `method` String (optional)
|
||||
* `session` Object (optional)
|
||||
* `session` Session | null (optional)
|
||||
* `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
@@ -388,10 +388,8 @@ which sends a `Buffer` as a response.
|
||||
* `redirectRequest` Object
|
||||
* `url` String
|
||||
* `method` String (optional)
|
||||
* `session` Object | null (optional)
|
||||
* `uploadData` Object (optional)
|
||||
* `contentType` String - MIME type of the content.
|
||||
* `data` String - Content to be sent.
|
||||
* `session` Session | null (optional)
|
||||
* `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ property, so writing `let { screen } = require('electron')` will not work.
|
||||
|
||||
An example of creating a window that fills the whole screen:
|
||||
|
||||
```javascript
|
||||
```javascript fiddle='docs/fiddles/screen/fit-screen'
|
||||
const { app, BrowserWindow, screen } = require('electron')
|
||||
|
||||
let win
|
||||
|
||||
@@ -91,6 +91,20 @@ session.defaultSession.on('will-download', (event, item, webContents) => {
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'preconnect' _Experimental_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `preconnectUrl` String - The URL being requested for preconnection by the
|
||||
renderer.
|
||||
* `allowCredentials` Boolean - True if the renderer is requesting that the
|
||||
connection include credentials (see the
|
||||
[spec](https://w3c.github.io/resource-hints/#preconnect) for more details.)
|
||||
|
||||
Emitted when a render process requests preconnection to a URL, generally due to
|
||||
a [resource hint](https://w3c.github.io/resource-hints/).
|
||||
|
||||
### Instance Methods
|
||||
|
||||
The following methods are available on instances of `Session`:
|
||||
@@ -238,6 +252,14 @@ window.webContents.session.enableNetworkEmulation({
|
||||
window.webContents.session.enableNetworkEmulation({ offline: true })
|
||||
```
|
||||
|
||||
#### `ses.preconnect(options)` _Experimental_
|
||||
|
||||
* `options` Object
|
||||
* `url` String - URL for preconnect. Only the origin is relevant for opening the socket.
|
||||
* `numSockets` Number (optional) - number of sockets to preconnect. Must be between 1 and 6. Defaults to 1.
|
||||
|
||||
Preconnects the given number of sockets to an origin.
|
||||
|
||||
#### `ses.disableNetworkEmulation()`
|
||||
|
||||
Disables any network emulation already active for the `session`. Resets to
|
||||
|
||||
4
docs/api/structures/extension-info.md
Normal file
4
docs/api/structures/extension-info.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# ExtensionInfo Object
|
||||
|
||||
* `name` String
|
||||
* `version` String
|
||||
4
docs/api/structures/new-window-event.md
Normal file
4
docs/api/structures/new-window-event.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# NewWindowEvent Object extends `Event`
|
||||
|
||||
* `newGuest` BrowserWindow (optional)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ProtocolResponseUploadData Object
|
||||
|
||||
* `contentType` String - MIME type of the content.
|
||||
* `data` String - Content to be sent.
|
||||
* `data` String | Buffer - Content to be sent.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
include in the trace. If not specified, trace all processes.
|
||||
* `histogram_names` String[] (optional) - a list of [histogram][] names to report
|
||||
with the trace.
|
||||
* `memory_dump_config` Object (optional) - if the
|
||||
* `memory_dump_config` Record<String, any> (optional) - if the
|
||||
`disabled-by-default-memory-infra` category is enabled, this contains
|
||||
optional additional configuration for data collection. See the [Chromium
|
||||
memory-infra docs][memory-infra docs] for more information.
|
||||
|
||||
@@ -27,28 +27,34 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
|
||||
### Event: 'inverted-color-scheme-changed' _Windows_
|
||||
### Event: 'inverted-color-scheme-changed' _Windows_ _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `invertedColorScheme` Boolean - `true` if an inverted color scheme (a high contrast color scheme with light text and dark backgrounds) is being used, `false` otherwise.
|
||||
|
||||
### Event: 'high-contrast-color-scheme-changed' _Windows_
|
||||
**Deprecated:** Should use the new [`updated`](native-theme.md#event-updated) event on the `nativeTheme` module.
|
||||
|
||||
### Event: 'high-contrast-color-scheme-changed' _Windows_ _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `highContrastColorScheme` Boolean - `true` if a high contrast theme is being used, `false` otherwise.
|
||||
|
||||
**Deprecated:** Should use the new [`updated`](native-theme.md#event-updated) event on the `nativeTheme` module.
|
||||
|
||||
## Methods
|
||||
|
||||
### `systemPreferences.isDarkMode()` _macOS_ _Windows_
|
||||
### `systemPreferences.isDarkMode()` _macOS_ _Windows_ _Deprecated_
|
||||
|
||||
Returns `Boolean` - Whether the system is in Dark Mode.
|
||||
|
||||
**Note:** On macOS 10.15 Catalina in order for this API to return the correct value when in the "automatic" dark mode setting you must either have `NSRequiresAquaSystemAppearance=false` in your `Info.plist` or be on Electron `>=7.0.0`. See the [dark mode guide](../tutorial/mojave-dark-mode-guide.md) for more information.
|
||||
|
||||
**Deprecated:** Should use the new [`nativeTheme.shouldUseDarkColors`](native-theme.md#nativethemeshouldusedarkcolors-readonly) API.
|
||||
|
||||
### `systemPreferences.isSwipeTrackingFromScrollEventsEnabled()` _macOS_
|
||||
|
||||
Returns `Boolean` - Whether the Swipe between pages setting is on.
|
||||
@@ -56,7 +62,7 @@ Returns `Boolean` - Whether the Swipe between pages setting is on.
|
||||
### `systemPreferences.postNotification(event, userInfo[, deliverImmediately])` _macOS_
|
||||
|
||||
* `event` String
|
||||
* `userInfo` Object
|
||||
* `userInfo` Record<String, any>
|
||||
* `deliverImmediately` Boolean (optional) - `true` to post notifications immediately even when the subscribing app is inactive.
|
||||
|
||||
Posts `event` as native notifications of macOS. The `userInfo` is an Object
|
||||
@@ -65,7 +71,7 @@ that contains the user information dictionary sent along with the notification.
|
||||
### `systemPreferences.postLocalNotification(event, userInfo)` _macOS_
|
||||
|
||||
* `event` String
|
||||
* `userInfo` Object
|
||||
* `userInfo` Record<String, any>
|
||||
|
||||
Posts `event` as native notifications of macOS. The `userInfo` is an Object
|
||||
that contains the user information dictionary sent along with the notification.
|
||||
@@ -73,7 +79,7 @@ that contains the user information dictionary sent along with the notification.
|
||||
### `systemPreferences.postWorkspaceNotification(event, userInfo)` _macOS_
|
||||
|
||||
* `event` String
|
||||
* `userInfo` Object
|
||||
* `userInfo` Record<String, any>
|
||||
|
||||
Posts `event` as native notifications of macOS. The `userInfo` is an Object
|
||||
that contains the user information dictionary sent along with the notification.
|
||||
@@ -83,7 +89,7 @@ that contains the user information dictionary sent along with the notification.
|
||||
* `event` String
|
||||
* `callback` Function
|
||||
* `event` String
|
||||
* `userInfo` Object
|
||||
* `userInfo` Record<String, unknown>
|
||||
* `object` String
|
||||
|
||||
Returns `Number` - The ID of this subscription
|
||||
@@ -110,7 +116,7 @@ example values of `event` are:
|
||||
* `event` String
|
||||
* `callback` Function
|
||||
* `event` String
|
||||
* `userInfo` Object
|
||||
* `userInfo` Record<String, unknown>
|
||||
* `object` String
|
||||
|
||||
Returns `Number` - The ID of this subscription
|
||||
@@ -123,7 +129,7 @@ This is necessary for events such as `NSUserDefaultsDidChangeNotification`.
|
||||
* `event` String
|
||||
* `callback` Function
|
||||
* `event` String
|
||||
* `userInfo` Object
|
||||
* `userInfo` Record<String, unknown>
|
||||
* `object` String
|
||||
|
||||
Same as `subscribeNotification`, but uses `NSWorkspace.sharedWorkspace.notificationCenter`.
|
||||
@@ -149,7 +155,7 @@ Same as `unsubscribeNotification`, but removes the subscriber from `NSWorkspace.
|
||||
|
||||
### `systemPreferences.registerDefaults(defaults)` _macOS_
|
||||
|
||||
* `defaults` Object - a dictionary of (`key: value`) user defaults
|
||||
* `defaults` Record<String, String | Boolean | Number> - a dictionary of (`key: value`) user defaults
|
||||
|
||||
Add the specified defaults to your application's `NSUserDefaults`.
|
||||
|
||||
@@ -342,14 +348,18 @@ Returns `String` - The standard system color formatted as `#RRGGBBAA`.
|
||||
|
||||
Returns one of several standard system colors that automatically adapt to vibrancy and changes in accessibility settings like 'Increase contrast' and 'Reduce transparency'. See [Apple Documentation](https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/color#system-colors) for more details.
|
||||
|
||||
### `systemPreferences.isInvertedColorScheme()` _Windows_
|
||||
### `systemPreferences.isInvertedColorScheme()` _Windows_ _Deprecated_
|
||||
|
||||
Returns `Boolean` - `true` if an inverted color scheme (a high contrast color scheme with light text and dark backgrounds) is active, `false` otherwise.
|
||||
|
||||
### `systemPreferences.isHighContrastColorScheme()` _macOS_ _Windows_
|
||||
**Deprecated:** Should use the new [`nativeTheme.shouldUseInvertedColorScheme`](native-theme.md#nativethemeshoulduseinvertedcolorscheme-macos-windows-readonly) API.
|
||||
|
||||
### `systemPreferences.isHighContrastColorScheme()` _macOS_ _Windows_ _Deprecated_
|
||||
|
||||
Returns `Boolean` - `true` if a high contrast theme is active, `false` otherwise.
|
||||
|
||||
**Depreacted:** Should use the new [`nativeTheme.shouldUseHighContrastColors`](native-theme.md#nativethemeshouldusehighcontrastcolors-macos-windows-readonly) API.
|
||||
|
||||
### `systemPreferences.getEffectiveAppearance()` _macOS_
|
||||
|
||||
Returns `String` - Can be `dark`, `light` or `unknown`.
|
||||
@@ -365,7 +375,9 @@ using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode
|
||||
packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
|
||||
for more details.
|
||||
|
||||
### `systemPreferences.getAppLevelAppearance()` _macOS_
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `systemPreferences.getAppLevelAppearance()` _macOS_ _Deprecated_
|
||||
|
||||
Returns `String` | `null` - Can be `dark`, `light` or `unknown`.
|
||||
|
||||
@@ -375,7 +387,7 @@ You can use the `setAppLevelAppearance` API to set this value.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `systemPreferences.setAppLevelAppearance(appearance)` _macOS_
|
||||
### `systemPreferences.setAppLevelAppearance(appearance)` _macOS_ _Deprecated_
|
||||
|
||||
* `appearance` String | null - Can be `dark` or `light`
|
||||
|
||||
@@ -390,8 +402,6 @@ Returns `Boolean` - whether or not this device has the ability to use Touch ID.
|
||||
|
||||
**NOTE:** This API will return `false` on macOS systems older than Sierra 10.12.2.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `systemPreferences.promptTouchID(reason)` _macOS_
|
||||
|
||||
* `reason` String - The reason you are asking for Touch ID authentication
|
||||
@@ -450,10 +460,25 @@ Returns an object with system animation settings.
|
||||
|
||||
### `systemPreferences.appLevelAppearance` _macOS_
|
||||
|
||||
A `String` property that determines the macOS appearance setting for
|
||||
A `String` property that can be `dark`, `light` or `unknown`. It determines the macOS appearance setting for
|
||||
your application. This maps to values in: [NSApplication.appearance](https://developer.apple.com/documentation/appkit/nsapplication/2967170-appearance?language=objc). Setting this will override the
|
||||
system default as well as the value of `getEffectiveAppearance`.
|
||||
|
||||
Possible values that can be set are `dark` and `light`, and possible return values are `dark`, `light`, and `unknown`.
|
||||
|
||||
This property is only available on macOS 10.14 Mojave or newer.
|
||||
|
||||
### `systemPreferences.effectiveAppearance` _macOS_ _Readonly_
|
||||
|
||||
A `String` property that can be `dark`, `light` or `unknown`.
|
||||
|
||||
Returns the macOS appearance setting that is currently applied to your application,
|
||||
maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc)
|
||||
|
||||
Please note that until Electron is built targeting the 10.14 SDK, your application's
|
||||
`effectiveAppearance` will default to 'light' and won't inherit the OS preference. In
|
||||
the interim in order for your application to inherit the OS preference you must set the
|
||||
`NSRequiresAquaSystemAppearance` key in your apps `Info.plist` to `false`. If you are
|
||||
using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode`
|
||||
packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
|
||||
for more details.
|
||||
|
||||
@@ -138,12 +138,12 @@ Emitted when page receives favicon urls.
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `event` NewWindowEvent
|
||||
* `url` String
|
||||
* `frameName` String
|
||||
* `disposition` String - Can be `default`, `foreground-tab`, `background-tab`,
|
||||
`new-window`, `save-to-disk` and `other`.
|
||||
* `options` Object - The options which will be used for creating the new
|
||||
* `options` BrowserWindowConstructorOptions - The options which will be used for creating the new
|
||||
[`BrowserWindow`](browser-window.md).
|
||||
* `additionalFeatures` String[] - The non-standard features (features not handled
|
||||
by Chromium or Electron) given to `window.open()`.
|
||||
@@ -454,10 +454,8 @@ The usage is the same with [the `select-client-certificate` event of
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `request` Object
|
||||
* `method` String
|
||||
* `authenticationResponseDetails` Object
|
||||
* `url` URL
|
||||
* `referrer` URL
|
||||
* `authInfo` Object
|
||||
* `isProxy` Boolean
|
||||
* `scheme` String
|
||||
@@ -465,8 +463,8 @@ Returns:
|
||||
* `port` Integer
|
||||
* `realm` String
|
||||
* `callback` Function
|
||||
* `username` String
|
||||
* `password` String
|
||||
* `username` String (optional)
|
||||
* `password` String (optional)
|
||||
|
||||
Emitted when `webContents` wants to do basic auth.
|
||||
|
||||
@@ -481,7 +479,7 @@ Returns:
|
||||
* `requestId` Integer
|
||||
* `activeMatchOrdinal` Integer - Position of the active match.
|
||||
* `matches` Integer - Number of Matches.
|
||||
* `selectionArea` Object - Coordinates of first match region.
|
||||
* `selectionArea` Rectangle - Coordinates of first match region.
|
||||
* `finalUpdate` Boolean
|
||||
|
||||
Emitted when a result is available for
|
||||
@@ -669,10 +667,10 @@ Emitted when the devtools window instructs the webContents to reload
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webPreferences` Object - The web preferences that will be used by the guest
|
||||
* `webPreferences` WebPreferences - The web preferences that will be used by the guest
|
||||
page. This object can be modified to adjust the preferences for the guest
|
||||
page.
|
||||
* `params` Object - The other `<webview>` parameters such as the `src` URL.
|
||||
* `params` Record<string, string> - The other `<webview>` parameters such as the `src` URL.
|
||||
This object can be modified to adjust the parameters of the guest page.
|
||||
|
||||
Emitted when a `<webview>`'s web contents is being attached to this web
|
||||
@@ -842,7 +840,7 @@ webContents.loadURL('https://github.com', options)
|
||||
|
||||
* `filePath` String
|
||||
* `options` Object (optional)
|
||||
* `query` Object (optional) - Passed to `url.format()`.
|
||||
* `query` Record<String, String> (optional) - Passed to `url.format()`.
|
||||
* `search` String (optional) - Passed to `url.format()`.
|
||||
* `hash` String (optional) - Passed to `url.format()`.
|
||||
|
||||
@@ -1068,11 +1066,13 @@ Returns `Boolean` - Whether audio is currently playing.
|
||||
|
||||
#### `contents.setZoomFactor(factor)`
|
||||
|
||||
* `factor` Number - Zoom factor.
|
||||
* `factor` Double - Zoom factor; default is 1.0.
|
||||
|
||||
Changes the zoom factor to the specified factor. Zoom factor is
|
||||
zoom percent divided by 100, so 300% = 3.0.
|
||||
|
||||
The factor must be greater than 0.0.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.getZoomFactor()`
|
||||
@@ -1234,11 +1234,30 @@ Returns `Promise<NativeImage>` - Resolves with a [NativeImage](native-image.md)
|
||||
|
||||
Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page.
|
||||
|
||||
#### `contents.isBeingCaptured()`
|
||||
|
||||
Returns `Boolean` - Whether this page is being captured. It returns true when the capturer count
|
||||
is large then 0.
|
||||
|
||||
#### `contents.incrementCapturerCount([size])`
|
||||
|
||||
* `size` [Size](structures/size.md) (optional) - The perferred size for the capturer.
|
||||
|
||||
Increase the capturer count by one. The page is considered visible when its browser window is
|
||||
hidden and the capturer count is non-zero.
|
||||
|
||||
This also affects the Page Visibility API.
|
||||
|
||||
#### `contents.decrementCapturerCount()`
|
||||
|
||||
Decrease the capturer count by one. The page will be set to hidden or occluded state when its
|
||||
browser window is hidden or occluded and the capturer count reaches zero.
|
||||
|
||||
#### `contents.getPrinters()`
|
||||
|
||||
Get the system printer list.
|
||||
|
||||
Returns [`PrinterInfo[]`](structures/printer-info.md).
|
||||
Returns [`PrinterInfo[]`](structures/printer-info.md)
|
||||
|
||||
#### `contents.print([options], [callback])`
|
||||
|
||||
@@ -1246,7 +1265,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md).
|
||||
* `silent` Boolean (optional) - Don't ask user for print settings. Default is `false`.
|
||||
* `printBackground` Boolean (optional) - Prints the background color and image of
|
||||
the web page. Default is `false`.
|
||||
* `deviceName` String (optional) - Set the printer device name to use. Default is `''`.
|
||||
* `deviceName` String (optional) - Set the printer device name to use. Must be the system-defined name and not the 'friendly' name, e.g 'Brother_QL_820NWB' and not 'Brother QL-820NWB'.
|
||||
* `color` Boolean (optional) - Set whether the printed web page will be in color or grayscale. Default is `true`.
|
||||
* `margins` Object (optional)
|
||||
* `marginType` String (optional) - Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen, you will also need to specify `top`, `bottom`, `left`, and `right`.
|
||||
@@ -1266,14 +1285,10 @@ Returns [`PrinterInfo[]`](structures/printer-info.md).
|
||||
* `vertical` Number (optional) - The vertical dpi.
|
||||
* `callback` Function (optional)
|
||||
* `success` Boolean - Indicates success of the print call.
|
||||
* `failureReason` String - Called back if the print fails; can be `cancelled` or `failed`.
|
||||
* `failureReason` String - Error description called back if the print fails.
|
||||
|
||||
Prints window's web page. When `silent` is set to `true`, Electron will pick
|
||||
the system's default printer if `deviceName` is empty and the default settings
|
||||
for printing.
|
||||
|
||||
Calling `window.print()` in web page is equivalent to calling
|
||||
`webContents.print({ silent: false, printBackground: false, deviceName: '' })`.
|
||||
the system's default printer if `deviceName` is empty and the default settings for printing.
|
||||
|
||||
Use `page-break-before: always;` CSS style to force to print to a new page.
|
||||
|
||||
@@ -1329,12 +1344,13 @@ win.loadURL('http://github.com')
|
||||
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
// Use default printing options
|
||||
win.webContents.printToPDF({}, (error, data) => {
|
||||
if (error) throw error
|
||||
win.webContents.printToPDF({}).then(data => {
|
||||
fs.writeFile('/tmp/print.pdf', data, (error) => {
|
||||
if (error) throw error
|
||||
console.log('Write PDF successfully.')
|
||||
})
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
})
|
||||
```
|
||||
@@ -1563,13 +1579,6 @@ Disable device emulation enabled by `webContents.enableDeviceEmulation`.
|
||||
#### `contents.sendInputEvent(inputEvent)`
|
||||
|
||||
* `inputEvent` [MouseInputEvent](structures/mouse-input-event.md) | [MouseWheelInputEvent](structures/mouse-wheel-input-event.md) | [KeyboardInputEvent](structures/keyboard-input-event.md)
|
||||
* `type` String (**required**) - The type of the event, can be `mouseDown`,
|
||||
`mouseUp`, `mouseEnter`, `mouseLeave`, `contextMenu`, `mouseWheel`,
|
||||
`mouseMove`, `keyDown`, `keyUp` or `char`.
|
||||
* `modifiers` String[] - An array of modifiers of the event, can
|
||||
include `shift`, `control`, `alt`, `meta`, `isKeypad`, `isAutoRepeat`,
|
||||
`leftButtonDown`, `middleButtonDown`, `rightButtonDown`, `capsLock`,
|
||||
`numLock`, `left`, `right`.
|
||||
|
||||
Sends an input `event` to the page.
|
||||
**Note:** The [`BrowserWindow`](browser-window.md) containing the contents needs to be focused for
|
||||
@@ -1774,7 +1783,7 @@ A [`WebContents`](web-contents.md) instance that might own this `WebContents`.
|
||||
|
||||
#### `contents.devToolsWebContents` _Readonly_
|
||||
|
||||
A `WebContents` of DevTools for this `WebContents`.
|
||||
A `WebContents | null` property that represents the of DevTools `WebContents` associated with a given `WebContents`.
|
||||
|
||||
**Note:** Users should never store this object because it may become `null`
|
||||
when the DevTools has been closed.
|
||||
|
||||
@@ -22,11 +22,13 @@ The `WebFrame` class has the following instance methods:
|
||||
|
||||
### `webFrame.setZoomFactor(factor)`
|
||||
|
||||
* `factor` Number - Zoom factor.
|
||||
* `factor` Double - Zoom factor; default is 1.0.
|
||||
|
||||
Changes the zoom factor to the specified factor. Zoom factor is
|
||||
zoom percent divided by 100, so 300% = 3.0.
|
||||
|
||||
The factor must be greater than 0.0.
|
||||
|
||||
### `webFrame.getZoomFactor()`
|
||||
|
||||
Returns `Number` - The current zoom factor.
|
||||
@@ -139,10 +141,14 @@ this limitation.
|
||||
* `userGesture` Boolean (optional) - Default is `false`.
|
||||
|
||||
Returns `Promise<any>` - A promise that resolves with the result of the executed code
|
||||
or is rejected if the result of the code is a rejected promise.
|
||||
or is rejected if execution could not start.
|
||||
|
||||
Works like `executeJavaScript` but evaluates `scripts` in an isolated context.
|
||||
|
||||
Note that when the execution of script fails, the returned promise will not
|
||||
and the `result` would be `undefined`. This is because Chromium does not
|
||||
errors of isolated worlds to foreign worlds.
|
||||
|
||||
### `webFrame.setIsolatedWorldInfo(worldId, info)`
|
||||
* `worldId` Integer - The ID of the world to run the javascript in, `0` is the default world, `999` is the world used by Electrons `contextIsolation` feature. Chrome extensions reserve the range of IDs in `[1 << 20, 1 << 29)`. You can provide any integer here.
|
||||
* `info` Object
|
||||
|
||||
@@ -99,16 +99,16 @@ Some examples of valid `urls`:
|
||||
* `timestamp` Double
|
||||
* `requestHeaders` Record<string, string>
|
||||
* `callback` Function
|
||||
* `response` Object
|
||||
* `beforeSendResponse` Object
|
||||
* `cancel` Boolean (optional)
|
||||
* `requestHeaders` Record<string, string> (optional) - When provided, request will be made
|
||||
* `requestHeaders` Record<string, string | string[]> (optional) - When provided, request will be made
|
||||
with these headers.
|
||||
|
||||
The `listener` will be called with `listener(details, callback)` before sending
|
||||
an HTTP request, once the request headers are available. This may occur after a
|
||||
TCP connection is made to the server, but before any http data is sent.
|
||||
|
||||
The `callback` has to be called with an `response` object.
|
||||
The `callback` has to be called with a `response` object.
|
||||
|
||||
#### `webRequest.onSendHeaders([filter, ]listener)`
|
||||
|
||||
@@ -146,11 +146,12 @@ response are visible by the time this listener is fired.
|
||||
* `timestamp` Double
|
||||
* `statusLine` String
|
||||
* `statusCode` Integer
|
||||
* `responseHeaders` Record<string, string> (optional)
|
||||
* `requestHeaders` Record<string, string>
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `callback` Function
|
||||
* `response` Object
|
||||
* `headersReceivedResponse` Object
|
||||
* `cancel` Boolean (optional)
|
||||
* `responseHeaders` Record<string, string> (optional) - When provided, the server is assumed
|
||||
* `responseHeaders` Record<string, string | string[]> (optional) - When provided, the server is assumed
|
||||
to have responded with these headers.
|
||||
* `statusLine` String (optional) - Should be provided when overriding
|
||||
`responseHeaders` to change header status otherwise original response
|
||||
@@ -159,7 +160,7 @@ response are visible by the time this listener is fired.
|
||||
The `listener` will be called with `listener(details, callback)` when HTTP
|
||||
response headers of a request have been received.
|
||||
|
||||
The `callback` has to be called with an `response` object.
|
||||
The `callback` has to be called with a `response` object.
|
||||
|
||||
#### `webRequest.onResponseStarted([filter, ]listener)`
|
||||
|
||||
@@ -175,7 +176,7 @@ The `callback` has to be called with an `response` object.
|
||||
* `resourceType` String
|
||||
* `referrer` String
|
||||
* `timestamp` Double
|
||||
* `responseHeaders` Record<string, string> (optional)
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `fromCache` Boolean - Indicates whether the response was fetched from disk
|
||||
cache.
|
||||
* `statusCode` Integer
|
||||
@@ -201,10 +202,11 @@ and response headers are available.
|
||||
* `timestamp` Double
|
||||
* `redirectURL` String
|
||||
* `statusCode` Integer
|
||||
* `statusLine` String
|
||||
* `ip` String (optional) - The server IP address that the request was
|
||||
actually sent to.
|
||||
* `fromCache` Boolean
|
||||
* `responseHeaders` Record<string, string> (optional)
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
|
||||
The `listener` will be called with `listener(details)` when a server initiated
|
||||
redirect is about to occur.
|
||||
@@ -223,10 +225,11 @@ redirect is about to occur.
|
||||
* `resourceType` String
|
||||
* `referrer` String
|
||||
* `timestamp` Double
|
||||
* `responseHeaders` Record<string, string> (optional)
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `fromCache` Boolean
|
||||
* `statusCode` Integer
|
||||
* `statusLine` String
|
||||
* `error` String
|
||||
|
||||
The `listener` will be called with `listener(details)` when a request is
|
||||
completed.
|
||||
|
||||
@@ -593,7 +593,7 @@ examples.
|
||||
|
||||
### `<webview>.sendInputEvent(event)`
|
||||
|
||||
* `event` Object
|
||||
* `event` [MouseInputEvent](structures/mouse-input-event.md) | [MouseWheelInputEvent](structures/mouse-wheel-input-event.md) | [KeyboardInputEvent](structures/keyboard-input-event.md)
|
||||
|
||||
Returns `Promise<void>`
|
||||
|
||||
@@ -767,7 +767,7 @@ Returns:
|
||||
* `requestId` Integer
|
||||
* `activeMatchOrdinal` Integer - Position of the active match.
|
||||
* `matches` Integer - Number of Matches.
|
||||
* `selectionArea` Object - Coordinates of first match region.
|
||||
* `selectionArea` Rectangle - Coordinates of first match region.
|
||||
* `finalUpdate` Boolean
|
||||
|
||||
Fired when a result is available for
|
||||
@@ -791,7 +791,7 @@ Returns:
|
||||
* `frameName` String
|
||||
* `disposition` String - Can be `default`, `foreground-tab`, `background-tab`,
|
||||
`new-window`, `save-to-disk` and `other`.
|
||||
* `options` Object - The options which should be used for creating the new
|
||||
* `options` BrowserWindowConstructorOptions - The options which should be used for creating the new
|
||||
[`BrowserWindow`](browser-window.md).
|
||||
|
||||
Fired when the guest page attempts to open a new browser window.
|
||||
@@ -872,7 +872,7 @@ webview.addEventListener('close', () => {
|
||||
Returns:
|
||||
|
||||
* `channel` String
|
||||
* `args` Array
|
||||
* `args` any[]
|
||||
|
||||
Fired when the guest page has sent an asynchronous message to embedder page.
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ You can avoid much of the wait by reusing Electron CI's build output via
|
||||
optional steps (listed below) and these two environment variables:
|
||||
|
||||
```sh
|
||||
export SCCACHE_BUCKET="electronjs-sccache"
|
||||
export SCCACHE_BUCKET="electronjs-sccache-ci"
|
||||
export SCCACHE_TWO_TIER=true
|
||||
```
|
||||
|
||||
|
||||
20
docs/fiddles/screen/fit-screen/main.js
Normal file
20
docs/fiddles/screen/fit-screen/main.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// Retrieve information about screen size, displays, cursor position, etc.
|
||||
//
|
||||
// For more info, see:
|
||||
// https://electronjs.org/docs/api/screen
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
// We cannot require the screen module until the app is ready.
|
||||
const { screen } = require('electron')
|
||||
|
||||
// Create a window that fills the screen's available work area.
|
||||
const primaryDisplay = screen.getPrimaryDisplay()
|
||||
const { width, height } = primaryDisplay.workAreaSize
|
||||
|
||||
mainWindow = new BrowserWindow({ width, height })
|
||||
mainWindow.loadURL('https://electronjs.org')
|
||||
})
|
||||
BIN
docs/images/performance-cpu-prof.png
Normal file
BIN
docs/images/performance-cpu-prof.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 868 KiB |
BIN
docs/images/performance-heap-prof.png
Normal file
BIN
docs/images/performance-heap-prof.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -38,11 +38,15 @@ npm install --platform=win32 electron
|
||||
|
||||
## Proxies
|
||||
|
||||
If you need to use an HTTP proxy you can [set these environment variables][proxy-env].
|
||||
If you need to use an HTTP proxy, you need to set the `ELECTRON_GET_USE_PROXY` variable to any
|
||||
value, plus additional environment variables depending on your host system's Node version:
|
||||
|
||||
* [Node 10 and above][proxy-env-10]
|
||||
* [Before Node 10][proxy-env]
|
||||
|
||||
## Custom Mirrors and Caches
|
||||
During installation, the `electron` module will call out to
|
||||
[`electron-download`][electron-download] to download prebuilt binaries of
|
||||
[`@electron/get`][electron-get] to download prebuilt binaries of
|
||||
Electron for your platform. It will do so by contacting GitHub's
|
||||
release download page (`https://github.com/electron/electron/releases/tag/v$VERSION`,
|
||||
where `$VERSION` is the exact version of Electron).
|
||||
@@ -52,7 +56,7 @@ can do so by either providing a mirror or an existing cache directory.
|
||||
|
||||
#### Mirror
|
||||
You can use environment variables to override the base URL, the path at which to
|
||||
look for Electron binaries, and the binary filename. The url used by `electron-download`
|
||||
look for Electron binaries, and the binary filename. The url used by `@electron/get`
|
||||
is composed as follows:
|
||||
|
||||
```plaintext
|
||||
@@ -62,11 +66,11 @@ url = ELECTRON_MIRROR + ELECTRON_CUSTOM_DIR + '/' + ELECTRON_CUSTOM_FILENAME
|
||||
For instance, to use the China mirror:
|
||||
|
||||
```plaintext
|
||||
ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
|
||||
ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/"
|
||||
```
|
||||
|
||||
#### Cache
|
||||
Alternatively, you can override the local cache. `electron-download` will cache
|
||||
Alternatively, you can override the local cache. `@electron/get` will cache
|
||||
downloaded binaries in a local directory to not stress your network. You can use
|
||||
that cache folder to provide custom builds of Electron or to avoid making contact
|
||||
with the network at all.
|
||||
@@ -85,16 +89,26 @@ The cache contains the version's official zip file as well as a checksum, stored
|
||||
a text file. A typical cache might look like this:
|
||||
|
||||
```sh
|
||||
├── electron-v1.7.9-darwin-x64.zip
|
||||
├── electron-v1.8.1-darwin-x64.zip
|
||||
├── electron-v1.8.2-beta.1-darwin-x64.zip
|
||||
├── electron-v1.8.2-beta.2-darwin-x64.zip
|
||||
├── electron-v1.8.2-beta.3-darwin-x64.zip
|
||||
├── SHASUMS256.txt-1.7.9
|
||||
├── SHASUMS256.txt-1.8.1
|
||||
├── SHASUMS256.txt-1.8.2-beta.1
|
||||
├── SHASUMS256.txt-1.8.2-beta.2
|
||||
├── SHASUMS256.txt-1.8.2-beta.3
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.7.9electron-v1.7.9-darwin-x64.zip
|
||||
│ └── electron-v1.7.9-darwin-x64.zip
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.7.9SHASUMS256.txt
|
||||
│ └── SHASUMS256.txt
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.1electron-v1.8.1-darwin-x64.zip
|
||||
│ └── electron-v1.8.1-darwin-x64.zip
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.1SHASUMS256.txt
|
||||
│ └── SHASUMS256.txt
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.1electron-v1.8.2-beta.1-darwin-x64.zip
|
||||
│ └── electron-v1.8.2-beta.1-darwin-x64.zip
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.1SHASUMS256.txt
|
||||
│ └── SHASUMS256.txt
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.2electron-v1.8.2-beta.2-darwin-x64.zip
|
||||
│ └── electron-v1.8.2-beta.2-darwin-x64.zip
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.2SHASUMS256.txt
|
||||
│ └── SHASUMS256.txt
|
||||
├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.3electron-v1.8.2-beta.3-darwin-x64.zip
|
||||
│ └── electron-v1.8.2-beta.3-darwin-x64.zip
|
||||
└── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.3SHASUMS256.txt
|
||||
└── SHASUMS256.txt
|
||||
```
|
||||
|
||||
## Skip binary download
|
||||
@@ -146,7 +160,8 @@ If you need to force a re-download of the asset and the SHASUM file set the
|
||||
[npm]: https://docs.npmjs.com
|
||||
[versioning]: ./electron-versioning.md
|
||||
[releases]: https://github.com/electron/electron/releases
|
||||
[proxy-env]: https://github.com/request/request/tree/f0c4ec061141051988d1216c24936ad2e7d5c45d#controlling-proxy-behaviour-using-environment-variables
|
||||
[electron-download]: https://github.com/electron-userland/electron-download
|
||||
[proxy-env-10]: https://github.com/gajus/global-agent/blob/v2.1.5/README.md#environment-variables
|
||||
[proxy-env]: https://github.com/np-maintain/global-tunnel/blob/v2.7.1/README.md#auto-config
|
||||
[electron-get]: https://github.com/electron/get
|
||||
[npm-permissions]: https://docs.npmjs.com/getting-started/fixing-npm-permissions
|
||||
[unsafe-perm]: https://docs.npmjs.com/misc/config#unsafe-perm
|
||||
|
||||
431
docs/tutorial/performance.md
Normal file
431
docs/tutorial/performance.md
Normal file
@@ -0,0 +1,431 @@
|
||||
# Performance
|
||||
|
||||
Developers frequently ask about strategies to optimize the performance of
|
||||
Electron applications. Software engineers, consumers, and framework developers
|
||||
do not always agree on one single definition of what "performance" means. This
|
||||
document outlines some of the Electron maintainers' favorite ways to reduce the
|
||||
amount of memory, CPU, and disk resources being used while ensuring that your
|
||||
app is responsive to user input and completes operations as quickly as
|
||||
possible. Furthermore, we want all performance strategies to maintain a high
|
||||
standard for your app's security.
|
||||
|
||||
Wisdom and information about how to build performant websites with JavaScript
|
||||
generally applies to Electron apps, too. To a certain extent, resources
|
||||
discussing how to build performant Node.js applications also apply, but be
|
||||
careful to understand that the term "performance" means different things for
|
||||
a Node.js backend than it does for an application running on a client.
|
||||
|
||||
This list is provided for your convenience – and is, much like our
|
||||
[security checklist][security] – not meant to exhaustive. It is probably possible
|
||||
to build a slow Electron app that follows all the steps outlined below. Electron
|
||||
is a powerful development platform that enables you, the developer, to do more
|
||||
or less whatever you want. All that freedom means that performance is largely
|
||||
your responsibility.
|
||||
|
||||
## Measure, Measure, Measure
|
||||
|
||||
The list below contains a number of steps that are fairly straightforward and
|
||||
easy to implement. However, building the most performant version of your app
|
||||
will require you to go beyond a number of steps. Instead, you will have to
|
||||
closely examine all the code running in your app by carefully profiling and
|
||||
measuring. Where are the bottlenecks? When the user clicks a button, what
|
||||
operations take up the brunt of the time? While the app is simply idling, which
|
||||
objects take up the most memory?
|
||||
|
||||
Time and time again, we have seen that the most successful strategy for building
|
||||
a performant Electron app is to profile the running code, find the most
|
||||
resource-hungry piece of it, and to optimize it. Repeating this seemingly
|
||||
laborious process over and over again will dramatically increase your app's
|
||||
performance. Experience from working with major apps like Visual Studio Code or
|
||||
Slack has shown that this practice is by far the most reliable strategy to
|
||||
improve performance.
|
||||
|
||||
To learn more about how to profile your app's code, familiarize yourself with
|
||||
the Chrome Developer Tools. For advanced analysis looking at multiple processes
|
||||
at once, consider the [Chrome Tracing] tool.
|
||||
|
||||
### Recommended Reading
|
||||
|
||||
* [Get Started With Analyzing Runtime Performance][chrome-devtools-tutorial]
|
||||
* [Talk: "Visual Studio Code - The First Second"][vscode-first-second]
|
||||
|
||||
## Checklist
|
||||
|
||||
Chances are that your app could be a little leaner, faster, and generally less
|
||||
resource-hungry if you attempt these steps.
|
||||
|
||||
1. [Carelessly including modules](#1-carelessly-including-modules)
|
||||
2. [Loading and running code too soon](#2-loading-and-running-code-too-soon)
|
||||
3. [Blocking the main process](#3-blocking-the-main-process)
|
||||
4. [Blocking the renderer process](#4-blocking-the-renderer-process)
|
||||
5. [Unnecessary polyfills](#5-unnecessary-polyfills)
|
||||
6. [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests)
|
||||
7. [Bundle your code](#7-bundle-your-code)
|
||||
|
||||
## 1) Carelessly including modules
|
||||
|
||||
Before adding a Node.js module to your application, examine said module. How
|
||||
many dependencies does that module include? What kind of resources does
|
||||
it need to simply be called in a `require()` statement? You might find
|
||||
that the module with the most downloads on the NPM package registry or the most stars on GitHub
|
||||
is not in fact the leanest or smallest one available.
|
||||
|
||||
### Why?
|
||||
|
||||
The reasoning behind this recommendation is best illustrated with a real-world
|
||||
example. During the early days of Electron, reliable detection of network
|
||||
connectivity was a problem, resulting many apps to use a module that exposed a
|
||||
simple `isOnline()` method.
|
||||
|
||||
That module detected your network connectivity by attempting to reach out to a
|
||||
number of well-known endpoints. For the list of those endpoints, it depended on
|
||||
a different module, which also contained a list of well-known ports. This
|
||||
dependency itself relied on a module containing information about ports, which
|
||||
came in the form of a JSON file with more than 100,000 lines of content.
|
||||
Whenever the module was loaded (usually in a `require('module')` statement),
|
||||
it would load all its dependencies and eventually read and parse this JSON
|
||||
file. Parsing many thousands lines of JSON is a very expensive operation. On
|
||||
a slow machine it can take up whole seconds of time.
|
||||
|
||||
In many server contexts, startup time is virtually irrelevant. A Node.js server
|
||||
that requires information about all ports is likely actually "more performant"
|
||||
if it loads all required information into memory whenever the server boots at
|
||||
the benefit of serving requests faster. The module discussed in this example is
|
||||
not a "bad" module. Electron apps, however, should not be loading, parsing, and
|
||||
storing in memory information that it does not actually need.
|
||||
|
||||
In short, a seemingly excellent module written primarily for Node.js servers
|
||||
running Linux might be bad news for your app's performance. In this particular
|
||||
example, the correct solution was to use no module at all, and to instead use
|
||||
connectivity checks included in later versions of Chromium.
|
||||
|
||||
### How?
|
||||
|
||||
When considering a module, we recommend that you check:
|
||||
|
||||
1. the size of dependencies included
|
||||
2) the resources required to load (`require()`) it
|
||||
3. the resources required to perform the action you're interested in
|
||||
|
||||
Generating a CPU profile and a heap memory profile for loading a module can be done
|
||||
with a single command on the command line. In the example below, we're looking at
|
||||
the popular module `request`.
|
||||
|
||||
```sh
|
||||
node --cpu-prof --heap-prof -e "require('request')"
|
||||
```
|
||||
|
||||
Executing this command results in a `.cpuprofile` file and a `.heapprofile`
|
||||
file in the directory you executed it in. Both files can be analyzed using
|
||||
the Chrome Developer Tools, using the `Performance` and `Memory` tabs
|
||||
respectively.
|
||||
|
||||
![performance-cpu-prof]
|
||||
|
||||
![performance-heap-prof]
|
||||
|
||||
In this example, on the author's machine, we saw that loading `request` took
|
||||
almost half a second, whereas `node-fetch` took dramatically less memory
|
||||
and less than 50ms.
|
||||
|
||||
## 2) Loading and running code too soon
|
||||
|
||||
If you have expensive setup operations, consider deferring those. Inspect all
|
||||
the work being executed right after the application starts. Instead of firing
|
||||
off all operations right away, consider staggering them in a sequence more
|
||||
closely aligned with the user's journey.
|
||||
|
||||
In traditional Node.js development, we're used to putting all our `require()`
|
||||
statements at the top. If you're currently writing your Electron application
|
||||
using the same strategy _and_ are using sizable modules that you do not
|
||||
immediately need, apply the same strategy and defer loading to a more
|
||||
opportune time.
|
||||
|
||||
### Why?
|
||||
|
||||
Loading modules is a surprisingly expensive operation, especially on Windows.
|
||||
When your app starts, it should not make users wait for operations that are
|
||||
currently not necessary.
|
||||
|
||||
This might seem obvious, but many applications tend to do a large amount of
|
||||
work immediately after the app has launched - like checking for updates,
|
||||
downloading content used in a later flow, or performing heavy disk I/O
|
||||
operations.
|
||||
|
||||
Let's consider Visual Studio Code as an example. When you open a file, it will
|
||||
immediately display the file to you without any code highlighting, prioritizing
|
||||
your ability to interact with the text. Once it has done that work, it will
|
||||
move on to code highlighting.
|
||||
|
||||
### How?
|
||||
|
||||
Let's consider an example and assume that your application is parsing files
|
||||
in the fictitious `.foo` format. In order to do that, it relies on the
|
||||
equally fictitious `foo-parser` module. In traditional Node.js development,
|
||||
you might write code that eagerly loads dependencies:
|
||||
|
||||
```js
|
||||
const fs = require('fs')
|
||||
const fooParser = require('foo-parser')
|
||||
|
||||
class Parser {
|
||||
constructor () {
|
||||
this.files = fs.readdirSync('.')
|
||||
}
|
||||
|
||||
getParsedFiles () {
|
||||
return fooParser.parse(this.files)
|
||||
}
|
||||
}
|
||||
|
||||
const parser = new Parser()
|
||||
|
||||
module.exports = { parser }
|
||||
```
|
||||
|
||||
In the above example, we're doing a lot of work that's being executed as soon
|
||||
as the file is loaded. Do we need to get parsed files right away? Could we
|
||||
do this work a little later, when `getParsedFiles()` is actually called?
|
||||
|
||||
```js
|
||||
// "fs" is likely already being loaded, so the `require()` call is cheap
|
||||
const fs = require('fs')
|
||||
|
||||
class Parser {
|
||||
async getFiles () {
|
||||
// Touch the disk as soon as `getFiles` is called, not sooner.
|
||||
// Also, ensure that we're not blocking other operations by using
|
||||
// the asynchronous version.
|
||||
this.files = this.files || await fs.readdir('.')
|
||||
|
||||
return this.files
|
||||
}
|
||||
|
||||
async getParsedFiles () {
|
||||
// Our fictitious foo-parser is a big and expensive module to load, so
|
||||
// defer that work until we actually need to parse files.
|
||||
// Since `require()` comes with a module cache, the `require()` call
|
||||
// will only be expensive once - subsequent calls of `getParsedFiles()`
|
||||
// will be faster.
|
||||
const fooParser = require('foo-parser')
|
||||
const files = await this.getFiles()
|
||||
|
||||
return fooParser.parse(files)
|
||||
}
|
||||
}
|
||||
|
||||
// This operation is now a lot cheaper than in our previous example
|
||||
const parser = new Parser()
|
||||
|
||||
module.exports = { parser }
|
||||
```
|
||||
|
||||
In short, allocate resources "just in time" rather than allocating them all
|
||||
when your app starts.
|
||||
|
||||
## 3) Blocking the main process
|
||||
|
||||
Electron's main process (sometimes called "browser process") is special: It is
|
||||
the parent process to all your app's other processes and the primary process
|
||||
the operating system interacts with. It handles windows, interactions, and the
|
||||
communication between various components inside your app. It also houses the
|
||||
UI thread.
|
||||
|
||||
Under no circumstances should you block this process and the UI thread with
|
||||
long-running operations. Blocking the UI thread means that your entire app
|
||||
will freeze until the main process is ready to continue processing.
|
||||
|
||||
### Why?
|
||||
|
||||
The main process and its UI thread are essentially the control tower for major
|
||||
operations inside your app. When the operating system tells your app about a
|
||||
mouse click, it'll go through the main process before it reaches your window.
|
||||
If your window is rendering a buttery-smooth animation, it'll need to talk to
|
||||
the GPU process about that – once again going through the main process.
|
||||
|
||||
Electron and Chromium are careful to put heavy disk I/O and CPU-bound operations
|
||||
onto new threads to avoid blocking the UI thread. You should do the same.
|
||||
|
||||
### How?
|
||||
|
||||
Electron's powerful multi-process architecture stands ready to assist you with
|
||||
your long-running tasks, but also includes a small number of performance traps.
|
||||
|
||||
1) For long running CPU-heavy tasks, make use of
|
||||
[worker threads][worker-threads], consider moving them to the BrowserWindow, or
|
||||
(as a last resort) spawn a dedicated process.
|
||||
|
||||
2) Avoid using the synchronous IPC and the `remote` module as much as possible.
|
||||
While there are legitimate use cases, it is far too easy to unknowingly block
|
||||
the UI thread using the `remote` module.
|
||||
|
||||
3) Avoid using blocking I/O operations in the main process. In short, whenever
|
||||
core Node.js modules (like `fs` or `child_process`) offer a synchronous or an
|
||||
asynchronous version, you should prefer the asynchronous and non-blocking
|
||||
variant.
|
||||
|
||||
|
||||
## 4) Blocking the renderer process
|
||||
|
||||
Since Electron ships with a current version of Chrome, you can make use of the
|
||||
latest and greatest features the Web Platform offers to defer or offload heavy
|
||||
operations in a way that keeps your app smooth and responsive.
|
||||
|
||||
### Why?
|
||||
|
||||
Your app probably has a lot of JavaScript to run in the renderer process. The
|
||||
trick is to execute operations as quickly as possible without taking away
|
||||
resources needed to keep scrolling smooth, respond to user input, or animations
|
||||
at 60fps.
|
||||
|
||||
Orchestrating the flow of operations in your renderer's code is
|
||||
particularly useful if users complain about your app sometimes "stuttering".
|
||||
|
||||
### How?
|
||||
|
||||
Generally speaking, all advice for building performant web apps for modern
|
||||
browsers apply to Electron's renderers, too. The two primary tools at your
|
||||
disposal are currently `requestIdleCallback()` for small operations and
|
||||
`Web Workers` for long-running operations.
|
||||
|
||||
*`requestIdleCallback()`* allows developers to queue up a function to be
|
||||
executed as soon as the process is entering an idle period. It enables you to
|
||||
perform low-priority or background work without impacting the user experience.
|
||||
For more information about how to use it,
|
||||
[check out its documentation on MDN][request-idle-callback].
|
||||
|
||||
*Web Workers* are a powerful tool to run code on a separate thread. There are
|
||||
some caveats to consider – consult Electron's
|
||||
[multithreading documentation][multithreading] and the
|
||||
[MDN documentation for Web Workers][web-workers]. They're an ideal solution
|
||||
for any operation that requires a lot of CPU power for an extended period of
|
||||
time.
|
||||
|
||||
|
||||
## 5) Unnecessary polyfills
|
||||
|
||||
One of Electron's great benefits is that you know exactly which engine will
|
||||
parse your JavaScript, HTML, and CSS. If you're re-purposing code that was
|
||||
written for the web at large, make sure to not polyfill features included in
|
||||
Electron.
|
||||
|
||||
### Why?
|
||||
|
||||
When building a web application for today's Internet, the oldest environments
|
||||
dictate what features you can and cannot use. Even though Electron supports
|
||||
well-performing CSS filters and animations, an older browser might not. Where
|
||||
you could use WebGL, your developers may have chosen a more resource-hungry
|
||||
solution to support older phones.
|
||||
|
||||
When it comes to JavaScript, you may have included toolkit libraries like
|
||||
jQuery for DOM selectors or polyfills like the `regenerator-runtime` to support
|
||||
`async/await`.
|
||||
|
||||
It is rare for a JavaScript-based polyfill to be faster than the equivalent
|
||||
native feature in Electron. Do not slow down your Electron app by shipping your
|
||||
own version of standard web platform features.
|
||||
|
||||
### How?
|
||||
|
||||
Operate under the assumption that polyfills in current versions of Electron
|
||||
are unnecessary. If you have doubts, check [caniuse.com][https://caniuse.com/]
|
||||
and check if the [version of Chromium used in your Electron version](../api/process.md#processversionschrome-readonly)
|
||||
supports the feature you desire.
|
||||
|
||||
In addition, carefully examine the libraries you use. Are they really necessary?
|
||||
`jQuery`, for example, was such a success that many of its features are now part
|
||||
of the [standard JavaScript feature set available][jquery-need].
|
||||
|
||||
If you're using a transpiler/compiler like TypeScript, examine its configuration
|
||||
and ensure that you're targeting the latest ECMAScript version supported by
|
||||
Electron.
|
||||
|
||||
|
||||
## 6) Unnecessary or blocking network requests
|
||||
|
||||
Avoid fetching rarely changing resources from the internet if they could easily
|
||||
be bundled with your application.
|
||||
|
||||
### Why?
|
||||
|
||||
Many users of Electron start with an entirely web-based app that they're
|
||||
turning into a desktop application. As web developers, we are used to loading
|
||||
resources from a variety of content delivery networks. Now that you are
|
||||
shipping a proper desktop application, attempt to "cut the cord" where possible
|
||||
- and avoid letting your users wait for resources that never change and could
|
||||
easily be included in your app.
|
||||
|
||||
A typical example is Google Fonts. Many developers make use of Google's
|
||||
impressive collection of free fonts, which comes with a content delivery
|
||||
network. The pitch is straightforward: Include a few lines of CSS and Google
|
||||
will take care of the rest.
|
||||
|
||||
When building an Electron app, your users are better served if you download
|
||||
the fonts and include them in your app's bundle.
|
||||
|
||||
### How?
|
||||
|
||||
In an ideal world, your application wouldn't need the network to operate at
|
||||
all. To get there, you must understand what resources your app is downloading
|
||||
\- and how large those resources are.
|
||||
|
||||
To do so, open up the developer tools. Navigate to the `Network` tab and check
|
||||
the `Disable cache` option. Then, reload your renderer. Unless your app
|
||||
prohibits such reloads, you can usually trigger a reload by hitting `Cmd + R`
|
||||
or `Ctrl + R` with the developer tools in focus.
|
||||
|
||||
The tools will now meticulously record all network requests. In a first pass,
|
||||
take stock of all the resources being downloaded, focusing on the larger files
|
||||
first. Are any of them images, fonts, or media files that don't change and
|
||||
could be included with your bundle? If so, include them.
|
||||
|
||||
As a next step, enable `Network Throttling`. Find the drop-down that currently
|
||||
reads `Online` and select a slower speed such as `Fast 3G`. Reload your
|
||||
renderer and see if there are any resources that your app is unnecessarily
|
||||
waiting for. In many cases, an app will wait for a network request to complete
|
||||
despite not actually needing the involved resource.
|
||||
|
||||
As a tip, loading resources from the Internet that you might want to change
|
||||
without shipping an application update is a powerful strategy. For advanced
|
||||
control over how resources are being loaded, consider investing in
|
||||
[Service Workers][service-workers].
|
||||
|
||||
## 7) Bundle your code
|
||||
|
||||
As already pointed out in
|
||||
"[Loading and running code too soon](#2-loading-and-running-code-too-soon)",
|
||||
calling `require()` is an expensive operation. If you are able to do so,
|
||||
bundle your application's code into a single file.
|
||||
|
||||
### Why?
|
||||
|
||||
Modern JavaScript development usually involves many files and modules. While
|
||||
that's perfectly fine for developing with Electron, we heavily recommend that
|
||||
you bundle all your code into one single file to ensure that the overhead
|
||||
included in calling `require()` is only paid once when your application loads.
|
||||
|
||||
### How?
|
||||
|
||||
There are numerous JavaScript bundlers out there and we know better than to
|
||||
anger the community by recommending one tool over another. We do however
|
||||
recommend that you use a bundler that is able to handle Electron's unique
|
||||
environment that needs to handle both Node.js and browser environments.
|
||||
|
||||
As of writing this article, the popular choices include [Webpack][webpack],
|
||||
[Parcel][parcel], and [rollup.js][rollup].
|
||||
|
||||
[security]: ./security.md
|
||||
[performance-cpu-prof]: ../images/performance-cpu-prof.png
|
||||
[performance-heap-prof]: ../images/performance-heap-prof.png
|
||||
[chrome-devtools-tutorial]: https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/
|
||||
[worker-threads]: https://nodejs.org/api/worker_threads.html
|
||||
[web-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
|
||||
[request-idle-callback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
||||
[multithreading]: ./multithreading.md
|
||||
[caniuse]: https://caniuse.com/
|
||||
[jquery-need]: http://youmightnotneedjquery.com/
|
||||
[service-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
|
||||
[webpack]: https://webpack.js.org/
|
||||
[parcel]: https://parceljs.org/
|
||||
[rollup]: https://rollupjs.org/
|
||||
[vscode-first-second]: https://www.youtube.com/watch?v=r0OeHRUCCb4
|
||||
@@ -27,18 +27,8 @@ see [SECURITY.md](https://github.com/electron/electron/tree/master/SECURITY.md)
|
||||
|
||||
## Chromium Security Issues and Upgrades
|
||||
|
||||
While Electron strives to support new versions of Chromium as soon as possible,
|
||||
developers should be aware that upgrading is a serious undertaking - involving
|
||||
hand-editing dozens or even hundreds of files. Given the resources and
|
||||
contributions available today, Electron will often not be on the very latest
|
||||
version of Chromium, lagging behind by several weeks or a few months.
|
||||
|
||||
We feel that our current system of updating the Chromium component strikes an
|
||||
appropriate balance between the resources we have available and the needs of
|
||||
the majority of applications built on top of the framework. We definitely are
|
||||
interested in hearing more about specific use cases from the people that build
|
||||
things on top of Electron. Pull requests and contributions supporting this
|
||||
effort are always very welcome.
|
||||
Electron keeps up to date with alternating Chromium releases. For more information,
|
||||
see the [Electron Release Cadence blog post](https://electronjs.org/blog/12-week-cadence).
|
||||
|
||||
## Security Is Everyone's Responsibility
|
||||
|
||||
|
||||
@@ -13,9 +13,60 @@
|
||||
|
||||
<!-- Desktop Capturer API -->
|
||||
<message name="IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME" desc="Name for screens in the desktop media picker UI when there is only one monitor.">
|
||||
Entire screen
|
||||
Entire Screen
|
||||
</message>
|
||||
<message name="IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME" desc="Name for screens in the desktop media picker UI when there are multiple monitors.">
|
||||
{SCREEN_INDEX, plural, =1{Screen #} other{Screen #}}
|
||||
</message>
|
||||
|
||||
<!-- Picture-in-Picture -->
|
||||
<if expr="is_macosx">
|
||||
<message name="IDS_PICTURE_IN_PICTURE_TITLE_TEXT" desc="Title of the Picture-in-Picture window. This appears in the system tray and window header.">
|
||||
Picture in Picture
|
||||
</message>
|
||||
</if>
|
||||
<if expr="not is_macosx">
|
||||
<message name="IDS_PICTURE_IN_PICTURE_TITLE_TEXT" desc="Title of the Picture-in-Picture window. This appears in the system tray and window header.">
|
||||
Picture in picture
|
||||
</message>
|
||||
</if>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_PAUSE_CONTROL_TEXT" desc="Text label of the pause control button. The button appears when the user hovers over the Picture-in-Picture window and the video is currently playing.">
|
||||
Pause
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_PLAY_CONTROL_TEXT" desc="Text label of the play control button. The button appears when the user hovers over the Picture-in-Picture window and the video is currently paused.">
|
||||
Play
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_REPLAY_CONTROL_TEXT" desc="Text label of the replay control button. The button appears when the user hovers over the Picture-in-Picture window and the video is ended.">
|
||||
Play from the beginning
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_BACK_TO_TAB_CONTROL_TEXT" desc="Text label of the back to tab control button. The button appears when the user hovers over the Picture-in-Picture window.">
|
||||
Back to video player
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_MUTE_CONTROL_TEXT" desc="Text label of the mute control button. The button appears when the user hovers over the Picture-in-Picture window and the video is currently unmuted.">
|
||||
Mute
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_UNMUTE_CONTROL_TEXT" desc="Text label of the mute control button. The button appears when the user hovers over the Picture-in-Picture window and the video is currently muted.">
|
||||
Unmute
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_SKIP_AD_CONTROL_TEXT" desc="Text label of the skip ad control button. The button appears when the user hovers over the Picture-in-Picture window.">
|
||||
Skip Ad
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_CLOSE_CONTROL_TEXT" desc="Text label of the close control button. The button appears when the user hovers over the Picture-in-Picture window.">
|
||||
Close
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_RESIZE_HANDLE_TEXT" desc="Text label of the resize handle. The button appears when the user hovers over the Picture-in-Picture window.">
|
||||
Resize
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_PLAY_PAUSE_CONTROL_ACCESSIBLE_TEXT" desc="Accessible text label used for the controls button in the Picture-in-Picture window. The button toggles between play and pause controls.">
|
||||
Toggle video to play or pause
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_MUTE_CONTROL_ACCESSIBLE_TEXT" desc="Accessible text label used for the controls button in the Picture-in-Picture window. The button toggles mute state.">
|
||||
Toggle mute
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_NEXT_TRACK_CONTROL_ACCESSIBLE_TEXT" desc="Accessible text label used for the controls button in the Picture-in-Picture window. The button invokes next track action.">
|
||||
Next track
|
||||
</message>
|
||||
<message name="IDS_PICTURE_IN_PICTURE_PREVIOUS_TRACK_CONTROL_ACCESSIBLE_TEXT" desc="Accessible text label used for the controls button in the Picture-in-Picture window. The button invokes previous track action.">
|
||||
Previous track
|
||||
</message>
|
||||
</grit-part>
|
||||
|
||||
@@ -14,6 +14,7 @@ auto_filenames = {
|
||||
"docs/api/clipboard.md",
|
||||
"docs/api/command-line.md",
|
||||
"docs/api/content-tracing.md",
|
||||
"docs/api/context-bridge.md",
|
||||
"docs/api/cookies.md",
|
||||
"docs/api/crash-reporter.md",
|
||||
"docs/api/debugger.md",
|
||||
@@ -34,6 +35,7 @@ auto_filenames = {
|
||||
"docs/api/menu.md",
|
||||
"docs/api/modernization",
|
||||
"docs/api/native-image.md",
|
||||
"docs/api/native-theme.md",
|
||||
"docs/api/net-log.md",
|
||||
"docs/api/net.md",
|
||||
"docs/api/notification.md",
|
||||
@@ -76,6 +78,7 @@ auto_filenames = {
|
||||
"docs/api/structures/desktop-capturer-source.md",
|
||||
"docs/api/structures/display.md",
|
||||
"docs/api/structures/event.md",
|
||||
"docs/api/structures/extension-info.md",
|
||||
"docs/api/structures/file-filter.md",
|
||||
"docs/api/structures/file-path-with-headers.md",
|
||||
"docs/api/structures/gpu-feature-status.md",
|
||||
@@ -93,6 +96,7 @@ auto_filenames = {
|
||||
"docs/api/structures/mime-typed-buffer.md",
|
||||
"docs/api/structures/mouse-input-event.md",
|
||||
"docs/api/structures/mouse-wheel-input-event.md",
|
||||
"docs/api/structures/new-window-event.md",
|
||||
"docs/api/structures/notification-action.md",
|
||||
"docs/api/structures/point.md",
|
||||
"docs/api/structures/printer-info.md",
|
||||
@@ -132,12 +136,14 @@ auto_filenames = {
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/buffer-utils.ts",
|
||||
"lib/common/clipboard-utils.ts",
|
||||
"lib/common/crash-reporter.js",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/is-promise.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.js",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/ipc-renderer.js",
|
||||
@@ -161,6 +167,7 @@ auto_filenames = {
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/web-view/web-view-impl.ts",
|
||||
"lib/renderer/web-view/web-view-init.ts",
|
||||
"lib/renderer/window-setup.ts",
|
||||
"lib/sandboxed_renderer/api/exports/electron.js",
|
||||
"lib/sandboxed_renderer/api/module-list.js",
|
||||
"lib/sandboxed_renderer/init.js",
|
||||
@@ -171,13 +178,9 @@ auto_filenames = {
|
||||
|
||||
isolated_bundle_deps = [
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/isolated_renderer/init.js",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/window-setup.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
@@ -186,7 +189,9 @@ auto_filenames = {
|
||||
content_script_bundle_deps = [
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/content_script/init.js",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/chrome-api.ts",
|
||||
"lib/renderer/extensions/event.ts",
|
||||
"lib/renderer/extensions/i18n.ts",
|
||||
@@ -220,6 +225,7 @@ auto_filenames = {
|
||||
"lib/browser/api/menu-utils.js",
|
||||
"lib/browser/api/menu.js",
|
||||
"lib/browser/api/module-list.js",
|
||||
"lib/browser/api/native-theme.ts",
|
||||
"lib/browser/api/net-log.js",
|
||||
"lib/browser/api/net.js",
|
||||
"lib/browser/api/notification.js",
|
||||
@@ -228,7 +234,7 @@ auto_filenames = {
|
||||
"lib/browser/api/protocol.ts",
|
||||
"lib/browser/api/screen.ts",
|
||||
"lib/browser/api/session.js",
|
||||
"lib/browser/api/system-preferences.js",
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/api/top-level-window.js",
|
||||
"lib/browser/api/touch-bar.js",
|
||||
"lib/browser/api/tray.js",
|
||||
@@ -263,7 +269,6 @@ auto_filenames = {
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/buffer-utils.ts",
|
||||
"lib/common/clipboard-utils.ts",
|
||||
"lib/common/crash-reporter.js",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
@@ -271,7 +276,9 @@ auto_filenames = {
|
||||
"lib/common/is-promise.ts",
|
||||
"lib/common/parse-features-string.js",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"package.json",
|
||||
@@ -288,14 +295,16 @@ auto_filenames = {
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/buffer-utils.ts",
|
||||
"lib/common/clipboard-utils.ts",
|
||||
"lib/common/crash-reporter.js",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/is-promise.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.js",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/exports/electron.js",
|
||||
@@ -338,13 +347,15 @@ auto_filenames = {
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/buffer-utils.ts",
|
||||
"lib/common/clipboard-utils.ts",
|
||||
"lib/common/crash-reporter.js",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/is-promise.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.js",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/exports/electron.js",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
filenames = {
|
||||
default_app_ts_sources = [
|
||||
"default_app/default_app.ts",
|
||||
"default_app/index.ts",
|
||||
"default_app/main.ts",
|
||||
"default_app/preload.ts",
|
||||
]
|
||||
@@ -47,6 +46,8 @@ filenames = {
|
||||
"shell/browser/api/atom_api_content_tracing.cc",
|
||||
"shell/browser/api/atom_api_cookies.cc",
|
||||
"shell/browser/api/atom_api_cookies.h",
|
||||
"shell/browser/api/atom_api_data_pipe_holder.cc",
|
||||
"shell/browser/api/atom_api_data_pipe_holder.h",
|
||||
"shell/browser/api/atom_api_debugger.cc",
|
||||
"shell/browser/api/atom_api_debugger.h",
|
||||
"shell/browser/api/atom_api_dialog.cc",
|
||||
@@ -63,6 +64,9 @@ filenames = {
|
||||
"shell/browser/api/atom_api_menu_mac.mm",
|
||||
"shell/browser/api/atom_api_menu_views.cc",
|
||||
"shell/browser/api/atom_api_menu_views.h",
|
||||
"shell/browser/api/atom_api_native_theme.cc",
|
||||
"shell/browser/api/atom_api_native_theme.h",
|
||||
"shell/browser/api/atom_api_native_theme_mac.mm",
|
||||
"shell/browser/api/atom_api_net.cc",
|
||||
"shell/browser/api/atom_api_net.h",
|
||||
"shell/browser/api/atom_api_net_log.cc",
|
||||
@@ -75,8 +79,6 @@ filenames = {
|
||||
"shell/browser/api/atom_api_power_monitor.h",
|
||||
"shell/browser/api/atom_api_power_save_blocker.cc",
|
||||
"shell/browser/api/atom_api_power_save_blocker.h",
|
||||
"shell/browser/api/atom_api_protocol.cc",
|
||||
"shell/browser/api/atom_api_protocol.h",
|
||||
"shell/browser/api/atom_api_protocol_ns.cc",
|
||||
"shell/browser/api/atom_api_protocol_ns.h",
|
||||
"shell/browser/api/atom_api_screen.cc",
|
||||
@@ -91,10 +93,8 @@ filenames = {
|
||||
"shell/browser/api/atom_api_top_level_window.h",
|
||||
"shell/browser/api/atom_api_tray.cc",
|
||||
"shell/browser/api/atom_api_tray.h",
|
||||
"shell/browser/api/atom_api_url_request.cc",
|
||||
"shell/browser/api/atom_api_url_request.h",
|
||||
"shell/browser/api/atom_api_url_request_ns.cc",
|
||||
"shell/browser/api/atom_api_url_request_ns.h",
|
||||
"shell/browser/api/atom_api_url_loader.cc",
|
||||
"shell/browser/api/atom_api_url_loader.h",
|
||||
"shell/browser/api/atom_api_view.cc",
|
||||
"shell/browser/api/atom_api_view.h",
|
||||
"shell/browser/api/atom_api_web_contents.cc",
|
||||
@@ -103,8 +103,6 @@ filenames = {
|
||||
"shell/browser/api/atom_api_web_contents_mac.mm",
|
||||
"shell/browser/api/atom_api_web_contents_view.cc",
|
||||
"shell/browser/api/atom_api_web_contents_view.h",
|
||||
"shell/browser/api/atom_api_web_request.cc",
|
||||
"shell/browser/api/atom_api_web_request.h",
|
||||
"shell/browser/api/atom_api_web_request_ns.cc",
|
||||
"shell/browser/api/atom_api_web_request_ns.h",
|
||||
"shell/browser/api/atom_api_web_view_manager.cc",
|
||||
@@ -116,8 +114,6 @@ filenames = {
|
||||
"shell/browser/api/event.h",
|
||||
"shell/browser/api/event_emitter.cc",
|
||||
"shell/browser/api/event_emitter.h",
|
||||
"shell/browser/api/stream_subscriber.cc",
|
||||
"shell/browser/api/stream_subscriber.h",
|
||||
"shell/browser/api/trackable_object.cc",
|
||||
"shell/browser/api/trackable_object.h",
|
||||
"shell/browser/api/frame_subscriber.cc",
|
||||
@@ -133,8 +129,6 @@ filenames = {
|
||||
"shell/browser/auto_updater.cc",
|
||||
"shell/browser/auto_updater.h",
|
||||
"shell/browser/auto_updater_mac.mm",
|
||||
"shell/browser/atom_blob_reader.cc",
|
||||
"shell/browser/atom_blob_reader.h",
|
||||
"shell/browser/atom_browser_client.cc",
|
||||
"shell/browser/atom_browser_client.h",
|
||||
"shell/browser/atom_browser_context.cc",
|
||||
@@ -176,8 +170,6 @@ filenames = {
|
||||
"shell/browser/common_web_contents_delegate.h",
|
||||
"shell/browser/cookie_change_notifier.cc",
|
||||
"shell/browser/cookie_change_notifier.h",
|
||||
"shell/browser/io_thread.cc",
|
||||
"shell/browser/io_thread.h",
|
||||
"shell/browser/javascript_environment.cc",
|
||||
"shell/browser/javascript_environment.h",
|
||||
"shell/browser/lib/bluetooth_chooser.cc",
|
||||
@@ -185,8 +177,6 @@ filenames = {
|
||||
"shell/browser/lib/power_observer.h",
|
||||
"shell/browser/lib/power_observer_linux.h",
|
||||
"shell/browser/lib/power_observer_linux.cc",
|
||||
"shell/browser/loader/layered_resource_handler.cc",
|
||||
"shell/browser/loader/layered_resource_handler.h",
|
||||
"shell/browser/login_handler.cc",
|
||||
"shell/browser/login_handler.h",
|
||||
"shell/browser/mac/atom_application.h",
|
||||
@@ -223,58 +213,29 @@ filenames = {
|
||||
"shell/browser/media/media_device_id_salt.h",
|
||||
"shell/browser/media/media_stream_devices_controller.cc",
|
||||
"shell/browser/media/media_stream_devices_controller.h",
|
||||
"shell/browser/net/about_protocol_handler.cc",
|
||||
"shell/browser/net/about_protocol_handler.h",
|
||||
"shell/browser/net/asar/asar_protocol_handler.cc",
|
||||
"shell/browser/net/asar/asar_protocol_handler.h",
|
||||
"shell/browser/net/asar/asar_url_loader.cc",
|
||||
"shell/browser/net/asar/asar_url_loader.h",
|
||||
"shell/browser/net/asar/url_request_asar_job.cc",
|
||||
"shell/browser/net/asar/url_request_asar_job.h",
|
||||
"shell/browser/net/atom_cert_verifier.cc",
|
||||
"shell/browser/net/atom_cert_verifier.h",
|
||||
"shell/browser/net/atom_network_delegate.cc",
|
||||
"shell/browser/net/atom_network_delegate.h",
|
||||
"shell/browser/net/atom_url_loader_factory.cc",
|
||||
"shell/browser/net/atom_url_loader_factory.h",
|
||||
"shell/browser/net/atom_url_request.cc",
|
||||
"shell/browser/net/atom_url_request.h",
|
||||
"shell/browser/net/atom_url_request_job_factory.cc",
|
||||
"shell/browser/net/atom_url_request_job_factory.h",
|
||||
"shell/browser/net/http_protocol_handler.cc",
|
||||
"shell/browser/net/http_protocol_handler.h",
|
||||
"shell/browser/net/cert_verifier_client.cc",
|
||||
"shell/browser/net/cert_verifier_client.h",
|
||||
"shell/browser/net/proxying_url_loader_factory.cc",
|
||||
"shell/browser/net/proxying_url_loader_factory.h",
|
||||
"shell/browser/net/js_asker.cc",
|
||||
"shell/browser/net/js_asker.h",
|
||||
"shell/browser/net/proxying_websocket.cc",
|
||||
"shell/browser/net/proxying_websocket.h",
|
||||
"shell/browser/net/network_context_service_factory.cc",
|
||||
"shell/browser/net/network_context_service_factory.h",
|
||||
"shell/browser/net/network_context_service.cc",
|
||||
"shell/browser/net/network_context_service.h",
|
||||
"shell/browser/net/node_stream_loader.cc",
|
||||
"shell/browser/net/node_stream_loader.h",
|
||||
"shell/browser/net/require_ct_delegate.cc",
|
||||
"shell/browser/net/require_ct_delegate.h",
|
||||
"shell/browser/net/resolve_proxy_helper.cc",
|
||||
"shell/browser/net/resolve_proxy_helper.h",
|
||||
"shell/browser/net/system_network_context_manager.cc",
|
||||
"shell/browser/net/system_network_context_manager.h",
|
||||
"shell/browser/net/url_pipe_loader.cc",
|
||||
"shell/browser/net/url_pipe_loader.h",
|
||||
"shell/browser/net/url_request_about_job.cc",
|
||||
"shell/browser/net/url_request_about_job.h",
|
||||
"shell/browser/net/url_request_async_asar_job.cc",
|
||||
"shell/browser/net/url_request_async_asar_job.h",
|
||||
"shell/browser/net/url_request_string_job.cc",
|
||||
"shell/browser/net/url_request_string_job.h",
|
||||
"shell/browser/net/url_request_buffer_job.cc",
|
||||
"shell/browser/net/url_request_buffer_job.h",
|
||||
"shell/browser/net/url_request_context_getter.cc",
|
||||
"shell/browser/net/url_request_context_getter.h",
|
||||
"shell/browser/net/url_request_fetch_job.cc",
|
||||
"shell/browser/net/url_request_fetch_job.h",
|
||||
"shell/browser/net/url_request_stream_job.cc",
|
||||
"shell/browser/net/url_request_stream_job.h",
|
||||
"shell/browser/net/web_request_api_interface.h",
|
||||
"shell/browser/notifications/linux/libnotify_notification.cc",
|
||||
"shell/browser/notifications/linux/libnotify_notification.h",
|
||||
"shell/browser/notifications/linux/notification_presenter_linux.cc",
|
||||
@@ -316,6 +277,8 @@ filenames = {
|
||||
"shell/browser/relauncher_win.cc",
|
||||
"shell/browser/relauncher.cc",
|
||||
"shell/browser/relauncher.h",
|
||||
"shell/browser/renderer_host/electron_render_message_filter.cc",
|
||||
"shell/browser/renderer_host/electron_render_message_filter.h",
|
||||
"shell/browser/session_preferences.cc",
|
||||
"shell/browser/session_preferences.h",
|
||||
"shell/browser/special_storage_policy.cc",
|
||||
@@ -364,6 +327,8 @@ filenames = {
|
||||
"shell/browser/ui/file_dialog_gtk.cc",
|
||||
"shell/browser/ui/file_dialog_mac.mm",
|
||||
"shell/browser/ui/file_dialog_win.cc",
|
||||
"shell/browser/ui/util_gtk.cc",
|
||||
"shell/browser/ui/util_gtk.h",
|
||||
"shell/browser/ui/inspectable_web_contents.cc",
|
||||
"shell/browser/ui/inspectable_web_contents.h",
|
||||
"shell/browser/ui/inspectable_web_contents_delegate.h",
|
||||
@@ -391,6 +356,7 @@ filenames = {
|
||||
"shell/browser/ui/tray_icon_observer.h",
|
||||
"shell/browser/ui/tray_icon_win.cc",
|
||||
"shell/browser/ui/views/atom_views_delegate.cc",
|
||||
"shell/browser/ui/views/atom_views_delegate_win.cc",
|
||||
"shell/browser/ui/views/atom_views_delegate.h",
|
||||
"shell/browser/ui/views/autofill_popup_view.cc",
|
||||
"shell/browser/ui/views/autofill_popup_view.h",
|
||||
@@ -418,6 +384,8 @@ filenames = {
|
||||
"shell/browser/ui/win/atom_desktop_native_widget_aura.h",
|
||||
"shell/browser/ui/win/atom_desktop_window_tree_host_win.cc",
|
||||
"shell/browser/ui/win/atom_desktop_window_tree_host_win.h",
|
||||
"shell/browser/ui/win/dialog_thread.cc",
|
||||
"shell/browser/ui/win/dialog_thread.h",
|
||||
"shell/browser/ui/win/jump_list.cc",
|
||||
"shell/browser/ui/win/jump_list.h",
|
||||
"shell/browser/ui/win/notify_icon_host.cc",
|
||||
@@ -508,6 +476,11 @@ filenames = {
|
||||
"shell/common/crash_reporter/linux/crash_dump_handler.h",
|
||||
"shell/common/crash_reporter/win/crash_service_main.cc",
|
||||
"shell/common/crash_reporter/win/crash_service_main.h",
|
||||
"shell/common/gin_converters/callback_converter_gin_adapter.h",
|
||||
"shell/common/gin_converters/gurl_converter_gin_adapter.h",
|
||||
"shell/common/gin_converters/net_converter_gin_adapter.h",
|
||||
"shell/common/gin_converters/std_converter.h",
|
||||
"shell/common/gin_converters/value_converter_gin_adapter.h",
|
||||
"shell/common/gin_util.h",
|
||||
"shell/common/heap_snapshot.cc",
|
||||
"shell/common/heap_snapshot.h",
|
||||
@@ -516,6 +489,10 @@ filenames = {
|
||||
"shell/common/keyboard_util.h",
|
||||
"shell/common/deprecate_util.cc",
|
||||
"shell/common/deprecate_util.h",
|
||||
"shell/common/language_util.h",
|
||||
"shell/common/language_util_linux.cc",
|
||||
"shell/common/language_util_mac.mm",
|
||||
"shell/common/language_util_win.cc",
|
||||
"shell/common/mouse_util.cc",
|
||||
"shell/common/mouse_util.h",
|
||||
"shell/common/mac/main_application_bundle.h",
|
||||
@@ -569,6 +546,10 @@ filenames = {
|
||||
"shell/common/promise_util.cc",
|
||||
"shell/common/skia_util.h",
|
||||
"shell/common/skia_util.cc",
|
||||
"shell/renderer/api/context_bridge/render_frame_context_bridge_store.cc",
|
||||
"shell/renderer/api/context_bridge/render_frame_context_bridge_store.h",
|
||||
"shell/renderer/api/atom_api_context_bridge.cc",
|
||||
"shell/renderer/api/atom_api_context_bridge.h",
|
||||
"shell/renderer/api/atom_api_renderer_ipc.cc",
|
||||
"shell/renderer/api/atom_api_spell_check_client.cc",
|
||||
"shell/renderer/api/atom_api_spell_check_client.h",
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { deprecate, Menu } from 'electron'
|
||||
import { EventEmitter } from 'events'
|
||||
import { deprecate, Menu } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const bindings = process.electronBinding('app')
|
||||
const commandLine = process.electronBinding('command_line')
|
||||
const { app, App } = bindings
|
||||
const bindings = process.electronBinding('app');
|
||||
const commandLine = process.electronBinding('command_line');
|
||||
const { app, App } = bindings;
|
||||
|
||||
// Only one app object permitted.
|
||||
export default app
|
||||
export default app;
|
||||
|
||||
let dockMenu: Electron.Menu | null = null
|
||||
let dockMenu: Electron.Menu | null = null;
|
||||
|
||||
// App is an EventEmitter.
|
||||
Object.setPrototypeOf(App.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(app as any)
|
||||
Object.setPrototypeOf(App.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(app as any);
|
||||
|
||||
Object.assign(app, {
|
||||
commandLine: {
|
||||
@@ -24,99 +24,99 @@ Object.assign(app, {
|
||||
appendSwitch: (theSwitch: string, value?: string) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)),
|
||||
appendArgument: (arg: string) => commandLine.appendArgument(String(arg))
|
||||
} as Electron.CommandLine
|
||||
})
|
||||
});
|
||||
|
||||
// we define this here because it'd be overly complicated to
|
||||
// do in native land
|
||||
Object.defineProperty(app, 'applicationMenu', {
|
||||
get () {
|
||||
return Menu.getApplicationMenu()
|
||||
return Menu.getApplicationMenu();
|
||||
},
|
||||
set (menu: Electron.Menu | null) {
|
||||
return Menu.setApplicationMenu(menu)
|
||||
return Menu.setApplicationMenu(menu);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
App.prototype.isPackaged = (() => {
|
||||
const execFile = path.basename(process.execPath).toLowerCase()
|
||||
const execFile = path.basename(process.execPath).toLowerCase();
|
||||
if (process.platform === 'win32') {
|
||||
return execFile !== 'electron.exe'
|
||||
return execFile !== 'electron.exe';
|
||||
}
|
||||
return execFile !== 'electron'
|
||||
})()
|
||||
return execFile !== 'electron';
|
||||
})();
|
||||
|
||||
app._setDefaultAppPaths = (packagePath) => {
|
||||
// Set the user path according to application's name.
|
||||
app.setPath('userData', path.join(app.getPath('appData'), app.name!))
|
||||
app.setPath('userCache', path.join(app.getPath('cache'), app.name!))
|
||||
app.setAppPath(packagePath)
|
||||
app.setPath('userData', path.join(app.getPath('appData'), app.name!));
|
||||
app.setPath('userCache', path.join(app.getPath('cache'), app.name!));
|
||||
app.setAppPath(packagePath);
|
||||
|
||||
// Add support for --user-data-dir=
|
||||
if (app.commandLine.hasSwitch('user-data-dir')) {
|
||||
const userDataDir = app.commandLine.getSwitchValue('user-data-dir')
|
||||
if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir)
|
||||
const userDataDir = app.commandLine.getSwitchValue('user-data-dir');
|
||||
if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const setDockMenu = app.dock!.setMenu
|
||||
const setDockMenu = app.dock!.setMenu;
|
||||
app.dock!.setMenu = (menu) => {
|
||||
dockMenu = menu
|
||||
setDockMenu(menu)
|
||||
}
|
||||
app.dock!.getMenu = () => dockMenu
|
||||
dockMenu = menu;
|
||||
setDockMenu(menu);
|
||||
};
|
||||
app.dock!.getMenu = () => dockMenu;
|
||||
}
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m
|
||||
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m
|
||||
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m;
|
||||
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m;
|
||||
|
||||
const getStatus = (pid: number) => {
|
||||
try {
|
||||
return fs.readFileSync(`/proc/${pid}/status`, 'utf8')
|
||||
return fs.readFileSync(`/proc/${pid}/status`, 'utf8');
|
||||
} catch {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getEntry = (file: string, pattern: RegExp) => {
|
||||
const match = file.match(pattern)
|
||||
return match ? parseInt(match[1], 10) : 0
|
||||
}
|
||||
const match = file.match(pattern);
|
||||
return match ? parseInt(match[1], 10) : 0;
|
||||
};
|
||||
|
||||
const getProcessMemoryInfo = (pid: number) => {
|
||||
const file = getStatus(pid)
|
||||
const file = getStatus(pid);
|
||||
|
||||
return {
|
||||
workingSetSize: getEntry(file, patternVmRSS),
|
||||
peakWorkingSetSize: getEntry(file, patternVmHWM)
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const nativeFn = app.getAppMetrics
|
||||
const nativeFn = app.getAppMetrics;
|
||||
app.getAppMetrics = () => {
|
||||
const metrics = nativeFn.call(app)
|
||||
const metrics = nativeFn.call(app);
|
||||
for (const metric of metrics) {
|
||||
metric.memory = getProcessMemoryInfo(metric.pid)
|
||||
metric.memory = getProcessMemoryInfo(metric.pid);
|
||||
}
|
||||
|
||||
return metrics
|
||||
}
|
||||
return metrics;
|
||||
};
|
||||
}
|
||||
|
||||
// Routes the events to webContents.
|
||||
const events = ['login', 'certificate-error', 'select-client-certificate']
|
||||
const events = ['certificate-error', 'select-client-certificate'];
|
||||
for (const name of events) {
|
||||
app.on(name as 'login', (event, webContents, ...args: any[]) => {
|
||||
webContents.emit(name, event, ...args)
|
||||
})
|
||||
app.on(name as 'certificate-error', (event, webContents, ...args: any[]) => {
|
||||
webContents.emit(name, event, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
// Property Deprecations
|
||||
deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled')
|
||||
deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount')
|
||||
deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName')
|
||||
deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled');
|
||||
deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount');
|
||||
deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName');
|
||||
|
||||
// Wrappers for native classes.
|
||||
const { DownloadItem } = process.electronBinding('download_item')
|
||||
Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype)
|
||||
const { DownloadItem } = process.electronBinding('download_item');
|
||||
Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win')
|
||||
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win');
|
||||
} else {
|
||||
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native')
|
||||
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native');
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater')
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater');
|
||||
|
||||
// AutoUpdater is an EventEmitter.
|
||||
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(autoUpdater)
|
||||
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(autoUpdater);
|
||||
|
||||
module.exports = autoUpdater
|
||||
module.exports = autoUpdater;
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { app } = require('electron')
|
||||
const { EventEmitter } = require('events')
|
||||
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win')
|
||||
const { app } = require('electron');
|
||||
const { EventEmitter } = require('events');
|
||||
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win');
|
||||
|
||||
class AutoUpdater extends EventEmitter {
|
||||
quitAndInstall () {
|
||||
if (!this.updateAvailable) {
|
||||
return this.emitError('No update available, can\'t quit and install')
|
||||
return this.emitError('No update available, can\'t quit and install');
|
||||
}
|
||||
squirrelUpdate.processStart()
|
||||
app.quit()
|
||||
squirrelUpdate.processStart();
|
||||
app.quit();
|
||||
}
|
||||
|
||||
getFeedURL () {
|
||||
return this.updateURL
|
||||
return this.updateURL;
|
||||
}
|
||||
|
||||
setFeedURL (options) {
|
||||
let updateURL
|
||||
let updateURL;
|
||||
if (typeof options === 'object') {
|
||||
if (typeof options.url === 'string') {
|
||||
updateURL = options.url
|
||||
updateURL = options.url;
|
||||
} else {
|
||||
throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call')
|
||||
throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call');
|
||||
}
|
||||
} else if (typeof options === 'string') {
|
||||
updateURL = options
|
||||
updateURL = options;
|
||||
} else {
|
||||
throw new Error('Expected an options object with a \'url\' property to be provided')
|
||||
throw new Error('Expected an options object with a \'url\' property to be provided');
|
||||
}
|
||||
this.updateURL = updateURL
|
||||
this.updateURL = updateURL;
|
||||
}
|
||||
|
||||
checkForUpdates () {
|
||||
if (!this.updateURL) {
|
||||
return this.emitError('Update URL is not set')
|
||||
return this.emitError('Update URL is not set');
|
||||
}
|
||||
if (!squirrelUpdate.supported()) {
|
||||
return this.emitError('Can not find Squirrel')
|
||||
return this.emitError('Can not find Squirrel');
|
||||
}
|
||||
this.emit('checking-for-update')
|
||||
this.emit('checking-for-update');
|
||||
squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error)
|
||||
return this.emitError(error);
|
||||
}
|
||||
if (update == null) {
|
||||
return this.emit('update-not-available')
|
||||
return this.emit('update-not-available');
|
||||
}
|
||||
this.updateAvailable = true
|
||||
this.emit('update-available')
|
||||
this.updateAvailable = true;
|
||||
this.emit('update-available');
|
||||
squirrelUpdate.update(this.updateURL, (error) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error)
|
||||
return this.emitError(error);
|
||||
}
|
||||
const { releaseNotes, version } = update
|
||||
const { releaseNotes, version } = update;
|
||||
// Date is not available on Windows, so fake it.
|
||||
const date = new Date()
|
||||
const date = new Date();
|
||||
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
|
||||
this.quitAndInstall()
|
||||
})
|
||||
})
|
||||
})
|
||||
this.quitAndInstall();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Private: Emit both error object and message, this is to keep compatibility
|
||||
// with Old APIs.
|
||||
emitError (message) {
|
||||
this.emit('error', new Error(message), message)
|
||||
this.emit('error', new Error(message), message);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AutoUpdater()
|
||||
module.exports = new AutoUpdater();
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const spawn = require('child_process').spawn
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
// i.e. my-app/app-0.1.13/
|
||||
const appFolder = path.dirname(process.execPath)
|
||||
const appFolder = path.dirname(process.execPath);
|
||||
|
||||
// i.e. my-app/Update.exe
|
||||
const updateExe = path.resolve(appFolder, '..', 'Update.exe')
|
||||
const exeName = path.basename(process.execPath)
|
||||
let spawnedArgs = []
|
||||
let spawnedProcess
|
||||
const updateExe = path.resolve(appFolder, '..', 'Update.exe');
|
||||
const exeName = path.basename(process.execPath);
|
||||
let spawnedArgs = [];
|
||||
let spawnedProcess;
|
||||
|
||||
const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i])
|
||||
const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]);
|
||||
|
||||
// Spawn a command and invoke the callback when it completes with an error
|
||||
// and the output from standard out.
|
||||
const spawnUpdate = function (args, detached, callback) {
|
||||
let error, errorEmitted, stderr, stdout
|
||||
let error, errorEmitted, stderr, stdout;
|
||||
|
||||
try {
|
||||
// Ensure we don't spawn multiple squirrel processes
|
||||
@@ -28,92 +28,92 @@ const spawnUpdate = function (args, detached, callback) {
|
||||
if (spawnedProcess && !isSameArgs(args)) {
|
||||
// Disabled for backwards compatibility:
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback(`AutoUpdater process with arguments ${args} is already running`)
|
||||
return callback(`AutoUpdater process with arguments ${args} is already running`);
|
||||
} else if (!spawnedProcess) {
|
||||
spawnedProcess = spawn(updateExe, args, {
|
||||
detached: detached,
|
||||
windowsHide: true
|
||||
})
|
||||
spawnedArgs = args || []
|
||||
});
|
||||
spawnedArgs = args || [];
|
||||
}
|
||||
} catch (error1) {
|
||||
error = error1
|
||||
error = error1;
|
||||
|
||||
// Shouldn't happen, but still guard it.
|
||||
process.nextTick(function () {
|
||||
return callback(error)
|
||||
})
|
||||
return
|
||||
return callback(error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
stdout = '';
|
||||
stderr = '';
|
||||
|
||||
spawnedProcess.stdout.on('data', (data) => { stdout += data })
|
||||
spawnedProcess.stderr.on('data', (data) => { stderr += data })
|
||||
spawnedProcess.stdout.on('data', (data) => { stdout += data; });
|
||||
spawnedProcess.stderr.on('data', (data) => { stderr += data; });
|
||||
|
||||
errorEmitted = false
|
||||
errorEmitted = false;
|
||||
spawnedProcess.on('error', (error) => {
|
||||
errorEmitted = true
|
||||
callback(error)
|
||||
})
|
||||
errorEmitted = true;
|
||||
callback(error);
|
||||
});
|
||||
|
||||
return spawnedProcess.on('exit', function (code, signal) {
|
||||
spawnedProcess = undefined
|
||||
spawnedArgs = []
|
||||
spawnedProcess = undefined;
|
||||
spawnedArgs = [];
|
||||
|
||||
// We may have already emitted an error.
|
||||
if (errorEmitted) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// Process terminated with error.
|
||||
if (code !== 0) {
|
||||
// Disabled for backwards compatibility:
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`)
|
||||
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`);
|
||||
}
|
||||
|
||||
// Success.
|
||||
callback(null, stdout)
|
||||
})
|
||||
}
|
||||
callback(null, stdout);
|
||||
});
|
||||
};
|
||||
|
||||
// Start an instance of the installed app.
|
||||
exports.processStart = function () {
|
||||
return spawnUpdate(['--processStartAndWait', exeName], true, function () {})
|
||||
}
|
||||
return spawnUpdate(['--processStartAndWait', exeName], true, function () {});
|
||||
};
|
||||
|
||||
// Download the releases specified by the URL and write new results to stdout.
|
||||
exports.checkForUpdate = function (updateURL, callback) {
|
||||
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) {
|
||||
let ref, ref1, update
|
||||
let ref, ref1, update;
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
return callback(error);
|
||||
}
|
||||
try {
|
||||
// Last line of output is the JSON details about the releases
|
||||
const json = stdout.trim().split('\n').pop()
|
||||
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0
|
||||
const json = stdout.trim().split('\n').pop();
|
||||
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0;
|
||||
} catch {
|
||||
// Disabled for backwards compatibility:
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback(`Invalid result:\n${stdout}`)
|
||||
return callback(`Invalid result:\n${stdout}`);
|
||||
}
|
||||
return callback(null, update)
|
||||
})
|
||||
}
|
||||
return callback(null, update);
|
||||
});
|
||||
};
|
||||
|
||||
// Update the application to the latest remote version specified by URL.
|
||||
exports.update = function (updateURL, callback) {
|
||||
return spawnUpdate(['--update', updateURL], false, callback)
|
||||
}
|
||||
return spawnUpdate(['--update', updateURL], false, callback);
|
||||
};
|
||||
|
||||
// Is the Update.exe installed with the current application?
|
||||
exports.supported = function () {
|
||||
try {
|
||||
fs.accessSync(updateExe, fs.R_OK)
|
||||
return true
|
||||
fs.accessSync(updateExe, fs.R_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { BrowserView } = process.electronBinding('browser_view')
|
||||
const { EventEmitter } = require('events');
|
||||
const { BrowserView } = process.electronBinding('browser_view');
|
||||
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
|
||||
|
||||
BrowserView.fromWebContents = (webContents) => {
|
||||
for (const view of BrowserView.getAllViews()) {
|
||||
if (view.webContents.equal(webContents)) return view
|
||||
if (view.webContents.equal(webContents)) return view;
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports = BrowserView
|
||||
module.exports = BrowserView;
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const { WebContentsView, TopLevelWindow, deprecate } = electron
|
||||
const { BrowserWindow } = process.electronBinding('window')
|
||||
const electron = require('electron');
|
||||
const { WebContentsView, TopLevelWindow, deprecate } = electron;
|
||||
const { BrowserWindow } = process.electronBinding('window');
|
||||
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype)
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype);
|
||||
|
||||
BrowserWindow.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
TopLevelWindow.prototype._init.call(this)
|
||||
TopLevelWindow.prototype._init.call(this);
|
||||
|
||||
// Avoid recursive require.
|
||||
const { app } = electron
|
||||
const { app } = electron;
|
||||
|
||||
// Create WebContentsView.
|
||||
this.setContentView(new WebContentsView(this.webContents))
|
||||
this.setContentView(new WebContentsView(this.webContents));
|
||||
|
||||
const nativeSetBounds = this.setBounds
|
||||
const nativeSetBounds = this.setBounds;
|
||||
this.setBounds = (bounds, ...opts) => {
|
||||
bounds = {
|
||||
...this.getBounds(),
|
||||
...bounds
|
||||
}
|
||||
nativeSetBounds.call(this, bounds, ...opts)
|
||||
}
|
||||
};
|
||||
nativeSetBounds.call(this, bounds, ...opts);
|
||||
};
|
||||
|
||||
// window.resizeTo(...)
|
||||
// window.moveTo(...)
|
||||
this.webContents.on('move', (event, size) => {
|
||||
this.setBounds(size)
|
||||
})
|
||||
this.setBounds(size);
|
||||
});
|
||||
|
||||
// Hide the auto-hide menu when webContents is focused.
|
||||
this.webContents.on('activate', () => {
|
||||
if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) {
|
||||
this.setMenuBarVisibility(false)
|
||||
if (process.platform !== 'darwin' && this.autoHideMenuBar && this.isMenuBarVisible()) {
|
||||
this.setMenuBarVisibility(false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Change window title to page title.
|
||||
this.webContents.on('page-title-updated', (event, title, ...args) => {
|
||||
// Route the event to BrowserWindow.
|
||||
this.emit('page-title-updated', event, title, ...args)
|
||||
if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title)
|
||||
})
|
||||
this.emit('page-title-updated', event, title, ...args);
|
||||
if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title);
|
||||
});
|
||||
|
||||
// Sometimes the webContents doesn't get focus when window is shown, so we
|
||||
// have to force focusing on webContents in this case. The safest way is to
|
||||
@@ -54,142 +54,170 @@ BrowserWindow.prototype._init = function () {
|
||||
// Finder, we still do it on all platforms in case of other bugs we don't
|
||||
// know.
|
||||
this.webContents.once('load-url', function () {
|
||||
this.focus()
|
||||
})
|
||||
this.focus();
|
||||
});
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event) => {
|
||||
app.emit('browser-window-blur', event, this)
|
||||
})
|
||||
app.emit('browser-window-blur', event, this);
|
||||
});
|
||||
this.on('focus', (event) => {
|
||||
app.emit('browser-window-focus', event, this)
|
||||
})
|
||||
app.emit('browser-window-focus', event, this);
|
||||
});
|
||||
|
||||
// Subscribe to visibilityState changes and pass to renderer process.
|
||||
let isVisible = this.isVisible() && !this.isMinimized()
|
||||
let isVisible = this.isVisible() && !this.isMinimized();
|
||||
const visibilityChanged = () => {
|
||||
const newState = this.isVisible() && !this.isMinimized()
|
||||
const newState = this.isVisible() && !this.isMinimized();
|
||||
if (isVisible !== newState) {
|
||||
isVisible = newState
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden'
|
||||
this.webContents.emit('-window-visibility-change', visibilityState)
|
||||
isVisible = newState;
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden';
|
||||
this.webContents.emit('-window-visibility-change', visibilityState);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
|
||||
for (const event of visibilityEvents) {
|
||||
this.on(event, visibilityChanged)
|
||||
this.on(event, visibilityChanged);
|
||||
}
|
||||
|
||||
// Notify the creation of the window.
|
||||
const event = process.electronBinding('event').createEmpty()
|
||||
app.emit('browser-window-created', event, this)
|
||||
const event = process.electronBinding('event').createEmpty();
|
||||
app.emit('browser-window-created', event, this);
|
||||
|
||||
Object.defineProperty(this, 'devToolsWebContents', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get () {
|
||||
return this.webContents.devToolsWebContents
|
||||
return this.webContents.devToolsWebContents;
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// Properties
|
||||
|
||||
Object.defineProperty(this, 'autoHideMenuBar', {
|
||||
get: () => this.isMenuBarAutoHide(),
|
||||
set: (autoHide) => this.setAutoHideMenuBar(autoHide)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'minimizable', {
|
||||
get: () => this.isMinimizable(),
|
||||
set: (min) => this.setMinimizable(min)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'maximizable', {
|
||||
get: () => this.isMaximizable(),
|
||||
set: (max) => this.setMaximizable(max)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'resizable', {
|
||||
get: () => this.isResizable(),
|
||||
set: (res) => this.setResizable(res)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'fullScreenable', {
|
||||
get: () => this.isFullScreenable(),
|
||||
set: (full) => this.setFullScreenable(full)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'closable', {
|
||||
get: () => this.isClosable(),
|
||||
set: (close) => this.setClosable(close)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'movable', {
|
||||
get: () => this.isMovable(),
|
||||
set: (move) => this.setMovable(move)
|
||||
});
|
||||
};
|
||||
|
||||
const isBrowserWindow = (win) => {
|
||||
return win && win.constructor.name === 'BrowserWindow'
|
||||
}
|
||||
return win && win.constructor.name === 'BrowserWindow';
|
||||
};
|
||||
|
||||
BrowserWindow.fromId = (id) => {
|
||||
const win = TopLevelWindow.fromId(id)
|
||||
return isBrowserWindow(win) ? win : null
|
||||
}
|
||||
const win = TopLevelWindow.fromId(id);
|
||||
return isBrowserWindow(win) ? win : null;
|
||||
};
|
||||
|
||||
BrowserWindow.getAllWindows = () => {
|
||||
return TopLevelWindow.getAllWindows().filter(isBrowserWindow)
|
||||
}
|
||||
return TopLevelWindow.getAllWindows().filter(isBrowserWindow);
|
||||
};
|
||||
|
||||
BrowserWindow.getFocusedWindow = () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.webContents.equal(webContents)) return window
|
||||
if (window.webContents && window.webContents.equal(webContents)) return window;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BrowserWindow.fromBrowserView = (browserView) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.getBrowserView() === browserView) return window
|
||||
if (window.getBrowserView() === browserView) return window;
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Helpers.
|
||||
Object.assign(BrowserWindow.prototype, {
|
||||
loadURL (...args) {
|
||||
return this.webContents.loadURL(...args)
|
||||
return this.webContents.loadURL(...args);
|
||||
},
|
||||
getURL (...args) {
|
||||
return this.webContents.getURL()
|
||||
return this.webContents.getURL();
|
||||
},
|
||||
loadFile (...args) {
|
||||
return this.webContents.loadFile(...args)
|
||||
return this.webContents.loadFile(...args);
|
||||
},
|
||||
reload (...args) {
|
||||
return this.webContents.reload(...args)
|
||||
return this.webContents.reload(...args);
|
||||
},
|
||||
send (...args) {
|
||||
return this.webContents.send(...args)
|
||||
return this.webContents.send(...args);
|
||||
},
|
||||
openDevTools (...args) {
|
||||
return this.webContents.openDevTools(...args)
|
||||
return this.webContents.openDevTools(...args);
|
||||
},
|
||||
closeDevTools () {
|
||||
return this.webContents.closeDevTools()
|
||||
return this.webContents.closeDevTools();
|
||||
},
|
||||
isDevToolsOpened () {
|
||||
return this.webContents.isDevToolsOpened()
|
||||
return this.webContents.isDevToolsOpened();
|
||||
},
|
||||
isDevToolsFocused () {
|
||||
return this.webContents.isDevToolsFocused()
|
||||
return this.webContents.isDevToolsFocused();
|
||||
},
|
||||
toggleDevTools () {
|
||||
return this.webContents.toggleDevTools()
|
||||
return this.webContents.toggleDevTools();
|
||||
},
|
||||
inspectElement (...args) {
|
||||
return this.webContents.inspectElement(...args)
|
||||
return this.webContents.inspectElement(...args);
|
||||
},
|
||||
inspectSharedWorker () {
|
||||
return this.webContents.inspectSharedWorker()
|
||||
return this.webContents.inspectSharedWorker();
|
||||
},
|
||||
inspectServiceWorker () {
|
||||
return this.webContents.inspectServiceWorker()
|
||||
return this.webContents.inspectServiceWorker();
|
||||
},
|
||||
showDefinitionForSelection () {
|
||||
return this.webContents.showDefinitionForSelection()
|
||||
return this.webContents.showDefinitionForSelection();
|
||||
},
|
||||
capturePage (...args) {
|
||||
return this.webContents.capturePage(...args)
|
||||
return this.webContents.capturePage(...args);
|
||||
},
|
||||
setTouchBar (touchBar) {
|
||||
electron.TouchBar._setOnWindow(touchBar, this)
|
||||
electron.TouchBar._setOnWindow(touchBar, this);
|
||||
},
|
||||
setBackgroundThrottling (allowed) {
|
||||
this.webContents.setBackgroundThrottling(allowed)
|
||||
this.webContents.setBackgroundThrottling(allowed);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Deprecations
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable')
|
||||
|
||||
module.exports = BrowserWindow
|
||||
module.exports = BrowserWindow;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
'use strict'
|
||||
module.exports = process.electronBinding('content_tracing')
|
||||
'use strict';
|
||||
module.exports = process.electronBinding('content_tracing');
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const CrashReporter = require('@electron/internal/common/crash-reporter')
|
||||
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
|
||||
const CrashReporter = require('@electron/internal/common/crash-reporter');
|
||||
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
|
||||
|
||||
class CrashReporterMain extends CrashReporter {
|
||||
init (options) {
|
||||
return crashReporterInit(options)
|
||||
return crashReporterInit(options);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new CrashReporterMain()
|
||||
module.exports = new CrashReporterMain();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { app, BrowserWindow, deprecate } = require('electron')
|
||||
const binding = process.electronBinding('dialog')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const { app, BrowserWindow, deprecate } = require('electron');
|
||||
const binding = process.electronBinding('dialog');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
const fileDialogProperties = {
|
||||
openFile: 1 << 0,
|
||||
@@ -13,15 +13,15 @@ const fileDialogProperties = {
|
||||
promptToCreate: 1 << 5,
|
||||
noResolveAliases: 1 << 6,
|
||||
treatPackageAsDirectory: 1 << 7
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeAccessKey = (text) => {
|
||||
if (typeof text !== 'string') return text
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
// macOS does not have access keys so remove single ampersands
|
||||
// and replace double ampersands with a single ampersand
|
||||
if (process.platform === 'darwin') {
|
||||
return text.replace(/&(&?)/g, '$1')
|
||||
return text.replace(/&(&?)/g, '$1');
|
||||
}
|
||||
|
||||
// Linux uses a single underscore as an access key prefix so escape
|
||||
@@ -30,29 +30,29 @@ const normalizeAccessKey = (text) => {
|
||||
// a single underscore
|
||||
if (process.platform === 'linux') {
|
||||
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
|
||||
if (after === '&') return after
|
||||
return `_${after}`
|
||||
})
|
||||
if (after === '&') return after;
|
||||
return `_${after}`;
|
||||
});
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
const checkAppInitialized = function () {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('dialog module can only be used after app is ready')
|
||||
throw new Error('dialog module can only be used after app is ready');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const saveDialog = (sync, window, options) => {
|
||||
checkAppInitialized()
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window
|
||||
window = null
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) options = { title: 'Save' }
|
||||
if (options == null) options = { title: 'Save' };
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
@@ -63,31 +63,31 @@ const saveDialog = (sync, window, options) => {
|
||||
securityScopedBookmarks = false,
|
||||
nameFieldLabel = '',
|
||||
showsTagField = true
|
||||
} = options
|
||||
} = options;
|
||||
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string')
|
||||
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string')
|
||||
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string')
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string')
|
||||
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string')
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
|
||||
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string');
|
||||
|
||||
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window }
|
||||
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings)
|
||||
}
|
||||
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window };
|
||||
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings);
|
||||
};
|
||||
|
||||
const openDialog = (sync, window, options) => {
|
||||
checkAppInitialized()
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window
|
||||
window = null
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -98,40 +98,40 @@ const openDialog = (sync, window, options) => {
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false
|
||||
} = options
|
||||
} = options;
|
||||
|
||||
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array')
|
||||
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array');
|
||||
|
||||
let dialogProperties = 0
|
||||
let dialogProperties = 0;
|
||||
for (const prop in fileDialogProperties) {
|
||||
if (properties.includes(prop)) {
|
||||
dialogProperties |= fileDialogProperties[prop]
|
||||
dialogProperties |= fileDialogProperties[prop];
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string')
|
||||
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string')
|
||||
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string')
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string')
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
|
||||
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
|
||||
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }
|
||||
settings.properties = dialogProperties
|
||||
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window };
|
||||
settings.properties = dialogProperties;
|
||||
|
||||
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings)
|
||||
}
|
||||
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings);
|
||||
};
|
||||
|
||||
const messageBox = (sync, window, options) => {
|
||||
checkAppInitialized()
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window
|
||||
window = null
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) options = { type: 'none' }
|
||||
if (options == null) options = { type: 'none' };
|
||||
|
||||
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
|
||||
const messageBoxOptions = { noLink: 1 << 0 }
|
||||
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
|
||||
const messageBoxOptions = { noLink: 1 << 0 };
|
||||
|
||||
let {
|
||||
buttons = [],
|
||||
@@ -141,101 +141,108 @@ const messageBox = (sync, window, options) => {
|
||||
defaultId = -1,
|
||||
detail = '',
|
||||
icon = null,
|
||||
noLink = false,
|
||||
message = '',
|
||||
title = '',
|
||||
type = 'none'
|
||||
} = options
|
||||
} = options;
|
||||
|
||||
const messageBoxType = messageBoxTypes.indexOf(type)
|
||||
if (messageBoxType === -1) throw new TypeError('Invalid message box type')
|
||||
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array')
|
||||
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey)
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string')
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string')
|
||||
if (typeof detail !== 'string') throw new TypeError('Detail must be a string')
|
||||
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string')
|
||||
const messageBoxType = messageBoxTypes.indexOf(type);
|
||||
if (messageBoxType === -1) throw new TypeError('Invalid message box type');
|
||||
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array');
|
||||
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey);
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
if (typeof detail !== 'string') throw new TypeError('Detail must be a string');
|
||||
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string');
|
||||
|
||||
checkboxChecked = !!checkboxChecked
|
||||
checkboxChecked = !!checkboxChecked;
|
||||
if (checkboxChecked && !checkboxLabel) {
|
||||
throw new Error('checkboxChecked requires that checkboxLabel also be passed');
|
||||
}
|
||||
|
||||
// Choose a default button to get selected when dialog is cancelled.
|
||||
if (cancelId == null) {
|
||||
// If the defaultId is set to 0, ensure the cancel button is a different index (1)
|
||||
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0
|
||||
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const text = buttons[i].toLowerCase()
|
||||
const text = buttons[i].toLowerCase();
|
||||
if (text === 'cancel' || text === 'no') {
|
||||
cancelId = i
|
||||
break
|
||||
cancelId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const flags = options.noLink ? messageBoxOptions.noLink : 0
|
||||
|
||||
const settings = {
|
||||
window,
|
||||
messageBoxType,
|
||||
buttons,
|
||||
defaultId,
|
||||
cancelId,
|
||||
flags,
|
||||
noLink,
|
||||
title,
|
||||
message,
|
||||
detail,
|
||||
checkboxLabel,
|
||||
checkboxChecked,
|
||||
icon
|
||||
}
|
||||
};
|
||||
|
||||
if (sync) {
|
||||
return binding.showMessageBoxSync(settings)
|
||||
return binding.showMessageBoxSync(settings);
|
||||
} else {
|
||||
return binding.showMessageBox(settings)
|
||||
return binding.showMessageBox(settings);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
showOpenDialog: function (window, options) {
|
||||
return openDialog(false, window, options)
|
||||
return openDialog(false, window, options);
|
||||
},
|
||||
|
||||
showOpenDialogSync: function (window, options) {
|
||||
return openDialog(true, window, options)
|
||||
return openDialog(true, window, options);
|
||||
},
|
||||
|
||||
showSaveDialog: function (window, options) {
|
||||
return saveDialog(false, window, options)
|
||||
return saveDialog(false, window, options);
|
||||
},
|
||||
|
||||
showSaveDialogSync: function (window, options) {
|
||||
return saveDialog(true, window, options)
|
||||
return saveDialog(true, window, options);
|
||||
},
|
||||
|
||||
showMessageBox: function (window, options) {
|
||||
return messageBox(false, window, options)
|
||||
return messageBox(false, window, options);
|
||||
},
|
||||
|
||||
showMessageBoxSync: function (window, options) {
|
||||
return messageBox(true, window, options)
|
||||
return messageBox(true, window, options);
|
||||
},
|
||||
|
||||
showErrorBox: function (...args) {
|
||||
return binding.showErrorBox(...args)
|
||||
return binding.showErrorBox(...args);
|
||||
},
|
||||
|
||||
showCertificateTrustDialog: function (window, options) {
|
||||
if (window && window.constructor !== BrowserWindow) options = window
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null || typeof options !== 'object') {
|
||||
throw new TypeError('options must be an object')
|
||||
throw new TypeError('options must be an object');
|
||||
}
|
||||
|
||||
const { certificate, message = '' } = options
|
||||
const { certificate, message = '' } = options;
|
||||
if (certificate == null || typeof certificate !== 'object') {
|
||||
throw new TypeError('certificate must be an object')
|
||||
throw new TypeError('certificate must be an object');
|
||||
}
|
||||
|
||||
if (typeof message !== 'string') throw new TypeError('message must be a string')
|
||||
if (typeof message !== 'string') throw new TypeError('message must be a string');
|
||||
|
||||
return binding.showCertificateTrustDialog(window, certificate, message)
|
||||
return binding.showCertificateTrustDialog(window, certificate, message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const common = require('@electron/internal/common/api/exports/electron')
|
||||
const common = require('@electron/internal/common/api/exports/electron');
|
||||
// since browser module list is also used in renderer, keep it separate.
|
||||
const moduleList = require('@electron/internal/browser/api/module-list')
|
||||
const moduleList = require('@electron/internal/browser/api/module-list');
|
||||
|
||||
// Import common modules.
|
||||
common.defineProperties(exports)
|
||||
common.defineProperties(exports);
|
||||
|
||||
for (const module of moduleList) {
|
||||
Object.defineProperty(exports, module.name, {
|
||||
enumerable: !module.private,
|
||||
get: common.handleESModule(module.loader)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('global_shortcut').globalShortcut
|
||||
module.exports = process.electronBinding('global_shortcut').globalShortcut;
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { deprecate } = require('electron')
|
||||
const { deprecate } = require('electron');
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const { EventEmitter } = require('events')
|
||||
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase')
|
||||
const { EventEmitter } = require('events');
|
||||
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase');
|
||||
|
||||
// inAppPurchase is an EventEmitter.
|
||||
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(inAppPurchase)
|
||||
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(inAppPurchase);
|
||||
|
||||
module.exports = inAppPurchase
|
||||
module.exports = inAppPurchase;
|
||||
} else {
|
||||
module.exports = {
|
||||
purchaseProduct: (productID, quantity, callback) => {
|
||||
throw new Error('The inAppPurchase module can only be used on macOS')
|
||||
throw new Error('The inAppPurchase module can only be used on macOS');
|
||||
},
|
||||
canMakePayments: () => false,
|
||||
getReceiptURL: () => ''
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { IpcMainInvokeEvent } from 'electron'
|
||||
import { EventEmitter } from 'events';
|
||||
import { IpcMainInvokeEvent } from 'electron';
|
||||
|
||||
class IpcMain extends EventEmitter {
|
||||
private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map();
|
||||
|
||||
handle: Electron.IpcMain['handle'] = (method, fn) => {
|
||||
if (this._invokeHandlers.has(method)) {
|
||||
throw new Error(`Attempted to register a second handler for '${method}'`)
|
||||
throw new Error(`Attempted to register a second handler for '${method}'`);
|
||||
}
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`)
|
||||
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`);
|
||||
}
|
||||
this._invokeHandlers.set(method, async (e, ...args) => {
|
||||
try {
|
||||
(e as any)._reply(await Promise.resolve(fn(e, ...args)))
|
||||
(e as any)._reply(await Promise.resolve(fn(e, ...args)));
|
||||
} catch (err) {
|
||||
(e as any)._throw(err)
|
||||
(e as any)._throw(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => {
|
||||
this.handle(method, (e, ...args) => {
|
||||
this.removeHandler(method)
|
||||
return fn(e, ...args)
|
||||
})
|
||||
this.removeHandler(method);
|
||||
return fn(e, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
removeHandler (method: string) {
|
||||
this._invokeHandlers.delete(method)
|
||||
this._invokeHandlers.delete(method);
|
||||
}
|
||||
}
|
||||
|
||||
const ipcMain = new IpcMain()
|
||||
const ipcMain = new IpcMain();
|
||||
|
||||
// Do not throw exception when channel name is "error".
|
||||
ipcMain.on('error', () => {})
|
||||
ipcMain.on('error', () => {});
|
||||
|
||||
export default ipcMain
|
||||
export default ipcMain;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { app } = require('electron')
|
||||
const { app } = require('electron');
|
||||
|
||||
const isMac = process.platform === 'darwin'
|
||||
const isWindows = process.platform === 'win32'
|
||||
const isLinux = process.platform === 'linux'
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
const roles = {
|
||||
about: {
|
||||
get label () {
|
||||
return isLinux ? 'About' : `About ${app.name}`
|
||||
}
|
||||
return isLinux ? 'About' : `About ${app.name}`;
|
||||
},
|
||||
...(isWindows && { appMethod: 'showAboutPanel' })
|
||||
},
|
||||
close: {
|
||||
label: isMac ? 'Close Window' : 'Close',
|
||||
@@ -38,7 +39,7 @@ const roles = {
|
||||
accelerator: 'Shift+CmdOrCtrl+R',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: (window) => {
|
||||
window.webContents.reloadIgnoringCache()
|
||||
window.webContents.reloadIgnoringCache();
|
||||
}
|
||||
},
|
||||
front: {
|
||||
@@ -49,7 +50,7 @@ const roles = {
|
||||
},
|
||||
hide: {
|
||||
get label () {
|
||||
return `Hide ${app.name}`
|
||||
return `Hide ${app.name}`;
|
||||
},
|
||||
accelerator: 'Command+H'
|
||||
},
|
||||
@@ -77,9 +78,9 @@ const roles = {
|
||||
quit: {
|
||||
get label () {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return `Quit ${app.name}`
|
||||
case 'win32': return 'Exit'
|
||||
default: return 'Quit'
|
||||
case 'darwin': return `Quit ${app.name}`;
|
||||
case 'win32': return 'Exit';
|
||||
default: return 'Quit';
|
||||
}
|
||||
},
|
||||
accelerator: isWindows ? undefined : 'CommandOrControl+Q',
|
||||
@@ -101,7 +102,7 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+0',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel = 0
|
||||
webContents.zoomLevel = 0;
|
||||
}
|
||||
},
|
||||
selectall: {
|
||||
@@ -134,7 +135,7 @@ const roles = {
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: isMac ? 'Control+Command+F' : 'F11',
|
||||
windowMethod: (window) => {
|
||||
window.setFullScreen(!window.isFullScreen())
|
||||
window.setFullScreen(!window.isFullScreen());
|
||||
}
|
||||
},
|
||||
undo: {
|
||||
@@ -156,7 +157,7 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+Plus',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel += 0.5
|
||||
webContents.zoomLevel += 0.5;
|
||||
}
|
||||
},
|
||||
zoomout: {
|
||||
@@ -164,13 +165,13 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+-',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel -= 0.5
|
||||
webContents.zoomLevel -= 0.5;
|
||||
}
|
||||
},
|
||||
// App submenu should be used for Mac only
|
||||
appmenu: {
|
||||
get label () {
|
||||
return app.name
|
||||
return app.name;
|
||||
},
|
||||
submenu: [
|
||||
{ role: 'about' },
|
||||
@@ -178,7 +179,7 @@ const roles = {
|
||||
{ role: 'services' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideothers' },
|
||||
{ role: 'hideOthers' },
|
||||
{ role: 'unhide' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'quit' }
|
||||
@@ -209,8 +210,8 @@ const roles = {
|
||||
{
|
||||
label: 'Speech',
|
||||
submenu: [
|
||||
{ role: 'startspeaking' },
|
||||
{ role: 'stopspeaking' }
|
||||
{ role: 'startSpeaking' },
|
||||
{ role: 'stopSpeaking' }
|
||||
]
|
||||
}
|
||||
] : [
|
||||
@@ -225,12 +226,12 @@ const roles = {
|
||||
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' }
|
||||
]
|
||||
@@ -249,71 +250,71 @@ const roles = {
|
||||
])
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.roleList = roles
|
||||
exports.roleList = roles;
|
||||
|
||||
const canExecuteRole = (role) => {
|
||||
if (!roles.hasOwnProperty(role)) return false
|
||||
if (!isMac) return true
|
||||
if (!roles.hasOwnProperty(role)) return false;
|
||||
if (!isMac) return true;
|
||||
|
||||
// macOS handles all roles natively except for a few
|
||||
return roles[role].nonNativeMacOSRole
|
||||
}
|
||||
return roles[role].nonNativeMacOSRole;
|
||||
};
|
||||
|
||||
exports.getDefaultLabel = (role) => {
|
||||
return roles.hasOwnProperty(role) ? roles[role].label : ''
|
||||
}
|
||||
return roles.hasOwnProperty(role) ? roles[role].label : '';
|
||||
};
|
||||
|
||||
exports.getDefaultAccelerator = (role) => {
|
||||
if (roles.hasOwnProperty(role)) return roles[role].accelerator
|
||||
}
|
||||
if (roles.hasOwnProperty(role)) return roles[role].accelerator;
|
||||
};
|
||||
|
||||
exports.shouldRegisterAccelerator = (role) => {
|
||||
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined
|
||||
return hasRoleRegister ? roles[role].registerAccelerator : true
|
||||
}
|
||||
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined;
|
||||
return hasRoleRegister ? roles[role].registerAccelerator : true;
|
||||
};
|
||||
|
||||
exports.getDefaultSubmenu = (role) => {
|
||||
if (!roles.hasOwnProperty(role)) return
|
||||
if (!roles.hasOwnProperty(role)) return;
|
||||
|
||||
let { submenu } = roles[role]
|
||||
let { submenu } = roles[role];
|
||||
|
||||
// remove null items from within the submenu
|
||||
if (Array.isArray(submenu)) {
|
||||
submenu = submenu.filter((item) => item != null)
|
||||
submenu = submenu.filter((item) => item != null);
|
||||
}
|
||||
|
||||
return submenu
|
||||
}
|
||||
return submenu;
|
||||
};
|
||||
|
||||
exports.execute = (role, focusedWindow, focusedWebContents) => {
|
||||
if (!canExecuteRole(role)) return false
|
||||
if (!canExecuteRole(role)) return false;
|
||||
|
||||
const { appMethod, webContentsMethod, windowMethod } = roles[role]
|
||||
const { appMethod, webContentsMethod, windowMethod } = roles[role];
|
||||
|
||||
if (appMethod) {
|
||||
app[appMethod]()
|
||||
return true
|
||||
app[appMethod]();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (windowMethod && focusedWindow != null) {
|
||||
if (typeof windowMethod === 'function') {
|
||||
windowMethod(focusedWindow)
|
||||
windowMethod(focusedWindow);
|
||||
} else {
|
||||
focusedWindow[windowMethod]()
|
||||
focusedWindow[windowMethod]();
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
if (webContentsMethod && focusedWebContents != null) {
|
||||
if (typeof webContentsMethod === 'function') {
|
||||
webContentsMethod(focusedWebContents)
|
||||
webContentsMethod(focusedWebContents);
|
||||
} else {
|
||||
focusedWebContents[webContentsMethod]()
|
||||
focusedWebContents[webContentsMethod]();
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const roles = require('@electron/internal/browser/api/menu-item-roles')
|
||||
const roles = require('@electron/internal/browser/api/menu-item-roles');
|
||||
|
||||
let nextCommandId = 0
|
||||
let nextCommandId = 0;
|
||||
|
||||
const MenuItem = function (options) {
|
||||
const { Menu } = require('electron')
|
||||
const { Menu } = require('electron');
|
||||
|
||||
// Preserve extra fields specified by user
|
||||
for (const key in options) {
|
||||
if (!(key in this)) this[key] = options[key]
|
||||
if (!(key in this)) this[key] = options[key];
|
||||
}
|
||||
if (typeof this.role === 'string' || this.role instanceof String) {
|
||||
this.role = this.role.toLowerCase()
|
||||
this.role = this.role.toLowerCase();
|
||||
}
|
||||
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role)
|
||||
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role);
|
||||
if (this.submenu != null && this.submenu.constructor !== Menu) {
|
||||
this.submenu = Menu.buildFromTemplate(this.submenu)
|
||||
this.submenu = Menu.buildFromTemplate(this.submenu);
|
||||
}
|
||||
if (this.type == null && this.submenu != null) {
|
||||
this.type = 'submenu'
|
||||
this.type = 'submenu';
|
||||
}
|
||||
if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) {
|
||||
throw new Error('Invalid submenu')
|
||||
throw new Error('Invalid submenu');
|
||||
}
|
||||
|
||||
this.overrideReadOnlyProperty('type', 'normal')
|
||||
this.overrideReadOnlyProperty('role')
|
||||
this.overrideReadOnlyProperty('accelerator')
|
||||
this.overrideReadOnlyProperty('icon')
|
||||
this.overrideReadOnlyProperty('submenu')
|
||||
this.overrideReadOnlyProperty('type', 'normal');
|
||||
this.overrideReadOnlyProperty('role');
|
||||
this.overrideReadOnlyProperty('accelerator');
|
||||
this.overrideReadOnlyProperty('icon');
|
||||
this.overrideReadOnlyProperty('submenu');
|
||||
|
||||
this.overrideProperty('label', roles.getDefaultLabel(this.role))
|
||||
this.overrideProperty('sublabel', '')
|
||||
this.overrideProperty('toolTip', '')
|
||||
this.overrideProperty('enabled', true)
|
||||
this.overrideProperty('visible', true)
|
||||
this.overrideProperty('checked', false)
|
||||
this.overrideProperty('acceleratorWorksWhenHidden', true)
|
||||
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role))
|
||||
this.overrideProperty('label', roles.getDefaultLabel(this.role));
|
||||
this.overrideProperty('sublabel', '');
|
||||
this.overrideProperty('toolTip', '');
|
||||
this.overrideProperty('enabled', true);
|
||||
this.overrideProperty('visible', true);
|
||||
this.overrideProperty('checked', false);
|
||||
this.overrideProperty('acceleratorWorksWhenHidden', true);
|
||||
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role));
|
||||
|
||||
if (!MenuItem.types.includes(this.type)) {
|
||||
throw new Error(`Unknown menu item type: ${this.type}`)
|
||||
throw new Error(`Unknown menu item type: ${this.type}`);
|
||||
}
|
||||
|
||||
this.overrideReadOnlyProperty('commandId', ++nextCommandId)
|
||||
this.overrideReadOnlyProperty('commandId', ++nextCommandId);
|
||||
|
||||
const click = options.click
|
||||
const click = options.click;
|
||||
this.click = (event, focusedWindow, focusedWebContents) => {
|
||||
// Manually flip the checked flags when clicked.
|
||||
if (this.type === 'checkbox' || this.type === 'radio') {
|
||||
this.checked = !this.checked
|
||||
this.checked = !this.checked;
|
||||
}
|
||||
|
||||
if (!roles.execute(this.role, focusedWindow, focusedWebContents)) {
|
||||
if (typeof click === 'function') {
|
||||
click(this, focusedWindow, event)
|
||||
click(this, focusedWindow, event);
|
||||
} else if (typeof this.selector === 'string' && process.platform === 'darwin') {
|
||||
Menu.sendActionToFirstResponder(this.selector)
|
||||
Menu.sendActionToFirstResponder(this.selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
|
||||
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'];
|
||||
|
||||
MenuItem.prototype.getDefaultRoleAccelerator = function () {
|
||||
return roles.getDefaultAccelerator(this.role)
|
||||
}
|
||||
return roles.getDefaultAccelerator(this.role);
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideProperty = function (name, defaultValue = null) {
|
||||
if (this[name] == null) {
|
||||
this[name] = defaultValue
|
||||
this[name] = defaultValue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
|
||||
this.overrideProperty(name, defaultValue)
|
||||
this.overrideProperty(name, defaultValue);
|
||||
Object.defineProperty(this, name, {
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
value: this[name]
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = MenuItem
|
||||
module.exports = MenuItem;
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
function splitArray (arr, predicate) {
|
||||
const result = arr.reduce((multi, item) => {
|
||||
const current = multi[multi.length - 1]
|
||||
const current = multi[multi.length - 1];
|
||||
if (predicate(item)) {
|
||||
if (current.length > 0) multi.push([])
|
||||
if (current.length > 0) multi.push([]);
|
||||
} else {
|
||||
current.push(item)
|
||||
current.push(item);
|
||||
}
|
||||
return multi
|
||||
}, [[]])
|
||||
return multi;
|
||||
}, [[]]);
|
||||
|
||||
if (result[result.length - 1].length === 0) {
|
||||
return result.slice(0, result.length - 1)
|
||||
return result.slice(0, result.length - 1);
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
function joinArrays (arrays, joinIDs) {
|
||||
return arrays.reduce((joined, arr, i) => {
|
||||
if (i > 0 && arr.length) {
|
||||
if (joinIDs.length > 0) {
|
||||
joined.push(joinIDs[0])
|
||||
joinIDs.splice(0, 1)
|
||||
joined.push(joinIDs[0]);
|
||||
joinIDs.splice(0, 1);
|
||||
} else {
|
||||
joined.push({ type: 'separator' })
|
||||
joined.push({ type: 'separator' });
|
||||
}
|
||||
}
|
||||
return joined.concat(arr)
|
||||
}, [])
|
||||
return joined.concat(arr);
|
||||
}, []);
|
||||
}
|
||||
|
||||
function pushOntoMultiMap (map, key, value) {
|
||||
if (!map.has(key)) {
|
||||
map.set(key, [])
|
||||
map.set(key, []);
|
||||
}
|
||||
map.get(key).push(value)
|
||||
map.get(key).push(value);
|
||||
}
|
||||
|
||||
function indexOfGroupContainingID (groups, id, ignoreGroup) {
|
||||
@@ -45,102 +45,102 @@ function indexOfGroupContainingID (groups, id, ignoreGroup) {
|
||||
candidateGroup.some(
|
||||
candidateItem => candidateItem.id === id
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Sort nodes topologically using a depth-first approach. Encountered cycles
|
||||
// are broken.
|
||||
function sortTopologically (originalOrder, edgesById) {
|
||||
const sorted = []
|
||||
const marked = new Set()
|
||||
const sorted = [];
|
||||
const marked = new Set();
|
||||
|
||||
const visit = (mark) => {
|
||||
if (marked.has(mark)) return
|
||||
marked.add(mark)
|
||||
const edges = edgesById.get(mark)
|
||||
if (marked.has(mark)) return;
|
||||
marked.add(mark);
|
||||
const edges = edgesById.get(mark);
|
||||
if (edges != null) {
|
||||
edges.forEach(visit)
|
||||
edges.forEach(visit);
|
||||
}
|
||||
sorted.push(mark)
|
||||
}
|
||||
sorted.push(mark);
|
||||
};
|
||||
|
||||
originalOrder.forEach(visit)
|
||||
return sorted
|
||||
originalOrder.forEach(visit);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
function attemptToMergeAGroup (groups) {
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
const group = groups[i]
|
||||
const group = groups[i];
|
||||
for (const item of group) {
|
||||
const toIDs = [...(item.before || []), ...(item.after || [])]
|
||||
const toIDs = [...(item.before || []), ...(item.after || [])];
|
||||
for (const id of toIDs) {
|
||||
const index = indexOfGroupContainingID(groups, id, group)
|
||||
if (index === -1) continue
|
||||
const mergeTarget = groups[index]
|
||||
const index = indexOfGroupContainingID(groups, id, group);
|
||||
if (index === -1) continue;
|
||||
const mergeTarget = groups[index];
|
||||
|
||||
mergeTarget.push(...group)
|
||||
groups.splice(i, 1)
|
||||
return true
|
||||
mergeTarget.push(...group);
|
||||
groups.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
function mergeGroups (groups) {
|
||||
let merged = true
|
||||
let merged = true;
|
||||
while (merged) {
|
||||
merged = attemptToMergeAGroup(groups)
|
||||
merged = attemptToMergeAGroup(groups);
|
||||
}
|
||||
return groups
|
||||
return groups;
|
||||
}
|
||||
|
||||
function sortItemsInGroup (group) {
|
||||
const originalOrder = group.map((node, i) => i)
|
||||
const edges = new Map()
|
||||
const idToIndex = new Map(group.map((item, i) => [item.id, i]))
|
||||
const originalOrder = group.map((node, i) => i);
|
||||
const edges = new Map();
|
||||
const idToIndex = new Map(group.map((item, i) => [item.id, i]));
|
||||
|
||||
group.forEach((item, i) => {
|
||||
if (item.before) {
|
||||
item.before.forEach(toID => {
|
||||
const to = idToIndex.get(toID)
|
||||
const to = idToIndex.get(toID);
|
||||
if (to != null) {
|
||||
pushOntoMultiMap(edges, to, i)
|
||||
pushOntoMultiMap(edges, to, i);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
if (item.after) {
|
||||
item.after.forEach(toID => {
|
||||
const to = idToIndex.get(toID)
|
||||
const to = idToIndex.get(toID);
|
||||
if (to != null) {
|
||||
pushOntoMultiMap(edges, i, to)
|
||||
pushOntoMultiMap(edges, i, to);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const sortedNodes = sortTopologically(originalOrder, edges)
|
||||
return sortedNodes.map(i => group[i])
|
||||
const sortedNodes = sortTopologically(originalOrder, edges);
|
||||
return sortedNodes.map(i => group[i]);
|
||||
}
|
||||
|
||||
function findEdgesInGroup (groups, i, edges) {
|
||||
const group = groups[i]
|
||||
const group = groups[i];
|
||||
for (const item of group) {
|
||||
if (item.beforeGroupContaining) {
|
||||
for (const id of item.beforeGroupContaining) {
|
||||
const to = indexOfGroupContainingID(groups, id, group)
|
||||
const to = indexOfGroupContainingID(groups, id, group);
|
||||
if (to !== -1) {
|
||||
pushOntoMultiMap(edges, to, i)
|
||||
return
|
||||
pushOntoMultiMap(edges, to, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.afterGroupContaining) {
|
||||
for (const id of item.afterGroupContaining) {
|
||||
const to = indexOfGroupContainingID(groups, id, group)
|
||||
const to = indexOfGroupContainingID(groups, id, group);
|
||||
if (to !== -1) {
|
||||
pushOntoMultiMap(edges, i, to)
|
||||
return
|
||||
pushOntoMultiMap(edges, i, to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,29 +148,29 @@ function findEdgesInGroup (groups, i, edges) {
|
||||
}
|
||||
|
||||
function sortGroups (groups) {
|
||||
const originalOrder = groups.map((item, i) => i)
|
||||
const edges = new Map()
|
||||
const originalOrder = groups.map((item, i) => i);
|
||||
const edges = new Map();
|
||||
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
findEdgesInGroup(groups, i, edges)
|
||||
findEdgesInGroup(groups, i, edges);
|
||||
}
|
||||
|
||||
const sortedGroupIndexes = sortTopologically(originalOrder, edges)
|
||||
return sortedGroupIndexes.map(i => groups[i])
|
||||
const sortedGroupIndexes = sortTopologically(originalOrder, edges);
|
||||
return sortedGroupIndexes.map(i => groups[i]);
|
||||
}
|
||||
|
||||
function sortMenuItems (menuItems) {
|
||||
const isSeparator = (item) => item.type === 'separator'
|
||||
const separators = menuItems.filter(i => i.type === 'separator')
|
||||
const isSeparator = (item) => item.type === 'separator';
|
||||
const separators = menuItems.filter(i => i.type === 'separator');
|
||||
|
||||
// Split the items into their implicit groups based upon separators.
|
||||
const groups = splitArray(menuItems, isSeparator)
|
||||
const mergedGroups = mergeGroups(groups)
|
||||
const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup)
|
||||
const sortedGroups = sortGroups(mergedGroupsWithSortedItems)
|
||||
const groups = splitArray(menuItems, isSeparator);
|
||||
const mergedGroups = mergeGroups(groups);
|
||||
const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup);
|
||||
const sortedGroups = sortGroups(mergedGroupsWithSortedItems);
|
||||
|
||||
const joined = joinArrays(sortedGroups, separators)
|
||||
return joined
|
||||
const joined = joinArrays(sortedGroups, separators);
|
||||
return joined;
|
||||
}
|
||||
|
||||
module.exports = { sortMenuItems }
|
||||
module.exports = { sortMenuItems };
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { TopLevelWindow, MenuItem, webContents } = require('electron')
|
||||
const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const bindings = process.electronBinding('menu')
|
||||
const { TopLevelWindow, MenuItem, webContents } = require('electron');
|
||||
const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
const bindings = process.electronBinding('menu');
|
||||
|
||||
const { Menu } = bindings
|
||||
let applicationMenu = null
|
||||
let groupIdIndex = 0
|
||||
const { Menu } = bindings;
|
||||
let applicationMenu = null;
|
||||
let groupIdIndex = 0;
|
||||
|
||||
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype);
|
||||
|
||||
// Menu Delegate.
|
||||
// This object should hold no reference to |Menu| to avoid cyclic reference.
|
||||
@@ -20,172 +20,172 @@ const delegate = {
|
||||
shouldCommandIdWorkWhenHidden: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].acceleratorWorksWhenHidden : undefined,
|
||||
isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
|
||||
getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
|
||||
const command = menu.commandsMap[id]
|
||||
if (!command) return
|
||||
if (command.accelerator != null) return command.accelerator
|
||||
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
|
||||
const command = menu.commandsMap[id];
|
||||
if (!command) return;
|
||||
if (command.accelerator != null) return command.accelerator;
|
||||
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator();
|
||||
},
|
||||
shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined,
|
||||
executeCommand: (menu, event, id) => {
|
||||
const command = menu.commandsMap[id]
|
||||
if (!command) return
|
||||
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents())
|
||||
const command = menu.commandsMap[id];
|
||||
if (!command) return;
|
||||
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents());
|
||||
},
|
||||
menuWillShow: (menu) => {
|
||||
// Ensure radio groups have at least one menu item selected
|
||||
for (const id in menu.groupsMap) {
|
||||
const found = menu.groupsMap[id].find(item => item.checked) || null
|
||||
if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true)
|
||||
for (const id of Object.keys(menu.groupsMap)) {
|
||||
const found = menu.groupsMap[id].find(item => item.checked) || null;
|
||||
if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Instance Methods */
|
||||
|
||||
Menu.prototype._init = function () {
|
||||
this.commandsMap = {}
|
||||
this.groupsMap = {}
|
||||
this.items = []
|
||||
this.delegate = delegate
|
||||
}
|
||||
this.commandsMap = {};
|
||||
this.groupsMap = {};
|
||||
this.items = [];
|
||||
this.delegate = delegate;
|
||||
};
|
||||
|
||||
Menu.prototype.popup = function (options = {}) {
|
||||
if (options == null || typeof options !== 'object') {
|
||||
throw new TypeError('Options must be an object')
|
||||
throw new TypeError('Options must be an object');
|
||||
}
|
||||
let { window, x, y, positioningItem, callback } = options
|
||||
let { window, x, y, positioningItem, callback } = options;
|
||||
|
||||
// no callback passed
|
||||
if (!callback || typeof callback !== 'function') callback = () => {}
|
||||
if (!callback || typeof callback !== 'function') callback = () => {};
|
||||
|
||||
// set defaults
|
||||
if (typeof x !== 'number') x = -1
|
||||
if (typeof y !== 'number') y = -1
|
||||
if (typeof positioningItem !== 'number') positioningItem = -1
|
||||
if (typeof x !== 'number') x = -1;
|
||||
if (typeof y !== 'number') y = -1;
|
||||
if (typeof positioningItem !== 'number') positioningItem = -1;
|
||||
|
||||
// find which window to use
|
||||
const wins = TopLevelWindow.getAllWindows()
|
||||
const wins = TopLevelWindow.getAllWindows();
|
||||
if (!wins || wins.indexOf(window) === -1) {
|
||||
window = TopLevelWindow.getFocusedWindow()
|
||||
window = TopLevelWindow.getFocusedWindow();
|
||||
if (!window && wins && wins.length > 0) {
|
||||
window = wins[0]
|
||||
window = wins[0];
|
||||
}
|
||||
if (!window) {
|
||||
throw new Error(`Cannot open Menu without a TopLevelWindow present`)
|
||||
throw new Error(`Cannot open Menu without a TopLevelWindow present`);
|
||||
}
|
||||
}
|
||||
|
||||
this.popupAt(window, x, y, positioningItem, callback)
|
||||
return { browserWindow: window, x, y, position: positioningItem }
|
||||
}
|
||||
this.popupAt(window, x, y, positioningItem, callback);
|
||||
return { browserWindow: window, x, y, position: positioningItem };
|
||||
};
|
||||
|
||||
Menu.prototype.closePopup = function (window) {
|
||||
if (window instanceof TopLevelWindow) {
|
||||
this.closePopupAt(window.id)
|
||||
this.closePopupAt(window.id);
|
||||
} else {
|
||||
// Passing -1 (invalid) would make closePopupAt close the all menu runners
|
||||
// belong to this menu.
|
||||
this.closePopupAt(-1)
|
||||
this.closePopupAt(-1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Menu.prototype.getMenuItemById = function (id) {
|
||||
const items = this.items
|
||||
const items = this.items;
|
||||
|
||||
let found = items.find(item => item.id === id) || null
|
||||
let found = items.find(item => item.id === id) || null;
|
||||
for (let i = 0; !found && i < items.length; i++) {
|
||||
if (items[i].submenu) {
|
||||
found = items[i].submenu.getMenuItemById(id)
|
||||
found = items[i].submenu.getMenuItemById(id);
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
return found;
|
||||
};
|
||||
|
||||
Menu.prototype.append = function (item) {
|
||||
return this.insert(this.getItemCount(), item)
|
||||
}
|
||||
return this.insert(this.getItemCount(), item);
|
||||
};
|
||||
|
||||
Menu.prototype.insert = function (pos, item) {
|
||||
if ((item ? item.constructor : void 0) !== MenuItem) {
|
||||
throw new TypeError('Invalid item')
|
||||
throw new TypeError('Invalid item');
|
||||
}
|
||||
|
||||
if (pos < 0) {
|
||||
throw new RangeError(`Position ${pos} cannot be less than 0`)
|
||||
throw new RangeError(`Position ${pos} cannot be less than 0`);
|
||||
} else if (pos > this.getItemCount()) {
|
||||
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`)
|
||||
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`);
|
||||
}
|
||||
|
||||
// insert item depending on its type
|
||||
insertItemByType.call(this, item, pos)
|
||||
insertItemByType.call(this, item, pos);
|
||||
|
||||
// set item properties
|
||||
if (item.sublabel) this.setSublabel(pos, item.sublabel)
|
||||
if (item.toolTip) this.setToolTip(pos, item.toolTip)
|
||||
if (item.icon) this.setIcon(pos, item.icon)
|
||||
if (item.role) this.setRole(pos, item.role)
|
||||
if (item.sublabel) this.setSublabel(pos, item.sublabel);
|
||||
if (item.toolTip) this.setToolTip(pos, item.toolTip);
|
||||
if (item.icon) this.setIcon(pos, item.icon);
|
||||
if (item.role) this.setRole(pos, item.role);
|
||||
|
||||
// Make menu accessable to items.
|
||||
item.overrideReadOnlyProperty('menu', this)
|
||||
item.overrideReadOnlyProperty('menu', this);
|
||||
|
||||
// Remember the items.
|
||||
this.items.splice(pos, 0, item)
|
||||
this.commandsMap[item.commandId] = item
|
||||
}
|
||||
this.items.splice(pos, 0, item);
|
||||
this.commandsMap[item.commandId] = item;
|
||||
};
|
||||
|
||||
Menu.prototype._callMenuWillShow = function () {
|
||||
if (this.delegate) this.delegate.menuWillShow(this)
|
||||
if (this.delegate) this.delegate.menuWillShow(this);
|
||||
this.items.forEach(item => {
|
||||
if (item.submenu) item.submenu._callMenuWillShow()
|
||||
})
|
||||
}
|
||||
if (item.submenu) item.submenu._callMenuWillShow();
|
||||
});
|
||||
};
|
||||
|
||||
/* Static Methods */
|
||||
|
||||
Menu.getApplicationMenu = () => applicationMenu
|
||||
Menu.getApplicationMenu = () => applicationMenu;
|
||||
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
|
||||
|
||||
// set application menu with a preexisting menu
|
||||
Menu.setApplicationMenu = function (menu) {
|
||||
if (menu && menu.constructor !== Menu) {
|
||||
throw new TypeError('Invalid menu')
|
||||
throw new TypeError('Invalid menu');
|
||||
}
|
||||
|
||||
applicationMenu = menu
|
||||
v8Util.setHiddenValue(global, 'applicationMenuSet', true)
|
||||
applicationMenu = menu;
|
||||
v8Util.setHiddenValue(global, 'applicationMenuSet', true);
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
if (!menu) return
|
||||
menu._callMenuWillShow()
|
||||
bindings.setApplicationMenu(menu)
|
||||
if (!menu) return;
|
||||
menu._callMenuWillShow();
|
||||
bindings.setApplicationMenu(menu);
|
||||
} else {
|
||||
const windows = TopLevelWindow.getAllWindows()
|
||||
return windows.map(w => w.setMenu(menu))
|
||||
const windows = TopLevelWindow.getAllWindows();
|
||||
return windows.map(w => w.setMenu(menu));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Menu.buildFromTemplate = function (template) {
|
||||
if (!Array.isArray(template)) {
|
||||
throw new TypeError('Invalid template for Menu: Menu template must be an array')
|
||||
throw new TypeError('Invalid template for Menu: Menu template must be an array');
|
||||
}
|
||||
if (!areValidTemplateItems(template)) {
|
||||
throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type')
|
||||
throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type');
|
||||
}
|
||||
const filtered = removeExtraSeparators(template)
|
||||
const sorted = sortTemplate(filtered)
|
||||
const filtered = removeExtraSeparators(template);
|
||||
const sorted = sortTemplate(filtered);
|
||||
|
||||
const menu = new Menu()
|
||||
const menu = new Menu();
|
||||
sorted.forEach(item => {
|
||||
if (item instanceof MenuItem) {
|
||||
menu.append(item)
|
||||
menu.append(item);
|
||||
} else {
|
||||
menu.append(new MenuItem(item))
|
||||
menu.append(new MenuItem(item));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return menu
|
||||
}
|
||||
return menu;
|
||||
};
|
||||
|
||||
/* Helper Functions */
|
||||
|
||||
@@ -196,51 +196,50 @@ function areValidTemplateItems (template) {
|
||||
typeof item === 'object' &&
|
||||
(item.hasOwnProperty('label') ||
|
||||
item.hasOwnProperty('role') ||
|
||||
item.type === 'separator'))
|
||||
item.type === 'separator'));
|
||||
}
|
||||
|
||||
function sortTemplate (template) {
|
||||
const sorted = sortMenuItems(template)
|
||||
for (const id in sorted) {
|
||||
const item = sorted[id]
|
||||
const sorted = sortMenuItems(template);
|
||||
for (const item of sorted) {
|
||||
if (Array.isArray(item.submenu)) {
|
||||
item.submenu = sortTemplate(item.submenu)
|
||||
item.submenu = sortTemplate(item.submenu);
|
||||
}
|
||||
}
|
||||
return sorted
|
||||
return sorted;
|
||||
}
|
||||
|
||||
// Search between separators to find a radio menu item and return its group id
|
||||
function generateGroupId (items, pos) {
|
||||
if (pos > 0) {
|
||||
for (let idx = pos - 1; idx >= 0; idx--) {
|
||||
if (items[idx].type === 'radio') return items[idx].groupId
|
||||
if (items[idx].type === 'separator') break
|
||||
if (items[idx].type === 'radio') return items[idx].groupId;
|
||||
if (items[idx].type === 'separator') break;
|
||||
}
|
||||
} else if (pos < items.length) {
|
||||
for (let idx = pos; idx <= items.length - 1; idx++) {
|
||||
if (items[idx].type === 'radio') return items[idx].groupId
|
||||
if (items[idx].type === 'separator') break
|
||||
if (items[idx].type === 'radio') return items[idx].groupId;
|
||||
if (items[idx].type === 'separator') break;
|
||||
}
|
||||
}
|
||||
groupIdIndex += 1
|
||||
return groupIdIndex
|
||||
groupIdIndex += 1;
|
||||
return groupIdIndex;
|
||||
}
|
||||
|
||||
function removeExtraSeparators (items) {
|
||||
// fold adjacent separators together
|
||||
let ret = items.filter((e, idx, arr) => {
|
||||
if (e.visible === false) return true
|
||||
return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator'
|
||||
})
|
||||
if (e.visible === false) return true;
|
||||
return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator';
|
||||
});
|
||||
|
||||
// remove edge separators
|
||||
ret = ret.filter((e, idx, arr) => {
|
||||
if (e.visible === false) return true
|
||||
return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1)
|
||||
})
|
||||
if (e.visible === false) return true;
|
||||
return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1);
|
||||
});
|
||||
|
||||
return ret
|
||||
return ret;
|
||||
}
|
||||
|
||||
function insertItemByType (item, pos) {
|
||||
@@ -251,28 +250,28 @@ function insertItemByType (item, pos) {
|
||||
submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
|
||||
radio: () => {
|
||||
// Grouping radio menu items
|
||||
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
|
||||
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos));
|
||||
if (this.groupsMap[item.groupId] == null) {
|
||||
this.groupsMap[item.groupId] = []
|
||||
this.groupsMap[item.groupId] = [];
|
||||
}
|
||||
this.groupsMap[item.groupId].push(item)
|
||||
this.groupsMap[item.groupId].push(item);
|
||||
|
||||
// Setting a radio menu item should flip other items in the group.
|
||||
v8Util.setHiddenValue(item, 'checked', item.checked)
|
||||
v8Util.setHiddenValue(item, 'checked', item.checked);
|
||||
Object.defineProperty(item, 'checked', {
|
||||
enumerable: true,
|
||||
get: () => v8Util.getHiddenValue(item, 'checked'),
|
||||
set: () => {
|
||||
this.groupsMap[item.groupId].forEach(other => {
|
||||
if (other !== item) v8Util.setHiddenValue(other, 'checked', false)
|
||||
})
|
||||
v8Util.setHiddenValue(item, 'checked', true)
|
||||
if (other !== item) v8Util.setHiddenValue(other, 'checked', false);
|
||||
});
|
||||
v8Util.setHiddenValue(item, 'checked', true);
|
||||
}
|
||||
})
|
||||
this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
|
||||
});
|
||||
this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
|
||||
}
|
||||
}
|
||||
types[item.type]()
|
||||
};
|
||||
types[item.type]();
|
||||
}
|
||||
|
||||
module.exports = Menu
|
||||
module.exports = Menu;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
// TODO: Figure out a way to not duplicate this information between here and module-list
|
||||
// It is currently duplicated as module-list "require"s all the browser API file and the
|
||||
// remote module in the renderer process depends on that file. As a result webpack
|
||||
// includes all the browser API files in the renderer process as well and we want to avoid that
|
||||
|
||||
const features = process.electronBinding('features')
|
||||
const features = process.electronBinding('features');
|
||||
|
||||
// Browser side modules, please sort alphabetically.
|
||||
module.exports = [
|
||||
@@ -21,6 +21,7 @@ module.exports = [
|
||||
{ name: 'inAppPurchase' },
|
||||
{ name: 'Menu' },
|
||||
{ name: 'MenuItem' },
|
||||
{ name: 'nativeTheme' },
|
||||
{ name: 'net' },
|
||||
{ name: 'netLog' },
|
||||
{ name: 'Notification' },
|
||||
@@ -36,7 +37,7 @@ module.exports = [
|
||||
{ name: 'View' },
|
||||
{ name: 'webContents' },
|
||||
{ name: 'WebContentsView' }
|
||||
]
|
||||
];
|
||||
|
||||
if (features.isViewApiEnabled()) {
|
||||
module.exports.push(
|
||||
@@ -47,5 +48,5 @@ if (features.isViewApiEnabled()) {
|
||||
{ name: 'MdTextButton' },
|
||||
{ name: 'ResizeArea' },
|
||||
{ name: 'TextField' }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
// TODO: Updating this file also required updating the module-keys file
|
||||
|
||||
const features = process.electronBinding('features')
|
||||
const features = process.electronBinding('features');
|
||||
|
||||
// Browser side modules, please sort alphabetically.
|
||||
module.exports = [
|
||||
@@ -18,6 +18,7 @@ module.exports = [
|
||||
{ name: 'inAppPurchase', loader: () => require('./in-app-purchase') },
|
||||
{ name: 'Menu', loader: () => require('./menu') },
|
||||
{ name: 'MenuItem', loader: () => require('./menu-item') },
|
||||
{ name: 'nativeTheme', loader: () => require('./native-theme') },
|
||||
{ name: 'net', loader: () => require('./net') },
|
||||
{ name: 'netLog', loader: () => require('./net-log') },
|
||||
{ name: 'Notification', loader: () => require('./notification') },
|
||||
@@ -33,7 +34,7 @@ module.exports = [
|
||||
{ name: 'View', loader: () => require('./view') },
|
||||
{ name: 'webContents', loader: () => require('./web-contents') },
|
||||
{ name: 'WebContentsView', loader: () => require('./web-contents-view') }
|
||||
]
|
||||
];
|
||||
|
||||
if (features.isViewApiEnabled()) {
|
||||
module.exports.push(
|
||||
@@ -44,5 +45,5 @@ if (features.isViewApiEnabled()) {
|
||||
{ name: 'MdTextButton', loader: () => require('./views/md-text-button') },
|
||||
{ name: 'ResizeArea', loader: () => require('./views/resize-area') },
|
||||
{ name: 'TextField', loader: () => require('./views/text-field') }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
8
lib/browser/api/native-theme.ts
Normal file
8
lib/browser/api/native-theme.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme');
|
||||
|
||||
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(nativeTheme as any);
|
||||
|
||||
module.exports = nativeTheme;
|
||||
@@ -1,32 +1,32 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
// TODO(deepak1556): Deprecate and remove standalone netLog module,
|
||||
// it is now a property of session module.
|
||||
const { app, session } = require('electron')
|
||||
const { app, session } = require('electron');
|
||||
|
||||
// Fallback to default session.
|
||||
Object.setPrototypeOf(module.exports, new Proxy({}, {
|
||||
get (target, property) {
|
||||
if (!app.isReady()) return
|
||||
if (!app.isReady()) return;
|
||||
|
||||
const netLog = session.defaultSession.netLog
|
||||
const netLog = session.defaultSession.netLog;
|
||||
|
||||
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return
|
||||
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return;
|
||||
|
||||
// check for properties on the prototype chain that aren't functions
|
||||
if (typeof netLog[property] !== 'function') return netLog[property]
|
||||
if (typeof netLog[property] !== 'function') return netLog[property];
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args) => netLog[property](...args)
|
||||
return (...args) => netLog[property](...args);
|
||||
},
|
||||
|
||||
ownKeys () {
|
||||
if (!app.isReady()) return []
|
||||
if (!app.isReady()) return [];
|
||||
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog))
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog));
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor (target) {
|
||||
return { configurable: true, enumerable: true }
|
||||
return { configurable: true, enumerable: true };
|
||||
}
|
||||
}))
|
||||
}));
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const url = require('url')
|
||||
const { EventEmitter } = require('events')
|
||||
const { Readable } = require('stream')
|
||||
const { app } = require('electron')
|
||||
const { Session } = process.electronBinding('session')
|
||||
const { net, Net } = process.electronBinding('net')
|
||||
const { URLRequest } = net
|
||||
const url = require('url');
|
||||
const { EventEmitter } = require('events');
|
||||
const { Readable, Writable } = require('stream');
|
||||
const { app } = require('electron');
|
||||
const { Session } = process.electronBinding('session');
|
||||
const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net');
|
||||
const { URLLoader } = net;
|
||||
|
||||
// Net is an EventEmitter.
|
||||
Object.setPrototypeOf(Net.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(net)
|
||||
Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype);
|
||||
|
||||
Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype)
|
||||
|
||||
const kSupportedProtocols = new Set(['http:', 'https:'])
|
||||
const kSupportedProtocols = new Set(['http:', 'https:']);
|
||||
|
||||
// set of headers that Node.js discards duplicates for
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
@@ -37,375 +33,445 @@ const discardableDuplicateHeaders = new Set([
|
||||
'server',
|
||||
'age',
|
||||
'expires'
|
||||
])
|
||||
]);
|
||||
|
||||
class IncomingMessage extends Readable {
|
||||
constructor (urlRequest) {
|
||||
super()
|
||||
this.urlRequest = urlRequest
|
||||
this.shouldPush = false
|
||||
this.data = []
|
||||
this.urlRequest.on('data', (event, chunk) => {
|
||||
this._storeInternalData(chunk)
|
||||
this._pushInternalData()
|
||||
})
|
||||
this.urlRequest.on('end', () => {
|
||||
this._storeInternalData(null)
|
||||
this._pushInternalData()
|
||||
})
|
||||
constructor (responseHead) {
|
||||
super();
|
||||
this._shouldPush = false;
|
||||
this._data = [];
|
||||
this._responseHead = responseHead;
|
||||
}
|
||||
|
||||
get statusCode () {
|
||||
return this.urlRequest.statusCode
|
||||
return this._responseHead.statusCode;
|
||||
}
|
||||
|
||||
get statusMessage () {
|
||||
return this.urlRequest.statusMessage
|
||||
return this._responseHead.statusMessage;
|
||||
}
|
||||
|
||||
get headers () {
|
||||
const filteredHeaders = {}
|
||||
const rawHeaders = this.urlRequest.rawResponseHeaders
|
||||
Object.keys(rawHeaders).forEach(header => {
|
||||
if (header in filteredHeaders && discardableDuplicateHeaders.has(header)) {
|
||||
const filteredHeaders = {};
|
||||
const { rawHeaders } = this._responseHead;
|
||||
rawHeaders.forEach(header => {
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) &&
|
||||
discardableDuplicateHeaders.has(header.key)) {
|
||||
// do nothing with discardable duplicate headers
|
||||
} else {
|
||||
if (header === 'set-cookie') {
|
||||
if (header.key === 'set-cookie') {
|
||||
// keep set-cookie as an array per Node.js rules
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
filteredHeaders[header] = rawHeaders[header]
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||
filteredHeaders[header.key].push(header.value);
|
||||
} else {
|
||||
filteredHeaders[header.key] = [header.value];
|
||||
}
|
||||
} else {
|
||||
// for non-cookie headers, the values are joined together with ', '
|
||||
filteredHeaders[header] = rawHeaders[header].join(', ')
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||
filteredHeaders[header.key] += `, ${header.value}`;
|
||||
} else {
|
||||
filteredHeaders[header.key] = header.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return filteredHeaders
|
||||
});
|
||||
return filteredHeaders;
|
||||
}
|
||||
|
||||
get httpVersion () {
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
|
||||
}
|
||||
|
||||
get httpVersionMajor () {
|
||||
return this.urlRequest.httpVersionMajor
|
||||
return this._responseHead.httpVersion.major;
|
||||
}
|
||||
|
||||
get httpVersionMinor () {
|
||||
return this.urlRequest.httpVersionMinor
|
||||
return this._responseHead.httpVersion.minor;
|
||||
}
|
||||
|
||||
get rawTrailers () {
|
||||
throw new Error('HTTP trailers are not supported')
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
}
|
||||
|
||||
get trailers () {
|
||||
throw new Error('HTTP trailers are not supported')
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
}
|
||||
|
||||
_storeInternalData (chunk) {
|
||||
this.data.push(chunk)
|
||||
this._data.push(chunk);
|
||||
this._pushInternalData();
|
||||
}
|
||||
|
||||
_pushInternalData () {
|
||||
while (this.shouldPush && this.data.length > 0) {
|
||||
const chunk = this.data.shift()
|
||||
this.shouldPush = this.push(chunk)
|
||||
while (this._shouldPush && this._data.length > 0) {
|
||||
const chunk = this._data.shift();
|
||||
this._shouldPush = this.push(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
_read () {
|
||||
this.shouldPush = true
|
||||
this._pushInternalData()
|
||||
this._shouldPush = true;
|
||||
this._pushInternalData();
|
||||
}
|
||||
}
|
||||
|
||||
URLRequest.prototype._emitRequestEvent = function (isAsync, ...rest) {
|
||||
if (isAsync) {
|
||||
process.nextTick(() => {
|
||||
this.clientRequest.emit(...rest)
|
||||
})
|
||||
/** Writable stream that buffers up everything written to it. */
|
||||
class SlurpStream extends Writable {
|
||||
constructor () {
|
||||
super();
|
||||
this._data = Buffer.alloc(0);
|
||||
}
|
||||
_write (chunk, encoding, callback) {
|
||||
this._data = Buffer.concat([this._data, chunk]);
|
||||
callback();
|
||||
}
|
||||
data () { return this._data; }
|
||||
}
|
||||
|
||||
class ChunkedBodyStream extends Writable {
|
||||
constructor (clientRequest) {
|
||||
super();
|
||||
this._clientRequest = clientRequest;
|
||||
}
|
||||
|
||||
_write (chunk, encoding, callback) {
|
||||
if (this._downstream) {
|
||||
this._downstream.write(chunk).then(callback, callback);
|
||||
} else {
|
||||
// the contract of _write is that we won't be called again until we call
|
||||
// the callback, so we're good to just save a single chunk.
|
||||
this._pendingChunk = chunk;
|
||||
this._pendingCallback = callback;
|
||||
|
||||
// The first write to a chunked body stream begins the request.
|
||||
this._clientRequest._startRequest();
|
||||
}
|
||||
}
|
||||
|
||||
_final (callback) {
|
||||
this._downstream.done();
|
||||
callback();
|
||||
}
|
||||
|
||||
startReading (pipe) {
|
||||
if (this._downstream) {
|
||||
throw new Error('two startReading calls???');
|
||||
}
|
||||
this._downstream = pipe;
|
||||
if (this._pendingChunk) {
|
||||
const doneWriting = (maybeError) => {
|
||||
const cb = this._pendingCallback;
|
||||
delete this._pendingCallback;
|
||||
delete this._pendingChunk;
|
||||
cb(maybeError);
|
||||
};
|
||||
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseOptions (options) {
|
||||
if (typeof options === 'string') {
|
||||
options = url.parse(options);
|
||||
} else {
|
||||
this.clientRequest.emit(...rest)
|
||||
options = { ...options };
|
||||
}
|
||||
|
||||
const method = (options.method || 'GET').toUpperCase();
|
||||
let urlStr = options.url;
|
||||
|
||||
if (!urlStr) {
|
||||
const urlObj = {};
|
||||
const protocol = options.protocol || 'http:';
|
||||
if (!kSupportedProtocols.has(protocol)) {
|
||||
throw new Error('Protocol "' + protocol + '" not supported');
|
||||
}
|
||||
urlObj.protocol = protocol;
|
||||
|
||||
if (options.host) {
|
||||
urlObj.host = options.host;
|
||||
} else {
|
||||
if (options.hostname) {
|
||||
urlObj.hostname = options.hostname;
|
||||
} else {
|
||||
urlObj.hostname = 'localhost';
|
||||
}
|
||||
|
||||
if (options.port) {
|
||||
urlObj.port = options.port;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.path && / /.test(options.path)) {
|
||||
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
|
||||
// with an additional rule for ignoring percentage-escaped characters
|
||||
// but that's a) hard to capture in a regular expression that performs
|
||||
// well, and b) possibly too restrictive for real-world usage. That's
|
||||
// why it only scans for spaces because those are guaranteed to create
|
||||
// an invalid request.
|
||||
throw new TypeError('Request path contains unescaped characters');
|
||||
}
|
||||
const pathObj = url.parse(options.path || '/');
|
||||
urlObj.pathname = pathObj.pathname;
|
||||
urlObj.search = pathObj.search;
|
||||
urlObj.hash = pathObj.hash;
|
||||
urlStr = url.format(urlObj);
|
||||
}
|
||||
|
||||
const redirectPolicy = options.redirect || 'follow';
|
||||
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
|
||||
throw new Error('redirect mode should be one of follow, error or manual');
|
||||
}
|
||||
|
||||
if (options.headers != null && typeof options.headers !== 'object') {
|
||||
throw new TypeError('headers must be an object');
|
||||
}
|
||||
|
||||
const urlLoaderOptions = {
|
||||
method: method,
|
||||
url: urlStr,
|
||||
redirectPolicy,
|
||||
extraHeaders: options.headers || {},
|
||||
useSessionCookies: options.useSessionCookies || false
|
||||
};
|
||||
for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders)) {
|
||||
if (!_isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!_isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
}
|
||||
if (options.session) {
|
||||
if (options.session instanceof Session) {
|
||||
urlLoaderOptions.session = options.session;
|
||||
} else {
|
||||
throw new TypeError('`session` should be an instance of the Session class');
|
||||
}
|
||||
} else if (options.partition) {
|
||||
if (typeof options.partition === 'string') {
|
||||
urlLoaderOptions.partition = options.partition;
|
||||
} else {
|
||||
throw new TypeError('`partition` should be a string');
|
||||
}
|
||||
}
|
||||
return urlLoaderOptions;
|
||||
}
|
||||
|
||||
URLRequest.prototype._emitResponseEvent = function (isAsync, ...rest) {
|
||||
if (isAsync) {
|
||||
process.nextTick(() => {
|
||||
this._response.emit(...rest)
|
||||
})
|
||||
} else {
|
||||
this._response.emit(...rest)
|
||||
}
|
||||
}
|
||||
|
||||
class ClientRequest extends EventEmitter {
|
||||
class ClientRequest extends Writable {
|
||||
constructor (options, callback) {
|
||||
super()
|
||||
super({ autoDestroy: true });
|
||||
|
||||
if (!app.isReady()) {
|
||||
throw new Error('net module can only be used after app is ready')
|
||||
throw new Error('net module can only be used after app is ready');
|
||||
}
|
||||
|
||||
if (typeof options === 'string') {
|
||||
options = url.parse(options)
|
||||
} else {
|
||||
options = Object.assign({}, options)
|
||||
}
|
||||
|
||||
const method = (options.method || 'GET').toUpperCase()
|
||||
let urlStr = options.url
|
||||
|
||||
if (!urlStr) {
|
||||
const urlObj = {}
|
||||
const protocol = options.protocol || 'http:'
|
||||
if (!kSupportedProtocols.has(protocol)) {
|
||||
throw new Error('Protocol "' + protocol + '" not supported')
|
||||
}
|
||||
urlObj.protocol = protocol
|
||||
|
||||
if (options.host) {
|
||||
urlObj.host = options.host
|
||||
} else {
|
||||
if (options.hostname) {
|
||||
urlObj.hostname = options.hostname
|
||||
} else {
|
||||
urlObj.hostname = 'localhost'
|
||||
}
|
||||
|
||||
if (options.port) {
|
||||
urlObj.port = options.port
|
||||
}
|
||||
}
|
||||
|
||||
if (options.path && / /.test(options.path)) {
|
||||
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
|
||||
// with an additional rule for ignoring percentage-escaped characters
|
||||
// but that's a) hard to capture in a regular expression that performs
|
||||
// well, and b) possibly too restrictive for real-world usage. That's
|
||||
// why it only scans for spaces because those are guaranteed to create
|
||||
// an invalid request.
|
||||
throw new TypeError('Request path contains unescaped characters')
|
||||
}
|
||||
const pathObj = url.parse(options.path || '/')
|
||||
urlObj.pathname = pathObj.pathname
|
||||
urlObj.search = pathObj.search
|
||||
urlObj.hash = pathObj.hash
|
||||
urlStr = url.format(urlObj)
|
||||
}
|
||||
|
||||
const redirectPolicy = options.redirect || 'follow'
|
||||
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
|
||||
throw new Error('redirect mode should be one of follow, error or manual')
|
||||
}
|
||||
|
||||
const urlRequestOptions = {
|
||||
method: method,
|
||||
url: urlStr,
|
||||
redirect: redirectPolicy
|
||||
}
|
||||
if (options.session) {
|
||||
if (options.session instanceof Session) {
|
||||
urlRequestOptions.session = options.session
|
||||
} else {
|
||||
throw new TypeError('`session` should be an instance of the Session class')
|
||||
}
|
||||
} else if (options.partition) {
|
||||
if (typeof options.partition === 'string') {
|
||||
urlRequestOptions.partition = options.partition
|
||||
} else {
|
||||
throw new TypeError('`partition` should be a string')
|
||||
}
|
||||
}
|
||||
|
||||
const urlRequest = new URLRequest(urlRequestOptions)
|
||||
|
||||
// Set back and forward links.
|
||||
this.urlRequest = urlRequest
|
||||
urlRequest.clientRequest = this
|
||||
|
||||
// This is a copy of the extra headers structure held by the native
|
||||
// net::URLRequest. The main reason is to keep the getHeader API synchronous
|
||||
// after the request starts.
|
||||
this.extraHeaders = {}
|
||||
|
||||
if (options.headers) {
|
||||
for (const key in options.headers) {
|
||||
this.setHeader(key, options.headers[key])
|
||||
}
|
||||
}
|
||||
|
||||
// Set when the request uses chunked encoding. Can be switched
|
||||
// to true only once and never set back to false.
|
||||
this.chunkedEncodingEnabled = false
|
||||
|
||||
urlRequest.on('response', () => {
|
||||
const response = new IncomingMessage(urlRequest)
|
||||
urlRequest._response = response
|
||||
this.emit('response', response)
|
||||
})
|
||||
|
||||
urlRequest.on('login', (event, authInfo, callback) => {
|
||||
this.emit('login', authInfo, (username, password) => {
|
||||
// If null or undefined username/password, force to empty string.
|
||||
if (username === null || username === undefined) {
|
||||
username = ''
|
||||
}
|
||||
if (typeof username !== 'string') {
|
||||
throw new Error('username must be a string')
|
||||
}
|
||||
if (password === null || password === undefined) {
|
||||
password = ''
|
||||
}
|
||||
if (typeof password !== 'string') {
|
||||
throw new Error('password must be a string')
|
||||
}
|
||||
callback(username, password)
|
||||
})
|
||||
})
|
||||
|
||||
if (callback) {
|
||||
this.once('response', callback)
|
||||
this.once('response', callback);
|
||||
}
|
||||
}
|
||||
|
||||
get chunkedEncoding () {
|
||||
return this.chunkedEncodingEnabled
|
||||
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options);
|
||||
this._urlLoaderOptions = urlLoaderOptions;
|
||||
this._redirectPolicy = redirectPolicy;
|
||||
this._started = false;
|
||||
}
|
||||
|
||||
set chunkedEncoding (value) {
|
||||
if (!this.urlRequest.notStarted) {
|
||||
throw new Error('Can\'t set the transfer encoding, headers have been sent')
|
||||
if (this._started) {
|
||||
throw new Error('chunkedEncoding can only be set before the request is started');
|
||||
}
|
||||
if (typeof this._chunkedEncoding !== 'undefined') {
|
||||
throw new Error('chunkedEncoding can only be set once');
|
||||
}
|
||||
this._chunkedEncoding = !!value;
|
||||
if (this._chunkedEncoding) {
|
||||
this._body = new ChunkedBodyStream(this);
|
||||
this._urlLoaderOptions.body = (pipe) => {
|
||||
this._body.startReading(pipe);
|
||||
};
|
||||
}
|
||||
this.chunkedEncodingEnabled = value
|
||||
}
|
||||
|
||||
setHeader (name, value) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)')
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)');
|
||||
}
|
||||
if (value == null) {
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)')
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)');
|
||||
}
|
||||
if (!this.urlRequest.notStarted) {
|
||||
throw new Error('Can\'t set headers after they are sent')
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t set headers after they are sent');
|
||||
}
|
||||
if (!_isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!_isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
|
||||
const key = name.toLowerCase()
|
||||
this.extraHeaders[key] = value
|
||||
this.urlRequest.setExtraHeader(name, value.toString())
|
||||
const key = name.toLowerCase();
|
||||
this._urlLoaderOptions.extraHeaders[key] = value;
|
||||
}
|
||||
|
||||
getHeader (name) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for getHeader(name)')
|
||||
throw new Error('`name` is required for getHeader(name)');
|
||||
}
|
||||
|
||||
if (!this.extraHeaders) {
|
||||
return
|
||||
}
|
||||
|
||||
const key = name.toLowerCase()
|
||||
return this.extraHeaders[key]
|
||||
const key = name.toLowerCase();
|
||||
return this._urlLoaderOptions.extraHeaders[key];
|
||||
}
|
||||
|
||||
removeHeader (name) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for removeHeader(name)')
|
||||
throw new Error('`name` is required for removeHeader(name)');
|
||||
}
|
||||
|
||||
if (!this.urlRequest.notStarted) {
|
||||
throw new Error('Can\'t remove headers after they are sent')
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t remove headers after they are sent');
|
||||
}
|
||||
|
||||
const key = name.toLowerCase()
|
||||
delete this.extraHeaders[key]
|
||||
this.urlRequest.removeExtraHeader(name)
|
||||
const key = name.toLowerCase();
|
||||
delete this._urlLoaderOptions.extraHeaders[key];
|
||||
}
|
||||
|
||||
_write (chunk, encoding, callback, isLast) {
|
||||
const chunkIsString = typeof chunk === 'string'
|
||||
const chunkIsBuffer = chunk instanceof Buffer
|
||||
if (!chunkIsString && !chunkIsBuffer) {
|
||||
throw new TypeError('First argument must be a string or Buffer')
|
||||
_write (chunk, encoding, callback) {
|
||||
this._firstWrite = true;
|
||||
if (!this._body) {
|
||||
this._body = new SlurpStream();
|
||||
this._body.on('finish', () => {
|
||||
this._urlLoaderOptions.body = this._body.data();
|
||||
this._startRequest();
|
||||
});
|
||||
}
|
||||
|
||||
if (chunkIsString) {
|
||||
// We convert all strings into binary buffers.
|
||||
chunk = Buffer.from(chunk, encoding)
|
||||
}
|
||||
|
||||
// Since writing to the network is asynchronous, we conservatively
|
||||
// assume that request headers are written after delivering the first
|
||||
// buffer to the network IO thread.
|
||||
if (this.urlRequest.notStarted) {
|
||||
this.urlRequest.setChunkedUpload(this.chunkedEncoding)
|
||||
}
|
||||
|
||||
// Headers are assumed to be sent on first call to _writeBuffer,
|
||||
// i.e. after the first call to write or end.
|
||||
const result = this.urlRequest.write(chunk, isLast)
|
||||
|
||||
// The write callback is fired asynchronously to mimic Node.js.
|
||||
if (callback) {
|
||||
process.nextTick(callback)
|
||||
}
|
||||
|
||||
return result
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.write(chunk, encoding, callback);
|
||||
}
|
||||
|
||||
write (data, encoding, callback) {
|
||||
if (this.urlRequest.finished) {
|
||||
const error = new Error('Write after end')
|
||||
process.nextTick(writeAfterEndNT, this, error, callback)
|
||||
return true
|
||||
_final (callback) {
|
||||
if (this._body) {
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.end(callback);
|
||||
} else {
|
||||
// end() called without a body, go ahead and start the request
|
||||
this._startRequest();
|
||||
callback();
|
||||
}
|
||||
|
||||
return this._write(data, encoding, callback, false)
|
||||
}
|
||||
|
||||
end (data, encoding, callback) {
|
||||
if (this.urlRequest.finished) {
|
||||
return false
|
||||
}
|
||||
_startRequest () {
|
||||
this._started = true;
|
||||
const stringifyValues = (obj) => {
|
||||
const ret = {};
|
||||
for (const k of Object.keys(obj)) {
|
||||
ret[k] = obj[k].toString();
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) };
|
||||
this._urlLoader = new URLLoader(opts);
|
||||
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
|
||||
const response = this._response = new IncomingMessage(responseHead);
|
||||
this.emit('response', response);
|
||||
});
|
||||
this._urlLoader.on('data', (event, data) => {
|
||||
this._response._storeInternalData(Buffer.from(data));
|
||||
});
|
||||
this._urlLoader.on('complete', () => {
|
||||
if (this._response) { this._response._storeInternalData(null); }
|
||||
});
|
||||
this._urlLoader.on('error', (event, netErrorString) => {
|
||||
const error = new Error(netErrorString);
|
||||
if (this._response) this._response.destroy(error);
|
||||
this._die(error);
|
||||
});
|
||||
|
||||
if (typeof data === 'function') {
|
||||
callback = data
|
||||
encoding = null
|
||||
data = null
|
||||
} else if (typeof encoding === 'function') {
|
||||
callback = encoding
|
||||
encoding = null
|
||||
}
|
||||
this._urlLoader.on('login', (event, authInfo, callback) => {
|
||||
const handled = this.emit('login', authInfo, callback);
|
||||
if (!handled) {
|
||||
// If there were no listeners, cancel the authentication request.
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
||||
data = data || ''
|
||||
this._urlLoader.on('redirect', (event, redirectInfo, headers) => {
|
||||
const { statusCode, newMethod, newUrl } = redirectInfo;
|
||||
if (this._redirectPolicy === 'error') {
|
||||
this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`));
|
||||
} else if (this._redirectPolicy === 'manual') {
|
||||
let _followRedirect = false;
|
||||
this._followRedirectCb = () => { _followRedirect = true; };
|
||||
try {
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers);
|
||||
} finally {
|
||||
this._followRedirectCb = null;
|
||||
if (!_followRedirect && !this._aborted) {
|
||||
this._die(new Error('Redirect was cancelled'));
|
||||
}
|
||||
}
|
||||
} else if (this._redirectPolicy === 'follow') {
|
||||
// Calling followRedirect() when the redirect policy is 'follow' is
|
||||
// allowed but does nothing. (Perhaps it should throw an error
|
||||
// though...? Since the redirect will happen regardless.)
|
||||
try {
|
||||
this._followRedirectCb = () => {};
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers);
|
||||
} finally {
|
||||
this._followRedirectCb = null;
|
||||
}
|
||||
} else {
|
||||
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`));
|
||||
}
|
||||
});
|
||||
|
||||
return this._write(data, encoding, callback, true)
|
||||
this._urlLoader.on('upload-progress', (event, position, total) => {
|
||||
this._uploadProgress = { active: true, started: true, current: position, total };
|
||||
this.emit('upload-progress', position, total); // Undocumented, for now
|
||||
});
|
||||
|
||||
this._urlLoader.on('download-progress', (event, current) => {
|
||||
if (this._response) {
|
||||
this._response.emit('download-progress', current); // Undocumented, for now
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
followRedirect () {
|
||||
this.urlRequest.followRedirect()
|
||||
if (this._followRedirectCb) {
|
||||
this._followRedirectCb();
|
||||
} else {
|
||||
throw new Error('followRedirect() called, but was not waiting for a redirect');
|
||||
}
|
||||
}
|
||||
|
||||
abort () {
|
||||
this.urlRequest.cancel()
|
||||
if (!this._aborted) {
|
||||
process.nextTick(() => { this.emit('abort'); });
|
||||
}
|
||||
this._aborted = true;
|
||||
this._die();
|
||||
}
|
||||
|
||||
_die (err) {
|
||||
this.destroy(err);
|
||||
if (this._urlLoader) {
|
||||
this._urlLoader.cancel();
|
||||
if (this._response) this._response.destroy(err);
|
||||
}
|
||||
}
|
||||
|
||||
getUploadProgress () {
|
||||
return this.urlRequest.getUploadProgress()
|
||||
return this._uploadProgress ? { ...this._uploadProgress } : { active: false };
|
||||
}
|
||||
}
|
||||
|
||||
function writeAfterEndNT (self, error, callback) {
|
||||
self.emit('error', error)
|
||||
if (callback) callback(error)
|
||||
}
|
||||
|
||||
Net.prototype.request = function (options, callback) {
|
||||
return new ClientRequest(options, callback)
|
||||
}
|
||||
return new ClientRequest(options, callback);
|
||||
};
|
||||
|
||||
net.ClientRequest = ClientRequest
|
||||
net.ClientRequest = ClientRequest;
|
||||
|
||||
module.exports = net
|
||||
module.exports = net;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { Notification, isSupported } = process.electronBinding('notification')
|
||||
const { EventEmitter } = require('events');
|
||||
const { Notification, isSupported } = process.electronBinding('notification');
|
||||
|
||||
Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype);
|
||||
|
||||
Notification.isSupported = isSupported
|
||||
Notification.isSupported = isSupported;
|
||||
|
||||
module.exports = Notification
|
||||
module.exports = Notification;
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
import { createLazyInstance } from '../utils'
|
||||
import { createLazyInstance } from '../utils';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor')
|
||||
const { EventEmitter } = require('events');
|
||||
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor');
|
||||
|
||||
// PowerMonitor is an EventEmitter.
|
||||
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype);
|
||||
|
||||
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true)
|
||||
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true);
|
||||
|
||||
// On Linux we need to call blockShutdown() to subscribe to shutdown event.
|
||||
if (process.platform === 'linux') {
|
||||
powerMonitor.on('newListener', (event:string) => {
|
||||
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
|
||||
powerMonitor.blockShutdown()
|
||||
}
|
||||
})
|
||||
|
||||
powerMonitor.on('removeListener', (event: string) => {
|
||||
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
|
||||
powerMonitor.unblockShutdown()
|
||||
}
|
||||
})
|
||||
// In order to delay system shutdown when e.preventDefault() is invoked
|
||||
// on a powerMonitor 'shutdown' event, we need an org.freedesktop.login1
|
||||
// shutdown delay lock. For more details see the "Taking Delay Locks"
|
||||
// section of https://www.freedesktop.org/wiki/Software/systemd/inhibit/
|
||||
//
|
||||
// So here we watch for 'shutdown' listeners to be added or removed and
|
||||
// set or unset our shutdown delay lock accordingly.
|
||||
const { app } = require('electron');
|
||||
app.whenReady().then(() => {
|
||||
powerMonitor.on('newListener', (event: string) => {
|
||||
// whenever the listener count is incremented to one...
|
||||
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
|
||||
powerMonitor.blockShutdown();
|
||||
}
|
||||
});
|
||||
powerMonitor.on('removeListener', (event: string) => {
|
||||
// whenever the listener count is decremented to zero...
|
||||
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
|
||||
powerMonitor.unblockShutdown();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = powerMonitor
|
||||
module.exports = powerMonitor;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker
|
||||
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker;
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import { app, session } from 'electron'
|
||||
import { app, session } from 'electron';
|
||||
|
||||
// Global protocol APIs.
|
||||
const protocol = process.electronBinding('protocol')
|
||||
const protocol = process.electronBinding('protocol');
|
||||
|
||||
// Fallback protocol APIs of default session.
|
||||
Object.setPrototypeOf(protocol, new Proxy({}, {
|
||||
get (_target, property) {
|
||||
if (!app.isReady()) return
|
||||
if (!app.isReady()) return;
|
||||
|
||||
const protocol = session.defaultSession!.protocol
|
||||
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return
|
||||
const protocol = session.defaultSession!.protocol;
|
||||
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return;
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args)
|
||||
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args);
|
||||
},
|
||||
|
||||
ownKeys () {
|
||||
if (!app.isReady()) return []
|
||||
if (!app.isReady()) return [];
|
||||
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol))
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol));
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor () {
|
||||
return { configurable: true, enumerable: true }
|
||||
return { configurable: true, enumerable: true };
|
||||
}
|
||||
}))
|
||||
}));
|
||||
|
||||
export default protocol
|
||||
export default protocol;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
import { createLazyInstance } from '../utils'
|
||||
const { EventEmitter } = require('events')
|
||||
const { Screen, createScreen } = process.electronBinding('screen')
|
||||
import { createLazyInstance } from '../utils';
|
||||
const { EventEmitter } = require('events');
|
||||
const { Screen, createScreen } = process.electronBinding('screen');
|
||||
|
||||
// Screen is an EventEmitter.
|
||||
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
|
||||
|
||||
module.exports = createLazyInstance(createScreen, Screen, true)
|
||||
module.exports = createLazyInstance(createScreen, Screen, true);
|
||||
|
||||
@@ -1,52 +1,53 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { app, deprecate } = require('electron')
|
||||
const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session')
|
||||
const { EventEmitter } = require('events');
|
||||
const { app, deprecate } = require('electron');
|
||||
const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session');
|
||||
|
||||
// Public API.
|
||||
Object.defineProperties(exports, {
|
||||
defaultSession: {
|
||||
enumerable: true,
|
||||
get () { return fromPartition('') }
|
||||
get () { return fromPartition(''); }
|
||||
},
|
||||
fromPartition: {
|
||||
enumerable: true,
|
||||
value: fromPartition
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype);
|
||||
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype);
|
||||
|
||||
Session.prototype._init = function () {
|
||||
app.emit('session-created', this)
|
||||
}
|
||||
app.emit('session-created', this);
|
||||
};
|
||||
|
||||
const _originalStartLogging = NetLog.prototype.startLogging
|
||||
const _originalStartLogging = NetLog.prototype.startLogging;
|
||||
NetLog.prototype.startLogging = function (path, ...args) {
|
||||
this._currentlyLoggingPath = path
|
||||
this._currentlyLoggingPath = path;
|
||||
try {
|
||||
return _originalStartLogging.call(this, path, ...args)
|
||||
return _originalStartLogging.call(this, path, ...args);
|
||||
} catch (e) {
|
||||
this._currentlyLoggingPath = null
|
||||
throw e
|
||||
this._currentlyLoggingPath = null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const _originalStopLogging = NetLog.prototype.stopLogging
|
||||
const _originalStopLogging = NetLog.prototype.stopLogging;
|
||||
NetLog.prototype.stopLogging = function () {
|
||||
this._currentlyLoggingPath = null
|
||||
return _originalStopLogging.call(this)
|
||||
}
|
||||
const logPath = this._currentlyLoggingPath;
|
||||
this._currentlyLoggingPath = null;
|
||||
return _originalStopLogging.call(this).then(() => logPath);
|
||||
};
|
||||
|
||||
const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath')
|
||||
const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath');
|
||||
Object.defineProperties(NetLog.prototype, {
|
||||
currentlyLoggingPath: {
|
||||
enumerable: true,
|
||||
get () {
|
||||
currentlyLoggingPathDeprecated()
|
||||
return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath
|
||||
currentlyLoggingPathDeprecated();
|
||||
return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { deprecate } = require('electron')
|
||||
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences')
|
||||
|
||||
// SystemPreferences is an EventEmitter.
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(systemPreferences)
|
||||
|
||||
if ('appLevelAppearance' in systemPreferences) {
|
||||
deprecate.fnToProperty(SystemPreferences.prototype, 'appLevelAppearance', '_getAppLevelAppearance', '_setAppLevelAppearance')
|
||||
}
|
||||
|
||||
module.exports = systemPreferences
|
||||
42
lib/browser/api/system-preferences.ts
Normal file
42
lib/browser/api/system-preferences.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { deprecate } from 'electron';
|
||||
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences');
|
||||
|
||||
// SystemPreferences is an EventEmitter.
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(systemPreferences);
|
||||
|
||||
if ('appLevelAppearance' in systemPreferences) {
|
||||
deprecate.fnToProperty(
|
||||
SystemPreferences.prototype,
|
||||
'appLevelAppearance',
|
||||
'_getAppLevelAppearance',
|
||||
'_setAppLevelAppearance'
|
||||
);
|
||||
}
|
||||
|
||||
if ('effectiveAppearance' in systemPreferences) {
|
||||
deprecate.fnToProperty(
|
||||
SystemPreferences.prototype,
|
||||
'effectiveAppearance',
|
||||
'_getEffectiveAppearance'
|
||||
);
|
||||
}
|
||||
|
||||
SystemPreferences.prototype.isDarkMode = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isDarkMode,
|
||||
'systemPreferences.isDarkMode()',
|
||||
'nativeTheme.shouldUseDarkColors'
|
||||
);
|
||||
SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isInvertedColorScheme,
|
||||
'systemPreferences.isInvertedColorScheme()',
|
||||
'nativeTheme.shouldUseInvertedColorScheme'
|
||||
);
|
||||
SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isHighContrastColorScheme,
|
||||
'systemPreferences.isHighContrastColorScheme()',
|
||||
'nativeTheme.shouldUseHighContrastColors'
|
||||
);
|
||||
|
||||
module.exports = systemPreferences;
|
||||
@@ -1,24 +1,24 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const { EventEmitter } = require('events')
|
||||
const { TopLevelWindow } = process.electronBinding('top_level_window')
|
||||
const electron = require('electron');
|
||||
const { EventEmitter } = require('events');
|
||||
const { TopLevelWindow } = process.electronBinding('top_level_window');
|
||||
|
||||
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype);
|
||||
|
||||
TopLevelWindow.prototype._init = function () {
|
||||
// Avoid recursive require.
|
||||
const { app } = electron
|
||||
const { app } = electron;
|
||||
|
||||
// Simulate the application menu on platforms other than macOS.
|
||||
if (process.platform !== 'darwin') {
|
||||
const menu = app.applicationMenu
|
||||
if (menu) this.setMenu(menu)
|
||||
const menu = app.applicationMenu;
|
||||
if (menu) this.setMenu(menu);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TopLevelWindow.getFocusedWindow = () => {
|
||||
return TopLevelWindow.getAllWindows().find((win) => win.isFocused())
|
||||
}
|
||||
return TopLevelWindow.getAllWindows().find((win) => win.isFocused());
|
||||
};
|
||||
|
||||
module.exports = TopLevelWindow
|
||||
module.exports = TopLevelWindow;
|
||||
|
||||
@@ -1,337 +1,337 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { EventEmitter } = require('events');
|
||||
|
||||
let nextItemID = 1
|
||||
let nextItemID = 1;
|
||||
|
||||
class TouchBar extends EventEmitter {
|
||||
// Bind a touch bar to a window
|
||||
static _setOnWindow (touchBar, window) {
|
||||
if (window._touchBar != null) {
|
||||
window._touchBar._removeFromWindow(window)
|
||||
window._touchBar._removeFromWindow(window);
|
||||
}
|
||||
|
||||
if (touchBar == null) {
|
||||
window._setTouchBarItems([])
|
||||
return
|
||||
window._setTouchBarItems([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(touchBar)) {
|
||||
touchBar = new TouchBar(touchBar)
|
||||
touchBar = new TouchBar(touchBar);
|
||||
}
|
||||
touchBar._addToWindow(window)
|
||||
touchBar._addToWindow(window);
|
||||
}
|
||||
|
||||
constructor (options) {
|
||||
super()
|
||||
super();
|
||||
|
||||
if (options == null) {
|
||||
throw new Error('Must specify options object as first argument')
|
||||
throw new Error('Must specify options object as first argument');
|
||||
}
|
||||
|
||||
let { items, escapeItem } = options
|
||||
let { items, escapeItem } = options;
|
||||
|
||||
if (!Array.isArray(items)) {
|
||||
items = []
|
||||
items = [];
|
||||
}
|
||||
|
||||
this.changeListener = (item) => {
|
||||
this.emit('change', item.id, item.type)
|
||||
}
|
||||
this.emit('change', item.id, item.type);
|
||||
};
|
||||
|
||||
this.windowListeners = {}
|
||||
this.items = {}
|
||||
this.ordereredItems = []
|
||||
this.escapeItem = escapeItem
|
||||
this.windowListeners = {};
|
||||
this.items = {};
|
||||
this.ordereredItems = [];
|
||||
this.escapeItem = escapeItem;
|
||||
|
||||
const registerItem = (item) => {
|
||||
this.items[item.id] = item
|
||||
item.on('change', this.changeListener)
|
||||
this.items[item.id] = item;
|
||||
item.on('change', this.changeListener);
|
||||
if (item.child instanceof TouchBar) {
|
||||
item.child.ordereredItems.forEach(registerItem)
|
||||
item.child.ordereredItems.forEach(registerItem);
|
||||
}
|
||||
}
|
||||
};
|
||||
items.forEach((item) => {
|
||||
if (!(item instanceof TouchBarItem)) {
|
||||
throw new Error('Each item must be an instance of TouchBarItem')
|
||||
throw new Error('Each item must be an instance of TouchBarItem');
|
||||
}
|
||||
this.ordereredItems.push(item)
|
||||
registerItem(item)
|
||||
})
|
||||
this.ordereredItems.push(item);
|
||||
registerItem(item);
|
||||
});
|
||||
}
|
||||
|
||||
set escapeItem (item) {
|
||||
if (item != null && !(item instanceof TouchBarItem)) {
|
||||
throw new Error('Escape item must be an instance of TouchBarItem')
|
||||
throw new Error('Escape item must be an instance of TouchBarItem');
|
||||
}
|
||||
if (this.escapeItem != null) {
|
||||
this.escapeItem.removeListener('change', this.changeListener)
|
||||
this.escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
this._escapeItem = item
|
||||
this._escapeItem = item;
|
||||
if (this.escapeItem != null) {
|
||||
this.escapeItem.on('change', this.changeListener)
|
||||
this.escapeItem.on('change', this.changeListener);
|
||||
}
|
||||
this.emit('escape-item-change', item)
|
||||
this.emit('escape-item-change', item);
|
||||
}
|
||||
|
||||
get escapeItem () {
|
||||
return this._escapeItem
|
||||
return this._escapeItem;
|
||||
}
|
||||
|
||||
_addToWindow (window) {
|
||||
const { id } = window
|
||||
const { id } = window;
|
||||
|
||||
// Already added to window
|
||||
if (this.windowListeners.hasOwnProperty(id)) return
|
||||
if (this.windowListeners.hasOwnProperty(id)) return;
|
||||
|
||||
window._touchBar = this
|
||||
window._touchBar = this;
|
||||
|
||||
const changeListener = (itemID) => {
|
||||
window._refreshTouchBarItem(itemID)
|
||||
}
|
||||
this.on('change', changeListener)
|
||||
window._refreshTouchBarItem(itemID);
|
||||
};
|
||||
this.on('change', changeListener);
|
||||
|
||||
const escapeItemListener = (item) => {
|
||||
window._setEscapeTouchBarItem(item != null ? item : {})
|
||||
}
|
||||
this.on('escape-item-change', escapeItemListener)
|
||||
window._setEscapeTouchBarItem(item != null ? item : {});
|
||||
};
|
||||
this.on('escape-item-change', escapeItemListener);
|
||||
|
||||
const interactionListener = (event, itemID, details) => {
|
||||
let item = this.items[itemID]
|
||||
let item = this.items[itemID];
|
||||
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
|
||||
item = this.escapeItem
|
||||
item = this.escapeItem;
|
||||
}
|
||||
if (item != null && item.onInteraction != null) {
|
||||
item.onInteraction(details)
|
||||
item.onInteraction(details);
|
||||
}
|
||||
}
|
||||
window.on('-touch-bar-interaction', interactionListener)
|
||||
};
|
||||
window.on('-touch-bar-interaction', interactionListener);
|
||||
|
||||
const removeListeners = () => {
|
||||
this.removeListener('change', changeListener)
|
||||
this.removeListener('escape-item-change', escapeItemListener)
|
||||
window.removeListener('-touch-bar-interaction', interactionListener)
|
||||
window.removeListener('closed', removeListeners)
|
||||
window._touchBar = null
|
||||
delete this.windowListeners[id]
|
||||
this.removeListener('change', changeListener);
|
||||
this.removeListener('escape-item-change', escapeItemListener);
|
||||
window.removeListener('-touch-bar-interaction', interactionListener);
|
||||
window.removeListener('closed', removeListeners);
|
||||
window._touchBar = null;
|
||||
delete this.windowListeners[id];
|
||||
const unregisterItems = (items) => {
|
||||
for (const item of items) {
|
||||
item.removeListener('change', this.changeListener)
|
||||
item.removeListener('change', this.changeListener);
|
||||
if (item.child instanceof TouchBar) {
|
||||
unregisterItems(item.child.ordereredItems)
|
||||
unregisterItems(item.child.ordereredItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
unregisterItems(this.ordereredItems)
|
||||
};
|
||||
unregisterItems(this.ordereredItems);
|
||||
if (this.escapeItem) {
|
||||
this.escapeItem.removeListener('change', this.changeListener)
|
||||
this.escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
}
|
||||
window.once('closed', removeListeners)
|
||||
this.windowListeners[id] = removeListeners
|
||||
};
|
||||
window.once('closed', removeListeners);
|
||||
this.windowListeners[id] = removeListeners;
|
||||
|
||||
window._setTouchBarItems(this.ordereredItems)
|
||||
escapeItemListener(this.escapeItem)
|
||||
window._setTouchBarItems(this.ordereredItems);
|
||||
escapeItemListener(this.escapeItem);
|
||||
}
|
||||
|
||||
_removeFromWindow (window) {
|
||||
const removeListeners = this.windowListeners[window.id]
|
||||
if (removeListeners != null) removeListeners()
|
||||
const removeListeners = this.windowListeners[window.id];
|
||||
if (removeListeners != null) removeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class TouchBarItem extends EventEmitter {
|
||||
constructor () {
|
||||
super()
|
||||
this._addImmutableProperty('id', `${nextItemID++}`)
|
||||
this._parents = []
|
||||
super();
|
||||
this._addImmutableProperty('id', `${nextItemID++}`);
|
||||
this._parents = [];
|
||||
}
|
||||
|
||||
_addImmutableProperty (name, value) {
|
||||
Object.defineProperty(this, name, {
|
||||
get: function () {
|
||||
return value
|
||||
return value;
|
||||
},
|
||||
set: function () {
|
||||
throw new Error(`Cannot override property ${name}`)
|
||||
throw new Error(`Cannot override property ${name}`);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
_addLiveProperty (name, initialValue) {
|
||||
const privateName = `_${name}`
|
||||
this[privateName] = initialValue
|
||||
const privateName = `_${name}`;
|
||||
this[privateName] = initialValue;
|
||||
Object.defineProperty(this, name, {
|
||||
get: function () {
|
||||
return this[privateName]
|
||||
return this[privateName];
|
||||
},
|
||||
set: function (value) {
|
||||
this[privateName] = value
|
||||
this.emit('change', this)
|
||||
this[privateName] = value;
|
||||
this.emit('change', this);
|
||||
},
|
||||
enumerable: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
_addParent (item) {
|
||||
const existing = this._parents.some(test => test.id === item.id)
|
||||
const existing = this._parents.some(test => test.id === item.id);
|
||||
if (!existing) {
|
||||
this._parents.push({
|
||||
id: item.id,
|
||||
type: item.type
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'button')
|
||||
const { click, icon, iconPosition, label, backgroundColor } = config
|
||||
this._addLiveProperty('label', label)
|
||||
this._addLiveProperty('backgroundColor', backgroundColor)
|
||||
this._addLiveProperty('icon', icon)
|
||||
this._addLiveProperty('iconPosition', iconPosition)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'button');
|
||||
const { click, icon, iconPosition, label, backgroundColor } = config;
|
||||
this._addLiveProperty('label', label);
|
||||
this._addLiveProperty('backgroundColor', backgroundColor);
|
||||
this._addLiveProperty('icon', icon);
|
||||
this._addLiveProperty('iconPosition', iconPosition);
|
||||
if (typeof click === 'function') {
|
||||
this._addImmutableProperty('onInteraction', () => {
|
||||
config.click()
|
||||
})
|
||||
config.click();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'colorpicker')
|
||||
const { availableColors, change, selectedColor } = config
|
||||
this._addLiveProperty('availableColors', availableColors)
|
||||
this._addLiveProperty('selectedColor', selectedColor)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'colorpicker');
|
||||
const { availableColors, change, selectedColor } = config;
|
||||
this._addLiveProperty('availableColors', availableColors);
|
||||
this._addLiveProperty('selectedColor', selectedColor);
|
||||
|
||||
if (typeof change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
this._selectedColor = details.color
|
||||
change(details.color)
|
||||
})
|
||||
this._selectedColor = details.color;
|
||||
change(details.color);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'group')
|
||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
|
||||
this._addLiveProperty('child', defaultChild)
|
||||
this.child.ordereredItems.forEach((item) => item._addParent(this))
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'group');
|
||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
|
||||
this._addLiveProperty('child', defaultChild);
|
||||
this.child.ordereredItems.forEach((item) => item._addParent(this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'label')
|
||||
this._addLiveProperty('label', config.label)
|
||||
this._addLiveProperty('textColor', config.textColor)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'label');
|
||||
this._addLiveProperty('label', config.label);
|
||||
this._addLiveProperty('textColor', config.textColor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'popover')
|
||||
this._addLiveProperty('label', config.label)
|
||||
this._addLiveProperty('icon', config.icon)
|
||||
this._addLiveProperty('showCloseButton', config.showCloseButton)
|
||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
|
||||
this._addLiveProperty('child', defaultChild)
|
||||
this.child.ordereredItems.forEach((item) => item._addParent(this))
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'popover');
|
||||
this._addLiveProperty('label', config.label);
|
||||
this._addLiveProperty('icon', config.icon);
|
||||
this._addLiveProperty('showCloseButton', config.showCloseButton);
|
||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
|
||||
this._addLiveProperty('child', defaultChild);
|
||||
this.child.ordereredItems.forEach((item) => item._addParent(this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'slider')
|
||||
const { change, label, minValue, maxValue, value } = config
|
||||
this._addLiveProperty('label', label)
|
||||
this._addLiveProperty('minValue', minValue)
|
||||
this._addLiveProperty('maxValue', maxValue)
|
||||
this._addLiveProperty('value', value)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'slider');
|
||||
const { change, label, minValue, maxValue, value } = config;
|
||||
this._addLiveProperty('label', label);
|
||||
this._addLiveProperty('minValue', minValue);
|
||||
this._addLiveProperty('maxValue', maxValue);
|
||||
this._addLiveProperty('value', value);
|
||||
|
||||
if (typeof change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
this._value = details.value
|
||||
change(details.value)
|
||||
})
|
||||
this._value = details.value;
|
||||
change(details.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'spacer')
|
||||
this._addImmutableProperty('size', config.size)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'spacer');
|
||||
this._addImmutableProperty('size', config.size);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
const { segmentStyle, segments, selectedIndex, change, mode } = config
|
||||
this._addImmutableProperty('type', 'segmented_control')
|
||||
this._addLiveProperty('segmentStyle', segmentStyle)
|
||||
this._addLiveProperty('segments', segments || [])
|
||||
this._addLiveProperty('selectedIndex', selectedIndex)
|
||||
this._addLiveProperty('mode', mode)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
const { segmentStyle, segments, selectedIndex, change, mode } = config;
|
||||
this._addImmutableProperty('type', 'segmented_control');
|
||||
this._addLiveProperty('segmentStyle', segmentStyle);
|
||||
this._addLiveProperty('segments', segments || []);
|
||||
this._addLiveProperty('selectedIndex', selectedIndex);
|
||||
this._addLiveProperty('mode', mode);
|
||||
|
||||
if (typeof change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
this._selectedIndex = details.selectedIndex
|
||||
change(details.selectedIndex, details.isSelected)
|
||||
})
|
||||
this._selectedIndex = details.selectedIndex;
|
||||
change(details.selectedIndex, details.isSelected);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
const { items, selectedStyle, overlayStyle, showArrowButtons, continuous, mode } = config
|
||||
let { select, highlight } = config
|
||||
this._addImmutableProperty('type', 'scrubber')
|
||||
this._addLiveProperty('items', items)
|
||||
this._addLiveProperty('selectedStyle', selectedStyle || null)
|
||||
this._addLiveProperty('overlayStyle', overlayStyle || null)
|
||||
this._addLiveProperty('showArrowButtons', showArrowButtons || false)
|
||||
this._addLiveProperty('mode', mode || 'free')
|
||||
this._addLiveProperty('continuous', typeof continuous === 'undefined' ? true : continuous)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
const { items, selectedStyle, overlayStyle, showArrowButtons, continuous, mode } = config;
|
||||
let { select, highlight } = config;
|
||||
this._addImmutableProperty('type', 'scrubber');
|
||||
this._addLiveProperty('items', items);
|
||||
this._addLiveProperty('selectedStyle', selectedStyle || null);
|
||||
this._addLiveProperty('overlayStyle', overlayStyle || null);
|
||||
this._addLiveProperty('showArrowButtons', showArrowButtons || false);
|
||||
this._addLiveProperty('mode', mode || 'free');
|
||||
this._addLiveProperty('continuous', typeof continuous === 'undefined' ? true : continuous);
|
||||
|
||||
if (typeof select === 'function' || typeof highlight === 'function') {
|
||||
if (select == null) select = () => {}
|
||||
if (highlight == null) highlight = () => {}
|
||||
if (select == null) select = () => {};
|
||||
if (highlight == null) highlight = () => {};
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
if (details.type === 'select' && typeof select === 'function') {
|
||||
select(details.selectedIndex)
|
||||
select(details.selectedIndex);
|
||||
} else if (details.type === 'highlight' && typeof highlight === 'function') {
|
||||
highlight(details.highlightedIndex)
|
||||
highlight(details.highlightedIndex);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = TouchBar
|
||||
module.exports = TouchBar;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { deprecate } = require('electron')
|
||||
const { Tray } = process.electronBinding('tray')
|
||||
const { EventEmitter } = require('events');
|
||||
const { deprecate } = require('electron');
|
||||
const { Tray } = process.electronBinding('tray');
|
||||
|
||||
Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype);
|
||||
|
||||
module.exports = Tray
|
||||
module.exports = Tray;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user