mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
263 Commits
7-2-x
...
v8.0.0-nig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e0b87732f | ||
|
|
9abdfc11a9 | ||
|
|
ae6f08d56e | ||
|
|
26014d19e1 | ||
|
|
e83c299454 | ||
|
|
b136819371 | ||
|
|
c46a386970 | ||
|
|
6f2fe7560a | ||
|
|
e459114149 | ||
|
|
624ba4f642 | ||
|
|
63f08fcdb0 | ||
|
|
3f5833ce5d | ||
|
|
ffe2182883 | ||
|
|
e510af77b2 | ||
|
|
afc59aefb7 | ||
|
|
b0fe3e20e6 | ||
|
|
eb2d2264d0 | ||
|
|
104088b86b | ||
|
|
11cd0db86b | ||
|
|
8bf74164e2 | ||
|
|
eaa18041e1 | ||
|
|
d2a94e6b13 | ||
|
|
42999f42f2 | ||
|
|
dfd2135647 | ||
|
|
7c45c2fdef | ||
|
|
2cac40444b | ||
|
|
f566e562a3 | ||
|
|
0e61709fa7 | ||
|
|
2b316f3843 | ||
|
|
660e566201 | ||
|
|
999f7df158 | ||
|
|
816bc5c8a7 | ||
|
|
99f1b96c4f | ||
|
|
334ea36f38 | ||
|
|
c485c1e4b0 | ||
|
|
b2652beceb | ||
|
|
3ec17a88ba | ||
|
|
5dee8a5717 | ||
|
|
ace0284eef | ||
|
|
5ccc043d86 | ||
|
|
fd31a99ef0 | ||
|
|
ff1f224d96 | ||
|
|
0b26d7a0e2 | ||
|
|
5f59c6ddfe | ||
|
|
a9f2a52ce0 | ||
|
|
d84e2e3972 | ||
|
|
49bd74ff0e | ||
|
|
d395799917 | ||
|
|
6e9a23a884 | ||
|
|
0979fb7eee | ||
|
|
9ab06af786 | ||
|
|
6087f89aac | ||
|
|
432ae81823 | ||
|
|
2c23e44ed9 | ||
|
|
7be1905023 | ||
|
|
20e3c519dd | ||
|
|
8a0a41b9ef | ||
|
|
93da8e2b3d | ||
|
|
196fceabdb | ||
|
|
0d16be9560 | ||
|
|
137622931b | ||
|
|
cd096289e9 | ||
|
|
88e32ef231 | ||
|
|
bff16e027a | ||
|
|
a453f2ae6c | ||
|
|
8b523cf88a | ||
|
|
65ebb6e547 | ||
|
|
c29e4b096d | ||
|
|
8cdfd30ba5 | ||
|
|
81ae154714 | ||
|
|
504cdb13f0 | ||
|
|
b601f3a031 | ||
|
|
96b42bddb8 | ||
|
|
cc9436f288 | ||
|
|
bedc5f7da9 | ||
|
|
96c3fec855 | ||
|
|
cad73732c0 | ||
|
|
39475f9404 | ||
|
|
8589ab27a4 | ||
|
|
f537366387 | ||
|
|
d7161742d2 | ||
|
|
6e88b6b445 | ||
|
|
614079654c | ||
|
|
51015c5b48 | ||
|
|
c621a36320 | ||
|
|
e37ad09330 | ||
|
|
bfe256891c | ||
|
|
0f5ff1f5bb | ||
|
|
e96a042223 | ||
|
|
5cbbd489d5 | ||
|
|
805a55099b | ||
|
|
654338693f | ||
|
|
a9e3dabc8a | ||
|
|
609403fba6 | ||
|
|
6b55584923 | ||
|
|
81e6f317c9 | ||
|
|
7d4e0ad7b0 | ||
|
|
35ebbb5f6e | ||
|
|
c819fbe852 | ||
|
|
c03288f458 | ||
|
|
90d62e5b98 | ||
|
|
b3947d6a83 | ||
|
|
eed72c35d7 | ||
|
|
79e936aea8 | ||
|
|
01fdb80f7c | ||
|
|
04debd5890 | ||
|
|
af138dab55 | ||
|
|
f212ed85dd | ||
|
|
99de0975c3 | ||
|
|
41d8247ffc | ||
|
|
217ed9aabc | ||
|
|
538c4763cf | ||
|
|
3bc5302d78 | ||
|
|
987300c97a | ||
|
|
27ce6a9cd3 | ||
|
|
832c926712 | ||
|
|
ae9424d93a | ||
|
|
1dcda7b809 | ||
|
|
bdc84d0bfb | ||
|
|
4b8e1588b4 | ||
|
|
4eee71ffbf | ||
|
|
bdb20d53cb | ||
|
|
7e61cd0dfb | ||
|
|
2542c51c48 | ||
|
|
92c3a4e4c2 | ||
|
|
4e809f0048 | ||
|
|
d04072d1eb | ||
|
|
cddbddc543 | ||
|
|
c57db7f163 | ||
|
|
94c6b256de | ||
|
|
f6c523db13 | ||
|
|
080fdb3817 | ||
|
|
181f663cf1 | ||
|
|
1eda92859f | ||
|
|
f54bc1cb2e | ||
|
|
b7d25ccb77 | ||
|
|
c89debd19a | ||
|
|
ef6d4a46c2 | ||
|
|
81e9dab52f | ||
|
|
698120daf0 | ||
|
|
c61020e9d3 | ||
|
|
6144408f3a | ||
|
|
7825d043f2 | ||
|
|
c6a8b7f959 | ||
|
|
6a3922d330 | ||
|
|
f7e3e1f97a | ||
|
|
ab0bf6d238 | ||
|
|
6667969887 | ||
|
|
12b00ca942 | ||
|
|
1f43af5cea | ||
|
|
268094e0ef | ||
|
|
9ccd6aa0dd | ||
|
|
46b6bcd99b | ||
|
|
4bc7b3b1a4 | ||
|
|
5e525b3e0f | ||
|
|
612e3f7ff7 | ||
|
|
991a56135d | ||
|
|
3bd829aee0 | ||
|
|
674779899e | ||
|
|
3f49f984e6 | ||
|
|
b7defaaf6a | ||
|
|
f10f44acf5 | ||
|
|
d258797d05 | ||
|
|
fb214a599e | ||
|
|
a4c0f26f06 | ||
|
|
145b4fae94 | ||
|
|
cd1b15a155 | ||
|
|
1dc02e6dbc | ||
|
|
0851697cb7 | ||
|
|
bd11be98aa | ||
|
|
a54d438e18 | ||
|
|
b33558d822 | ||
|
|
43e6d7fe88 | ||
|
|
8f1c51eaea | ||
|
|
090786b037 | ||
|
|
dba3927b4e | ||
|
|
56d10aeef7 | ||
|
|
6a76540cb6 | ||
|
|
2c002a3342 | ||
|
|
42ac64284f | ||
|
|
2595cb5698 | ||
|
|
c3bb73a711 | ||
|
|
e59095423e | ||
|
|
db21391156 | ||
|
|
12df0e8994 | ||
|
|
ca0cf5415e | ||
|
|
33f2ec6355 | ||
|
|
680399f476 | ||
|
|
d0c7a91a50 | ||
|
|
27b2747b61 | ||
|
|
9b1f698770 | ||
|
|
efa1818cb4 | ||
|
|
246187a20f | ||
|
|
4c54cfc692 | ||
|
|
5d892a557f | ||
|
|
49fe2604b3 | ||
|
|
28466a39d8 | ||
|
|
e1824c00a9 | ||
|
|
b5798326e8 | ||
|
|
fee84de782 | ||
|
|
e8fa248571 | ||
|
|
ba5ee79af8 | ||
|
|
69eac0d9d2 | ||
|
|
9713fa09e7 | ||
|
|
f654da9f56 | ||
|
|
ef03c4b7bb | ||
|
|
13df748f02 | ||
|
|
04dbd5c53f | ||
|
|
398c5d553d | ||
|
|
39f0bd3e82 | ||
|
|
2dffc9f6eb | ||
|
|
bc0a2d1b28 | ||
|
|
1749af9707 | ||
|
|
57507ca37c | ||
|
|
7861e9f728 | ||
|
|
84cbc1d6c0 | ||
|
|
03debb4ef9 | ||
|
|
ed3b69ffb1 | ||
|
|
0fb3c8cb7c | ||
|
|
58840f39bb | ||
|
|
da29ce355f | ||
|
|
7a9e6659f0 | ||
|
|
9eb89b4ac7 | ||
|
|
fec54c9c5d | ||
|
|
67169a5d0c | ||
|
|
eebea63bed | ||
|
|
9c7a216814 | ||
|
|
a8861e6a66 | ||
|
|
8a9a5d69b6 | ||
|
|
761a4deab3 | ||
|
|
50cc54e50b | ||
|
|
da2401ff39 | ||
|
|
c3d0fed9ed | ||
|
|
c0982d7316 | ||
|
|
f0396c19be | ||
|
|
33a9d898a6 | ||
|
|
cfd230d7f1 | ||
|
|
ee674acca4 | ||
|
|
8f043bbdaa | ||
|
|
750005f02b | ||
|
|
0bb227f8a4 | ||
|
|
40d9f828d4 | ||
|
|
49f2071e22 | ||
|
|
f08be2162a | ||
|
|
45e452557b | ||
|
|
df94cc1b13 | ||
|
|
bd28b876a0 | ||
|
|
e959137a4b | ||
|
|
d0800aa200 | ||
|
|
3011a0f14f | ||
|
|
ee64c6ab86 | ||
|
|
79277cc383 | ||
|
|
e78b902a7d | ||
|
|
e8c4e6f0b3 | ||
|
|
2e785899dd | ||
|
|
115b57649e | ||
|
|
b80429ab7f | ||
|
|
c190e64012 | ||
|
|
3a6cafaf0a | ||
|
|
c76b0b70c1 | ||
|
|
c71cdce0b7 | ||
|
|
a5f87cee6b | ||
|
|
bf66fe51f6 |
@@ -75,6 +75,7 @@ machine-mac-large: &machine-mac-large
|
||||
# Build configurations options.
|
||||
env-debug-build: &env-debug-build
|
||||
GN_CONFIG: //electron/build/args/debug.gn
|
||||
SKIP_DIST_ZIP: '1'
|
||||
|
||||
env-testing-build: &env-testing-build
|
||||
GN_CONFIG: //electron/build/args/testing.gn
|
||||
@@ -84,7 +85,6 @@ 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'
|
||||
@@ -131,31 +131,22 @@ env-enable-sccache: &env-enable-sccache
|
||||
env-send-slack-notifications: &env-send-slack-notifications
|
||||
NOTIFY_SLACK: true
|
||||
|
||||
env-global: &env-global
|
||||
ELECTRON_OUT_DIR: Default
|
||||
|
||||
env-linux-medium: &env-linux-medium
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 3
|
||||
|
||||
env-linux-2xlarge: &env-linux-2xlarge
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 34
|
||||
|
||||
env-linux-2xlarge-release: &env-linux-2xlarge-release
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 16
|
||||
|
||||
env-machine-mac: &env-machine-mac
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 6
|
||||
|
||||
env-mac-large: &env-mac-large
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 18
|
||||
|
||||
env-mac-large-release: &env-mac-large-release
|
||||
<<: *env-global
|
||||
NUMBER_OF_NINJA_PROCESSES: 8
|
||||
|
||||
env-disable-crash-reporter-tests: &env-disable-crash-reporter-tests
|
||||
@@ -217,25 +208,7 @@ step-gclient-sync: &step-gclient-sync
|
||||
$GCLIENT_EXTRA_ARGS \
|
||||
"$CIRCLE_REPOSITORY_URL"
|
||||
|
||||
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
|
||||
gclient sync --with_branch_heads --with_tags
|
||||
fi
|
||||
|
||||
step-setup-env-for-build: &step-setup-env-for-build
|
||||
@@ -251,7 +224,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-ci"' >> $BASH_ENV
|
||||
echo 'export SCCACHE_BUCKET="electronjs-sccache"' >> $BASH_ENV
|
||||
echo 'export SCCACHE_TWO_TIER=true' >> $BASH_ENV
|
||||
fi
|
||||
fi
|
||||
@@ -320,7 +293,7 @@ step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
cd src/electron
|
||||
./script/codesign/generate-identity.sh
|
||||
./script/codesign/import-testing-cert-ci.sh
|
||||
fi
|
||||
|
||||
step-install-gnutar-on-mac: &step-install-gnutar-on-mac
|
||||
@@ -362,18 +335,9 @@ step-maybe-electron-dist-strip: &step-maybe-electron-dist-strip
|
||||
run:
|
||||
name: Strip electron binaries
|
||||
command: |
|
||||
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
|
||||
if [ "$STRIP_BINARIES" == "true" ] && [ "`uname`" != "Darwin" ]; then
|
||||
cd src
|
||||
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
|
||||
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH"
|
||||
fi
|
||||
|
||||
step-electron-dist-build: &step-electron-dist-build
|
||||
@@ -381,28 +345,30 @@ step-electron-dist-build: &step-electron-dist-build
|
||||
name: Build dist.zip
|
||||
command: |
|
||||
cd src
|
||||
ninja -C out/Default electron:electron_dist_zip
|
||||
if [ "$CHECK_DIST_MANIFEST" == "1" ]; then
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
target_os=mac
|
||||
target_cpu=x64
|
||||
if [ x"$MAS_BUILD" == x"true" ]; then
|
||||
target_os=mac_mas
|
||||
fi
|
||||
elif [ "`uname`" == "Linux" ]; then
|
||||
target_os=linux
|
||||
if [ x"$TARGET_ARCH" == x ]; then
|
||||
if [ "$SKIP_DIST_ZIP" != "1" ]; then
|
||||
ninja -C out/Default electron:electron_dist_zip
|
||||
if [ "$CHECK_DIST_MANIFEST" == "1" ]; then
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
target_os=mac
|
||||
target_cpu=x64
|
||||
elif [ "$TARGET_ARCH" == "ia32" ]; then
|
||||
target_cpu=x86
|
||||
if [ x"$MAS_BUILD" == x"true" ]; then
|
||||
target_os=mac_mas
|
||||
fi
|
||||
elif [ "`uname`" == "Linux" ]; then
|
||||
target_os=linux
|
||||
if [ x"$TARGET_ARCH" == x ]; then
|
||||
target_cpu=x64
|
||||
elif [ "$TARGET_ARCH" == "ia32" ]; then
|
||||
target_cpu=x86
|
||||
else
|
||||
target_cpu="$TARGET_ARCH"
|
||||
fi
|
||||
else
|
||||
target_cpu="$TARGET_ARCH"
|
||||
echo "Unknown system: `uname`"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Unknown system: `uname`"
|
||||
exit 1
|
||||
electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.$target_os.$target_cpu.manifest
|
||||
fi
|
||||
electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.$target_os.$target_cpu.manifest
|
||||
fi
|
||||
|
||||
step-electron-dist-store: &step-electron-dist-store
|
||||
@@ -416,9 +382,7 @@ step-electron-chromedriver-build: &step-electron-chromedriver-build
|
||||
command: |
|
||||
cd src
|
||||
ninja -C out/Default chrome/test/chromedriver -j $NUMBER_OF_NINJA_PROCESSES
|
||||
if [ "`uname`" == "Linux" ]; then
|
||||
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver
|
||||
fi
|
||||
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver
|
||||
ninja -C out/Default electron:electron_chromedriver_zip
|
||||
|
||||
step-electron-chromedriver-store: &step-electron-chromedriver-store
|
||||
@@ -460,6 +424,9 @@ step-persist-data-for-tests: &step-persist-data-for-tests
|
||||
- src/out/Default/mksnapshot.zip
|
||||
- src/out/Default/gen/node_headers
|
||||
- src/out/ffmpeg/ffmpeg.zip
|
||||
- src/electron
|
||||
- src/third_party/electron_node
|
||||
- src/third_party/nan
|
||||
|
||||
step-electron-dist-unzip: &step-electron-dist-unzip
|
||||
run:
|
||||
@@ -538,20 +505,18 @@ 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
|
||||
if [ "$SKIP_DIST_ZIP" != "1" ]; then
|
||||
ninja -C out/Default electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
fi
|
||||
|
||||
step-mksnapshot-store: &step-mksnapshot-store
|
||||
store_artifacts:
|
||||
@@ -565,8 +530,6 @@ step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
|
||||
if [ "$GENERATE_SYMBOLS" == "true" ]; then
|
||||
cd src
|
||||
ninja -C out/Default electron:electron_symbols
|
||||
cd out/Default/breakpad_symbols
|
||||
find . -name \*.sym -print0 | xargs -0 npx @sentry/cli@1.51.1 difutil bundle-sources
|
||||
fi
|
||||
|
||||
step-maybe-zip-symbols: &step-maybe-zip-symbols
|
||||
@@ -575,6 +538,8 @@ step-maybe-zip-symbols: &step-maybe-zip-symbols
|
||||
command: |
|
||||
cd src
|
||||
export BUILD_PATH="$PWD/out/Default"
|
||||
ninja -C out/Default electron:licenses
|
||||
ninja -C out/Default electron:electron_version
|
||||
electron/script/zip-symbols.py -b $BUILD_PATH
|
||||
|
||||
step-maybe-cross-arch-snapshot: &step-maybe-cross-arch-snapshot
|
||||
@@ -784,8 +749,6 @@ steps-checkout-fast: &steps-checkout-fast
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
- store_artifacts:
|
||||
path: patches
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- run:
|
||||
name: Wipe Electron
|
||||
@@ -817,8 +780,6 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
- store_artifacts:
|
||||
path: patches
|
||||
- *step-save-git-cache
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- run:
|
||||
@@ -847,10 +808,16 @@ steps-electron-build: &steps-electron-build
|
||||
at: .
|
||||
- *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
|
||||
- *step-maybe-electron-dist-strip
|
||||
- *step-electron-dist-build
|
||||
- *step-electron-dist-store
|
||||
- *step-ninja-summary
|
||||
@@ -861,10 +828,54 @@ steps-electron-build: &steps-electron-build
|
||||
|
||||
- *step-show-sccache-stats
|
||||
|
||||
steps-electron-build-for-tests: &steps-electron-build-for-tests
|
||||
# mksnapshot
|
||||
- *step-mksnapshot-build
|
||||
- *step-mksnapshot-store
|
||||
- *step-maybe-cross-arch-snapshot
|
||||
- *step-maybe-cross-arch-snapshot-store
|
||||
|
||||
# ffmpeg
|
||||
- *step-ffmpeg-gn-gen
|
||||
- *step-ffmpeg-build
|
||||
- *step-ffmpeg-store
|
||||
|
||||
# Save all data needed for a further tests run.
|
||||
- *step-persist-data-for-tests
|
||||
|
||||
- *step-maybe-generate-breakpad-symbols
|
||||
- *step-maybe-zip-symbols
|
||||
|
||||
# Trigger tests on arm hardware if needed
|
||||
- *step-maybe-trigger-arm-test
|
||||
|
||||
- *step-maybe-notify-slack-failure
|
||||
|
||||
steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with-inline-checkout-for-tests
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
# Checkout - Copied ffrom steps-checkout
|
||||
- *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
|
||||
# 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-depot-tools-add-to-path
|
||||
- *step-setup-env-for-build
|
||||
- *step-restore-brew-cache
|
||||
@@ -1043,6 +1054,7 @@ 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
|
||||
@@ -1075,6 +1087,7 @@ 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
|
||||
@@ -1089,6 +1102,7 @@ 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
|
||||
@@ -1156,7 +1170,8 @@ jobs:
|
||||
<<: *env-debug-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-x64-debug-gn-check:
|
||||
<<: *machine-linux-medium
|
||||
@@ -1172,7 +1187,8 @@ jobs:
|
||||
<<: *env-testing-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-x64-testing-no-run-as-node:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1182,7 +1198,8 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *env-disable-run-as-node
|
||||
<<: *steps-electron-build-for-tests
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-x64-testing-gn-check:
|
||||
<<: *machine-linux-medium
|
||||
@@ -1203,12 +1220,12 @@ jobs:
|
||||
linux-x64-release:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-send-slack-notifications
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
linux-x64-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1216,7 +1233,6 @@ jobs:
|
||||
<<: *env-linux-2xlarge-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
@@ -1228,7 +1244,8 @@ jobs:
|
||||
<<: *env-debug-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-ia32-testing:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1238,7 +1255,8 @@ jobs:
|
||||
<<: *env-testing-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-ia32-chromedriver:
|
||||
<<: *machine-linux-medium
|
||||
@@ -1253,13 +1271,13 @@ jobs:
|
||||
linux-ia32-release:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-ia32
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-send-slack-notifications
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
linux-ia32-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1268,7 +1286,6 @@ jobs:
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-ia32
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
@@ -1280,7 +1297,8 @@ jobs:
|
||||
<<: *env-debug-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-arm-testing:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1291,7 +1309,8 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
TRIGGER_ARM_TEST: true
|
||||
<<: *steps-electron-build-for-tests
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-arm-chromedriver:
|
||||
<<: *machine-linux-medium
|
||||
@@ -1306,13 +1325,13 @@ jobs:
|
||||
linux-arm-release:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-send-slack-notifications
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
linux-arm-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1320,7 +1339,6 @@ jobs:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
@@ -1333,7 +1351,8 @@ jobs:
|
||||
<<: *env-debug-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-arm64-debug-gn-check:
|
||||
<<: *machine-linux-medium
|
||||
@@ -1352,7 +1371,8 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
TRIGGER_ARM_TEST: true
|
||||
<<: *steps-electron-build-for-tests
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-build-with-inline-checkout-for-tests
|
||||
|
||||
linux-arm64-testing-gn-check:
|
||||
<<: *machine-linux-medium
|
||||
@@ -1375,13 +1395,13 @@ jobs:
|
||||
linux-arm64-release:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm64
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-send-slack-notifications
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
linux-arm64-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1389,7 +1409,6 @@ jobs:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm64
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
@@ -1401,7 +1420,7 @@ jobs:
|
||||
<<: *env-testing-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
osx-debug:
|
||||
<<: *machine-mac-large
|
||||
@@ -1410,7 +1429,7 @@ jobs:
|
||||
<<: *env-debug-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
osx-debug-gn-check:
|
||||
<<: *machine-mac
|
||||
@@ -1442,14 +1461,13 @@ jobs:
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
osx-publish:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
@@ -1462,7 +1480,7 @@ jobs:
|
||||
<<: *env-testing-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
mas-debug:
|
||||
<<: *machine-mac-large
|
||||
@@ -1472,7 +1490,7 @@ jobs:
|
||||
<<: *env-debug-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
mas-debug-gn-check:
|
||||
<<: *machine-mac
|
||||
@@ -1507,7 +1525,7 @@ jobs:
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
mas-publish:
|
||||
<<: *machine-mac-large
|
||||
@@ -1515,7 +1533,6 @@ jobs:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-mas
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
@@ -1772,7 +1789,7 @@ jobs:
|
||||
- *step-maybe-notify-slack-success
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
version: 2.1
|
||||
|
||||
# The publish workflows below each contain one job so that they are
|
||||
# compatible with how sudowoodo works today. If these workflows are
|
||||
@@ -1783,38 +1800,32 @@ workflows:
|
||||
publish-x64-linux:
|
||||
when: << pipeline.parameters.run-linux-x64-publish >>
|
||||
jobs:
|
||||
- linux-x64-publish:
|
||||
context: release-env
|
||||
- linux-x64-publish
|
||||
|
||||
publish-ia32-linux:
|
||||
when: << pipeline.parameters.run-linux-ia32-publish >>
|
||||
jobs:
|
||||
- linux-ia32-publish:
|
||||
context: release-env
|
||||
- linux-ia32-publish
|
||||
|
||||
publish-arm-linux:
|
||||
when: << pipeline.parameters.run-linux-arm-publish >>
|
||||
jobs:
|
||||
- linux-arm-publish:
|
||||
context: release-env
|
||||
- linux-arm-publish
|
||||
|
||||
publish-arm64-linux:
|
||||
when: << pipeline.parameters.run-linux-arm64-publish >>
|
||||
jobs:
|
||||
- linux-arm64-publish:
|
||||
context: release-env
|
||||
- linux-arm64-publish
|
||||
|
||||
publish-osx:
|
||||
when: << pipeline.parameters.run-osx-publish >>
|
||||
jobs:
|
||||
- osx-publish:
|
||||
context: release-env
|
||||
- osx-publish
|
||||
|
||||
publish-mas:
|
||||
when: << pipeline.parameters.run-mas-publish >>
|
||||
jobs:
|
||||
- mas-publish:
|
||||
context: release-env
|
||||
- mas-publish
|
||||
|
||||
lint:
|
||||
when: << pipeline.parameters.run-lint >>
|
||||
@@ -1827,18 +1838,12 @@ workflows:
|
||||
- linux-checkout-fast
|
||||
- linux-checkout-and-save-cache
|
||||
|
||||
- linux-x64-debug:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-x64-debug
|
||||
- linux-x64-debug-gn-check:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-x64-testing:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-x64-testing-no-run-as-node:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-x64-testing
|
||||
- linux-x64-testing-no-run-as-node
|
||||
- linux-x64-testing-gn-check:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
@@ -1852,12 +1857,8 @@ workflows:
|
||||
requires:
|
||||
- linux-x64-testing
|
||||
|
||||
- linux-ia32-debug:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-ia32-testing:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-ia32-debug
|
||||
- linux-ia32-testing
|
||||
- linux-ia32-testing-tests:
|
||||
requires:
|
||||
- linux-ia32-testing
|
||||
@@ -1868,22 +1869,14 @@ workflows:
|
||||
requires:
|
||||
- linux-ia32-testing
|
||||
|
||||
- linux-arm-debug:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-arm-testing:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-arm-debug
|
||||
- linux-arm-testing
|
||||
|
||||
- linux-arm64-debug:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-debug
|
||||
- linux-arm64-debug-gn-check:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-testing:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-testing
|
||||
- linux-arm64-testing-gn-check:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
|
||||
@@ -6,11 +6,9 @@
|
||||
"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,4 +1,3 @@
|
||||
# `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
|
||||
|
||||
10
.github/main.workflow
vendored
10
.github/main.workflow
vendored
@@ -1,10 +0,0 @@
|
||||
workflow "Clerk" {
|
||||
#TODO(codebytere): make this work properly on pull_request
|
||||
on = "repository_dispatch"
|
||||
resolves = "Check release notes"
|
||||
}
|
||||
|
||||
action "Check release notes" {
|
||||
uses = "electron/clerk@master"
|
||||
secrets = [ "GITHUB_TOKEN" ]
|
||||
}
|
||||
25
.github/stale.yml
vendored
25
.github/stale.yml
vendored
@@ -1,25 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 45
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- fixme/bug
|
||||
- fixme/crash
|
||||
- fixme/regression
|
||||
- fixme/security
|
||||
- blocked
|
||||
- blocking-stable
|
||||
- needs-review
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity and is not currently prioritized. It will be closed
|
||||
in a week if no further activity occurs :)
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
If you still think this issue is relevant, please ping a maintainer or
|
||||
leave a comment!
|
||||
36
BUILD.gn
36
BUILD.gn
@@ -26,7 +26,6 @@ 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) {
|
||||
@@ -281,6 +280,7 @@ if (is_linux) {
|
||||
"notify_notification_add_action",
|
||||
"notify_notification_set_image_from_pixbuf",
|
||||
"notify_notification_set_timeout",
|
||||
"notify_notification_set_urgency",
|
||||
"notify_notification_set_hint_string",
|
||||
"notify_notification_show",
|
||||
"notify_notification_close",
|
||||
@@ -580,6 +580,17 @@ source_set("electron_lib") {
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_remote_module) {
|
||||
sources += [
|
||||
"shell/common/api/remote/object_life_monitor.cc",
|
||||
"shell/common/api/remote/object_life_monitor.h",
|
||||
"shell/common/api/remote/remote_callback_freer.cc",
|
||||
"shell/common/api/remote/remote_callback_freer.h",
|
||||
"shell/common/api/remote/remote_object_freer.cc",
|
||||
"shell/common/api/remote/remote_object_freer.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_desktop_capturer) {
|
||||
if (is_component_build && !is_linux) {
|
||||
# On windows the implementation relies on unexported
|
||||
@@ -656,12 +667,6 @@ 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",
|
||||
@@ -776,7 +781,6 @@ if (is_mac) {
|
||||
":electron_framework_libraries",
|
||||
":electron_framework_resources",
|
||||
":electron_swiftshader_library",
|
||||
":electron_xibs",
|
||||
]
|
||||
if (!is_mas_build) {
|
||||
deps += [ ":electron_crashpad_helper" ]
|
||||
@@ -1289,18 +1293,12 @@ 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 = mksnapshot_deps
|
||||
data_deps = [
|
||||
"//v8:mksnapshot($v8_snapshot_toolchain)",
|
||||
"//tools/v8_context_snapshot:v8_context_snapshot_generator",
|
||||
":licenses",
|
||||
]
|
||||
outputs = [
|
||||
"$root_build_dir/mksnapshot.zip",
|
||||
]
|
||||
|
||||
8
DEPS
8
DEPS
@@ -11,9 +11,9 @@ gclient_gn_args = [
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'78.0.3904.130',
|
||||
'af5e1805f244bb71328690460ddd3d48ef3b1eca',
|
||||
'node_version':
|
||||
'v12.8.1',
|
||||
'v12.9.1',
|
||||
'nan_version':
|
||||
'2ee313aaca52e2b478965ac50eb5082520380d1b',
|
||||
|
||||
@@ -114,7 +114,7 @@ hooks = [
|
||||
'pattern': 'src/electron/script/update-external-binaries.py',
|
||||
'condition': 'download_external_binaries',
|
||||
'action': [
|
||||
'python3',
|
||||
'python',
|
||||
'src/electron/script/update-external-binaries.py',
|
||||
],
|
||||
},
|
||||
@@ -152,5 +152,3 @@ hooks = [
|
||||
recursedeps = [
|
||||
'src',
|
||||
]
|
||||
|
||||
# Touch DEPS to bust cache
|
||||
|
||||
@@ -1 +1 @@
|
||||
7.2.4
|
||||
8.0.0-nightly.20190920
|
||||
@@ -80,6 +80,5 @@ steps:
|
||||
- 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()
|
||||
@@ -3,7 +3,7 @@ use_jumbo_build = true
|
||||
root_extra_deps = [ "//electron" ]
|
||||
|
||||
# Registry of NMVs --> https://github.com/nodejs/node/blob/master/doc/abi_version_registry.json
|
||||
node_module_version = 75
|
||||
node_module_version = 76
|
||||
|
||||
v8_promise_internal_field_count = 1
|
||||
v8_typed_array_max_size_in_heap = 0
|
||||
@@ -24,3 +24,6 @@ is_cfi = false
|
||||
# TODO: Remove this and update CI to contain 10.14 SDK once
|
||||
# crbug.com/986701 is fixed.
|
||||
mac_sdk_min = "10.13"
|
||||
|
||||
# TODO: disabled due to crashes. re-enable.
|
||||
enable_osr = false
|
||||
|
||||
@@ -6,8 +6,6 @@ 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.
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
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}}",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ module.exports = ({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@electron/internal': path.resolve(electronRoot, 'lib'),
|
||||
'electron': path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.js'),
|
||||
'electron': path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts'),
|
||||
// Force timers to resolve to our dependency that doens't use window.postMessage
|
||||
'timers': path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js')
|
||||
},
|
||||
@@ -74,10 +74,7 @@ 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,10 +16,6 @@ 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.
|
||||
@@ -55,13 +51,14 @@ def main(argv):
|
||||
with open(runtime_deps) as f:
|
||||
for dep in f.readlines():
|
||||
dep = dep.strip()
|
||||
if not skip_path(dep, dist_zip, target_cpu):
|
||||
dist_files.add(dep)
|
||||
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:
|
||||
|
||||
@@ -12,6 +12,7 @@ buildflag_header("buildflags") {
|
||||
"ENABLE_DESKTOP_CAPTURER=$enable_desktop_capturer",
|
||||
"ENABLE_RUN_AS_NODE=$enable_run_as_node",
|
||||
"ENABLE_OSR=$enable_osr",
|
||||
"ENABLE_REMOTE_MODULE=$enable_remote_module",
|
||||
"ENABLE_VIEW_API=$enable_view_api",
|
||||
"ENABLE_PEPPER_FLASH=$enable_pepper_flash",
|
||||
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
|
||||
@@ -19,7 +20,6 @@ buildflag_header("buildflags") {
|
||||
"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",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ declare_args() {
|
||||
|
||||
enable_osr = true
|
||||
|
||||
enable_remote_module = true
|
||||
|
||||
enable_view_api = false
|
||||
|
||||
enable_pdf_viewer = false
|
||||
@@ -20,8 +22,6 @@ declare_args() {
|
||||
|
||||
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.
|
||||
|
||||
@@ -43,6 +43,8 @@ static_library("chrome") {
|
||||
"//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/printing/printing_service.cc",
|
||||
"//chrome/browser/printing/printing_service.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",
|
||||
@@ -162,8 +164,6 @@ 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 += [
|
||||
|
||||
@@ -73,7 +73,7 @@ net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
|
||||
void CertificateManagerModel::Create(content::BrowserContext* browser_context,
|
||||
const CreationCallback& callback) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
base::PostTaskWithTraits(
|
||||
base::PostTask(
|
||||
FROM_HERE, {BrowserThread::IO},
|
||||
base::BindOnce(&CertificateManagerModel::GetCertDBOnIOThread,
|
||||
browser_context->GetResourceContext(), callback));
|
||||
@@ -86,7 +86,7 @@ CertificateManagerModel::CertificateManagerModel(
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
}
|
||||
|
||||
CertificateManagerModel::~CertificateManagerModel() {}
|
||||
CertificateManagerModel::~CertificateManagerModel() = default;
|
||||
|
||||
int CertificateManagerModel::ImportFromPKCS12(
|
||||
PK11SlotInfo* slot_info,
|
||||
@@ -146,7 +146,7 @@ void CertificateManagerModel::DidGetCertDBOnIOThread(
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
bool is_user_db_available = !!cert_db->GetPublicSlot();
|
||||
base::PostTaskWithTraits(
|
||||
base::PostTask(
|
||||
FROM_HERE, {BrowserThread::UI},
|
||||
base::BindOnce(&CertificateManagerModel::DidGetCertDBOnUIThread, cert_db,
|
||||
is_user_db_available, callback));
|
||||
|
||||
@@ -185,7 +185,7 @@ int WaitSocketForRead(int fd, const base::TimeDelta& timeout) {
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(fd, &read_fds);
|
||||
|
||||
return HANDLE_EINTR(select(fd + 1, &read_fds, NULL, NULL, &tv));
|
||||
return HANDLE_EINTR(select(fd + 1, &read_fds, nullptr, nullptr, &tv));
|
||||
}
|
||||
|
||||
// Read a message from a socket fd, with an optional timeout.
|
||||
@@ -705,7 +705,7 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
if (shutdown(fd_, SHUT_WR) < 0)
|
||||
PLOG(ERROR) << "shutdown() failed";
|
||||
|
||||
base::PostTaskWithTraits(
|
||||
base::PostTask(
|
||||
FROM_HERE, {BrowserThread::IO},
|
||||
base::BindOnce(&ProcessSingleton::LinuxWatcher::RemoveSocketReader,
|
||||
parent_, this));
|
||||
@@ -885,10 +885,9 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
|
||||
|
||||
void ProcessSingleton::StartListeningOnSocket() {
|
||||
watcher_ = new LinuxWatcher(this);
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {BrowserThread::IO},
|
||||
base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening, watcher_,
|
||||
sock_));
|
||||
base::PostTask(FROM_HERE, {BrowserThread::IO},
|
||||
base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening,
|
||||
watcher_, sock_));
|
||||
}
|
||||
|
||||
void ProcessSingleton::OnBrowserReady() {
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(options);
|
||||
mainWindow.on('ready-to-show', () => mainWindow!.show());
|
||||
if (process.platform === 'linux') {
|
||||
options.icon = path.join(__dirname, 'icon.png')
|
||||
}
|
||||
|
||||
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,9 +2,10 @@
|
||||
|
||||
<head>
|
||||
<title>Electron</title>
|
||||
<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'" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; 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>
|
||||
@@ -83,9 +84,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<script>
|
||||
window.electronDefaultApp.initialize()
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
30
default_app/index.ts
Normal file
30
default_app/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
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,53 +1,20 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import { ipcRenderer } from 'electron'
|
||||
|
||||
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`);
|
||||
|
||||
for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) {
|
||||
loadSVG(element);
|
||||
}
|
||||
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`)
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('electronDefaultApp', {
|
||||
initialize
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', initialize)
|
||||
|
||||
@@ -39,7 +39,6 @@ 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)
|
||||
@@ -135,6 +134,7 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||
* [MenuItem](api/menu-item.md)
|
||||
* [net](api/net.md)
|
||||
* [netLog](api/net-log.md)
|
||||
* [Notification](api/notification.md)
|
||||
* [powerMonitor](api/power-monitor.md)
|
||||
* [powerSaveBlocker](api/power-save-blocker.md)
|
||||
* [protocol](api/protocol.md)
|
||||
|
||||
@@ -314,8 +314,10 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `authenticationResponseDetails` Object
|
||||
* `request` Object
|
||||
* `method` String
|
||||
* `url` URL
|
||||
* `referrer` URL
|
||||
* `authInfo` Object
|
||||
* `isProxy` Boolean
|
||||
* `scheme` String
|
||||
@@ -323,8 +325,8 @@ Returns:
|
||||
* `port` Integer
|
||||
* `realm` String
|
||||
* `callback` Function
|
||||
* `username` String (optional)
|
||||
* `password` String (optional)
|
||||
* `username` String
|
||||
* `password` String
|
||||
|
||||
Emitted when `webContents` wants to do basic auth.
|
||||
|
||||
@@ -335,16 +337,12 @@ should prevent the default behavior with `event.preventDefault()` and call
|
||||
```javascript
|
||||
const { app } = require('electron')
|
||||
|
||||
app.on('login', (event, webContents, details, authInfo, callback) => {
|
||||
app.on('login', (event, webContents, request, 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.
|
||||
@@ -392,7 +390,7 @@ Emitted when Electron has created a new `session`.
|
||||
```javascript
|
||||
const { app } = require('electron')
|
||||
|
||||
app.on('session-created', (event, session) => {
|
||||
app.on('session-created', (session) => {
|
||||
console.log(session)
|
||||
})
|
||||
```
|
||||
@@ -677,6 +675,8 @@ preferred over `name` by Electron.
|
||||
|
||||
Overrides the current application's name.
|
||||
|
||||
**Note:** This function overrides the name used internally by Electron; it does not affect the name that the OS uses.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `app.getLocale()`
|
||||
@@ -711,34 +711,34 @@ Clears the recent documents list.
|
||||
|
||||
### `app.setAsDefaultProtocolClient(protocol[, path, args])`
|
||||
|
||||
* `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
|
||||
* `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
|
||||
|
||||
Returns `Boolean` - Whether the call succeeded.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
**Note:** On macOS, you can only register protocols that have been added to
|
||||
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.
|
||||
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.
|
||||
|
||||
**Note:** In a Windows Store environment (when packaged as an `appx`) this API
|
||||
will return `true` for all calls but the registry key it sets won't be accessible
|
||||
by other applications. In order to register your Windows Store application
|
||||
as a default protocol handler you must [declare the protocol in your manifest](https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol).
|
||||
|
||||
The API uses the Windows Registry and `LSSetDefaultHandlerForURLScheme` internally.
|
||||
The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally.
|
||||
|
||||
### `app.removeAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_
|
||||
|
||||
@@ -757,8 +757,10 @@ 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` - Whether the current executable is the default handler for a
|
||||
protocol (aka URI scheme).
|
||||
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.
|
||||
|
||||
**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
|
||||
@@ -766,7 +768,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_
|
||||
|
||||
@@ -1168,25 +1170,24 @@ This API must be called after the `ready` event is emitted.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `app.showAboutPanel()` _macOS_ _Linux_
|
||||
### `app.showAboutPanel()`
|
||||
|
||||
Show the app's about panel options. These options can be overridden with `app.setAboutPanelOptions(options)`.
|
||||
|
||||
### `app.setAboutPanelOptions(options)` _macOS_ _Linux_
|
||||
### `app.setAboutPanelOptions(options)`
|
||||
|
||||
* `options` Object
|
||||
* `applicationName` String (optional) - The app's name.
|
||||
* `applicationVersion` String (optional) - The app's version.
|
||||
* `copyright` String (optional) - Copyright information.
|
||||
* `version` String (optional) _macOS_ - The app's build version number.
|
||||
* `credits` String (optional) _macOS_ - Credit information.
|
||||
* `credits` String (optional) _macOS_ _Windows_ - Credit information.
|
||||
* `authors` String[] (optional) _Linux_ - List of app authors.
|
||||
* `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.
|
||||
* `iconPath` String (optional) _Linux_ _Windows_ - Path to the app's icon. On Linux, will be shown as 64x64 pixels while retaining aspect ratio.
|
||||
|
||||
Set the about panel options. This will override the values defined in the app's `.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.
|
||||
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.
|
||||
|
||||
### `app.isEmojiPanelSupported()`
|
||||
|
||||
@@ -1307,8 +1308,6 @@ 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
|
||||
|
||||
@@ -68,40 +68,6 @@ 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)`
|
||||
@@ -231,7 +197,7 @@ A new API, `protocol.registerSchemesAsPrivileged` has been added and should be u
|
||||
### webFrame Isolated World APIs
|
||||
|
||||
```js
|
||||
// Removed in Electron 7.0
|
||||
// Deprecated
|
||||
webFrame.setIsolatedWorldContentSecurityPolicy(worldId, csp)
|
||||
webFrame.setIsolatedWorldHumanReadableName(worldId, name)
|
||||
webFrame.setIsolatedWorldSecurityOrigin(worldId, securityOrigin)
|
||||
|
||||
@@ -206,8 +206,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
* `backgroundColor` String (optional) - Window's background color as a hexadecimal value,
|
||||
like `#66CD00` or `#FFF` or `#80FFFFFF` (alpha in #AARRGGBB format is supported if
|
||||
`transparent` is set to `true`). Default is `#FFF` (white).
|
||||
* `hasShadow` Boolean (optional) - Whether window should have a shadow. This is only
|
||||
implemented on macOS. Default is `true`.
|
||||
* `hasShadow` Boolean (optional) - Whether window should have a shadow. Default is `true`.
|
||||
* `opacity` Number (optional) - Set the initial opacity of the window, between 0.0 (fully
|
||||
transparent) and 1.0 (fully opaque). This is only implemented on Windows and macOS.
|
||||
* `darkTheme` Boolean (optional) - Forces using dark theme for the window, only works on
|
||||
@@ -273,6 +272,8 @@ 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
|
||||
@@ -381,8 +382,9 @@ 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`.
|
||||
* `accessibleTitle` String (optional) - An alternative title string provided only
|
||||
to accessibility tools such as screen readers. This string is not directly
|
||||
visible to users.
|
||||
|
||||
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
||||
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
||||
@@ -513,7 +515,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.
|
||||
|
||||
@@ -523,14 +525,14 @@ Note that this is only emitted when the window is being resized manually. Resizi
|
||||
|
||||
Emitted after the window has been resized.
|
||||
|
||||
#### Event: 'will-move' _Windows_
|
||||
#### Event: 'will-move' _macOS_ _Windows_
|
||||
|
||||
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.
|
||||
Emitted before the window is moved. On Windows, calling `event.preventDefault()` will prevent the window from being moved.
|
||||
|
||||
Note that this is only emitted when the window is being resized manually. Resizing the window with `setBounds`/`setSize` will not emit this event.
|
||||
|
||||
@@ -621,12 +623,6 @@ 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:
|
||||
@@ -834,6 +830,12 @@ const menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(menu)
|
||||
```
|
||||
|
||||
#### `win.accessibleTitle`
|
||||
|
||||
A `String` property that defines an alternative title provided only to
|
||||
accessibility tools such as screen readers. This string is not directly
|
||||
visible to users.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
Objects created with `new BrowserWindow` have the following instance methods:
|
||||
@@ -1207,6 +1209,14 @@ can not be focused on.
|
||||
|
||||
Returns `Boolean` - Whether the window is always on top of other windows.
|
||||
|
||||
#### `win.moveAbove(mediaSourceId)`
|
||||
|
||||
* `mediaSourceId` String - Window id in the format of DesktopCapturerSource's id. For example "window:1869:0".
|
||||
|
||||
Moves window above the source window in the sense of z-order. If the
|
||||
`mediaSourceId` is not of type window or if the window does not exist then
|
||||
this method throws an error.
|
||||
|
||||
#### `win.moveTop()`
|
||||
|
||||
Moves window to top(z-order) regardless of focus
|
||||
@@ -1279,6 +1289,15 @@ Enters or leaves the kiosk mode.
|
||||
|
||||
Returns `Boolean` - Whether the window is in kiosk mode.
|
||||
|
||||
#### `win.getMediaSourceId()`
|
||||
|
||||
Returns `String` - Window id in the format of DesktopCapturerSource's id. For example "window:1234:0".
|
||||
|
||||
More precisely the format is `window:id:other_id` where `id` is `HWND` on
|
||||
Windows, `CGWindowID` (`uint64_t`) on macOS and `Window` (`unsigned long`) on
|
||||
Linux. `other_id` is used to identify web contents (tabs) so within the same
|
||||
top level window.
|
||||
|
||||
#### `win.getNativeWindowHandle()`
|
||||
|
||||
Returns `Buffer` - The platform-specific handle of the window.
|
||||
@@ -1559,7 +1578,7 @@ Same as `webContents.showDefinitionForSelection()`.
|
||||
|
||||
#### `win.setIcon(icon)` _Windows_ _Linux_
|
||||
|
||||
* `icon` [NativeImage](native-image.md) | String
|
||||
* `icon` [NativeImage](native-image.md)
|
||||
|
||||
Changes window icon.
|
||||
|
||||
@@ -1726,14 +1745,14 @@ removed in future Electron releases.
|
||||
|
||||
#### `win.setBrowserView(browserView)` _Experimental_
|
||||
|
||||
* `browserView` [BrowserView](browser-view.md) | null - Attach browserView to win.
|
||||
If there is some other browserViews was attached they will be removed from
|
||||
* `browserView` [BrowserView](browser-view.md) | null - Attach `browserView` to `win`.
|
||||
If there are other `BrowserView`s attached, they will be removed from
|
||||
this window.
|
||||
|
||||
#### `win.getBrowserView()` _Experimental_
|
||||
|
||||
Returns `BrowserView | null` - an BrowserView what is attached. Returns `null`
|
||||
if none is attached. Throw error if multiple BrowserViews is attached.
|
||||
Returns `BrowserView | null` - The `BrowserView` attached to `win`. Returns `null`
|
||||
if one is not attached. Throws an error if multiple `BrowserView`s are attached.
|
||||
|
||||
#### `win.addBrowserView(browserView)` _Experimental_
|
||||
|
||||
|
||||
@@ -22,9 +22,6 @@ which the request is associated.
|
||||
with which the request is associated. Defaults to the empty string. The
|
||||
`session` option prevails on `partition`. Thus if a `session` is explicitly
|
||||
specified, `partition` is ignored.
|
||||
* `useSessionCookies` Boolean (optional) - Whether to send cookies with this
|
||||
request from the provided session. This will make the `net` request's
|
||||
cookie behavior match a `fetch` request. Default is `false`.
|
||||
* `protocol` String (optional) - The protocol scheme in the form 'scheme:'.
|
||||
Currently supported values are 'http:' or 'https:'. Defaults to 'http:'.
|
||||
* `host` String (optional) - The server host provided as a concatenation of
|
||||
@@ -35,8 +32,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
|
||||
cancelled unless [`request.followRedirect`](#requestfollowredirect) is invoked
|
||||
synchronously during the [`redirect`](#event-redirect) event.
|
||||
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.
|
||||
|
||||
`options` properties such as `protocol`, `host`, `hostname`, `port` and `path`
|
||||
strictly follow the Node.js model as described in the
|
||||
@@ -73,8 +70,8 @@ Returns:
|
||||
* `port` Integer
|
||||
* `realm` String
|
||||
* `callback` Function
|
||||
* `username` String (optional)
|
||||
* `password` String (optional)
|
||||
* `username` String
|
||||
* `password` String
|
||||
|
||||
Emitted when an authenticating proxy is asking for user credentials.
|
||||
|
||||
@@ -139,11 +136,8 @@ Returns:
|
||||
* `redirectUrl` String
|
||||
* `responseHeaders` Record<String, String[]>
|
||||
|
||||
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.
|
||||
Emitted when there is redirection and the mode is `manual`. Calling
|
||||
[`request.followRedirect`](#requestfollowredirect) will continue with the redirection.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
@@ -220,8 +214,7 @@ response object,it will emit the `aborted` event.
|
||||
|
||||
#### `request.followRedirect()`
|
||||
|
||||
Continues any pending redirection. Can only be called during a `'redirect'`
|
||||
event.
|
||||
Continues any deferred redirection request when the redirection mode is `manual`.
|
||||
|
||||
#### `request.getUploadProgress()`
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
# 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,6 +48,7 @@ The `dialog` module has the following methods:
|
||||
their target path.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
as a directory instead of a file.
|
||||
* `dontAddToRecent` _Windows_ - Do not add the item being opened to the recent documents list.
|
||||
* `message` String (optional) _macOS_ - Message to display above input
|
||||
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.
|
||||
@@ -110,6 +111,7 @@ dialog.showOpenDialogSync(mainWindow, {
|
||||
their target path.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
as a directory instead of a file.
|
||||
* `dontAddToRecent` _Windows_ - Do not add the item being opened to the recent documents list.
|
||||
* `message` String (optional) _macOS_ - Message to display above input
|
||||
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.
|
||||
@@ -118,7 +120,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. (For return values, see [table here](#bookmarks-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.
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
@@ -171,6 +173,13 @@ dialog.showOpenDialog(mainWindow, {
|
||||
displayed in front of the filename text field.
|
||||
* `showsTagField` Boolean (optional) _macOS_ - Show the tags input box,
|
||||
defaults to `true`.
|
||||
* `properties` String[] (optional)
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
as a directory instead of a file.
|
||||
* `showOverwriteConfirmation` _Linux_ - Sets whether the user will be presented a confirmation dialog if the user types a file name that already exists.
|
||||
* `dontAddToRecent` _Windows_ - Do not add the item being saved to the recent documents list.
|
||||
* `securityScopedBookmarks` Boolean (optional) _macOS_ _mas_ - Create a [security scoped bookmark](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. If this option is enabled and the file doesn't already exist a blank file will be created at the chosen path.
|
||||
|
||||
Returns `String | undefined`, the path of the file chosen by the user; if the dialog is cancelled it returns `undefined`.
|
||||
@@ -193,14 +202,20 @@ The `filters` specifies an array of file types that can be displayed, see
|
||||
* `message` String (optional) _macOS_ - Message to display above text fields.
|
||||
* `nameFieldLabel` String (optional) _macOS_ - Custom label for the text
|
||||
displayed in front of the filename text field.
|
||||
* `showsTagField` Boolean (optional) _macOS_ - Show the tags input box,
|
||||
defaults to `true`.
|
||||
* `showsTagField` Boolean (optional) _macOS_ - Show the tags input box, defaults to `true`.
|
||||
* `properties` String[] (optional)
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
as a directory instead of a file.
|
||||
* `showOverwriteConfirmation` _Linux_ - Sets whether the user will be presented a confirmation dialog if the user types a file name that already exists.
|
||||
* `dontAddToRecent` _Windows_ - Do not add the item being saved to the recent documents list.
|
||||
* `securityScopedBookmarks` Boolean (optional) _macOS_ _mas_ - Create a [security scoped bookmark](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. If this option is enabled and the file doesn't already exist a blank file will be created at the chosen path.
|
||||
|
||||
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. (For return values, see [table here](#bookmarks-array).)
|
||||
* `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.
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
@@ -254,7 +269,6 @@ 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)`
|
||||
|
||||
@@ -336,17 +350,6 @@ 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
|
||||
|
||||
@@ -123,14 +123,3 @@ 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.
|
||||
|
||||
@@ -77,7 +77,6 @@ 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`.
|
||||
@@ -148,4 +147,4 @@ found in the [`ipc-main-invoke-event`](structures/ipc-main-invoke-event.md)
|
||||
structure docs.
|
||||
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[web-contents-send]: web-contents.md#contentssendchannel-args
|
||||
[web-contents-send]: web-contents.md#contentssendchannel-arg1-arg2-
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The Electron team is currently undergoing an initiative to convert callback-based functions in Electron to return Promises. During this transition period, both the callback and Promise-based versions of these functions will work correctly, and will both be documented.
|
||||
|
||||
To enable deprecation warnings for these updated functions, use the `process.enablePromiseAPI` runtime flag.
|
||||
To enable deprecation warnings for these updated functions, use the [`process.enablePromiseAPIs` runtime flag](../process.md#processenablepromiseapis).
|
||||
|
||||
When a majority of affected functions are migrated, this flag will be enabled by default and all developers will be able to see these deprecation warnings. At that time, the callback-based versions will also be removed from documentation. This document will be continuously updated as more functions are converted.
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ image file path as a `String`:
|
||||
const { BrowserWindow, Tray } = require('electron')
|
||||
|
||||
const appIcon = new Tray('/Users/somebody/images/icon.png')
|
||||
let win = new BrowserWindow({ icon: '/Users/somebody/images/window.png' })
|
||||
const win = new BrowserWindow({ icon: '/Users/somebody/images/window.png' })
|
||||
console.log(appIcon, win)
|
||||
```
|
||||
|
||||
Or read the image from the clipboard which returns a `NativeImage`:
|
||||
Or read the image from the clipboard, which returns a `NativeImage`:
|
||||
|
||||
```javascript
|
||||
const { clipboard, Tray } = require('electron')
|
||||
@@ -33,19 +33,19 @@ Currently `PNG` and `JPEG` image formats are supported. `PNG` is recommended
|
||||
because of its support for transparency and lossless compression.
|
||||
|
||||
On Windows, you can also load `ICO` icons from file paths. For best visual
|
||||
quality it is recommended to include at least the following sizes in the:
|
||||
quality, it is recommended to include at least the following sizes in the:
|
||||
|
||||
* Small icon
|
||||
* 16x16 (100% DPI scale)
|
||||
* 20x20 (125% DPI scale)
|
||||
* 24x24 (150% DPI scale)
|
||||
* 32x32 (200% DPI scale)
|
||||
* 16x16 (100% DPI scale)
|
||||
* 20x20 (125% DPI scale)
|
||||
* 24x24 (150% DPI scale)
|
||||
* 32x32 (200% DPI scale)
|
||||
* Large icon
|
||||
* 32x32 (100% DPI scale)
|
||||
* 40x40 (125% DPI scale)
|
||||
* 48x48 (150% DPI scale)
|
||||
* 64x64 (200% DPI scale)
|
||||
* 256x256
|
||||
* 32x32 (100% DPI scale)
|
||||
* 40x40 (125% DPI scale)
|
||||
* 48x48 (150% DPI scale)
|
||||
* 64x64 (200% DPI scale)
|
||||
* 256x256
|
||||
|
||||
Check the *Size requirements* section in [this article][icons].
|
||||
|
||||
@@ -56,7 +56,7 @@ Check the *Size requirements* section in [this article][icons].
|
||||
On platforms that have high-DPI support such as Apple Retina displays, you can
|
||||
append `@2x` after image's base filename to mark it as a high resolution image.
|
||||
|
||||
For example if `icon.png` is a normal image that has standard resolution, then
|
||||
For example, if `icon.png` is a normal image that has standard resolution, then
|
||||
`icon@2x.png` will be treated as a high resolution image that has double DPI
|
||||
density.
|
||||
|
||||
@@ -73,11 +73,11 @@ images/
|
||||
|
||||
```javascript
|
||||
const { Tray } = require('electron')
|
||||
let appIcon = new Tray('/Users/somebody/images/icon.png')
|
||||
const appIcon = new Tray('/Users/somebody/images/icon.png')
|
||||
console.log(appIcon)
|
||||
```
|
||||
|
||||
Following suffixes for DPI are also supported:
|
||||
The following suffixes for DPI are also supported:
|
||||
|
||||
* `@1x`
|
||||
* `@1.25x`
|
||||
@@ -97,7 +97,7 @@ Template images consist of black and an alpha channel.
|
||||
Template images are not intended to be used as standalone images and are usually
|
||||
mixed with other content to create the desired final appearance.
|
||||
|
||||
The most common case is to use template images for a menu bar icon so it can
|
||||
The most common case is to use template images for a menu bar icon, so it can
|
||||
adapt to both light and dark menu bars.
|
||||
|
||||
**Note:** Template image is only supported on macOS.
|
||||
@@ -132,7 +132,7 @@ a valid image.
|
||||
```javascript
|
||||
const nativeImage = require('electron').nativeImage
|
||||
|
||||
let image = nativeImage.createFromPath('/Users/somebody/images/icon.png')
|
||||
const image = nativeImage.createFromPath('/Users/somebody/images/icon.png')
|
||||
console.log(image)
|
||||
```
|
||||
|
||||
@@ -180,7 +180,8 @@ Creates a new `NativeImage` instance from the NSImage that maps to the
|
||||
given image name. See [`System Icons`](https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/system-icons/)
|
||||
for a list of possible values.
|
||||
|
||||
The `hslShift` is applied to the image with the following rules
|
||||
The `hslShift` is applied to the image with the following rules:
|
||||
|
||||
* `hsl_shift[0]` (hue): The absolute hue value for the image - 0 and 1 map
|
||||
to 0 and 360 on the hue color wheel (red).
|
||||
* `hsl_shift[1]` (saturation): A saturation shift for the image, with the
|
||||
@@ -248,9 +249,9 @@ Returns `String` - The data URL of the image.
|
||||
|
||||
Returns `Buffer` - A [Buffer][buffer] that contains the image's raw bitmap pixel data.
|
||||
|
||||
The difference between `getBitmap()` and `toBitmap()` is, `getBitmap()` does not
|
||||
The difference between `getBitmap()` and `toBitmap()` is that `getBitmap()` does not
|
||||
copy the bitmap data, so you have to use the returned Buffer immediately in
|
||||
current event loop tick, otherwise the data might be changed or destroyed.
|
||||
current event loop tick; otherwise the data might be changed or destroyed.
|
||||
|
||||
#### `image.getNativeHandle()` _macOS_
|
||||
|
||||
@@ -295,7 +296,7 @@ Returns `NativeImage` - The cropped image.
|
||||
* `width` Integer (optional) - Defaults to the image's width.
|
||||
* `height` Integer (optional) - Defaults to the image's height.
|
||||
* `quality` String (optional) - The desired quality of the resize image.
|
||||
Possible values are `good`, `better` or `best`. The default is `best`.
|
||||
Possible values are `good`, `better`, or `best`. The default is `best`.
|
||||
These values express a desired quality/speed tradeoff. They are translated
|
||||
into an algorithm-specific method that depends on the capabilities
|
||||
(CPU, GPU) of the underlying platform. It is possible for all three methods
|
||||
@@ -328,9 +329,9 @@ can be called on empty images.
|
||||
|
||||
[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer
|
||||
|
||||
### Instance Properties
|
||||
## 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).
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu
|
||||
* `hasReply` Boolean (optional) _macOS_ - Whether or not to add an inline reply option to the notification.
|
||||
* `replyPlaceholder` String (optional) _macOS_ - The placeholder to write in the inline reply input field.
|
||||
* `sound` String (optional) _macOS_ - The name of the sound file to play when the notification is shown.
|
||||
* `urgency` String (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
|
||||
* `actions` [NotificationAction[]](structures/notification-action.md) (optional) _macOS_ - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation.
|
||||
* `closeButtonText` String (optional) _macOS_ - A custom title for the close button of an alert. An empty string will cause the default localized text to be used.
|
||||
|
||||
@@ -144,6 +145,12 @@ A `Boolean` property representing whether the notification is silent.
|
||||
|
||||
A `Boolean` property representing whether the notification has a reply action.
|
||||
|
||||
#### `notification.urgency` _Linux_
|
||||
|
||||
A `String` property representing the urgency level of the notification. Can be 'normal', 'critical', or 'low'.
|
||||
|
||||
Default is 'low' - see [NotifyUrgency](https://developer.gnome.org/notification-spec/#urgency-levels) for more information.
|
||||
|
||||
#### `notification.actions`
|
||||
|
||||
A [`NotificationAction[]`](structures/notification-action.md) property representing the actions of the notification.
|
||||
|
||||
@@ -217,15 +217,11 @@ that all statistics are reported in Kilobytes.
|
||||
|
||||
Returns `String` - The version of the host operating system.
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
|
||||
```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'
|
||||
```
|
||||
* `macOS` -> `10.13.6`
|
||||
* `Windows` -> `10.0.17763`
|
||||
* `Linux` -> `4.15.0-45-generic`
|
||||
|
||||
**Note:** It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`.
|
||||
|
||||
|
||||
@@ -389,7 +389,9 @@ which sends a `Buffer` as a response.
|
||||
* `url` String
|
||||
* `method` String (optional)
|
||||
* `session` Session | null (optional)
|
||||
* `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional)
|
||||
* `uploadData` Object (optional)
|
||||
* `contentType` String - MIME type of the content.
|
||||
* `data` String - Content to be sent.
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ session.defaultSession.on('will-download', (event, item, webContents) => {
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'preconnect' _Experimental_
|
||||
#### Event: 'preconnect'
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -139,9 +139,9 @@ Writes any unwritten DOMStorage data to disk.
|
||||
#### `ses.setProxy(config)`
|
||||
|
||||
* `config` Object
|
||||
* `pacScript` String - The URL associated with the PAC file.
|
||||
* `proxyRules` String - Rules indicating which proxies to use.
|
||||
* `proxyBypassRules` String - Rules indicating which URLs should
|
||||
* `pacScript` String (optional) - The URL associated with the PAC file.
|
||||
* `proxyRules` String (optional) - Rules indicating which proxies to use.
|
||||
* `proxyBypassRules` String (optional) - Rules indicating which URLs should
|
||||
bypass the proxy settings.
|
||||
|
||||
Returns `Promise<void>` - Resolves when the proxy setting process is complete.
|
||||
@@ -252,7 +252,7 @@ window.webContents.session.enableNetworkEmulation({
|
||||
window.webContents.session.enableNetworkEmulation({ offline: true })
|
||||
```
|
||||
|
||||
#### `ses.preconnect(options)` _Experimental_
|
||||
#### `ses.preconnect(options)`
|
||||
|
||||
* `options` Object
|
||||
* `url` String - URL for preconnect. Only the origin is relevant for opening the socket.
|
||||
@@ -267,7 +267,7 @@ the original network configuration.
|
||||
|
||||
#### `ses.setCertificateVerifyProc(proc)`
|
||||
|
||||
* `proc` Function
|
||||
* `proc` Function | null
|
||||
* `request` Object
|
||||
* `hostname` String
|
||||
* `certificate` [Certificate](structures/certificate.md)
|
||||
@@ -408,6 +408,17 @@ Returns `String` - The user agent for this session.
|
||||
|
||||
Returns `Promise<Buffer>` - resolves with blob data.
|
||||
|
||||
#### `ses.downloadURL(url)`
|
||||
|
||||
* `url` String
|
||||
|
||||
Initiates a download of the resource at `url`.
|
||||
The API will generate a [DownloadItem](download-item.md) that can be accessed
|
||||
with the [will-download](#event-will-download) event.
|
||||
|
||||
**Note:** This does not perform any security checks that relate to a page's origin,
|
||||
unlike [`webContents.downloadURL`](web-contents.md#contentsdownloadurlurl).
|
||||
|
||||
#### `ses.createInterruptedDownload(options)`
|
||||
|
||||
* `options` Object
|
||||
@@ -416,8 +427,8 @@ Returns `Promise<Buffer>` - resolves with blob data.
|
||||
* `mimeType` String (optional)
|
||||
* `offset` Integer - Start range for the download.
|
||||
* `length` Integer - Total length of the download.
|
||||
* `lastModified` String - Last-Modified header value.
|
||||
* `eTag` String - ETag header value.
|
||||
* `lastModified` String (optional) - Last-Modified header value.
|
||||
* `eTag` String (optional) - ETag header value.
|
||||
* `startTime` Double (optional) - Time when download was started in
|
||||
number of seconds since UNIX epoch.
|
||||
|
||||
|
||||
@@ -43,11 +43,12 @@ Returns `Promise<void>`
|
||||
|
||||
Open the given external protocol URL in the desktop's default manner. (For example, mailto: URLs in the user's default mail agent).
|
||||
|
||||
### `shell.moveItemToTrash(fullPath)`
|
||||
### `shell.moveItemToTrash(fullPath[, deleteOnFail])`
|
||||
|
||||
* `fullPath` String
|
||||
* `deleteOnFail` Boolean (optional) - Whether or not to unilaterally remove the item if the Trash is disabled or unsupported on the volume. _macOS_
|
||||
|
||||
Returns `Boolean` - Whether the item was successfully moved to the trash.
|
||||
Returns `Boolean` - Whether the item was successfully moved to the trash or otherwise deleted.
|
||||
|
||||
Move the given file to trash and returns a boolean status for the operation.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# InputEvent Object
|
||||
|
||||
* `modifiers` String[] - An array of modifiers of the event, can
|
||||
be `shift`, `control`, `alt`, `meta`, `isKeypad`, `isAutoRepeat`,
|
||||
`leftButtonDown`, `middleButtonDown`, `rightButtonDown`, `capsLock`,
|
||||
`numLock`, `left`, `right`.
|
||||
* `modifiers` String[] (optional) - An array of modifiers of the event, can
|
||||
be `shift`, `control`, `ctrl`, `alt`, `meta`, `command`, `cmd`, `isKeypad`,
|
||||
`isAutoRepeat`, `leftButtonDown`, `middleButtonDown`, `rightButtonDown`,
|
||||
`capsLock`, `numLock`, `left`, `right`.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# NewWindowEvent Object extends `Event`
|
||||
|
||||
* `newGuest` BrowserWindow (optional)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ProtocolResponseUploadData Object
|
||||
|
||||
* `contentType` String - MIME type of the content.
|
||||
* `data` String | Buffer - Content to be sent.
|
||||
* `data` String - Content to be sent.
|
||||
|
||||
@@ -402,6 +402,8 @@ 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
|
||||
|
||||
@@ -11,7 +11,7 @@ Process: [Main](../tutorial/application-architecture.md#main-and-renderer-proces
|
||||
* `backgroundColor` String (optional) - Button background color in hex format,
|
||||
i.e `#ABCDEF`.
|
||||
* `icon` [NativeImage](native-image.md) | String (optional) - Button icon.
|
||||
* `iconPosition` String (optional) - Can be `left`, `right` or `overlay`.
|
||||
* `iconPosition` String (optional) - Can be `left`, `right` or `overlay`. Defaults to `overlay`.
|
||||
* `click` Function (optional) - Function to call when the button is clicked.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
@@ -9,7 +9,7 @@ Process: [Main](../tutorial/application-architecture.md#main-and-renderer-proces
|
||||
* `options` Object
|
||||
* `label` String (optional) - Popover button text.
|
||||
* `icon` [NativeImage](native-image.md) (optional) - Popover button icon.
|
||||
* `items` [TouchBar](touch-bar.md) (optional) - Items to display in the popover.
|
||||
* `items` [TouchBar](touch-bar.md) - Items to display in the popover.
|
||||
* `showCloseButton` Boolean (optional) - `true` to display a close button
|
||||
on the left of the popover, `false` to not show it. Default is `true`.
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ Process: [Main](../tutorial/application-architecture.md#main-and-renderer-proces
|
||||
* `selectedIndex` Integer - The index of the item the user selected.
|
||||
* `highlight` Function (optional) - Called when the user taps any item.
|
||||
* `highlightedIndex` Integer - The index of the item the user touched.
|
||||
* `selectedStyle` String (optional) - Selected item style. Defaults to `null`.
|
||||
* `overlayStyle` String (optional) - Selected overlay item style. Defaults to `null`.
|
||||
* `selectedStyle` String (optional) - Selected item style. Can be `background`, `outline` or `none`. Defaults to `none`.
|
||||
* `overlayStyle` String (optional) - Selected overlay item style. Can be `background`, `outline` or `none`. Defaults to `none`.
|
||||
* `showArrowButtons` Boolean (optional) - Defaults to `false`.
|
||||
* `mode` String (optional) - Defaults to `free`.
|
||||
* `mode` String (optional) - Can be `fixed` or `free`. The default is `free`.
|
||||
* `continuous` Boolean (optional) - Defaults to `true`.
|
||||
|
||||
### Instance Properties
|
||||
@@ -34,7 +34,7 @@ updates the control in the touch bar. Possible values:
|
||||
|
||||
* `background` - Maps to `[NSScrubberSelectionStyle roundedBackgroundStyle]`.
|
||||
* `outline` - Maps to `[NSScrubberSelectionStyle outlineOverlayStyle]`.
|
||||
* `null` - Actually null, not a string, removes all styles.
|
||||
* `none` - Removes all styles.
|
||||
|
||||
#### `touchBarScrubber.overlayStyle`
|
||||
|
||||
@@ -44,7 +44,7 @@ touch bar. Possible values:
|
||||
|
||||
* `background` - Maps to `[NSScrubberSelectionStyle roundedBackgroundStyle]`.
|
||||
* `outline` - Maps to `[NSScrubberSelectionStyle outlineOverlayStyle]`.
|
||||
* `null` - Actually null, not a string, removes all styles.
|
||||
* `none` - Removes all styles.
|
||||
|
||||
#### `touchBarScrubber.showArrowButtons`
|
||||
|
||||
|
||||
@@ -10,23 +10,23 @@ Process: [Main](../tutorial/application-architecture.md#main-and-renderer-proces
|
||||
* `segmentStyle` String (optional) - Style of the segments:
|
||||
* `automatic` - Default. The appearance of the segmented control is
|
||||
automatically determined based on the type of window in which the control
|
||||
is displayed and the position within the window.
|
||||
* `rounded` - The control is displayed using the rounded style.
|
||||
is displayed and the position within the window. Maps to `NSSegmentStyleAutomatic`.
|
||||
* `rounded` - The control is displayed using the rounded style. Maps to `NSSegmentStyleRounded`.
|
||||
* `textured-rounded` - The control is displayed using the textured rounded
|
||||
style.
|
||||
* `round-rect` - The control is displayed using the round rect style.
|
||||
style. Maps to `NSSegmentStyleTexturedRounded`.
|
||||
* `round-rect` - The control is displayed using the round rect style. Maps to `NSSegmentStyleRoundRect`.
|
||||
* `textured-square` - The control is displayed using the textured square
|
||||
style.
|
||||
* `capsule` - The control is displayed using the capsule style.
|
||||
* `small-square` - The control is displayed using the small square style.
|
||||
style. Maps to `NSSegmentStyleTexturedSquare`.
|
||||
* `capsule` - The control is displayed using the capsule style. Maps to `NSSegmentStyleCapsule`.
|
||||
* `small-square` - The control is displayed using the small square style. Maps to `NSSegmentStyleSmallSquare`.
|
||||
* `separated` - The segments in the control are displayed very close to each
|
||||
other but not touching.
|
||||
other but not touching. Maps to `NSSegmentStyleSeparated`.
|
||||
* `mode` String (optional) - The selection mode of the control:
|
||||
* `single` - Default. One item selected at a time, selecting one deselects the previously selected item.
|
||||
* `multiple` - Multiple items can be selected at a time.
|
||||
* `buttons` - Make the segments act as buttons, each segment can be pressed and released but never marked as active.
|
||||
* `single` - Default. One item selected at a time, selecting one deselects the previously selected item. Maps to `NSSegmentSwitchTrackingSelectOne`.
|
||||
* `multiple` - Multiple items can be selected at a time. Maps to `NSSegmentSwitchTrackingSelectAny`.
|
||||
* `buttons` - Make the segments act as buttons, each segment can be pressed and released but never marked as active. Maps to `NSSegmentSwitchTrackingMomentary`.
|
||||
* `segments` [SegmentedControlSegment[]](structures/segmented-control-segment.md) - An array of segments to place in this control.
|
||||
* `selectedIndex` Integer (optional) - The index of the currently selected segment, will update automatically with user interaction. When the mode is multiple it will be the last selected item.
|
||||
* `selectedIndex` Integer (optional) - The index of the currently selected segment, will update automatically with user interaction. When the mode is `multiple` it will be the last selected item.
|
||||
* `change` Function (optional) - Called when the user selects a new segment.
|
||||
* `selectedIndex` Integer - The index of the segment the user selected.
|
||||
* `isSelected` Boolean - Whether as a result of user selection the segment is selected or not.
|
||||
|
||||
@@ -8,6 +8,6 @@ Process: [Main](../tutorial/application-architecture.md#main-and-renderer-proces
|
||||
|
||||
* `options` Object
|
||||
* `size` String (optional) - Size of spacer, possible values are:
|
||||
* `small` - Small space between items.
|
||||
* `large` - Large space between items.
|
||||
* `flexible` - Take up all available space.
|
||||
* `small` - Small space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceSmall`. This is the default.
|
||||
* `large` - Large space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceLarge`.
|
||||
* `flexible` - Take up all available space. Maps to `NSTouchBarItemIdentifierFlexibleSpace`.
|
||||
|
||||
@@ -222,12 +222,31 @@ Returns `Boolean` - Whether double click events will be ignored.
|
||||
#### `tray.displayBalloon(options)` _Windows_
|
||||
|
||||
* `options` Object
|
||||
* `icon` ([NativeImage](native-image.md) | String) (optional) -
|
||||
* `icon` ([NativeImage](native-image.md) | String) (optional) - Icon to use when `iconType` is `custom`.
|
||||
* `iconType` String (optional) - Can be `none`, `info`, `warning`, `error` or `custom`. Default is `custom`.
|
||||
* `title` String
|
||||
* `content` String
|
||||
* `largeIcon` Boolean (optional) - The large version of the icon should be used. Default is `true`. Maps to [`NIIF_LARGE_ICON`][NIIF_LARGE_ICON].
|
||||
* `noSound` Boolean (optional) - Do not play the associated sound. Default is `false`. Maps to [`NIIF_NOSOUND`][NIIF_NOSOUND].
|
||||
* `respectQuietTime` Boolean (optional) - Do not display the balloon notification if the current user is in "quiet time". Default is `false`. Maps to [`NIIF_RESPECT_QUIET_TIME`][NIIF_RESPECT_QUIET_TIME].
|
||||
|
||||
Displays a tray balloon.
|
||||
|
||||
[NIIF_NOSOUND]: https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_nosound-0x00000010
|
||||
[NIIF_LARGE_ICON]: https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_large_icon-0x00000020
|
||||
[NIIF_RESPECT_QUIET_TIME]: https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_respect_quiet_time-0x00000080
|
||||
|
||||
#### `tray.removeBalloon()` _Windows_
|
||||
|
||||
Removes a tray balloon.
|
||||
|
||||
#### `tray.focus()` _Windows_
|
||||
|
||||
Returns focus to the taskbar notification area.
|
||||
Notification area icons should use this message when they have completed their UI operation.
|
||||
For example, if the icon displays a shortcut menu, but the user presses ESC to cancel it,
|
||||
use `tray.focus()` to return focus to the notification area.
|
||||
|
||||
#### `tray.popUpContextMenu([menu, position])` _macOS_ _Windows_
|
||||
|
||||
* `menu` Menu (optional)
|
||||
|
||||
@@ -138,7 +138,7 @@ Emitted when page receives favicon urls.
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` NewWindowEvent
|
||||
* `event` Event
|
||||
* `url` String
|
||||
* `frameName` String
|
||||
* `disposition` String - Can be `default`, `foreground-tab`, `background-tab`,
|
||||
@@ -454,8 +454,10 @@ The usage is the same with [the `select-client-certificate` event of
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `authenticationResponseDetails` Object
|
||||
* `request` Object
|
||||
* `method` String
|
||||
* `url` URL
|
||||
* `referrer` URL
|
||||
* `authInfo` Object
|
||||
* `isProxy` Boolean
|
||||
* `scheme` String
|
||||
@@ -463,8 +465,8 @@ Returns:
|
||||
* `port` Integer
|
||||
* `realm` String
|
||||
* `callback` Function
|
||||
* `username` String (optional)
|
||||
* `password` String (optional)
|
||||
* `username` String
|
||||
* `password` String
|
||||
|
||||
Emitted when `webContents` wants to do basic auth.
|
||||
|
||||
@@ -1066,13 +1068,11 @@ Returns `Boolean` - Whether audio is currently playing.
|
||||
|
||||
#### `contents.setZoomFactor(factor)`
|
||||
|
||||
* `factor` Double - Zoom factor; default is 1.0.
|
||||
* `factor` Number - Zoom factor.
|
||||
|
||||
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,30 +1234,11 @@ 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])`
|
||||
|
||||
@@ -1265,7 +1246,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. Must be the system-defined name and not the 'friendly' name, e.g 'Brother_QL_820NWB' and not 'Brother QL-820NWB'.
|
||||
* `deviceName` String (optional) - Set the printer device name to use. Default is `''`.
|
||||
* `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`.
|
||||
@@ -1283,9 +1264,11 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
|
||||
* `dpi` Object (optional)
|
||||
* `horizontal` Number (optional) - The horizontal dpi.
|
||||
* `vertical` Number (optional) - The vertical dpi.
|
||||
* `header` String (optional) - String to be printed as page header.
|
||||
* `footer` String (optional) - String to be printed as page footer.
|
||||
* `callback` Function (optional)
|
||||
* `success` Boolean - Indicates success of the print call.
|
||||
* `failureReason` String - Error description called back if the print fails.
|
||||
* `failureReason` String - Called back if the print fails; can be `cancelled` or `failed`.
|
||||
|
||||
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.
|
||||
@@ -1344,13 +1327,12 @@ win.loadURL('http://github.com')
|
||||
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
// Use default printing options
|
||||
win.webContents.printToPDF({}).then(data => {
|
||||
win.webContents.printToPDF({}, (error, data) => {
|
||||
if (error) throw error
|
||||
fs.writeFile('/tmp/print.pdf', data, (error) => {
|
||||
if (error) throw error
|
||||
console.log('Write PDF successfully.')
|
||||
})
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
})
|
||||
```
|
||||
@@ -1611,8 +1593,8 @@ End subscribing for frame presentation events.
|
||||
|
||||
* `item` Object
|
||||
* `file` String[] | String - The path(s) to the file(s) being dragged.
|
||||
* `icon` [NativeImage](native-image.md) - The image must be non-empty on
|
||||
macOS.
|
||||
* `icon` [NativeImage](native-image.md) | String - The image must be
|
||||
non-empty on macOS.
|
||||
|
||||
Sets the `item` as dragging item for current drag-drop operation, `file` is the
|
||||
absolute path of the file to be dragged, and `icon` is the image showing under
|
||||
@@ -1783,7 +1765,7 @@ A [`WebContents`](web-contents.md) instance that might own this `WebContents`.
|
||||
|
||||
#### `contents.devToolsWebContents` _Readonly_
|
||||
|
||||
A `WebContents | null` property that represents the of DevTools `WebContents` associated with a given `WebContents`.
|
||||
A `WebContents` of DevTools for this `WebContents`.
|
||||
|
||||
**Note:** Users should never store this object because it may become `null`
|
||||
when the DevTools has been closed.
|
||||
|
||||
@@ -22,13 +22,11 @@ The `WebFrame` class has the following instance methods:
|
||||
|
||||
### `webFrame.setZoomFactor(factor)`
|
||||
|
||||
* `factor` Double - Zoom factor; default is 1.0.
|
||||
* `factor` Number - Zoom factor.
|
||||
|
||||
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.
|
||||
|
||||
@@ -146,8 +146,7 @@ response are visible by the time this listener is fired.
|
||||
* `timestamp` Double
|
||||
* `statusLine` String
|
||||
* `statusCode` Integer
|
||||
* `requestHeaders` Record<string, string>
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `responseHeaders` Record<string, string> (optional)
|
||||
* `callback` Function
|
||||
* `headersReceivedResponse` Object
|
||||
* `cancel` Boolean (optional)
|
||||
@@ -176,7 +175,7 @@ The `callback` has to be called with a `response` object.
|
||||
* `resourceType` String
|
||||
* `referrer` String
|
||||
* `timestamp` Double
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `responseHeaders` Record<string, string> (optional)
|
||||
* `fromCache` Boolean - Indicates whether the response was fetched from disk
|
||||
cache.
|
||||
* `statusCode` Integer
|
||||
@@ -206,7 +205,7 @@ and response headers are available.
|
||||
* `ip` String (optional) - The server IP address that the request was
|
||||
actually sent to.
|
||||
* `fromCache` Boolean
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `responseHeaders` Record<string, string> (optional)
|
||||
|
||||
The `listener` will be called with `listener(details)` when a server initiated
|
||||
redirect is about to occur.
|
||||
@@ -225,11 +224,10 @@ 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.
|
||||
|
||||
@@ -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-ci"
|
||||
export SCCACHE_BUCKET="electronjs-sccache"
|
||||
export SCCACHE_TWO_TIER=true
|
||||
```
|
||||
|
||||
|
||||
@@ -5,8 +5,15 @@ Follow the guidelines below for building Electron on Windows.
|
||||
## Prerequisites
|
||||
|
||||
* Windows 10 / Server 2012 R2 or higher
|
||||
* Visual Studio 2017 15.7.2 or higher - [download VS 2017 Community Edition for
|
||||
* Visual Studio 2017 15.7.2 or higher - [download VS 2019 Community Edition for
|
||||
free](https://www.visualstudio.com/vs/)
|
||||
* See [the Chromium build documentation](https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md#visual-studio) for more details on which Visual Studio
|
||||
components are required.
|
||||
* If your Visual Studio is installed in a directory other than the default, you'll need to
|
||||
set a few environment variables to point the toolchains to your installation path.
|
||||
* `vs2019_install = DRIVE:\path\to\Microsoft Visual Studio\2019\Community`
|
||||
(replace `2019` and `Community` with your installed versions)
|
||||
* `WINDOWSSDKDIR = DRIVE:\path\to\Windows Kits\10`
|
||||
* [Python 2.7.10 or higher](http://www.python.org/download/releases/2.7/)
|
||||
* Contrary to the `depot_tools` setup instructions linked below, you will need
|
||||
to use your locally installed Python with at least version 2.7.10 (with
|
||||
@@ -113,3 +120,10 @@ This can happen during build, when Debugging Tools for Windows has been installe
|
||||
### ImportError: No module named win32file
|
||||
|
||||
Make sure you have installed `pywin32` with `pip install pywin32`.
|
||||
|
||||
### Build Scripts Hang Until Keypress
|
||||
|
||||
This bug is a "feature" of Windows' command prompt. It happens when clicking inside the prompt window with
|
||||
`QuickEdit` enabled and is intended to allow selecting and copying output text easily.
|
||||
Since each accidental click will pause the build process, you might want to disable this
|
||||
feature in the command prompt properties.
|
||||
|
||||
@@ -48,3 +48,12 @@ would run `npm run test -- -g ipc`.
|
||||
1. Push the Windows key and search for _Manage optional features_.
|
||||
1. Click _Add a feature_.
|
||||
1. Select _Japanese Supplemental Fonts_ and click _Install_.
|
||||
|
||||
|
||||
Some tests which rely on precise pixel measurements may not work correctly on
|
||||
devices with Hi-DPI screen settings due to floating point precision errors.
|
||||
To run these tests correctly, make sure the device is set to 100% scaling.
|
||||
|
||||
To configure display scaling:
|
||||
1. Push the Windows key and search for _Display settings_.
|
||||
1. Under _Scale and layout_, make sure that the device is set to 100%.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 868 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB |
@@ -31,8 +31,7 @@ unifies the existing (and well maintained) build tools for Electron development
|
||||
into a cohesive package so that anyone can jump right in to Electron
|
||||
development.
|
||||
|
||||
Forge comes with [ready-to-use templates](https://electronforge.io/templates) for popular
|
||||
frameworks like React, Vue, or Angular. It uses the same core modules used by the
|
||||
Forge comes with [a ready-to-use template](https://electronforge.io/templates) using Webpack as a bundler. It includes an example typescript configuration and provides two configuration files to enable easy customization. It uses the same core modules used by the
|
||||
greater Electron community (like [`electron-packager`](https://github.com/electron/electron-packager)) –
|
||||
changes made by Electron maintainers (like Slack) benefit Forge's users, too.
|
||||
|
||||
|
||||
@@ -195,6 +195,8 @@ Finally the `index.html` is the web page you want to show:
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello World!</title>
|
||||
<!-- https://electronjs.org/docs/tutorial/security#csp-meta-tag -->
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self';" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
|
||||
@@ -38,15 +38,11 @@ npm install --platform=win32 electron
|
||||
|
||||
## Proxies
|
||||
|
||||
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]
|
||||
If you need to use an HTTP proxy you can [set these environment variables][proxy-env].
|
||||
|
||||
## Custom Mirrors and Caches
|
||||
During installation, the `electron` module will call out to
|
||||
[`@electron/get`][electron-get] to download prebuilt binaries of
|
||||
[`electron-download`][electron-download] 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).
|
||||
@@ -56,7 +52,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/get`
|
||||
look for Electron binaries, and the binary filename. The url used by `electron-download`
|
||||
is composed as follows:
|
||||
|
||||
```plaintext
|
||||
@@ -66,11 +62,11 @@ url = ELECTRON_MIRROR + ELECTRON_CUSTOM_DIR + '/' + ELECTRON_CUSTOM_FILENAME
|
||||
For instance, to use the China mirror:
|
||||
|
||||
```plaintext
|
||||
ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/"
|
||||
ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
|
||||
```
|
||||
|
||||
#### Cache
|
||||
Alternatively, you can override the local cache. `@electron/get` will cache
|
||||
Alternatively, you can override the local cache. `electron-download` 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.
|
||||
@@ -89,26 +85,16 @@ 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
|
||||
├── 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
|
||||
├── 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
|
||||
```
|
||||
|
||||
## Skip binary download
|
||||
@@ -160,8 +146,7 @@ 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-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
|
||||
[proxy-env]: https://github.com/request/request/tree/f0c4ec061141051988d1216c24936ad2e7d5c45d#controlling-proxy-behaviour-using-environment-variables
|
||||
[electron-download]: https://github.com/electron-userland/electron-download
|
||||
[npm-permissions]: https://docs.npmjs.com/getting-started/fixing-npm-permissions
|
||||
[unsafe-perm]: https://docs.npmjs.com/misc/config#unsafe-perm
|
||||
|
||||
@@ -1,431 +0,0 @@
|
||||
# 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
|
||||
@@ -43,7 +43,8 @@ When releasing your product, you’re also shipping a bundle composed of Electro
|
||||
Chromium shared library and Node.js. Vulnerabilities affecting these components
|
||||
may impact the security of your application. By updating Electron to the latest
|
||||
version, you ensure that critical vulnerabilities (such as *nodeIntegration bypasses*)
|
||||
are already patched and cannot be exploited in your application.
|
||||
are already patched and cannot be exploited in your application. For more information,
|
||||
see "[Use a current version of Electron](#17-use-a-current-version-of-electron)".
|
||||
|
||||
* **Evaluate your dependencies.** While NPM provides half a million reusable packages,
|
||||
it is your responsibility to choose trusted 3rd-party libraries. If you use outdated
|
||||
@@ -101,6 +102,7 @@ You should at least follow these steps to improve the security of your applicati
|
||||
14. [Do not use `openExternal` with untrusted content](#14-do-not-use-openexternal-with-untrusted-content)
|
||||
15. [Disable the `remote` module](#15-disable-the-remote-module)
|
||||
16. [Filter the `remote` module](#16-filter-the-remote-module)
|
||||
17. [Use a current version of Electron](#17-use-a-current-version-of-electron)
|
||||
|
||||
To automate the detection of misconfigurations and insecure patterns, it is
|
||||
possible to use
|
||||
@@ -511,7 +513,7 @@ no circumstances should you enable features speculatively.
|
||||
// Bad
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
enableBlinkFeatures: ['ExecCommandInJavaScript']
|
||||
enableBlinkFeatures: 'ExecCommandInJavaScript'
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -830,6 +832,29 @@ app.on('remote-get-guest-web-contents', (event, webContents, guestWebContents) =
|
||||
})
|
||||
```
|
||||
|
||||
## 17) Use a current version of Electron
|
||||
|
||||
You should strive for always using the latest available version of Electron.
|
||||
Whenever a new major version is released, you should attempt to update your
|
||||
app as quickly as possible.
|
||||
|
||||
### Why?
|
||||
|
||||
An application built with an older version of Electron, Chromium, and Node.js
|
||||
is an easier target than an application that is using more recent versions of
|
||||
those components. Generally speaking, security issues and exploits for older
|
||||
versions of Chromium and Node.js are more widely available.
|
||||
|
||||
Both Chromium and Node.js are impressive feats of engineering built by
|
||||
thousands of talented developers. Given their popularity, their security is
|
||||
carefully tested and analyzed by equally skilled security researchers. Many of
|
||||
those researchers [disclose vulnerabilities responsibly][responsible-disclosure],
|
||||
which generally means that researchers will give Chromium and Node.js some time
|
||||
to fix issues before publishing them. Your application will be more secure if
|
||||
it is running a recent version of Electron (and thus, Chromium and Node.js) for
|
||||
which potential security issues are not as widely known.
|
||||
|
||||
|
||||
[browser-window]: ../api/browser-window.md
|
||||
[browser-view]: ../api/browser-view.md
|
||||
[webview-tag]: ../api/webview-tag.md
|
||||
@@ -838,3 +863,4 @@ app.on('remote-get-guest-web-contents', (event, webContents, guestWebContents) =
|
||||
[will-navigate]: ../api/web-contents.md#event-will-navigate
|
||||
[open-external]: ../api/shell.md#shellopenexternalurl-options-callback
|
||||
[sandbox]: ../api/sandbox-option.md
|
||||
[responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure
|
||||
|
||||
@@ -14,7 +14,6 @@ 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",
|
||||
@@ -96,7 +95,6 @@ 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,24 +130,22 @@ auto_filenames = {
|
||||
"lib/browser/api/module-keys.js",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.js",
|
||||
"lib/common/api/module-list.ts",
|
||||
"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/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/is-promise.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/remote/buffer-utils.ts",
|
||||
"lib/common/remote/is-promise.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",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/callbacks-registry.ts",
|
||||
"lib/renderer/chrome-api.ts",
|
||||
"lib/renderer/content-scripts-injector.ts",
|
||||
"lib/renderer/extensions/event.ts",
|
||||
@@ -159,6 +155,7 @@ auto_filenames = {
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/remote/callbacks-registry.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
"lib/renderer/web-frame-init.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.ts",
|
||||
@@ -167,9 +164,8 @@ 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/api/exports/electron.ts",
|
||||
"lib/sandboxed_renderer/api/module-list.ts",
|
||||
"lib/sandboxed_renderer/init.js",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
@@ -178,9 +174,13 @@ 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",
|
||||
@@ -189,9 +189,7 @@ 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",
|
||||
@@ -216,7 +214,7 @@ auto_filenames = {
|
||||
"lib/browser/api/content-tracing.js",
|
||||
"lib/browser/api/crash-reporter.js",
|
||||
"lib/browser/api/dialog.js",
|
||||
"lib/browser/api/exports/electron.js",
|
||||
"lib/browser/api/exports/electron.ts",
|
||||
"lib/browser/api/global-shortcut.js",
|
||||
"lib/browser/api/in-app-purchase.js",
|
||||
"lib/browser/api/ipc-main.ts",
|
||||
@@ -224,7 +222,7 @@ auto_filenames = {
|
||||
"lib/browser/api/menu-item.js",
|
||||
"lib/browser/api/menu-utils.js",
|
||||
"lib/browser/api/menu.js",
|
||||
"lib/browser/api/module-list.js",
|
||||
"lib/browser/api/module-list.ts",
|
||||
"lib/browser/api/native-theme.ts",
|
||||
"lib/browser/api/net-log.js",
|
||||
"lib/browser/api/net.js",
|
||||
@@ -256,29 +254,30 @@ auto_filenames = {
|
||||
"lib/browser/guest-view-manager.js",
|
||||
"lib/browser/guest-window-manager.js",
|
||||
"lib/browser/init.ts",
|
||||
"lib/browser/ipc-main-impl.ts",
|
||||
"lib/browser/ipc-main-internal-utils.ts",
|
||||
"lib/browser/ipc-main-internal.ts",
|
||||
"lib/browser/navigation-controller.js",
|
||||
"lib/browser/objects-registry.js",
|
||||
"lib/browser/remote/objects-registry.js",
|
||||
"lib/browser/remote/server.js",
|
||||
"lib/browser/rpc-server.js",
|
||||
"lib/browser/utils.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/exports/electron.js",
|
||||
"lib/common/api/module-list.js",
|
||||
"lib/common/api/module-list.ts",
|
||||
"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/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/is-promise.ts",
|
||||
"lib/common/parse-features-string.js",
|
||||
"lib/common/remote/buffer-utils.ts",
|
||||
"lib/common/remote/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/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"package.json",
|
||||
@@ -290,29 +289,26 @@ auto_filenames = {
|
||||
"lib/browser/api/module-keys.js",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/exports/electron.js",
|
||||
"lib/common/api/module-list.js",
|
||||
"lib/common/api/module-list.ts",
|
||||
"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/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/is-promise.ts",
|
||||
"lib/common/remote/buffer-utils.ts",
|
||||
"lib/common/remote/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",
|
||||
"lib/renderer/api/ipc-renderer.js",
|
||||
"lib/renderer/api/module-list.js",
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/callbacks-registry.ts",
|
||||
"lib/renderer/chrome-api.ts",
|
||||
"lib/renderer/content-scripts-injector.ts",
|
||||
"lib/renderer/extensions/event.ts",
|
||||
@@ -323,6 +319,7 @@ auto_filenames = {
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/remote/callbacks-registry.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
"lib/renderer/web-frame-init.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.ts",
|
||||
@@ -342,30 +339,28 @@ auto_filenames = {
|
||||
"lib/browser/api/module-keys.js",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/exports/electron.js",
|
||||
"lib/common/api/module-list.js",
|
||||
"lib/common/api/module-list.ts",
|
||||
"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/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/is-promise.ts",
|
||||
"lib/common/remote/buffer-utils.ts",
|
||||
"lib/common/remote/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",
|
||||
"lib/renderer/api/ipc-renderer.js",
|
||||
"lib/renderer/api/module-list.js",
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/callbacks-registry.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/remote/callbacks-registry.ts",
|
||||
"lib/renderer/webpack-provider.ts",
|
||||
"lib/worker/init.js",
|
||||
"package.json",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
filenames = {
|
||||
default_app_ts_sources = [
|
||||
"default_app/default_app.ts",
|
||||
"default_app/index.ts",
|
||||
"default_app/main.ts",
|
||||
"default_app/preload.ts",
|
||||
]
|
||||
@@ -93,8 +94,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_loader.cc",
|
||||
"shell/browser/api/atom_api_url_loader.h",
|
||||
"shell/browser/api/atom_api_url_request_ns.cc",
|
||||
"shell/browser/api/atom_api_url_request_ns.h",
|
||||
"shell/browser/api/atom_api_view.cc",
|
||||
"shell/browser/api/atom_api_view.h",
|
||||
"shell/browser/api/atom_api_web_contents.cc",
|
||||
@@ -129,6 +130,10 @@ filenames = {
|
||||
"shell/browser/auto_updater.cc",
|
||||
"shell/browser/auto_updater.h",
|
||||
"shell/browser/auto_updater_mac.mm",
|
||||
"shell/browser/atom_autofill_driver_factory.cc",
|
||||
"shell/browser/atom_autofill_driver_factory.h",
|
||||
"shell/browser/atom_autofill_driver.cc",
|
||||
"shell/browser/atom_autofill_driver.h",
|
||||
"shell/browser/atom_browser_client.cc",
|
||||
"shell/browser/atom_browser_client.h",
|
||||
"shell/browser/atom_browser_context.cc",
|
||||
@@ -221,8 +226,6 @@ filenames = {
|
||||
"shell/browser/net/cert_verifier_client.h",
|
||||
"shell/browser/net/proxying_url_loader_factory.cc",
|
||||
"shell/browser/net/proxying_url_loader_factory.h",
|
||||
"shell/browser/net/proxying_websocket.cc",
|
||||
"shell/browser/net/proxying_websocket.h",
|
||||
"shell/browser/net/network_context_service_factory.cc",
|
||||
"shell/browser/net/network_context_service_factory.h",
|
||||
"shell/browser/net/network_context_service.cc",
|
||||
@@ -235,7 +238,6 @@ filenames = {
|
||||
"shell/browser/net/system_network_context_manager.h",
|
||||
"shell/browser/net/url_pipe_loader.cc",
|
||||
"shell/browser/net/url_pipe_loader.h",
|
||||
"shell/browser/net/web_request_api_interface.h",
|
||||
"shell/browser/notifications/linux/libnotify_notification.cc",
|
||||
"shell/browser/notifications/linux/libnotify_notification.h",
|
||||
"shell/browser/notifications/linux/notification_presenter_linux.cc",
|
||||
@@ -356,7 +358,6 @@ 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",
|
||||
@@ -384,8 +385,6 @@ 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",
|
||||
@@ -436,17 +435,11 @@ filenames = {
|
||||
"shell/common/api/electron_bindings.cc",
|
||||
"shell/common/api/electron_bindings.h",
|
||||
"shell/common/api/constructor.h",
|
||||
"shell/common/api/event_emitter_caller.cc",
|
||||
"shell/common/api/event_emitter_caller.h",
|
||||
"shell/common/api/event_emitter_caller_deprecated.cc",
|
||||
"shell/common/api/event_emitter_caller_deprecated.h",
|
||||
"shell/common/api/features.cc",
|
||||
"shell/common/api/locker.cc",
|
||||
"shell/common/api/locker.h",
|
||||
"shell/common/api/object_life_monitor.cc",
|
||||
"shell/common/api/object_life_monitor.h",
|
||||
"shell/common/api/remote_callback_freer.cc",
|
||||
"shell/common/api/remote_callback_freer.h",
|
||||
"shell/common/api/remote_object_freer.cc",
|
||||
"shell/common/api/remote_object_freer.h",
|
||||
"shell/common/asar/archive.cc",
|
||||
"shell/common/asar/archive.h",
|
||||
"shell/common/asar/asar_util.cc",
|
||||
@@ -476,12 +469,34 @@ 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/callback_converter.h",
|
||||
"shell/common/gin_converters/file_dialog_converter.cc",
|
||||
"shell/common/gin_converters/file_dialog_converter.h",
|
||||
"shell/common/gin_converters/file_path_converter.h",
|
||||
"shell/common/gin_converters/gurl_converter.h",
|
||||
"shell/common/gin_converters/image_converter.cc",
|
||||
"shell/common/gin_converters/image_converter.h",
|
||||
"shell/common/gin_converters/native_mate_handle_converter.h",
|
||||
"shell/common/gin_converters/message_box_converter.cc",
|
||||
"shell/common/gin_converters/message_box_converter.h",
|
||||
"shell/common/gin_converters/native_window_converter.h",
|
||||
"shell/common/gin_converters/net_converter.cc",
|
||||
"shell/common/gin_converters/net_converter.h",
|
||||
"shell/common/gin_converters/std_converter.h",
|
||||
"shell/common/gin_converters/value_converter_gin_adapter.h",
|
||||
"shell/common/gin_util.h",
|
||||
"shell/common/gin_helper/callback.cc",
|
||||
"shell/common/gin_helper/callback.h",
|
||||
"shell/common/gin_helper/destroyable.cc",
|
||||
"shell/common/gin_helper/destroyable.h",
|
||||
"shell/common/gin_helper/dictionary.h",
|
||||
"shell/common/gin_helper/error_thrower.cc",
|
||||
"shell/common/gin_helper/error_thrower.h",
|
||||
"shell/common/gin_helper/event_emitter_caller.cc",
|
||||
"shell/common/gin_helper/event_emitter_caller.h",
|
||||
"shell/common/gin_helper/function_template.cc",
|
||||
"shell/common/gin_helper/function_template.h",
|
||||
"shell/common/gin_helper/object_template_builder.cc",
|
||||
"shell/common/gin_helper/object_template_builder.h",
|
||||
"shell/common/heap_snapshot.cc",
|
||||
"shell/common/heap_snapshot.h",
|
||||
"shell/common/key_weak_map.h",
|
||||
@@ -489,10 +504,6 @@ 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",
|
||||
@@ -501,22 +512,18 @@ filenames = {
|
||||
"shell/common/native_mate_converters/accelerator_converter.h",
|
||||
"shell/common/native_mate_converters/blink_converter.cc",
|
||||
"shell/common/native_mate_converters/blink_converter.h",
|
||||
"shell/common/native_mate_converters/callback.cc",
|
||||
"shell/common/native_mate_converters/callback.h",
|
||||
"shell/common/native_mate_converters/callback_converter_deprecated.cc",
|
||||
"shell/common/native_mate_converters/callback_converter_deprecated.h",
|
||||
"shell/common/native_mate_converters/content_converter.cc",
|
||||
"shell/common/native_mate_converters/content_converter.h",
|
||||
"shell/common/native_mate_converters/file_dialog_converter.cc",
|
||||
"shell/common/native_mate_converters/file_dialog_converter.h",
|
||||
"shell/common/native_mate_converters/message_box_converter.cc",
|
||||
"shell/common/native_mate_converters/message_box_converter.h",
|
||||
"shell/common/native_mate_converters/file_path_converter.h",
|
||||
"shell/common/native_mate_converters/gfx_converter.cc",
|
||||
"shell/common/native_mate_converters/gfx_converter.h",
|
||||
"shell/common/native_mate_converters/gurl_converter.h",
|
||||
"shell/common/native_mate_converters/image_converter.cc",
|
||||
"shell/common/native_mate_converters/image_converter.h",
|
||||
"shell/common/native_mate_converters/map_converter.h",
|
||||
"shell/common/native_mate_converters/net_converter.cc",
|
||||
"shell/common/native_mate_converters/native_window_converter.h",
|
||||
"shell/common/native_mate_converters/net_converter.h",
|
||||
"shell/common/native_mate_converters/network_converter.cc",
|
||||
"shell/common/native_mate_converters/network_converter.h",
|
||||
@@ -546,10 +553,6 @@ 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 = ['certificate-error', 'select-client-certificate'];
|
||||
const events = ['login', 'certificate-error', 'select-client-certificate']
|
||||
for (const name of events) {
|
||||
app.on(name as 'certificate-error', (event, webContents, ...args: any[]) => {
|
||||
webContents.emit(name, event, ...args);
|
||||
});
|
||||
app.on(name as 'login', (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.autoHideMenuBar && this.isMenuBarVisible()) {
|
||||
this.setMenuBarVisibility(false);
|
||||
if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && 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,142 @@ BrowserWindow.prototype._init = function () {
|
||||
// Finder, we still do it on all platforms in case of other bugs we don't
|
||||
// know.
|
||||
this.webContents.once('load-url', function () {
|
||||
this.focus();
|
||||
});
|
||||
this.focus()
|
||||
})
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event) => {
|
||||
app.emit('browser-window-blur', event, this);
|
||||
});
|
||||
app.emit('browser-window-blur', event, this)
|
||||
})
|
||||
this.on('focus', (event) => {
|
||||
app.emit('browser-window-focus', event, this);
|
||||
});
|
||||
app.emit('browser-window-focus', event, this)
|
||||
})
|
||||
|
||||
// Subscribe to visibilityState changes and pass to renderer process.
|
||||
let isVisible = this.isVisible() && !this.isMinimized();
|
||||
let isVisible = this.isVisible() && !this.isMinimized()
|
||||
const visibilityChanged = () => {
|
||||
const newState = this.isVisible() && !this.isMinimized();
|
||||
const newState = this.isVisible() && !this.isMinimized()
|
||||
if (isVisible !== newState) {
|
||||
isVisible = newState;
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden';
|
||||
this.webContents.emit('-window-visibility-change', visibilityState);
|
||||
isVisible = newState
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden'
|
||||
this.webContents.emit('-window-visibility-change', visibilityState)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']
|
||||
for (const event of visibilityEvents) {
|
||||
this.on(event, visibilityChanged);
|
||||
this.on(event, visibilityChanged)
|
||||
}
|
||||
|
||||
// Notify the creation of the window.
|
||||
const event = process.electronBinding('event').createEmpty();
|
||||
app.emit('browser-window-created', event, this);
|
||||
const event = process.electronBinding('event').createEmpty()
|
||||
app.emit('browser-window-created', event, this)
|
||||
|
||||
Object.defineProperty(this, 'devToolsWebContents', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get () {
|
||||
return this.webContents.devToolsWebContents;
|
||||
return this.webContents.devToolsWebContents
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
const isBrowserWindow = (win) => {
|
||||
return win && win.constructor.name === 'BrowserWindow';
|
||||
};
|
||||
return win && win.constructor.name === 'BrowserWindow'
|
||||
}
|
||||
|
||||
BrowserWindow.fromId = (id) => {
|
||||
const win = TopLevelWindow.fromId(id);
|
||||
return isBrowserWindow(win) ? win : null;
|
||||
};
|
||||
const win = TopLevelWindow.fromId(id)
|
||||
return isBrowserWindow(win) ? win : null
|
||||
}
|
||||
|
||||
BrowserWindow.getAllWindows = () => {
|
||||
return TopLevelWindow.getAllWindows().filter(isBrowserWindow);
|
||||
};
|
||||
return TopLevelWindow.getAllWindows().filter(isBrowserWindow)
|
||||
}
|
||||
|
||||
BrowserWindow.getFocusedWindow = () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.webContents && window.webContents.equal(webContents)) return window;
|
||||
if (window.webContents.equal(webContents)) return window
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
BrowserWindow.fromBrowserView = (browserView) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.getBrowserView() === browserView) return window;
|
||||
if (window.getBrowserView() === browserView) return window
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
// Helpers.
|
||||
Object.assign(BrowserWindow.prototype, {
|
||||
loadURL (...args) {
|
||||
return this.webContents.loadURL(...args);
|
||||
return this.webContents.loadURL(...args)
|
||||
},
|
||||
getURL (...args) {
|
||||
return this.webContents.getURL();
|
||||
return this.webContents.getURL()
|
||||
},
|
||||
loadFile (...args) {
|
||||
return this.webContents.loadFile(...args);
|
||||
return this.webContents.loadFile(...args)
|
||||
},
|
||||
reload (...args) {
|
||||
return this.webContents.reload(...args);
|
||||
return this.webContents.reload(...args)
|
||||
},
|
||||
send (...args) {
|
||||
return this.webContents.send(...args);
|
||||
return this.webContents.send(...args)
|
||||
},
|
||||
openDevTools (...args) {
|
||||
return this.webContents.openDevTools(...args);
|
||||
return this.webContents.openDevTools(...args)
|
||||
},
|
||||
closeDevTools () {
|
||||
return this.webContents.closeDevTools();
|
||||
return this.webContents.closeDevTools()
|
||||
},
|
||||
isDevToolsOpened () {
|
||||
return this.webContents.isDevToolsOpened();
|
||||
return this.webContents.isDevToolsOpened()
|
||||
},
|
||||
isDevToolsFocused () {
|
||||
return this.webContents.isDevToolsFocused();
|
||||
return this.webContents.isDevToolsFocused()
|
||||
},
|
||||
toggleDevTools () {
|
||||
return this.webContents.toggleDevTools();
|
||||
return this.webContents.toggleDevTools()
|
||||
},
|
||||
inspectElement (...args) {
|
||||
return this.webContents.inspectElement(...args);
|
||||
return this.webContents.inspectElement(...args)
|
||||
},
|
||||
inspectSharedWorker () {
|
||||
return this.webContents.inspectSharedWorker();
|
||||
return this.webContents.inspectSharedWorker()
|
||||
},
|
||||
inspectServiceWorker () {
|
||||
return this.webContents.inspectServiceWorker();
|
||||
return this.webContents.inspectServiceWorker()
|
||||
},
|
||||
showDefinitionForSelection () {
|
||||
return this.webContents.showDefinitionForSelection();
|
||||
return this.webContents.showDefinitionForSelection()
|
||||
},
|
||||
capturePage (...args) {
|
||||
return this.webContents.capturePage(...args);
|
||||
return this.webContents.capturePage(...args)
|
||||
},
|
||||
setTouchBar (touchBar) {
|
||||
electron.TouchBar._setOnWindow(touchBar, this);
|
||||
electron.TouchBar._setOnWindow(touchBar, this)
|
||||
},
|
||||
setBackgroundThrottling (allowed) {
|
||||
this.webContents.setBackgroundThrottling(allowed);
|
||||
this.webContents.setBackgroundThrottling(allowed)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Deprecations
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar');
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable');
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable');
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable');
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable');
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable');
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable');
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable')
|
||||
|
||||
module.exports = BrowserWindow;
|
||||
module.exports = BrowserWindow
|
||||
|
||||
@@ -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,27 +1,41 @@
|
||||
'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 = {
|
||||
const DialogType = {
|
||||
OPEN: 'OPEN',
|
||||
SAVE: 'SAVE'
|
||||
}
|
||||
|
||||
const saveFileDialogProperties = {
|
||||
createDirectory: 1 << 0,
|
||||
showHiddenFiles: 1 << 1,
|
||||
treatPackageAsDirectory: 1 << 2,
|
||||
showOverwriteConfirmation: 1 << 3,
|
||||
dontAddToRecent: 1 << 4
|
||||
}
|
||||
|
||||
const openFileDialogProperties = {
|
||||
openFile: 1 << 0,
|
||||
openDirectory: 1 << 1,
|
||||
multiSelections: 1 << 2,
|
||||
createDirectory: 1 << 3,
|
||||
createDirectory: 1 << 3, // macOS
|
||||
showHiddenFiles: 1 << 4,
|
||||
promptToCreate: 1 << 5,
|
||||
noResolveAliases: 1 << 6,
|
||||
treatPackageAsDirectory: 1 << 7
|
||||
};
|
||||
promptToCreate: 1 << 5, // Windows
|
||||
noResolveAliases: 1 << 6, // macOS
|
||||
treatPackageAsDirectory: 1 << 7, // macOS
|
||||
dontAddToRecent: 1 << 8 // Windows
|
||||
}
|
||||
|
||||
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,64 +44,79 @@ 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 setupDialogProperties = (type, properties) => {
|
||||
const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties
|
||||
let dialogProperties = 0
|
||||
for (const prop in dialogPropertiesTypes) {
|
||||
if (properties.includes(prop)) {
|
||||
dialogProperties |= dialogPropertiesTypes[prop]
|
||||
}
|
||||
}
|
||||
|
||||
return dialogProperties
|
||||
}
|
||||
|
||||
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 = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = [],
|
||||
title = '',
|
||||
message = '',
|
||||
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 }
|
||||
settings.properties = setupDialogProperties(DialogType.SAVE, properties)
|
||||
|
||||
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 +127,33 @@ 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;
|
||||
for (const prop in fileDialogProperties) {
|
||||
if (properties.includes(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 = setupDialogProperties(DialogType.OPEN, properties)
|
||||
|
||||
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,104 +163,101 @@ 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 noLink !== 'boolean') throw new TypeError('noLink must be a boolean');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
if (typeof detail !== 'string') throw new TypeError('Detail must be a string');
|
||||
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string');
|
||||
const messageBoxType = messageBoxTypes.indexOf(type)
|
||||
if (messageBoxType === -1) throw new TypeError('Invalid message box type')
|
||||
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array')
|
||||
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey)
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string')
|
||||
if (typeof 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;
|
||||
if (checkboxChecked && !checkboxLabel) {
|
||||
throw new Error('checkboxChecked requires that checkboxLabel also be passed');
|
||||
}
|
||||
checkboxChecked = !!checkboxChecked
|
||||
|
||||
// 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,
|
||||
noLink,
|
||||
flags,
|
||||
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
|
||||
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 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
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');
|
||||
|
||||
// Import common modules.
|
||||
common.defineProperties(exports);
|
||||
|
||||
for (const module of moduleList) {
|
||||
Object.defineProperty(exports, module.name, {
|
||||
enumerable: !module.private,
|
||||
get: common.handleESModule(module.loader)
|
||||
});
|
||||
}
|
||||
6
lib/browser/api/exports/electron.ts
Normal file
6
lib/browser/api/exports/electron.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { defineProperties } from '@electron/internal/common/define-properties'
|
||||
import { commonModuleList } from '@electron/internal/common/api/module-list'
|
||||
import { browserModuleList } from '@electron/internal/browser/api/module-list'
|
||||
|
||||
defineProperties(exports, commonModuleList)
|
||||
defineProperties(exports, browserModuleList)
|
||||
@@ -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,8 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { IpcMainInvokeEvent } from 'electron';
|
||||
import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'
|
||||
|
||||
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}'`);
|
||||
}
|
||||
if (typeof fn !== 'function') {
|
||||
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)));
|
||||
} catch (err) {
|
||||
(e as any)._throw(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => {
|
||||
this.handle(method, (e, ...args) => {
|
||||
this.removeHandler(method);
|
||||
return fn(e, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
removeHandler (method: string) {
|
||||
this._invokeHandlers.delete(method);
|
||||
}
|
||||
}
|
||||
|
||||
const ipcMain = new IpcMain();
|
||||
const ipcMain = new IpcMainImpl()
|
||||
|
||||
// Do not throw exception when channel name is "error".
|
||||
ipcMain.on('error', () => {});
|
||||
ipcMain.on('error', () => {})
|
||||
|
||||
export default ipcMain;
|
||||
export default ipcMain
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
const { app } = require('electron');
|
||||
const { app } = require('electron')
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isLinux = process.platform === 'linux';
|
||||
const isMac = process.platform === 'darwin'
|
||||
const isWindows = process.platform === 'win32'
|
||||
const isLinux = process.platform === 'linux'
|
||||
|
||||
const roles = {
|
||||
about: {
|
||||
get label () {
|
||||
return isLinux ? 'About' : `About ${app.name}`;
|
||||
return isLinux ? 'About' : `About ${app.name}`
|
||||
}
|
||||
},
|
||||
close: {
|
||||
@@ -38,7 +38,7 @@ const roles = {
|
||||
accelerator: 'Shift+CmdOrCtrl+R',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: (window) => {
|
||||
window.webContents.reloadIgnoringCache();
|
||||
window.webContents.reloadIgnoringCache()
|
||||
}
|
||||
},
|
||||
front: {
|
||||
@@ -49,7 +49,7 @@ const roles = {
|
||||
},
|
||||
hide: {
|
||||
get label () {
|
||||
return `Hide ${app.name}`;
|
||||
return `Hide ${app.name}`
|
||||
},
|
||||
accelerator: 'Command+H'
|
||||
},
|
||||
@@ -77,9 +77,9 @@ const roles = {
|
||||
quit: {
|
||||
get label () {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return `Quit ${app.name}`;
|
||||
case 'win32': return 'Exit';
|
||||
default: return 'Quit';
|
||||
case 'darwin': return `Quit ${app.name}`
|
||||
case 'win32': return 'Exit'
|
||||
default: return 'Quit'
|
||||
}
|
||||
},
|
||||
accelerator: isWindows ? undefined : 'CommandOrControl+Q',
|
||||
@@ -101,7 +101,7 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+0',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel = 0;
|
||||
webContents.zoomLevel = 0
|
||||
}
|
||||
},
|
||||
selectall: {
|
||||
@@ -134,7 +134,7 @@ const roles = {
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: isMac ? 'Control+Command+F' : 'F11',
|
||||
windowMethod: (window) => {
|
||||
window.setFullScreen(!window.isFullScreen());
|
||||
window.setFullScreen(!window.isFullScreen())
|
||||
}
|
||||
},
|
||||
undo: {
|
||||
@@ -156,7 +156,7 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+Plus',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel += 0.5;
|
||||
webContents.zoomLevel += 0.5
|
||||
}
|
||||
},
|
||||
zoomout: {
|
||||
@@ -164,13 +164,13 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+-',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel -= 0.5;
|
||||
webContents.zoomLevel -= 0.5
|
||||
}
|
||||
},
|
||||
// App submenu should be used for Mac only
|
||||
appmenu: {
|
||||
get label () {
|
||||
return app.name;
|
||||
return app.name
|
||||
},
|
||||
submenu: [
|
||||
{ role: 'about' },
|
||||
@@ -249,71 +249,71 @@ const roles = {
|
||||
])
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.roleList = roles;
|
||||
exports.roleList = roles
|
||||
|
||||
const canExecuteRole = (role) => {
|
||||
if (!roles.hasOwnProperty(role)) return false;
|
||||
if (!isMac) return true;
|
||||
if (!roles.hasOwnProperty(role)) return false
|
||||
if (!isMac) return true
|
||||
|
||||
// macOS handles all roles natively except for a few
|
||||
return roles[role].nonNativeMacOSRole;
|
||||
};
|
||||
return roles[role].nonNativeMacOSRole
|
||||
}
|
||||
|
||||
exports.getDefaultLabel = (role) => {
|
||||
return roles.hasOwnProperty(role) ? roles[role].label : '';
|
||||
};
|
||||
return roles.hasOwnProperty(role) ? roles[role].label : ''
|
||||
}
|
||||
|
||||
exports.getDefaultAccelerator = (role) => {
|
||||
if (roles.hasOwnProperty(role)) return roles[role].accelerator;
|
||||
};
|
||||
if (roles.hasOwnProperty(role)) return roles[role].accelerator
|
||||
}
|
||||
|
||||
exports.shouldRegisterAccelerator = (role) => {
|
||||
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined;
|
||||
return hasRoleRegister ? roles[role].registerAccelerator : true;
|
||||
};
|
||||
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined
|
||||
return hasRoleRegister ? roles[role].registerAccelerator : true
|
||||
}
|
||||
|
||||
exports.getDefaultSubmenu = (role) => {
|
||||
if (!roles.hasOwnProperty(role)) return;
|
||||
if (!roles.hasOwnProperty(role)) return
|
||||
|
||||
let { submenu } = roles[role];
|
||||
let { submenu } = roles[role]
|
||||
|
||||
// remove null items from within the submenu
|
||||
if (Array.isArray(submenu)) {
|
||||
submenu = submenu.filter((item) => item != null);
|
||||
submenu = submenu.filter((item) => item != null)
|
||||
}
|
||||
|
||||
return submenu;
|
||||
};
|
||||
return submenu
|
||||
}
|
||||
|
||||
exports.execute = (role, focusedWindow, focusedWebContents) => {
|
||||
if (!canExecuteRole(role)) return false;
|
||||
if (!canExecuteRole(role)) return false
|
||||
|
||||
const { appMethod, webContentsMethod, windowMethod } = roles[role];
|
||||
const { appMethod, webContentsMethod, windowMethod } = roles[role]
|
||||
|
||||
if (appMethod) {
|
||||
app[appMethod]();
|
||||
return true;
|
||||
app[appMethod]()
|
||||
return true
|
||||
}
|
||||
|
||||
if (windowMethod && focusedWindow != null) {
|
||||
if (typeof windowMethod === 'function') {
|
||||
windowMethod(focusedWindow);
|
||||
windowMethod(focusedWindow)
|
||||
} else {
|
||||
focusedWindow[windowMethod]();
|
||||
focusedWindow[windowMethod]()
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
if (webContentsMethod && focusedWebContents != null) {
|
||||
if (typeof webContentsMethod === 'function') {
|
||||
webContentsMethod(focusedWebContents);
|
||||
webContentsMethod(focusedWebContents)
|
||||
} else {
|
||||
focusedWebContents[webContentsMethod]();
|
||||
focusedWebContents[webContentsMethod]()
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -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 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);
|
||||
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)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* 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,50 +196,51 @@ 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 item of sorted) {
|
||||
const sorted = sortMenuItems(template)
|
||||
for (const id in sorted) {
|
||||
const item = sorted[id]
|
||||
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) {
|
||||
@@ -250,28 +251,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,7 +21,6 @@ module.exports = [
|
||||
{ name: 'inAppPurchase' },
|
||||
{ name: 'Menu' },
|
||||
{ name: 'MenuItem' },
|
||||
{ name: 'nativeTheme' },
|
||||
{ name: 'net' },
|
||||
{ name: 'netLog' },
|
||||
{ name: 'Notification' },
|
||||
@@ -37,7 +36,7 @@ module.exports = [
|
||||
{ name: 'View' },
|
||||
{ name: 'webContents' },
|
||||
{ name: 'WebContentsView' }
|
||||
];
|
||||
]
|
||||
|
||||
if (features.isViewApiEnabled()) {
|
||||
module.exports.push(
|
||||
@@ -48,5 +47,5 @@ if (features.isViewApiEnabled()) {
|
||||
{ name: 'MdTextButton' },
|
||||
{ name: 'ResizeArea' },
|
||||
{ name: 'TextField' }
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
'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 = [
|
||||
export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'app', loader: () => require('./app') },
|
||||
{ name: 'autoUpdater', loader: () => require('./auto-updater') },
|
||||
{ name: 'BrowserView', loader: () => require('./browser-view') },
|
||||
@@ -34,10 +32,10 @@ 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(
|
||||
browserModuleList.push(
|
||||
{ name: 'BoxLayout', loader: () => require('./views/box-layout') },
|
||||
{ name: 'Button', loader: () => require('./views/button') },
|
||||
{ name: 'LabelButton', loader: () => require('./views/label-button') },
|
||||
@@ -45,5 +43,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') }
|
||||
);
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { EventEmitter } from 'events'
|
||||
|
||||
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme');
|
||||
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme')
|
||||
|
||||
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(nativeTheme as any);
|
||||
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(nativeTheme as any)
|
||||
|
||||
module.exports = nativeTheme;
|
||||
module.exports = nativeTheme
|
||||
|
||||
@@ -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,16 +1,20 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
const url = require('url');
|
||||
const { EventEmitter } = require('events');
|
||||
const { Readable, Writable } = require('stream');
|
||||
const { app } = require('electron');
|
||||
const { Session } = process.electronBinding('session');
|
||||
const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net');
|
||||
const { URLLoader } = net;
|
||||
const url = require('url')
|
||||
const { EventEmitter } = require('events')
|
||||
const { Readable } = require('stream')
|
||||
const { app } = require('electron')
|
||||
const { Session } = process.electronBinding('session')
|
||||
const { net, Net } = process.electronBinding('net')
|
||||
const { URLRequest } = net
|
||||
|
||||
Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype);
|
||||
// Net is an EventEmitter.
|
||||
Object.setPrototypeOf(Net.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(net)
|
||||
|
||||
const kSupportedProtocols = new Set(['http:', 'https:']);
|
||||
Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype)
|
||||
|
||||
const kSupportedProtocols = new Set(['http:', 'https:'])
|
||||
|
||||
// set of headers that Node.js discards duplicates for
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
@@ -33,445 +37,375 @@ const discardableDuplicateHeaders = new Set([
|
||||
'server',
|
||||
'age',
|
||||
'expires'
|
||||
]);
|
||||
])
|
||||
|
||||
class IncomingMessage extends Readable {
|
||||
constructor (responseHead) {
|
||||
super();
|
||||
this._shouldPush = false;
|
||||
this._data = [];
|
||||
this._responseHead = responseHead;
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
||||
get statusCode () {
|
||||
return this._responseHead.statusCode;
|
||||
return this.urlRequest.statusCode
|
||||
}
|
||||
|
||||
get statusMessage () {
|
||||
return this._responseHead.statusMessage;
|
||||
return this.urlRequest.statusMessage
|
||||
}
|
||||
|
||||
get headers () {
|
||||
const filteredHeaders = {};
|
||||
const { rawHeaders } = this._responseHead;
|
||||
rawHeaders.forEach(header => {
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) &&
|
||||
discardableDuplicateHeaders.has(header.key)) {
|
||||
const filteredHeaders = {}
|
||||
const rawHeaders = this.urlRequest.rawResponseHeaders
|
||||
Object.keys(rawHeaders).forEach(header => {
|
||||
if (header in filteredHeaders && discardableDuplicateHeaders.has(header)) {
|
||||
// do nothing with discardable duplicate headers
|
||||
} else {
|
||||
if (header.key === 'set-cookie') {
|
||||
if (header === 'set-cookie') {
|
||||
// keep set-cookie as an array per Node.js rules
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||
filteredHeaders[header.key].push(header.value);
|
||||
} else {
|
||||
filteredHeaders[header.key] = [header.value];
|
||||
}
|
||||
filteredHeaders[header] = rawHeaders[header]
|
||||
} else {
|
||||
// for non-cookie headers, the values are joined together with ', '
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||
filteredHeaders[header.key] += `, ${header.value}`;
|
||||
} else {
|
||||
filteredHeaders[header.key] = header.value;
|
||||
}
|
||||
filteredHeaders[header] = rawHeaders[header].join(', ')
|
||||
}
|
||||
}
|
||||
});
|
||||
return filteredHeaders;
|
||||
})
|
||||
return filteredHeaders
|
||||
}
|
||||
|
||||
get httpVersion () {
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`
|
||||
}
|
||||
|
||||
get httpVersionMajor () {
|
||||
return this._responseHead.httpVersion.major;
|
||||
return this.urlRequest.httpVersionMajor
|
||||
}
|
||||
|
||||
get httpVersionMinor () {
|
||||
return this._responseHead.httpVersion.minor;
|
||||
return this.urlRequest.httpVersionMinor
|
||||
}
|
||||
|
||||
get rawTrailers () {
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
throw new Error('HTTP trailers are not supported')
|
||||
}
|
||||
|
||||
get trailers () {
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
throw new Error('HTTP trailers are not supported')
|
||||
}
|
||||
|
||||
_storeInternalData (chunk) {
|
||||
this._data.push(chunk);
|
||||
this._pushInternalData();
|
||||
this.data.push(chunk)
|
||||
}
|
||||
|
||||
_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()
|
||||
}
|
||||
}
|
||||
|
||||
/** 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);
|
||||
URLRequest.prototype._emitRequestEvent = function (isAsync, ...rest) {
|
||||
if (isAsync) {
|
||||
process.nextTick(() => {
|
||||
this.clientRequest.emit(...rest)
|
||||
})
|
||||
} else {
|
||||
options = { ...options };
|
||||
this.clientRequest.emit(...rest)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
class ClientRequest extends Writable {
|
||||
URLRequest.prototype._emitResponseEvent = function (isAsync, ...rest) {
|
||||
if (isAsync) {
|
||||
process.nextTick(() => {
|
||||
this._response.emit(...rest)
|
||||
})
|
||||
} else {
|
||||
this._response.emit(...rest)
|
||||
}
|
||||
}
|
||||
|
||||
class ClientRequest extends EventEmitter {
|
||||
constructor (options, callback) {
|
||||
super({ autoDestroy: true });
|
||||
super()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options);
|
||||
this._urlLoaderOptions = urlLoaderOptions;
|
||||
this._redirectPolicy = redirectPolicy;
|
||||
this._started = false;
|
||||
get chunkedEncoding () {
|
||||
return this.chunkedEncodingEnabled
|
||||
}
|
||||
|
||||
set chunkedEncoding (value) {
|
||||
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);
|
||||
};
|
||||
if (!this.urlRequest.notStarted) {
|
||||
throw new Error('Can\'t set the transfer encoding, headers have been sent')
|
||||
}
|
||||
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._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}'`);
|
||||
if (!this.urlRequest.notStarted) {
|
||||
throw new Error('Can\'t set headers after they are sent')
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
this._urlLoaderOptions.extraHeaders[key] = value;
|
||||
const key = name.toLowerCase()
|
||||
this.extraHeaders[key] = value
|
||||
this.urlRequest.setExtraHeader(name, value.toString())
|
||||
}
|
||||
|
||||
getHeader (name) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for getHeader(name)');
|
||||
throw new Error('`name` is required for getHeader(name)')
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
return this._urlLoaderOptions.extraHeaders[key];
|
||||
if (!this.extraHeaders) {
|
||||
return
|
||||
}
|
||||
|
||||
const key = name.toLowerCase()
|
||||
return this.extraHeaders[key]
|
||||
}
|
||||
|
||||
removeHeader (name) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for removeHeader(name)');
|
||||
throw new Error('`name` is required for removeHeader(name)')
|
||||
}
|
||||
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t remove headers after they are sent');
|
||||
if (!this.urlRequest.notStarted) {
|
||||
throw new Error('Can\'t remove headers after they are sent')
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
delete this._urlLoaderOptions.extraHeaders[key];
|
||||
const key = name.toLowerCase()
|
||||
delete this.extraHeaders[key]
|
||||
this.urlRequest.removeExtraHeader(name)
|
||||
}
|
||||
|
||||
_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();
|
||||
});
|
||||
_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')
|
||||
}
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.write(chunk, encoding, callback);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
_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();
|
||||
write (data, encoding, callback) {
|
||||
if (this.urlRequest.finished) {
|
||||
const error = new Error('Write after end')
|
||||
process.nextTick(writeAfterEndNT, this, error, callback)
|
||||
return true
|
||||
}
|
||||
|
||||
return this._write(data, encoding, callback, 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);
|
||||
});
|
||||
end (data, encoding, callback) {
|
||||
if (this.urlRequest.finished) {
|
||||
return false
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
if (typeof data === 'function') {
|
||||
callback = data
|
||||
encoding = null
|
||||
data = null
|
||||
} else if (typeof encoding === 'function') {
|
||||
callback = encoding
|
||||
encoding = null
|
||||
}
|
||||
|
||||
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}'`));
|
||||
}
|
||||
});
|
||||
data = data || ''
|
||||
|
||||
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
|
||||
}
|
||||
});
|
||||
return this._write(data, encoding, callback, true)
|
||||
}
|
||||
|
||||
followRedirect () {
|
||||
if (this._followRedirectCb) {
|
||||
this._followRedirectCb();
|
||||
} else {
|
||||
throw new Error('followRedirect() called, but was not waiting for a redirect');
|
||||
}
|
||||
this.urlRequest.followRedirect()
|
||||
}
|
||||
|
||||
abort () {
|
||||
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);
|
||||
}
|
||||
this.urlRequest.cancel()
|
||||
}
|
||||
|
||||
getUploadProgress () {
|
||||
return this._uploadProgress ? { ...this._uploadProgress } : { active: false };
|
||||
return this.urlRequest.getUploadProgress()
|
||||
}
|
||||
}
|
||||
|
||||
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,38 +1,28 @@
|
||||
'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') {
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
});
|
||||
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()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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,53 +1,52 @@
|
||||
'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 () {
|
||||
const logPath = this._currentlyLoggingPath;
|
||||
this._currentlyLoggingPath = null;
|
||||
return _originalStopLogging.call(this).then(() => logPath);
|
||||
};
|
||||
this._currentlyLoggingPath = null
|
||||
return _originalStopLogging.call(this)
|
||||
}
|
||||
|
||||
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,10 +1,10 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { deprecate } from 'electron';
|
||||
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences');
|
||||
import { EventEmitter } from 'events'
|
||||
import { deprecate } from 'electron'
|
||||
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences')
|
||||
|
||||
// SystemPreferences is an EventEmitter.
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(systemPreferences);
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(systemPreferences)
|
||||
|
||||
if ('appLevelAppearance' in systemPreferences) {
|
||||
deprecate.fnToProperty(
|
||||
@@ -12,7 +12,7 @@ if ('appLevelAppearance' in systemPreferences) {
|
||||
'appLevelAppearance',
|
||||
'_getAppLevelAppearance',
|
||||
'_setAppLevelAppearance'
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
if ('effectiveAppearance' in systemPreferences) {
|
||||
@@ -20,23 +20,23 @@ if ('effectiveAppearance' in systemPreferences) {
|
||||
SystemPreferences.prototype,
|
||||
'effectiveAppearance',
|
||||
'_getEffectiveAppearance'
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
SystemPreferences.prototype.isDarkMode = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isDarkMode,
|
||||
'systemPreferences.isDarkMode()',
|
||||
'nativeTheme.shouldUseDarkColors'
|
||||
);
|
||||
)
|
||||
SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isInvertedColorScheme,
|
||||
'systemPreferences.isInvertedColorScheme()',
|
||||
'nativeTheme.shouldUseInvertedColorScheme'
|
||||
);
|
||||
)
|
||||
SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isHighContrastColorScheme,
|
||||
'systemPreferences.isHighContrastColorScheme()',
|
||||
'nativeTheme.shouldUseHighContrastColors'
|
||||
);
|
||||
)
|
||||
|
||||
module.exports = systemPreferences;
|
||||
module.exports = systemPreferences
|
||||
|
||||
@@ -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);
|
||||
if (this.escapeItem) {
|
||||
this.escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
};
|
||||
window.once('closed', removeListeners);
|
||||
this.windowListeners[id] = removeListeners;
|
||||
unregisterItems(this.ordereredItems)
|
||||
if (this.escapeItem) {
|
||||
this.escapeItem.removeListener('change', this.changeListener)
|
||||
}
|
||||
}
|
||||
window.once('closed', removeListeners)
|
||||
this.windowListeners[id] = removeListeners
|
||||
|
||||
window._setTouchBarItems(this.ordereredItems);
|
||||
escapeItemListener(this.escapeItem);
|
||||
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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
const { EventEmitter } = require('events');
|
||||
const { View } = process.electronBinding('view');
|
||||
const { EventEmitter } = require('events')
|
||||
const { View } = process.electronBinding('view')
|
||||
|
||||
Object.setPrototypeOf(View.prototype, EventEmitter.prototype);
|
||||
Object.setPrototypeOf(View.prototype, EventEmitter.prototype)
|
||||
|
||||
View.prototype._init = function () {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = View;
|
||||
module.exports = View
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user