mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
280 Commits
v7.1.2
...
v8.0.0-nig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e099edc2c2 | ||
|
|
5e942cea8d | ||
|
|
ead8f2bbcd | ||
|
|
efe6e7b04d | ||
|
|
cc0312567e | ||
|
|
593f1774e9 | ||
|
|
0653e31767 | ||
|
|
b18fb96ecf | ||
|
|
26d9ef9403 | ||
|
|
c32bea5ae0 | ||
|
|
7f0ec3091e | ||
|
|
597a8b5ec7 | ||
|
|
c25f0a1779 | ||
|
|
bac15ef86f | ||
|
|
297b81fa1d | ||
|
|
913ec1e0d0 | ||
|
|
d090b0cd2d | ||
|
|
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 |
@@ -1,3 +1,46 @@
|
||||
version: 2.1
|
||||
|
||||
parameters:
|
||||
upload-to-s3:
|
||||
type: string
|
||||
default: '1'
|
||||
|
||||
run-lint:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
run-build-linux:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
run-build-mac:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
run-linux-x64-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-linux-ia32-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-linux-arm-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-linux-arm64-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-osx-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-mas-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
# The config expects the following environment variables to be set:
|
||||
# - "SLACK_WEBHOOK" Slack hook URL to send notifications.
|
||||
#
|
||||
@@ -32,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
|
||||
@@ -180,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
|
||||
@@ -301,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
|
||||
@@ -378,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:
|
||||
@@ -465,7 +514,9 @@ step-mksnapshot-build: &step-mksnapshot-build
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/mksnapshot
|
||||
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:
|
||||
@@ -487,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
|
||||
@@ -651,7 +704,7 @@ steps-lint: &steps-lint
|
||||
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
|
||||
gn_version="$(curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
|
||||
|
||||
cipd ensure -ensure-file - -root . <<-CIPD
|
||||
cipd ensure -ensure-file - -root . \<<-CIPD
|
||||
\$ServiceURL https://chrome-infra-packages.appspot.com/
|
||||
@Subdir src/buildtools/linux64
|
||||
gn/gn/linux-amd64 $gn_version
|
||||
@@ -755,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
|
||||
@@ -769,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
|
||||
@@ -1008,7 +1111,6 @@ chromium-upgrade-branches: &chromium-upgrade-branches
|
||||
/chromium\-upgrade\/[0-9]+/
|
||||
|
||||
# List of all jobs.
|
||||
version: 2
|
||||
jobs:
|
||||
# Layer 0: Lint. Standalone.
|
||||
lint:
|
||||
@@ -1068,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
|
||||
@@ -1084,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
|
||||
@@ -1094,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
|
||||
@@ -1115,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
|
||||
@@ -1128,6 +1233,8 @@ jobs:
|
||||
<<: *env-linux-2xlarge-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
linux-ia32-debug:
|
||||
@@ -1138,7 +1245,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
|
||||
@@ -1148,7 +1256,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
|
||||
@@ -1163,13 +1272,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
|
||||
@@ -1178,6 +1287,8 @@ jobs:
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-ia32
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
linux-arm-debug:
|
||||
@@ -1188,7 +1299,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
|
||||
@@ -1199,7 +1311,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
|
||||
@@ -1214,13 +1327,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
|
||||
@@ -1228,7 +1341,9 @@ jobs:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
linux-arm64-debug:
|
||||
@@ -1239,7 +1354,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
|
||||
@@ -1258,7 +1374,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
|
||||
@@ -1281,13 +1398,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
|
||||
@@ -1295,7 +1412,9 @@ jobs:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm64
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
osx-testing:
|
||||
@@ -1305,7 +1424,7 @@ jobs:
|
||||
<<: *env-testing-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
osx-debug:
|
||||
<<: *machine-mac-large
|
||||
@@ -1314,7 +1433,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
|
||||
@@ -1346,14 +1465,16 @@ 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
|
||||
|
||||
mas-testing:
|
||||
@@ -1364,7 +1485,7 @@ jobs:
|
||||
<<: *env-testing-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
mas-debug:
|
||||
<<: *machine-mac-large
|
||||
@@ -1374,7 +1495,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
|
||||
@@ -1409,7 +1530,7 @@ jobs:
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build-for-tests
|
||||
<<: *steps-electron-build
|
||||
|
||||
mas-publish:
|
||||
<<: *machine-mac-large
|
||||
@@ -1417,7 +1538,9 @@ jobs:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-mas
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
|
||||
# Layer 3: Tests.
|
||||
@@ -1672,28 +1795,67 @@ 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
|
||||
# changed to have multiple jobs, then scripts/release/ci-release-build.js
|
||||
# will need to be updated and there will most likely need to be changes to
|
||||
# sudowoodo
|
||||
|
||||
publish-x64-linux:
|
||||
when: << pipeline.parameters.run-linux-x64-publish >>
|
||||
jobs:
|
||||
- linux-x64-publish:
|
||||
context: release-env
|
||||
|
||||
publish-ia32-linux:
|
||||
when: << pipeline.parameters.run-linux-ia32-publish >>
|
||||
jobs:
|
||||
- linux-ia32-publish:
|
||||
context: release-env
|
||||
|
||||
publish-arm-linux:
|
||||
when: << pipeline.parameters.run-linux-arm-publish >>
|
||||
jobs:
|
||||
- linux-arm-publish:
|
||||
context: release-env
|
||||
|
||||
publish-arm64-linux:
|
||||
when: << pipeline.parameters.run-linux-arm64-publish >>
|
||||
jobs:
|
||||
- linux-arm64-publish:
|
||||
context: release-env
|
||||
|
||||
publish-osx:
|
||||
when: << pipeline.parameters.run-osx-publish >>
|
||||
jobs:
|
||||
- osx-publish:
|
||||
context: release-env
|
||||
|
||||
publish-mas:
|
||||
when: << pipeline.parameters.run-mas-publish >>
|
||||
jobs:
|
||||
- mas-publish:
|
||||
context: release-env
|
||||
|
||||
lint:
|
||||
when: << pipeline.parameters.run-lint >>
|
||||
jobs:
|
||||
- lint
|
||||
|
||||
build-linux:
|
||||
when: << pipeline.parameters.run-build-linux >>
|
||||
jobs:
|
||||
- linux-checkout-fast
|
||||
- linux-checkout-and-save-cache
|
||||
|
||||
- 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
|
||||
@@ -1707,12 +1869,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
|
||||
@@ -1723,27 +1881,20 @@ 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
|
||||
|
||||
build-mac:
|
||||
when: << pipeline.parameters.run-build-mac >>
|
||||
jobs:
|
||||
- mac-checkout-fast
|
||||
- mac-checkout-and-save-cache
|
||||
@@ -1798,11 +1949,11 @@ workflows:
|
||||
- master
|
||||
- *chromium-upgrade-branches
|
||||
jobs:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
|
||||
- linux-x64-release:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-release-tests:
|
||||
requires:
|
||||
- linux-x64-release
|
||||
@@ -1814,7 +1965,7 @@ workflows:
|
||||
- linux-x64-release
|
||||
- linux-x64-chromedriver:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-x64-release-summary:
|
||||
requires:
|
||||
- linux-x64-release
|
||||
@@ -1824,7 +1975,7 @@ workflows:
|
||||
|
||||
- linux-ia32-release:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-ia32-release-tests:
|
||||
requires:
|
||||
- linux-ia32-release
|
||||
@@ -1836,7 +1987,7 @@ workflows:
|
||||
- linux-ia32-release
|
||||
- linux-ia32-chromedriver:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-ia32-release-summary:
|
||||
requires:
|
||||
- linux-ia32-release
|
||||
@@ -1846,10 +1997,10 @@ workflows:
|
||||
|
||||
- linux-arm-release:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm-chromedriver:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm-release-summary:
|
||||
requires:
|
||||
- linux-arm-release
|
||||
@@ -1858,10 +2009,10 @@ workflows:
|
||||
|
||||
- linux-arm64-release:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-chromedriver:
|
||||
requires:
|
||||
- linux-checkout
|
||||
- linux-checkout-fast
|
||||
- linux-arm64-release-summary:
|
||||
requires:
|
||||
- linux-arm64-release
|
||||
@@ -1877,11 +2028,11 @@ workflows:
|
||||
- master
|
||||
- *chromium-upgrade-branches
|
||||
jobs:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
|
||||
- osx-release:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- osx-release-tests:
|
||||
requires:
|
||||
- osx-release
|
||||
@@ -1893,7 +2044,7 @@ workflows:
|
||||
- osx-release
|
||||
- osx-chromedriver:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- osx-release-summary:
|
||||
requires:
|
||||
- osx-release
|
||||
@@ -1903,7 +2054,7 @@ workflows:
|
||||
|
||||
- mas-release:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- mas-release-tests:
|
||||
requires:
|
||||
- mas-release
|
||||
@@ -1915,7 +2066,7 @@ workflows:
|
||||
- mas-release
|
||||
- mas-chromedriver:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mac-checkout-fast
|
||||
- mas-release-summary:
|
||||
requires:
|
||||
- mas-release
|
||||
|
||||
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!
|
||||
20
BUILD.gn
20
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" ]
|
||||
|
||||
6
DEPS
6
DEPS
@@ -11,9 +11,9 @@ gclient_gn_args = [
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'78.0.3904.113',
|
||||
'6c775c358b709f4353bc693623bf58820adf3918',
|
||||
'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',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
7.1.2
|
||||
8.0.0-nightly.20190928
|
||||
@@ -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
|
||||
|
||||
@@ -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'],
|
||||
}),
|
||||
] : [])
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -10,6 +10,8 @@ declare_args() {
|
||||
|
||||
enable_osr = true
|
||||
|
||||
enable_remote_module = true
|
||||
|
||||
enable_view_api = false
|
||||
|
||||
enable_pdf_viewer = false
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,31 +1,4 @@
|
||||
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')
|
||||
@@ -42,12 +15,6 @@ async function initialize () {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -335,7 +337,7 @@ 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')
|
||||
})
|
||||
@@ -388,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)
|
||||
})
|
||||
```
|
||||
@@ -673,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()`
|
||||
@@ -1166,21 +1170,21 @@ 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.
|
||||
|
||||
@@ -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
|
||||
@@ -383,6 +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`.
|
||||
* `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
|
||||
@@ -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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -662,7 +664,8 @@ Returns `BrowserWindow | null` - The window that is focused in this application,
|
||||
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
|
||||
Returns `BrowserWindow` - The window that owns the given `webContents`.
|
||||
Returns `BrowserWindow | null` - The window that owns the given `webContents`
|
||||
or `null` if the contents are not owned by a window.
|
||||
|
||||
#### `BrowserWindow.fromBrowserView(browserView)`
|
||||
|
||||
@@ -828,6 +831,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:
|
||||
@@ -1201,6 +1210,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
|
||||
@@ -1273,6 +1290,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.
|
||||
@@ -1553,7 +1579,7 @@ Same as `webContents.showDefinitionForSelection()`.
|
||||
|
||||
#### `win.setIcon(icon)` _Windows_ _Linux_
|
||||
|
||||
* `icon` [NativeImage](native-image.md) | String
|
||||
* `icon` [NativeImage](native-image.md)
|
||||
|
||||
Changes window icon.
|
||||
|
||||
@@ -1720,14 +1746,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_
|
||||
|
||||
|
||||
@@ -1,111 +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 [BrowserWindow](browser-window.md) 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**. I.e. 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 un-serialized into the same object it will work. A table of type support
|
||||
has been included below for completeness.
|
||||
|
||||
| Type | Complexity | Parameter Support | Return Value Support | Limitations |
|
||||
| ---- | ---------- | ----------------- | -------------------- | ----------- |
|
||||
| `String` | Simple | ✅ | ✅ | N/A |
|
||||
| `Number` | Simple | ✅ | ✅ | N/A |
|
||||
| `Boolean` | Simple | ✅ | ✅ | N/A |
|
||||
| `Object` | Complex | ✅ | ✅ | Keys must be supported "Simple" types in this table. Values must be supported in this table. Prototype modifications are dropped. Sending custom classes will copy values but not the prototype. |
|
||||
| `Array` | Complex | ✅ | ✅ | Same limitations as the `Object` type |
|
||||
| `Error` | Complex | ✅ | ✅ | Errors that are thrown are also copied, this can result in the message and stack trace of the error changing slightly due to being thrown in a different context |
|
||||
| `Promise` | Complex | ✅ | ✅ | Promises are only proxied if they are a the return value or exact parameter. Promises nested in arrays or obejcts will be dropped. |
|
||||
| `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.
|
||||
@@ -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,8 +202,14 @@ 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:
|
||||
|
||||
@@ -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 +1,4 @@
|
||||
# ProtocolResponseUploadData Object
|
||||
|
||||
* `contentType` String - MIME type of the content.
|
||||
* `data` String | Buffer - Content to be sent.
|
||||
* `data` String - Content to be sent.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -1262,6 +1264,8 @@ 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 - Called back if the print fails; can be `cancelled` or `failed`.
|
||||
@@ -1589,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ $ code electron-quick-start
|
||||
"name": "Debug Main Process",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
|
||||
},
|
||||
"args" : ["."],
|
||||
"outputCapture": "std"
|
||||
|
||||
@@ -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).
|
||||
@@ -150,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
|
||||
|
||||
@@ -139,7 +139,7 @@ const options = {
|
||||
port: 9515, // "9515" is the port opened by chrome driver.
|
||||
desiredCapabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
'goog:chromeOptions': {
|
||||
binary: '/Path-to-Your-App/electron', // Path to your Electron binary.
|
||||
args: [/* cli arguments */] // Optional, perhaps 'app=' + /path/to/your/app/
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ template("electron_paks") {
|
||||
source_patterns = [
|
||||
"${root_gen_dir}/chrome/platform_locale_settings_",
|
||||
"${root_gen_dir}/components/strings/components_strings_",
|
||||
"${root_gen_dir}/content/app/strings/content_strings_",
|
||||
"${root_gen_dir}/third_party/blink/public/strings/blink_strings_",
|
||||
"${root_gen_dir}/device/bluetooth/strings/bluetooth_strings_",
|
||||
"${root_gen_dir}/services/strings/services_strings_",
|
||||
"${root_gen_dir}/ui/strings/app_locale_settings_",
|
||||
@@ -156,9 +156,9 @@ template("electron_paks") {
|
||||
deps = [
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//components/strings:components_strings",
|
||||
"//content/app/strings",
|
||||
"//device/bluetooth/strings",
|
||||
"//services/strings",
|
||||
"//third_party/blink/public/strings",
|
||||
"//ui/strings:app_locale_settings",
|
||||
"//ui/strings:ui_strings",
|
||||
]
|
||||
|
||||
@@ -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",
|
||||
@@ -131,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",
|
||||
@@ -158,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",
|
||||
@@ -166,8 +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/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",
|
||||
@@ -177,7 +175,6 @@ auto_filenames = {
|
||||
isolated_bundle_deps = [
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/error-utils.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/isolated_renderer/init.js",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
@@ -192,7 +189,6 @@ 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/chrome-api.ts",
|
||||
"lib/renderer/extensions/event.ts",
|
||||
@@ -218,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",
|
||||
@@ -226,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",
|
||||
@@ -258,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",
|
||||
@@ -292,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",
|
||||
@@ -325,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",
|
||||
@@ -344,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",
|
||||
]
|
||||
@@ -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",
|
||||
@@ -353,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",
|
||||
@@ -431,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",
|
||||
@@ -471,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",
|
||||
@@ -492,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",
|
||||
@@ -537,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",
|
||||
|
||||
@@ -105,9 +105,9 @@ if (process.platform === 'linux') {
|
||||
}
|
||||
|
||||
// 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[]) => {
|
||||
app.on(name as 'login', (event, webContents, ...args: any[]) => {
|
||||
webContents.emit(name, event, ...args)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ BrowserWindow.prototype._init = function () {
|
||||
|
||||
// Hide the auto-hide menu when webContents is focused.
|
||||
this.webContents.on('activate', () => {
|
||||
if (process.platform !== 'darwin' && this.autoHideMenuBar && this.isMenuBarVisible()) {
|
||||
if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) {
|
||||
this.setMenuBarVisibility(false)
|
||||
}
|
||||
})
|
||||
@@ -118,6 +118,8 @@ BrowserWindow.fromWebContents = (webContents) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.webContents.equal(webContents)) return window
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
BrowserWindow.fromBrowserView = (browserView) => {
|
||||
|
||||
@@ -4,15 +4,29 @@ 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) => {
|
||||
@@ -44,6 +58,18 @@ const checkAppInitialized = function () {
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -58,6 +84,7 @@ const saveDialog = (sync, window, options) => {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = [],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false,
|
||||
@@ -72,6 +99,8 @@ const saveDialog = (sync, window, options) => {
|
||||
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 }
|
||||
settings.properties = setupDialogProperties(DialogType.SAVE, properties)
|
||||
|
||||
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings)
|
||||
}
|
||||
|
||||
@@ -102,20 +131,13 @@ const openDialog = (sync, window, options) => {
|
||||
|
||||
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')
|
||||
|
||||
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }
|
||||
settings.properties = dialogProperties
|
||||
settings.properties = setupDialogProperties(DialogType.OPEN, properties)
|
||||
|
||||
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings)
|
||||
}
|
||||
|
||||
@@ -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,38 +1,6 @@
|
||||
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', () => {})
|
||||
|
||||
@@ -21,7 +21,6 @@ module.exports = [
|
||||
{ name: 'inAppPurchase' },
|
||||
{ name: 'Menu' },
|
||||
{ name: 'MenuItem' },
|
||||
{ name: 'nativeTheme' },
|
||||
{ name: 'net' },
|
||||
{ name: 'netLog' },
|
||||
{ name: 'Notification' },
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
// TODO: Updating this file also required updating the module-keys file
|
||||
|
||||
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') },
|
||||
@@ -37,7 +35,7 @@ module.exports = [
|
||||
]
|
||||
|
||||
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') },
|
||||
@@ -72,6 +72,7 @@ const defaultPrintingSetting = {
|
||||
headerFooterEnabled: false,
|
||||
marginsType: 0,
|
||||
isFirstRequest: false,
|
||||
requestID: getNextId(),
|
||||
previewUIID: 0,
|
||||
previewModifiable: true,
|
||||
printToPDF: true,
|
||||
@@ -204,10 +205,7 @@ WebContents.prototype.executeJavaScript = function (code, hasUserGesture) {
|
||||
|
||||
// Translate the options of printToPDF.
|
||||
WebContents.prototype.printToPDF = function (options) {
|
||||
const printingSetting = {
|
||||
...defaultPrintingSetting,
|
||||
requestID: getNextId()
|
||||
}
|
||||
const printingSetting = Object.assign({}, defaultPrintingSetting)
|
||||
if (options.landscape) {
|
||||
printingSetting.landscape = options.landscape
|
||||
}
|
||||
@@ -329,14 +327,15 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
})
|
||||
|
||||
this.on('-ipc-invoke', function (event, channel, args) {
|
||||
this.on('-ipc-invoke', function (event, internal, channel, args) {
|
||||
event._reply = (result) => event.sendReply({ result })
|
||||
event._throw = (error) => {
|
||||
console.error(`Error occurred in handler for '${channel}':`, error)
|
||||
event.sendReply({ error: error.toString() })
|
||||
}
|
||||
if (ipcMain._invokeHandlers.has(channel)) {
|
||||
ipcMain._invokeHandlers.get(channel)(event, ...args)
|
||||
const target = internal ? ipcMainInternal : ipcMain
|
||||
if (target._invokeHandlers.has(channel)) {
|
||||
target._invokeHandlers.get(channel)(event, ...args)
|
||||
} else {
|
||||
event._throw(`No handler registered for '${channel}'`)
|
||||
}
|
||||
@@ -415,10 +414,6 @@ WebContents.prototype._init = function () {
|
||||
})
|
||||
}
|
||||
|
||||
this.on('login', (event, ...args) => {
|
||||
app.emit('login', event, this, ...args)
|
||||
})
|
||||
|
||||
const event = process.electronBinding('event').createEmpty()
|
||||
app.emit('web-contents-created', event, this)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
if (process.electronBinding('features').isExtensionsEnabled()) {
|
||||
throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled')
|
||||
}
|
||||
|
||||
const { app, webContents, BrowserWindow } = require('electron')
|
||||
const { getAllWebContents } = process.electronBinding('web_contents')
|
||||
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
|
||||
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
@@ -156,7 +161,7 @@ const hookWebContentsEvents = function (webContents) {
|
||||
// Handle the chrome.* API messages.
|
||||
let nextId = 0
|
||||
|
||||
ipcMainUtils.handle('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
|
||||
ipcMainUtils.handleSync('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
|
||||
if (isBackgroundPage(event.sender)) {
|
||||
throw new Error('chrome.runtime.connect is not supported in background page')
|
||||
}
|
||||
@@ -178,7 +183,7 @@ ipcMainUtils.handle('CHROME_RUNTIME_CONNECT', function (event, extensionId, conn
|
||||
return { tabId, portId }
|
||||
})
|
||||
|
||||
ipcMainUtils.handle('CHROME_EXTENSION_MANIFEST', function (event, extensionId) {
|
||||
ipcMainUtils.handleSync('CHROME_EXTENSION_MANIFEST', function (event, extensionId) {
|
||||
const manifest = manifestMap[extensionId]
|
||||
if (!manifest) {
|
||||
throw new Error(`Invalid extensionId: ${extensionId}`)
|
||||
@@ -186,7 +191,7 @@ ipcMainUtils.handle('CHROME_EXTENSION_MANIFEST', function (event, extensionId) {
|
||||
return manifest
|
||||
})
|
||||
|
||||
ipcMainUtils.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) {
|
||||
ipcMainInternal.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) {
|
||||
if (isBackgroundPage(event.sender)) {
|
||||
throw new Error('chrome.runtime.sendMessage is not supported in background page')
|
||||
}
|
||||
@@ -199,7 +204,7 @@ ipcMainUtils.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extens
|
||||
return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message)
|
||||
})
|
||||
|
||||
ipcMainUtils.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) {
|
||||
ipcMainInternal.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) {
|
||||
const contents = webContents.fromId(tabId)
|
||||
if (!contents) {
|
||||
throw new Error(`Sending message to unknown tab ${tabId}`)
|
||||
@@ -233,7 +238,7 @@ const getMessagesPath = (extensionId) => {
|
||||
}
|
||||
}
|
||||
|
||||
ipcMainUtils.handle('CHROME_GET_MESSAGES', async function (event, extensionId) {
|
||||
ipcMainUtils.handleSync('CHROME_GET_MESSAGES', async function (event, extensionId) {
|
||||
const messagesPath = getMessagesPath(extensionId)
|
||||
return fs.promises.readFile(messagesPath)
|
||||
})
|
||||
@@ -252,7 +257,7 @@ const getChromeStoragePath = (storageType, extensionId) => {
|
||||
return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`)
|
||||
}
|
||||
|
||||
ipcMainUtils.handle('CHROME_STORAGE_READ', async function (event, storageType, extensionId) {
|
||||
ipcMainInternal.handle('CHROME_STORAGE_READ', async function (event, storageType, extensionId) {
|
||||
const filePath = getChromeStoragePath(storageType, extensionId)
|
||||
|
||||
try {
|
||||
@@ -266,7 +271,7 @@ ipcMainUtils.handle('CHROME_STORAGE_READ', async function (event, storageType, e
|
||||
}
|
||||
})
|
||||
|
||||
ipcMainUtils.handle('CHROME_STORAGE_WRITE', async function (event, storageType, extensionId, data) {
|
||||
ipcMainInternal.handle('CHROME_STORAGE_WRITE', async function (event, storageType, extensionId, data) {
|
||||
const filePath = getChromeStoragePath(storageType, extensionId)
|
||||
|
||||
try {
|
||||
@@ -291,7 +296,7 @@ const assertChromeExtension = function (contents, api) {
|
||||
}
|
||||
}
|
||||
|
||||
ipcMainUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) {
|
||||
ipcMainInternal.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) {
|
||||
assertChromeExtension(event.sender, 'chrome.tabs.executeScript()')
|
||||
|
||||
const contents = webContents.fromId(tabId)
|
||||
|
||||
@@ -2,7 +2,8 @@ import { dialog, Menu } from 'electron'
|
||||
import * as fs from 'fs'
|
||||
import * as url from 'url'
|
||||
|
||||
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'
|
||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils'
|
||||
|
||||
const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: number) => void) {
|
||||
return items.map(function (item) {
|
||||
@@ -59,7 +60,7 @@ const assertChromeDevTools = function (contents: Electron.WebContents, api: stri
|
||||
}
|
||||
}
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainEvent, items: ContextMenuItem[], isEditMenu: boolean) {
|
||||
ipcMainInternal.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainInvokeEvent, items: ContextMenuItem[], isEditMenu: boolean) {
|
||||
return new Promise(resolve => {
|
||||
assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()')
|
||||
|
||||
@@ -71,7 +72,7 @@ ipcMainUtils.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron
|
||||
})
|
||||
})
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainEvent) {
|
||||
ipcMainInternal.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainInvokeEvent) {
|
||||
assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()')
|
||||
|
||||
const result = await dialog.showOpenDialog({})
|
||||
@@ -83,7 +84,7 @@ ipcMainUtils.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Ele
|
||||
return [path, data]
|
||||
})
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainEvent, message: string = '', title: string = '') {
|
||||
ipcMainUtils.handleSync('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainInvokeEvent, message: string = '', title: string = '') {
|
||||
assertChromeDevTools(event.sender, 'window.confirm()')
|
||||
|
||||
const options = {
|
||||
|
||||
@@ -5,7 +5,6 @@ const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-interna
|
||||
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
|
||||
const parseFeaturesString = require('@electron/internal/common/parse-features-string')
|
||||
const { syncMethods, asyncMethods } = require('@electron/internal/common/web-view-methods')
|
||||
const { serialize } = require('@electron/internal/common/type-utils')
|
||||
|
||||
// Doesn't exist in early initialization.
|
||||
let webViewManager = null
|
||||
@@ -189,7 +188,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
|
||||
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
|
||||
enableRemoteModule: params.enableremotemodule,
|
||||
plugins: params.plugins,
|
||||
zoomFactor: embedder.zoomFactor,
|
||||
zoomFactor: embedder.getZoomFactor(),
|
||||
disablePopups: !params.allowpopups,
|
||||
webSecurity: !params.disablewebsecurity,
|
||||
enableBlinkFeatures: params.blinkfeatures,
|
||||
@@ -313,21 +312,33 @@ const isWebViewTagEnabled = function (contents) {
|
||||
return isWebViewTagEnabledCache.get(contents)
|
||||
}
|
||||
|
||||
const handleMessage = function (channel, handler) {
|
||||
ipcMainUtils.handle(channel, (event, ...args) => {
|
||||
const makeSafeHandler = function (channel, handler) {
|
||||
return (event, ...args) => {
|
||||
if (isWebViewTagEnabled(event.sender)) {
|
||||
return handler(event, ...args)
|
||||
} else {
|
||||
console.error(`<webview> IPC message ${channel} sent by WebContents with <webview> disabled (${event.sender.id})`)
|
||||
throw new Error('<webview> disabled')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleMessage = function (channel, handler) {
|
||||
ipcMainInternal.handle(channel, makeSafeHandler(channel, handler))
|
||||
}
|
||||
|
||||
const handleMessageSync = function (channel, handler) {
|
||||
ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler))
|
||||
}
|
||||
|
||||
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) {
|
||||
return createGuest(event.sender, params)
|
||||
})
|
||||
|
||||
handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) {
|
||||
return createGuest(event.sender, params)
|
||||
})
|
||||
|
||||
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
|
||||
try {
|
||||
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
|
||||
@@ -346,21 +357,22 @@ ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event,
|
||||
}
|
||||
})
|
||||
|
||||
const allMethods = new Set([ ...syncMethods, ...asyncMethods ])
|
||||
|
||||
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) {
|
||||
const guest = getGuestForWebContents(guestInstanceId, event.sender)
|
||||
if (!allMethods.has(method)) {
|
||||
if (!asyncMethods.has(method)) {
|
||||
throw new Error(`Invalid method: ${method}`)
|
||||
}
|
||||
|
||||
return guest[method](...args)
|
||||
})
|
||||
|
||||
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', async function (event, guestInstanceId, args) {
|
||||
handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) {
|
||||
const guest = getGuestForWebContents(guestInstanceId, event.sender)
|
||||
if (!syncMethods.has(method)) {
|
||||
throw new Error(`Invalid method: ${method}`)
|
||||
}
|
||||
|
||||
return serialize(await guest.capturePage(...args))
|
||||
return guest[method](...args)
|
||||
})
|
||||
|
||||
// Returns WebContents from its guest id hosted in given webContents.
|
||||
|
||||
@@ -271,15 +271,30 @@ ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', functio
|
||||
}
|
||||
})
|
||||
|
||||
const handleMessage = function (channel, handler) {
|
||||
ipcMainUtils.handle(channel, (event, guestId, ...args) => {
|
||||
const makeSafeHandler = function (handler) {
|
||||
return (event, guestId, ...args) => {
|
||||
const guestContents = webContents.fromId(guestId)
|
||||
if (!guestContents) {
|
||||
throw new Error(`Invalid guestId: ${guestId}`)
|
||||
}
|
||||
|
||||
return handler(event, guestContents, ...args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleMessage = function (channel, handler) {
|
||||
ipcMainInternal.handle(channel, makeSafeHandler(handler))
|
||||
}
|
||||
|
||||
const handleMessageSync = function (channel, handler) {
|
||||
ipcMainUtils.handleSync(channel, makeSafeHandler(handler))
|
||||
}
|
||||
|
||||
const assertCanAccessWindow = function (contents, guestContents) {
|
||||
if (!canAccessWindow(contents, guestContents)) {
|
||||
console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`)
|
||||
throw new Error(`Access denied to guestId: ${guestContents.id}`)
|
||||
}
|
||||
}
|
||||
|
||||
const windowMethods = new Set([
|
||||
@@ -289,10 +304,7 @@ const windowMethods = new Set([
|
||||
])
|
||||
|
||||
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => {
|
||||
if (!canAccessWindow(event.sender, guestContents)) {
|
||||
console.error(`Blocked ${event.sender.getURL()} from accessing guestId: ${guestContents.id}`)
|
||||
throw new Error(`Access denied to guestId: ${guestContents.id}`)
|
||||
}
|
||||
assertCanAccessWindow(event.sender, guestContents)
|
||||
|
||||
if (!windowMethods.has(method)) {
|
||||
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
|
||||
@@ -316,20 +328,31 @@ handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestC
|
||||
}
|
||||
})
|
||||
|
||||
const webContentsMethods = new Set([
|
||||
'getURL',
|
||||
const webContentsMethodsAsync = new Set([
|
||||
'loadURL',
|
||||
'executeJavaScript',
|
||||
'print'
|
||||
])
|
||||
|
||||
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
|
||||
if (!canAccessWindow(event.sender, guestContents)) {
|
||||
console.error(`Blocked ${event.sender.getURL()} from accessing guestId: ${guestContents.id}`)
|
||||
throw new Error(`Access denied to guestId: ${guestContents.id}`)
|
||||
}
|
||||
assertCanAccessWindow(event.sender, guestContents)
|
||||
|
||||
if (!webContentsMethods.has(method)) {
|
||||
if (!webContentsMethodsAsync.has(method)) {
|
||||
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
|
||||
throw new Error(`Invalid method: ${method}`)
|
||||
}
|
||||
|
||||
return guestContents[method](...args)
|
||||
})
|
||||
|
||||
const webContentsMethodsSync = new Set([
|
||||
'getURL'
|
||||
])
|
||||
|
||||
handleMessageSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
|
||||
assertCanAccessWindow(event.sender, guestContents)
|
||||
|
||||
if (!webContentsMethodsSync.has(method)) {
|
||||
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
|
||||
throw new Error(`Invalid method: ${method}`)
|
||||
}
|
||||
|
||||
@@ -151,8 +151,16 @@ app._setDefaultAppPaths(packagePath)
|
||||
// Load the chrome devtools support.
|
||||
require('@electron/internal/browser/devtools')
|
||||
|
||||
const features = process.electronBinding('features')
|
||||
|
||||
// Load the chrome extension support.
|
||||
require('@electron/internal/browser/chrome-extension')
|
||||
if (!features.isExtensionsEnabled()) {
|
||||
require('@electron/internal/browser/chrome-extension')
|
||||
}
|
||||
|
||||
if (features.isRemoteModuleEnabled()) {
|
||||
require('@electron/internal/browser/remote/server')
|
||||
}
|
||||
|
||||
// Load protocol module to ensure it is populated on app ready
|
||||
require('@electron/internal/browser/api/protocol')
|
||||
|
||||
33
lib/browser/ipc-main-impl.ts
Normal file
33
lib/browser/ipc-main-impl.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { IpcMainInvokeEvent } from 'electron'
|
||||
|
||||
export class IpcMainImpl 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)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,15 @@
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'
|
||||
import * as errorUtils from '@electron/internal/common/error-utils'
|
||||
|
||||
type IPCHandler = (event: ElectronInternal.IpcMainInternalEvent, ...args: any[]) => any
|
||||
type IPCHandler = (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any
|
||||
|
||||
const callHandler = async function (handler: IPCHandler, event: ElectronInternal.IpcMainInternalEvent, args: any[], reply: (args: any[]) => void) {
|
||||
try {
|
||||
const result = await handler(event, ...args)
|
||||
reply([null, result])
|
||||
} catch (error) {
|
||||
reply([errorUtils.serialize(error)])
|
||||
}
|
||||
}
|
||||
|
||||
export const handle = function <T extends IPCHandler> (channel: string, handler: T) {
|
||||
ipcMainInternal.on(channel, (event, requestId, ...args) => {
|
||||
callHandler(handler, event, args, responseArgs => {
|
||||
if (requestId) {
|
||||
event._replyInternal(`${channel}_RESPONSE_${requestId}`, ...responseArgs)
|
||||
} else {
|
||||
event.returnValue = responseArgs
|
||||
}
|
||||
})
|
||||
export const handleSync = function <T extends IPCHandler> (channel: string, handler: T) {
|
||||
ipcMainInternal.on(channel, async (event, ...args) => {
|
||||
try {
|
||||
event.returnValue = [null, await handler(event, ...args)]
|
||||
} catch (error) {
|
||||
event.returnValue = [errorUtils.serialize(error)]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'
|
||||
|
||||
const emitter = new EventEmitter()
|
||||
export const ipcMainInternal = new IpcMainImpl() as ElectronInternal.IpcMainInternal
|
||||
|
||||
// Do not throw exception when channel name is "error".
|
||||
emitter.on('error', () => {})
|
||||
|
||||
export const ipcMainInternal = emitter as ElectronInternal.IpcMainInternal
|
||||
ipcMainInternal.on('error', () => {})
|
||||
|
||||
@@ -127,10 +127,6 @@ class ObjectsRegistry {
|
||||
this.clear(webContents, contextId)
|
||||
}
|
||||
}
|
||||
// Note that the "render-view-deleted" event may not be emitted on time when
|
||||
// the renderer process get destroyed because of navigation, we rely on the
|
||||
// renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to
|
||||
// guard this situation.
|
||||
webContents.on('render-view-deleted', listener)
|
||||
}
|
||||
}
|
||||
471
lib/browser/remote/server.js
Normal file
471
lib/browser/remote/server.js
Normal file
@@ -0,0 +1,471 @@
|
||||
'use strict'
|
||||
|
||||
const electron = require('electron')
|
||||
const { EventEmitter } = require('events')
|
||||
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const eventBinding = process.electronBinding('event')
|
||||
const features = process.electronBinding('features')
|
||||
|
||||
if (!features.isRemoteModuleEnabled()) {
|
||||
throw new Error('remote module is disabled')
|
||||
}
|
||||
|
||||
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
|
||||
const objectsRegistry = require('@electron/internal/browser/remote/objects-registry')
|
||||
const guestViewManager = require('@electron/internal/browser/guest-view-manager')
|
||||
const bufferUtils = require('@electron/internal/common/remote/buffer-utils')
|
||||
const errorUtils = require('@electron/internal/common/error-utils')
|
||||
const { isPromise } = require('@electron/internal/common/remote/is-promise')
|
||||
|
||||
const hasProp = {}.hasOwnProperty
|
||||
|
||||
// The internal properties of Function.
|
||||
const FUNCTION_PROPERTIES = [
|
||||
'length', 'name', 'arguments', 'caller', 'prototype'
|
||||
]
|
||||
|
||||
// The remote functions in renderer processes.
|
||||
// id => Function
|
||||
const rendererFunctions = v8Util.createDoubleIDWeakMap()
|
||||
|
||||
// Return the description of object's members:
|
||||
const getObjectMembers = function (object) {
|
||||
let names = Object.getOwnPropertyNames(object)
|
||||
// For Function, we should not override following properties even though they
|
||||
// are "own" properties.
|
||||
if (typeof object === 'function') {
|
||||
names = names.filter((name) => {
|
||||
return !FUNCTION_PROPERTIES.includes(name)
|
||||
})
|
||||
}
|
||||
// Map properties to descriptors.
|
||||
return names.map((name) => {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(object, name)
|
||||
const member = { name, enumerable: descriptor.enumerable, writable: false }
|
||||
if (descriptor.get === undefined && typeof object[name] === 'function') {
|
||||
member.type = 'method'
|
||||
} else {
|
||||
if (descriptor.set || descriptor.writable) member.writable = true
|
||||
member.type = 'get'
|
||||
}
|
||||
return member
|
||||
})
|
||||
}
|
||||
|
||||
// Return the description of object's prototype.
|
||||
const getObjectPrototype = function (object) {
|
||||
const proto = Object.getPrototypeOf(object)
|
||||
if (proto === null || proto === Object.prototype) return null
|
||||
return {
|
||||
members: getObjectMembers(proto),
|
||||
proto: getObjectPrototype(proto)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a real value into meta data.
|
||||
const valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
|
||||
// Determine the type of value.
|
||||
const meta = { type: typeof value }
|
||||
if (meta.type === 'object') {
|
||||
// Recognize certain types of objects.
|
||||
if (value === null) {
|
||||
meta.type = 'value'
|
||||
} else if (bufferUtils.isBuffer(value)) {
|
||||
meta.type = 'buffer'
|
||||
} else if (Array.isArray(value)) {
|
||||
meta.type = 'array'
|
||||
} else if (value instanceof Error) {
|
||||
meta.type = 'error'
|
||||
} else if (value instanceof Date) {
|
||||
meta.type = 'date'
|
||||
} else if (isPromise(value)) {
|
||||
meta.type = 'promise'
|
||||
} else if (hasProp.call(value, 'callee') && value.length != null) {
|
||||
// Treat the arguments object as array.
|
||||
meta.type = 'array'
|
||||
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
|
||||
// Treat simple objects as value.
|
||||
meta.type = 'value'
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the meta object according to value's type.
|
||||
if (meta.type === 'array') {
|
||||
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
|
||||
} else if (meta.type === 'object' || meta.type === 'function') {
|
||||
meta.name = value.constructor ? value.constructor.name : ''
|
||||
|
||||
// Reference the original value if it's an object, because when it's
|
||||
// passed to renderer we would assume the renderer keeps a reference of
|
||||
// it.
|
||||
meta.id = objectsRegistry.add(sender, contextId, value)
|
||||
meta.members = getObjectMembers(value)
|
||||
meta.proto = getObjectPrototype(value)
|
||||
} else if (meta.type === 'buffer') {
|
||||
meta.value = bufferUtils.bufferToMeta(value)
|
||||
} else if (meta.type === 'promise') {
|
||||
// Add default handler to prevent unhandled rejections in main process
|
||||
// Instead they should appear in the renderer process
|
||||
value.then(function () {}, function () {})
|
||||
|
||||
meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
|
||||
value.then(onFulfilled, onRejected)
|
||||
})
|
||||
} else if (meta.type === 'error') {
|
||||
meta.members = plainObjectToMeta(value)
|
||||
|
||||
// Error.name is not part of own properties.
|
||||
meta.members.push({
|
||||
name: 'name',
|
||||
value: value.name
|
||||
})
|
||||
} else if (meta.type === 'date') {
|
||||
meta.value = value.getTime()
|
||||
} else {
|
||||
meta.type = 'value'
|
||||
meta.value = value
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
// Convert object to meta by value.
|
||||
const plainObjectToMeta = function (obj) {
|
||||
return Object.getOwnPropertyNames(obj).map(function (name) {
|
||||
return {
|
||||
name: name,
|
||||
value: obj[name]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Convert Error into meta data.
|
||||
const exceptionToMeta = function (error) {
|
||||
return {
|
||||
type: 'exception',
|
||||
value: errorUtils.serialize(error)
|
||||
}
|
||||
}
|
||||
|
||||
const throwRPCError = function (message) {
|
||||
const error = new Error(message)
|
||||
error.code = 'EBADRPC'
|
||||
error.errno = -72
|
||||
throw error
|
||||
}
|
||||
|
||||
const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => {
|
||||
const location = v8Util.getHiddenValue(callIntoRenderer, 'location')
|
||||
let message = `Attempting to call a function in a renderer window that has been closed or released.` +
|
||||
`\nFunction provided here: ${location}`
|
||||
|
||||
if (sender instanceof EventEmitter) {
|
||||
const remoteEvents = sender.eventNames().filter((eventName) => {
|
||||
return sender.listeners(eventName).includes(callIntoRenderer)
|
||||
})
|
||||
|
||||
if (remoteEvents.length > 0) {
|
||||
message += `\nRemote event names: ${remoteEvents.join(', ')}`
|
||||
remoteEvents.forEach((eventName) => {
|
||||
sender.removeListener(eventName, callIntoRenderer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(message)
|
||||
}
|
||||
|
||||
// Convert array of meta data from renderer into array of real values.
|
||||
const unwrapArgs = function (sender, frameId, contextId, args) {
|
||||
const metaToValue = function (meta) {
|
||||
switch (meta.type) {
|
||||
case 'value':
|
||||
return meta.value
|
||||
case 'remote-object':
|
||||
return objectsRegistry.get(meta.id)
|
||||
case 'array':
|
||||
return unwrapArgs(sender, frameId, contextId, meta.value)
|
||||
case 'buffer':
|
||||
return bufferUtils.metaToBuffer(meta.value)
|
||||
case 'date':
|
||||
return new Date(meta.value)
|
||||
case 'promise':
|
||||
return Promise.resolve({
|
||||
then: metaToValue(meta.then)
|
||||
})
|
||||
case 'object': {
|
||||
const ret = {}
|
||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
||||
|
||||
for (const { name, value } of meta.members) {
|
||||
ret[name] = metaToValue(value)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
case 'function-with-return-value':
|
||||
const returnValue = metaToValue(meta.value)
|
||||
return function () {
|
||||
return returnValue
|
||||
}
|
||||
case 'function': {
|
||||
// Merge contextId and meta.id, since meta.id can be the same in
|
||||
// different webContents.
|
||||
const objectId = [contextId, meta.id]
|
||||
|
||||
// Cache the callbacks in renderer.
|
||||
if (rendererFunctions.has(objectId)) {
|
||||
return rendererFunctions.get(objectId)
|
||||
}
|
||||
|
||||
const callIntoRenderer = function (...args) {
|
||||
let succeed = false
|
||||
if (!sender.isDestroyed()) {
|
||||
succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
|
||||
}
|
||||
if (!succeed) {
|
||||
removeRemoteListenersAndLogWarning(this, callIntoRenderer)
|
||||
}
|
||||
}
|
||||
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location)
|
||||
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
|
||||
|
||||
v8Util.setRemoteCallbackFreer(callIntoRenderer, contextId, meta.id, sender)
|
||||
rendererFunctions.set(objectId, callIntoRenderer)
|
||||
return callIntoRenderer
|
||||
}
|
||||
default:
|
||||
throw new TypeError(`Unknown type: ${meta.type}`)
|
||||
}
|
||||
}
|
||||
return args.map(metaToValue)
|
||||
}
|
||||
|
||||
const isRemoteModuleEnabledImpl = function (contents) {
|
||||
const webPreferences = contents.getLastWebPreferences() || {}
|
||||
return !!webPreferences.enableRemoteModule
|
||||
}
|
||||
|
||||
const isRemoteModuleEnabledCache = new WeakMap()
|
||||
|
||||
const isRemoteModuleEnabled = function (contents) {
|
||||
if (!isRemoteModuleEnabledCache.has(contents)) {
|
||||
isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents))
|
||||
}
|
||||
|
||||
return isRemoteModuleEnabledCache.get(contents)
|
||||
}
|
||||
|
||||
const handleRemoteCommand = function (channel, handler) {
|
||||
ipcMainInternal.on(channel, (event, contextId, ...args) => {
|
||||
let returnValue
|
||||
if (!isRemoteModuleEnabled(event.sender)) {
|
||||
event.returnValue = null
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
returnValue = handler(event, contextId, ...args)
|
||||
} catch (error) {
|
||||
returnValue = exceptionToMeta(error)
|
||||
}
|
||||
|
||||
if (returnValue !== undefined) {
|
||||
event.returnValue = returnValue
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const emitCustomEvent = function (contents, eventName, ...args) {
|
||||
const event = eventBinding.createWithSender(contents)
|
||||
|
||||
electron.app.emit(eventName, event, contents, ...args)
|
||||
contents.emit(eventName, event, ...args)
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) {
|
||||
const objectId = [passedContextId, id]
|
||||
if (!rendererFunctions.has(objectId)) {
|
||||
// Do nothing if the error has already been reported before.
|
||||
return
|
||||
}
|
||||
removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId))
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName)
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.require('${moduleName}')`)
|
||||
} else {
|
||||
customEvent.returnValue = process.mainModule.require(moduleName)
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName)
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`)
|
||||
} else {
|
||||
customEvent.returnValue = electron[moduleName]
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName)
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.getGlobal('${globalName}')`)
|
||||
} else {
|
||||
customEvent.returnValue = global[globalName]
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window')
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error('Blocked remote.getCurrentWindow()')
|
||||
} else {
|
||||
customEvent.returnValue = event.sender.getOwnerBrowserWindow()
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents')
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error('Blocked remote.getCurrentWebContents()')
|
||||
} else {
|
||||
customEvent.returnValue = event.sender
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const constructor = objectsRegistry.get(id)
|
||||
|
||||
if (constructor == null) {
|
||||
throwRPCError(`Cannot call constructor on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, new constructor(...args))
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const func = objectsRegistry.get(id)
|
||||
|
||||
if (func == null) {
|
||||
throwRPCError(`Cannot call function on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
try {
|
||||
return valueToMeta(event.sender, contextId, func(...args), true)
|
||||
} catch (error) {
|
||||
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`)
|
||||
err.cause = error
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const object = objectsRegistry.get(id)
|
||||
|
||||
if (object == null) {
|
||||
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, new object[method](...args))
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const object = objectsRegistry.get(id)
|
||||
|
||||
if (object == null) {
|
||||
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
try {
|
||||
return valueToMeta(event.sender, contextId, object[method](...args), true)
|
||||
} catch (error) {
|
||||
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`)
|
||||
err.cause = error
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
obj[name] = args[0]
|
||||
return null
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
|
||||
const obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, obj[name])
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id, rendererSideRefCount) {
|
||||
objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
|
||||
objectsRegistry.clear(event.sender, contextId)
|
||||
return null
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
|
||||
const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender)
|
||||
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-guest-web-contents', guest)
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.getGuestForWebContents()`)
|
||||
} else {
|
||||
customEvent.returnValue = guest
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
isRemoteModuleEnabled
|
||||
}
|
||||
@@ -1,276 +1,18 @@
|
||||
'use strict'
|
||||
|
||||
const electron = require('electron')
|
||||
const { EventEmitter } = require('events')
|
||||
const fs = require('fs')
|
||||
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const eventBinding = process.electronBinding('event')
|
||||
const clipboard = process.electronBinding('clipboard')
|
||||
const features = process.electronBinding('features')
|
||||
|
||||
const { getContentScripts } = require('@electron/internal/browser/chrome-extension')
|
||||
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
|
||||
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
|
||||
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
|
||||
const objectsRegistry = require('@electron/internal/browser/objects-registry')
|
||||
const guestViewManager = require('@electron/internal/browser/guest-view-manager')
|
||||
const bufferUtils = require('@electron/internal/common/buffer-utils')
|
||||
const errorUtils = require('@electron/internal/common/error-utils')
|
||||
const typeUtils = require('@electron/internal/common/type-utils')
|
||||
const { isPromise } = require('@electron/internal/common/is-promise')
|
||||
|
||||
const hasProp = {}.hasOwnProperty
|
||||
|
||||
// The internal properties of Function.
|
||||
const FUNCTION_PROPERTIES = [
|
||||
'length', 'name', 'arguments', 'caller', 'prototype'
|
||||
]
|
||||
|
||||
// The remote functions in renderer processes.
|
||||
// id => Function
|
||||
const rendererFunctions = v8Util.createDoubleIDWeakMap()
|
||||
|
||||
// Return the description of object's members:
|
||||
const getObjectMembers = function (object) {
|
||||
let names = Object.getOwnPropertyNames(object)
|
||||
// For Function, we should not override following properties even though they
|
||||
// are "own" properties.
|
||||
if (typeof object === 'function') {
|
||||
names = names.filter((name) => {
|
||||
return !FUNCTION_PROPERTIES.includes(name)
|
||||
})
|
||||
}
|
||||
// Map properties to descriptors.
|
||||
return names.map((name) => {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(object, name)
|
||||
const member = { name, enumerable: descriptor.enumerable, writable: false }
|
||||
if (descriptor.get === undefined && typeof object[name] === 'function') {
|
||||
member.type = 'method'
|
||||
} else {
|
||||
if (descriptor.set || descriptor.writable) member.writable = true
|
||||
member.type = 'get'
|
||||
}
|
||||
return member
|
||||
})
|
||||
}
|
||||
|
||||
// Return the description of object's prototype.
|
||||
const getObjectPrototype = function (object) {
|
||||
const proto = Object.getPrototypeOf(object)
|
||||
if (proto === null || proto === Object.prototype) return null
|
||||
return {
|
||||
members: getObjectMembers(proto),
|
||||
proto: getObjectPrototype(proto)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a real value into meta data.
|
||||
const valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
|
||||
// Determine the type of value.
|
||||
const meta = { type: typeof value }
|
||||
if (meta.type === 'object') {
|
||||
// Recognize certain types of objects.
|
||||
if (value === null) {
|
||||
meta.type = 'value'
|
||||
} else if (bufferUtils.isBuffer(value)) {
|
||||
meta.type = 'buffer'
|
||||
} else if (Array.isArray(value)) {
|
||||
meta.type = 'array'
|
||||
} else if (value instanceof Error) {
|
||||
meta.type = 'error'
|
||||
} else if (value instanceof Date) {
|
||||
meta.type = 'date'
|
||||
} else if (isPromise(value)) {
|
||||
meta.type = 'promise'
|
||||
} else if (hasProp.call(value, 'callee') && value.length != null) {
|
||||
// Treat the arguments object as array.
|
||||
meta.type = 'array'
|
||||
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
|
||||
// Treat simple objects as value.
|
||||
meta.type = 'value'
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the meta object according to value's type.
|
||||
if (meta.type === 'array') {
|
||||
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
|
||||
} else if (meta.type === 'object' || meta.type === 'function') {
|
||||
meta.name = value.constructor ? value.constructor.name : ''
|
||||
|
||||
// Reference the original value if it's an object, because when it's
|
||||
// passed to renderer we would assume the renderer keeps a reference of
|
||||
// it.
|
||||
meta.id = objectsRegistry.add(sender, contextId, value)
|
||||
meta.members = getObjectMembers(value)
|
||||
meta.proto = getObjectPrototype(value)
|
||||
} else if (meta.type === 'buffer') {
|
||||
meta.value = bufferUtils.bufferToMeta(value)
|
||||
} else if (meta.type === 'promise') {
|
||||
// Add default handler to prevent unhandled rejections in main process
|
||||
// Instead they should appear in the renderer process
|
||||
value.then(function () {}, function () {})
|
||||
|
||||
meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
|
||||
value.then(onFulfilled, onRejected)
|
||||
})
|
||||
} else if (meta.type === 'error') {
|
||||
meta.members = plainObjectToMeta(value)
|
||||
|
||||
// Error.name is not part of own properties.
|
||||
meta.members.push({
|
||||
name: 'name',
|
||||
value: value.name
|
||||
})
|
||||
} else if (meta.type === 'date') {
|
||||
meta.value = value.getTime()
|
||||
} else {
|
||||
meta.type = 'value'
|
||||
meta.value = value
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
// Convert object to meta by value.
|
||||
const plainObjectToMeta = function (obj) {
|
||||
return Object.getOwnPropertyNames(obj).map(function (name) {
|
||||
return {
|
||||
name: name,
|
||||
value: obj[name]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Convert Error into meta data.
|
||||
const exceptionToMeta = function (error) {
|
||||
return {
|
||||
type: 'exception',
|
||||
value: errorUtils.serialize(error)
|
||||
}
|
||||
}
|
||||
|
||||
const throwRPCError = function (message) {
|
||||
const error = new Error(message)
|
||||
error.code = 'EBADRPC'
|
||||
error.errno = -72
|
||||
throw error
|
||||
}
|
||||
|
||||
const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => {
|
||||
const location = v8Util.getHiddenValue(callIntoRenderer, 'location')
|
||||
let message = `Attempting to call a function in a renderer window that has been closed or released.` +
|
||||
`\nFunction provided here: ${location}`
|
||||
|
||||
if (sender instanceof EventEmitter) {
|
||||
const remoteEvents = sender.eventNames().filter((eventName) => {
|
||||
return sender.listeners(eventName).includes(callIntoRenderer)
|
||||
})
|
||||
|
||||
if (remoteEvents.length > 0) {
|
||||
message += `\nRemote event names: ${remoteEvents.join(', ')}`
|
||||
remoteEvents.forEach((eventName) => {
|
||||
sender.removeListener(eventName, callIntoRenderer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(message)
|
||||
}
|
||||
|
||||
// Convert array of meta data from renderer into array of real values.
|
||||
const unwrapArgs = function (sender, frameId, contextId, args) {
|
||||
const metaToValue = function (meta) {
|
||||
switch (meta.type) {
|
||||
case 'value':
|
||||
return meta.value
|
||||
case 'remote-object':
|
||||
return objectsRegistry.get(meta.id)
|
||||
case 'array':
|
||||
return unwrapArgs(sender, frameId, contextId, meta.value)
|
||||
case 'buffer':
|
||||
return bufferUtils.metaToBuffer(meta.value)
|
||||
case 'date':
|
||||
return new Date(meta.value)
|
||||
case 'promise':
|
||||
return Promise.resolve({
|
||||
then: metaToValue(meta.then)
|
||||
})
|
||||
case 'object': {
|
||||
const ret = {}
|
||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
||||
|
||||
for (const { name, value } of meta.members) {
|
||||
ret[name] = metaToValue(value)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
case 'function-with-return-value':
|
||||
const returnValue = metaToValue(meta.value)
|
||||
return function () {
|
||||
return returnValue
|
||||
}
|
||||
case 'function': {
|
||||
// Merge contextId and meta.id, since meta.id can be the same in
|
||||
// different webContents.
|
||||
const objectId = [contextId, meta.id]
|
||||
|
||||
// Cache the callbacks in renderer.
|
||||
if (rendererFunctions.has(objectId)) {
|
||||
return rendererFunctions.get(objectId)
|
||||
}
|
||||
|
||||
const callIntoRenderer = function (...args) {
|
||||
let succeed = false
|
||||
if (!sender.isDestroyed()) {
|
||||
succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
|
||||
}
|
||||
if (!succeed) {
|
||||
removeRemoteListenersAndLogWarning(this, callIntoRenderer)
|
||||
}
|
||||
}
|
||||
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location)
|
||||
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
|
||||
|
||||
v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender)
|
||||
rendererFunctions.set(objectId, callIntoRenderer)
|
||||
return callIntoRenderer
|
||||
}
|
||||
default:
|
||||
throw new TypeError(`Unknown type: ${meta.type}`)
|
||||
}
|
||||
}
|
||||
return args.map(metaToValue)
|
||||
}
|
||||
|
||||
const isRemoteModuleEnabledCache = new WeakMap()
|
||||
|
||||
const isRemoteModuleEnabled = function (contents) {
|
||||
if (!isRemoteModuleEnabledCache.has(contents)) {
|
||||
isRemoteModuleEnabledCache.set(contents, contents._isRemoteModuleEnabled())
|
||||
}
|
||||
|
||||
return isRemoteModuleEnabledCache.get(contents)
|
||||
}
|
||||
|
||||
const handleRemoteCommand = function (channel, handler) {
|
||||
ipcMainInternal.on(channel, (event, contextId, ...args) => {
|
||||
let returnValue
|
||||
if (!isRemoteModuleEnabled(event.sender)) {
|
||||
event.returnValue = null
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
returnValue = handler(event, contextId, ...args)
|
||||
} catch (error) {
|
||||
returnValue = exceptionToMeta(error)
|
||||
}
|
||||
|
||||
if (returnValue !== undefined) {
|
||||
event.returnValue = returnValue
|
||||
}
|
||||
})
|
||||
}
|
||||
const clipboardUtils = require('@electron/internal/common/clipboard-utils')
|
||||
|
||||
const emitCustomEvent = function (contents, eventName, ...args) {
|
||||
const event = eventBinding.createWithSender(contents)
|
||||
@@ -281,187 +23,6 @@ const emitCustomEvent = function (contents, eventName, ...args) {
|
||||
return event
|
||||
}
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) {
|
||||
const objectId = [passedContextId, id]
|
||||
if (!rendererFunctions.has(objectId)) {
|
||||
// Do nothing if the error has already been reported before.
|
||||
return
|
||||
}
|
||||
removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId))
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName)
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.require('${moduleName}')`)
|
||||
} else {
|
||||
customEvent.returnValue = process.mainModule.require(moduleName)
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName)
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`)
|
||||
} else {
|
||||
customEvent.returnValue = electron[moduleName]
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName)
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.getGlobal('${globalName}')`)
|
||||
} else {
|
||||
customEvent.returnValue = global[globalName]
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window')
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error('Blocked remote.getCurrentWindow()')
|
||||
} else {
|
||||
customEvent.returnValue = event.sender.getOwnerBrowserWindow()
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents')
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error('Blocked remote.getCurrentWebContents()')
|
||||
} else {
|
||||
customEvent.returnValue = event.sender
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const constructor = objectsRegistry.get(id)
|
||||
|
||||
if (constructor == null) {
|
||||
throwRPCError(`Cannot call constructor on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, new constructor(...args))
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const func = objectsRegistry.get(id)
|
||||
|
||||
if (func == null) {
|
||||
throwRPCError(`Cannot call function on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
try {
|
||||
return valueToMeta(event.sender, contextId, func(...args), true)
|
||||
} catch (error) {
|
||||
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`)
|
||||
err.cause = error
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const object = objectsRegistry.get(id)
|
||||
|
||||
if (object == null) {
|
||||
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, new object[method](...args))
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const object = objectsRegistry.get(id)
|
||||
|
||||
if (object == null) {
|
||||
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
try {
|
||||
return valueToMeta(event.sender, contextId, object[method](...args), true)
|
||||
} catch (error) {
|
||||
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`)
|
||||
err.cause = error
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
|
||||
args = unwrapArgs(event.sender, event.frameId, contextId, args)
|
||||
const obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
obj[name] = args[0]
|
||||
return null
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
|
||||
const obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, obj[name])
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id, rendererSideRefCount) {
|
||||
objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
|
||||
objectsRegistry.clear(event.sender, contextId)
|
||||
})
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
|
||||
const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender)
|
||||
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-guest-web-contents', guest)
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.getGuestForWebContents()`)
|
||||
} else {
|
||||
customEvent.returnValue = guest
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue)
|
||||
})
|
||||
|
||||
// Implements window.close()
|
||||
ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
|
||||
const window = event.sender.getOwnerBrowserWindow()
|
||||
@@ -471,11 +32,11 @@ ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
|
||||
event.returnValue = null
|
||||
})
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
|
||||
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
|
||||
return crashReporterInit(options)
|
||||
})
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
|
||||
ipcMainInternal.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
|
||||
return event.sender.getLastWebPreferences()
|
||||
})
|
||||
|
||||
@@ -491,18 +52,18 @@ const allowedClipboardMethods = (() => {
|
||||
}
|
||||
})()
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) {
|
||||
ipcMainUtils.handleSync('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) {
|
||||
if (!allowedClipboardMethods.has(method)) {
|
||||
throw new Error(`Invalid method: ${method}`)
|
||||
}
|
||||
|
||||
return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args)))
|
||||
return clipboardUtils.serialize(electron.clipboard[method](...clipboardUtils.deserialize(args)))
|
||||
})
|
||||
|
||||
if (features.isDesktopCapturerEnabled()) {
|
||||
const desktopCapturer = require('@electron/internal/browser/desktop-capturer')
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, ...args) {
|
||||
ipcMainInternal.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, ...args) {
|
||||
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources')
|
||||
|
||||
if (customEvent.defaultPrevented) {
|
||||
@@ -514,6 +75,10 @@ if (features.isDesktopCapturerEnabled()) {
|
||||
})
|
||||
}
|
||||
|
||||
const isRemoteModuleEnabled = features.isRemoteModuleEnabled()
|
||||
? require('@electron/internal/browser/remote/server').isRemoteModuleEnabled
|
||||
: () => false
|
||||
|
||||
const getPreloadScript = async function (preloadPath) {
|
||||
let preloadSrc = null
|
||||
let preloadError = null
|
||||
@@ -525,13 +90,24 @@ const getPreloadScript = async function (preloadPath) {
|
||||
return { preloadPath, preloadSrc, preloadError }
|
||||
}
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts())
|
||||
if (features.isExtensionsEnabled()) {
|
||||
ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => [])
|
||||
} else {
|
||||
const { getContentScripts } = require('@electron/internal/browser/chrome-extension')
|
||||
ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts())
|
||||
}
|
||||
|
||||
ipcMainUtils.handle('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
|
||||
ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
|
||||
const preloadPaths = event.sender._getPreloadPaths()
|
||||
|
||||
let contentScripts = []
|
||||
if (!features.isExtensionsEnabled()) {
|
||||
const { getContentScripts } = require('@electron/internal/browser/chrome-extension')
|
||||
contentScripts = getContentScripts()
|
||||
}
|
||||
|
||||
return {
|
||||
contentScripts: getContentScripts(),
|
||||
contentScripts,
|
||||
preloadScripts: await Promise.all(preloadPaths.map(path => getPreloadScript(path))),
|
||||
isRemoteModuleEnabled: isRemoteModuleEnabled(event.sender),
|
||||
isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender),
|
||||
|
||||
@@ -4,13 +4,13 @@ const clipboard = process.electronBinding('clipboard')
|
||||
|
||||
if (process.type === 'renderer') {
|
||||
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
|
||||
const typeUtils = require('@electron/internal/common/type-utils')
|
||||
const clipboardUtils = require('@electron/internal/common/clipboard-utils')
|
||||
|
||||
const makeRemoteMethod = function (method) {
|
||||
return (...args) => {
|
||||
args = typeUtils.serialize(args)
|
||||
args = clipboardUtils.serialize(args)
|
||||
const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args)
|
||||
return typeUtils.deserialize(result)
|
||||
return clipboardUtils.deserialize(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const moduleList = require('@electron/internal/common/api/module-list')
|
||||
|
||||
exports.handleESModule = (loader) => () => {
|
||||
const value = loader()
|
||||
if (value.__esModule && value.default) return value.default
|
||||
return value
|
||||
}
|
||||
|
||||
exports.memoizedGetter = (getter) => {
|
||||
/*
|
||||
* It's ok to leak this value as it would be leaked by the global
|
||||
* node module cache anyway at `Module._cache`. This memoization
|
||||
* is dramatically faster than relying on nodes module cache however
|
||||
*/
|
||||
let memoizedValue = null
|
||||
|
||||
return () => {
|
||||
if (memoizedValue === null) {
|
||||
memoizedValue = getter()
|
||||
}
|
||||
return memoizedValue
|
||||
}
|
||||
}
|
||||
|
||||
// Attaches properties to |targetExports|.
|
||||
exports.defineProperties = function (targetExports) {
|
||||
const descriptors = {}
|
||||
for (const module of moduleList) {
|
||||
descriptors[module.name] = {
|
||||
enumerable: !module.private,
|
||||
get: exports.handleESModule(module.loader)
|
||||
}
|
||||
}
|
||||
return Object.defineProperties(targetExports, descriptors)
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
// Common modules, please sort alphabetically
|
||||
module.exports = [
|
||||
export const commonModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'clipboard', loader: () => require('./clipboard') },
|
||||
{ name: 'nativeImage', loader: () => require('./native-image') },
|
||||
{ name: 'shell', loader: () => require('./shell') },
|
||||
@@ -8,8 +8,6 @@
|
||||
const path = require('path')
|
||||
const util = require('util')
|
||||
|
||||
const Promise = global.Promise
|
||||
|
||||
const envNoAsar = process.env.ELECTRON_NO_ASAR &&
|
||||
process.type !== 'browser' &&
|
||||
process.type !== 'renderer'
|
||||
@@ -42,6 +40,8 @@
|
||||
return newArchive
|
||||
}
|
||||
|
||||
const ASAR_EXTENSION = '.asar'
|
||||
|
||||
// Separate asar package's path from full path.
|
||||
const splitPath = archivePathOrBuffer => {
|
||||
// Shortcut for disabled asar.
|
||||
@@ -54,7 +54,22 @@
|
||||
}
|
||||
if (typeof archivePath !== 'string') return { isAsar: false }
|
||||
|
||||
return asar.splitPath(path.normalize(archivePath))
|
||||
if (archivePath.endsWith(ASAR_EXTENSION)) {
|
||||
return { isAsar: true, asarPath: archivePath, filePath: '' }
|
||||
}
|
||||
|
||||
archivePath = path.normalize(archivePath)
|
||||
const index = archivePath.lastIndexOf(`${ASAR_EXTENSION}${path.sep}`)
|
||||
if (index === -1) return { isAsar: false }
|
||||
|
||||
// E.g. for "//some/path/to/archive.asar/then/internal.file"...
|
||||
return {
|
||||
isAsar: true,
|
||||
// "//some/path/to/archive.asar"
|
||||
asarPath: archivePath.substr(0, index + ASAR_EXTENSION.length),
|
||||
// "then/internal.file" (with a path separator excluded)
|
||||
filePath: archivePath.substr(index + ASAR_EXTENSION.length + 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert asar archive's Stats object to fs's Stats object.
|
||||
|
||||
17
lib/common/define-properties.ts
Normal file
17
lib/common/define-properties.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
const handleESModule = (loader: ElectronInternal.ModuleLoader) => () => {
|
||||
const value = loader()
|
||||
if (value.__esModule && value.default) return value.default
|
||||
return value
|
||||
}
|
||||
|
||||
// Attaches properties to |targetExports|.
|
||||
export function defineProperties (targetExports: Object, moduleList: ElectronInternal.ModuleEntry[]) {
|
||||
const descriptors: PropertyDescriptorMap = {}
|
||||
for (const module of moduleList) {
|
||||
descriptors[module.name] = {
|
||||
enumerable: !module.private,
|
||||
get: handleESModule(module.loader)
|
||||
}
|
||||
}
|
||||
return Object.defineProperties(targetExports, descriptors)
|
||||
}
|
||||
@@ -52,6 +52,7 @@ export const syncMethods = new Set([
|
||||
|
||||
export const asyncMethods = new Set([
|
||||
'loadURL',
|
||||
'capturePage',
|
||||
'executeJavaScript',
|
||||
'insertCSS',
|
||||
'insertText',
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Captures original globals into a scope to ensure that userland modifications do
|
||||
// not impact Electron. Note that users doing:
|
||||
//
|
||||
// global.Promise.resolve = myFn
|
||||
//
|
||||
// Will mutate this captured one as well and that is OK.
|
||||
|
||||
export const Promise = global.Promise
|
||||
@@ -1,20 +0,0 @@
|
||||
const { hasSwitch } = process.electronBinding('command_line')
|
||||
const binding = process.electronBinding('context_bridge')
|
||||
|
||||
const contextIsolationEnabled = hasSwitch('context-isolation')
|
||||
|
||||
const checkContextIsolationEnabled = () => {
|
||||
if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled')
|
||||
}
|
||||
|
||||
const contextBridge = {
|
||||
exposeInMainWorld: (key: string, api: Record<string, any>) => {
|
||||
checkContextIsolationEnabled()
|
||||
return binding.exposeAPIInMainWorld(key, api)
|
||||
},
|
||||
debugGC: () => binding._debugGCMaps({})
|
||||
}
|
||||
|
||||
if (!binding._debugGCMaps) delete contextBridge.debugGC
|
||||
|
||||
export default contextBridge
|
||||
@@ -1,5 +1,5 @@
|
||||
import { nativeImage } from 'electron'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
|
||||
// |options.types| can't be empty and must be an array
|
||||
function isValid (options: Electron.SourcesOptions) {
|
||||
@@ -16,7 +16,7 @@ export async function getSources (options: Electron.SourcesOptions) {
|
||||
const { thumbnailSize = { width: 150, height: 150 } } = options
|
||||
const { fetchWindowIcons = false } = options
|
||||
|
||||
const sources = await ipcRendererUtils.invoke<ElectronInternal.GetSourcesResult[]>('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', {
|
||||
const sources = await ipcRendererInternal.invoke<ElectronInternal.GetSourcesResult[]>('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', {
|
||||
captureWindow,
|
||||
captureScreen,
|
||||
thumbnailSize,
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const common = require('@electron/internal/common/api/exports/electron')
|
||||
const moduleList = require('@electron/internal/renderer/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/renderer/api/exports/electron.ts
Normal file
6
lib/renderer/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 { rendererModuleList } from '@electron/internal/renderer/api/module-list'
|
||||
|
||||
defineProperties(exports, commonModuleList)
|
||||
defineProperties(exports, rendererModuleList)
|
||||
@@ -1,10 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const { ipc } = process.electronBinding('ipc')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
|
||||
// Created by init.js.
|
||||
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
|
||||
const ipcRenderer = v8Util.getHiddenValue<Electron.IpcRenderer>(global, 'ipc')
|
||||
const internal = false
|
||||
|
||||
ipcRenderer.send = function (channel, ...args) {
|
||||
@@ -23,15 +21,12 @@ ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
|
||||
return ipc.sendTo(internal, false, webContentsId, channel, args)
|
||||
}
|
||||
|
||||
ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
|
||||
return ipc.sendTo(internal, true, webContentsId, channel, args)
|
||||
ipcRenderer.invoke = async function (channel, ...args) {
|
||||
const { error, result } = await ipc.invoke(internal, channel, args)
|
||||
if (error) {
|
||||
throw new Error(`Error invoking remote method '${channel}': ${error}`)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
ipcRenderer.invoke = function (channel, ...args) {
|
||||
return ipc.invoke(channel, args).then(({ error, result }) => {
|
||||
if (error) { throw new Error(`Error invoking remote method '${channel}': ${error}`) }
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = ipcRenderer
|
||||
export default ipcRenderer
|
||||
@@ -1,23 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const features = process.electronBinding('features')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
|
||||
const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule')
|
||||
|
||||
// Renderer side modules, please sort alphabetically.
|
||||
// A module is `enabled` if there is no explicit condition defined.
|
||||
module.exports = [
|
||||
{ name: 'contextBridge', loader: () => require('./context-bridge') },
|
||||
{ name: 'crashReporter', loader: () => require('./crash-reporter') },
|
||||
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
||||
{ name: 'webFrame', loader: () => require('./web-frame') }
|
||||
]
|
||||
|
||||
if (features.isDesktopCapturerEnabled()) {
|
||||
module.exports.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') })
|
||||
}
|
||||
|
||||
if (enableRemoteModule) {
|
||||
module.exports.push({ name: 'remote', loader: () => require('./remote') })
|
||||
}
|
||||
19
lib/renderer/api/module-list.ts
Normal file
19
lib/renderer/api/module-list.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
const features = process.electronBinding('features')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
|
||||
const enableRemoteModule = v8Util.getHiddenValue<boolean>(global, 'enableRemoteModule')
|
||||
|
||||
// Renderer side modules, please sort alphabetically.
|
||||
export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'crashReporter', loader: () => require('./crash-reporter') },
|
||||
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
||||
{ name: 'webFrame', loader: () => require('./web-frame') }
|
||||
]
|
||||
|
||||
if (features.isDesktopCapturerEnabled()) {
|
||||
rendererModuleList.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') })
|
||||
}
|
||||
|
||||
if (features.isRemoteModuleEnabled() && enableRemoteModule) {
|
||||
rendererModuleList.push({ name: 'remote', loader: () => require('./remote') })
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
|
||||
const { CallbacksRegistry } = require('@electron/internal/renderer/callbacks-registry')
|
||||
const bufferUtils = require('@electron/internal/common/buffer-utils')
|
||||
const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry')
|
||||
const bufferUtils = require('@electron/internal/common/remote/buffer-utils')
|
||||
const errorUtils = require('@electron/internal/common/error-utils')
|
||||
const { isPromise } = require('@electron/internal/common/is-promise')
|
||||
const { isPromise } = require('@electron/internal/common/remote/is-promise')
|
||||
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal')
|
||||
|
||||
const callbacksRegistry = new CallbacksRegistry()
|
||||
@@ -20,7 +20,7 @@ const contextId = v8Util.getHiddenValue(global, 'contextId')
|
||||
// to guard that situation.
|
||||
process.on('exit', () => {
|
||||
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
||||
ipcRendererInternal.send(command, contextId)
|
||||
ipcRendererInternal.sendSync(command, contextId)
|
||||
})
|
||||
|
||||
// Convert the arguments object into an array of meta data.
|
||||
@@ -349,9 +349,8 @@ const addBuiltinProperty = (name) => {
|
||||
})
|
||||
}
|
||||
|
||||
const browserModules =
|
||||
require('@electron/internal/common/api/module-list').concat(
|
||||
require('@electron/internal/browser/api/module-keys'))
|
||||
const { commonModuleList } = require('@electron/internal/common/api/module-list')
|
||||
const browserModules = commonModuleList.concat(require('@electron/internal/browser/api/module-keys'))
|
||||
|
||||
// And add a helper receiver for each one.
|
||||
browserModules
|
||||
|
||||
@@ -67,6 +67,10 @@ class Port {
|
||||
|
||||
// Inject chrome API to the |context|
|
||||
export function injectTo (extensionId: string, context: any) {
|
||||
if (process.electronBinding('features').isExtensionsEnabled()) {
|
||||
throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled')
|
||||
}
|
||||
|
||||
const chrome = context.chrome = context.chrome || {}
|
||||
|
||||
ipcRendererInternal.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (
|
||||
@@ -116,11 +120,7 @@ export function injectTo (extensionId: string, context: any) {
|
||||
let targetExtensionId = extensionId
|
||||
let connectInfo = { name: '' }
|
||||
if (args.length === 1) {
|
||||
if (typeof args[0] === 'string') {
|
||||
targetExtensionId = args[0]
|
||||
} else {
|
||||
connectInfo = args[0]
|
||||
}
|
||||
targetExtensionId = args[0]
|
||||
} else if (args.length === 2) {
|
||||
[targetExtensionId, connectInfo] = args
|
||||
}
|
||||
@@ -157,7 +157,7 @@ export function injectTo (extensionId: string, context: any) {
|
||||
console.error('options are not supported')
|
||||
}
|
||||
|
||||
ipcRendererUtils.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback)
|
||||
ipcRendererInternal.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback)
|
||||
},
|
||||
|
||||
onConnect: new Event(),
|
||||
@@ -172,7 +172,7 @@ export function injectTo (extensionId: string, context: any) {
|
||||
details: Chrome.Tabs.ExecuteScriptDetails,
|
||||
resultCallback: Chrome.Tabs.ExecuteScriptCallback = () => {}
|
||||
) {
|
||||
ipcRendererUtils.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details)
|
||||
ipcRendererInternal.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details)
|
||||
.then((result: any) => resultCallback([result]))
|
||||
},
|
||||
|
||||
@@ -183,7 +183,7 @@ export function injectTo (extensionId: string, context: any) {
|
||||
_options: Chrome.Tabs.SendMessageDetails,
|
||||
responseCallback: Chrome.Tabs.SendMessageCallback = () => {}
|
||||
) {
|
||||
ipcRendererUtils.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback)
|
||||
ipcRendererInternal.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback)
|
||||
},
|
||||
|
||||
onUpdated: new Event(),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
|
||||
const getStorage = (storageType: string, extensionId: number, callback: Function) => {
|
||||
if (typeof callback !== 'function') throw new TypeError('No callback provided')
|
||||
|
||||
ipcRendererUtils.invoke<string>('CHROME_STORAGE_READ', storageType, extensionId)
|
||||
ipcRendererInternal.invoke<string>('CHROME_STORAGE_READ', storageType, extensionId)
|
||||
.then(data => {
|
||||
if (data !== null) {
|
||||
callback(JSON.parse(data))
|
||||
@@ -17,7 +17,7 @@ const getStorage = (storageType: string, extensionId: number, callback: Function
|
||||
|
||||
const setStorage = (storageType: string, extensionId: number, storage: Record<string, any>, callback: Function) => {
|
||||
const json = JSON.stringify(storage)
|
||||
ipcRendererUtils.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json)
|
||||
ipcRendererInternal.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json)
|
||||
.then(() => {
|
||||
if (callback) callback()
|
||||
})
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { Event } from '@electron/internal/renderer/extensions/event'
|
||||
import { IpcMainEvent } from 'electron'
|
||||
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal')
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
|
||||
class WebNavigation {
|
||||
private onBeforeNavigate = new Event()
|
||||
private onCompleted = new Event()
|
||||
|
||||
constructor () {
|
||||
ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event: IpcMainEvent, details: any) => {
|
||||
ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event: Electron.IpcRendererEvent, details: any) => {
|
||||
this.onBeforeNavigate.emit(details)
|
||||
})
|
||||
|
||||
ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event: IpcMainEvent, details: any) => {
|
||||
ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event: Electron.IpcRendererEvent, details: any) => {
|
||||
this.onCompleted.emit(details)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,7 +100,9 @@ switch (window.location.protocol) {
|
||||
}
|
||||
case 'chrome-extension:': {
|
||||
// Inject the chrome.* APIs that chrome extensions require
|
||||
require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window)
|
||||
if (!process.electronBinding('features').isExtensionsEnabled()) {
|
||||
require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'chrome:':
|
||||
@@ -111,8 +113,10 @@ switch (window.location.protocol) {
|
||||
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
|
||||
|
||||
// Inject content scripts.
|
||||
const contentScripts = ipcRendererUtils.invokeSync('ELECTRON_GET_CONTENT_SCRIPTS') as Electron.ContentScriptEntry[]
|
||||
require('@electron/internal/renderer/content-scripts-injector')(contentScripts)
|
||||
if (!process.electronBinding('features').isExtensionsEnabled()) {
|
||||
const contentScripts = ipcRendererUtils.invokeSync('ELECTRON_GET_CONTENT_SCRIPTS') as Electron.ContentScriptEntry[]
|
||||
require('@electron/internal/renderer/content-scripts-injector')(contentScripts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { invoke, invokeSync } from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
|
||||
window.onload = function () {
|
||||
// Use menu API to show context menu.
|
||||
@@ -19,7 +20,7 @@ function completeURL (project: string, path: string) {
|
||||
|
||||
// The DOM implementation expects (message?: string) => boolean
|
||||
(window.confirm as any) = function (message: string, title: string) {
|
||||
return invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean
|
||||
}
|
||||
|
||||
const useEditMenuItems = function (x: number, y: number, items: ContextMenuItem[]) {
|
||||
@@ -32,7 +33,7 @@ const useEditMenuItems = function (x: number, y: number, items: ContextMenuItem[
|
||||
|
||||
const createMenu = function (x: number, y: number, items: ContextMenuItem[]) {
|
||||
const isEditMenu = useEditMenuItems(x, y, items)
|
||||
invoke<number>('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => {
|
||||
ipcRendererInternal.invoke<number>('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => {
|
||||
if (typeof id === 'number') {
|
||||
window.DevToolsAPI!.contextMenuItemSelected(id)
|
||||
}
|
||||
@@ -41,7 +42,7 @@ const createMenu = function (x: number, y: number, items: ContextMenuItem[]) {
|
||||
}
|
||||
|
||||
const showFileChooserDialog = function (callback: (blob: File) => void) {
|
||||
invoke<[ string, any ]>('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => {
|
||||
ipcRendererInternal.invoke<[ string, any ]>('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => {
|
||||
if (path && data) {
|
||||
callback(dataToHtml5FileObject(path, data))
|
||||
}
|
||||
|
||||
@@ -4,38 +4,18 @@ import * as errorUtils from '@electron/internal/common/error-utils'
|
||||
type IPCHandler = (event: Electron.IpcRendererEvent, ...args: any[]) => any
|
||||
|
||||
export const handle = function <T extends IPCHandler> (channel: string, handler: T) {
|
||||
ipcRendererInternal.on(channel, (event, requestId, ...args) => {
|
||||
new Promise(resolve => resolve(handler(event, ...args))
|
||||
).then(result => {
|
||||
return [null, result]
|
||||
}, error => {
|
||||
return [errorUtils.serialize(error)]
|
||||
}).then(responseArgs => {
|
||||
event.sender.send(`${channel}_RESPONSE_${requestId}`, ...responseArgs)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let nextId = 0
|
||||
|
||||
export function invoke<T> (command: string, ...args: any[]) {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const requestId = ++nextId
|
||||
ipcRendererInternal.once(`${command}_RESPONSE_${requestId}`, (
|
||||
_event, error: Electron.SerializedError, result: any
|
||||
) => {
|
||||
if (error) {
|
||||
reject(errorUtils.deserialize(error))
|
||||
} else {
|
||||
resolve(result)
|
||||
}
|
||||
})
|
||||
ipcRendererInternal.send(command, requestId, ...args)
|
||||
ipcRendererInternal.on(channel, async (event, requestId, ...args) => {
|
||||
const replyChannel = `${channel}_RESPONSE_${requestId}`
|
||||
try {
|
||||
event.sender.send(replyChannel, null, await handler(event, ...args))
|
||||
} catch (error) {
|
||||
event.sender.send(replyChannel, errorUtils.serialize(error))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function invokeSync<T> (command: string, ...args: any[]): T {
|
||||
const [ error, result ] = ipcRendererInternal.sendSync(command, null, ...args)
|
||||
const [ error, result ] = ipcRendererInternal.sendSync(command, ...args)
|
||||
|
||||
if (error) {
|
||||
throw errorUtils.deserialize(error)
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
const binding = process.electronBinding('ipc')
|
||||
const { ipc } = process.electronBinding('ipc')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
|
||||
// Created by init.js.
|
||||
export const ipcRendererInternal: Electron.IpcRendererInternal = v8Util.getHiddenValue(global, 'ipc-internal')
|
||||
export const ipcRendererInternal = v8Util.getHiddenValue<Electron.IpcRendererInternal>(global, 'ipc-internal')
|
||||
const internal = true
|
||||
|
||||
ipcRendererInternal.send = function (channel, ...args) {
|
||||
return binding.ipc.send(internal, channel, args)
|
||||
return ipc.send(internal, channel, args)
|
||||
}
|
||||
|
||||
ipcRendererInternal.sendSync = function (channel, ...args) {
|
||||
return binding.ipc.sendSync(internal, channel, args)[0]
|
||||
return ipc.sendSync(internal, channel, args)[0]
|
||||
}
|
||||
|
||||
ipcRendererInternal.sendTo = function (webContentsId, channel, ...args) {
|
||||
return binding.ipc.sendTo(internal, false, webContentsId, channel, args)
|
||||
return ipc.sendTo(internal, false, webContentsId, channel, args)
|
||||
}
|
||||
|
||||
ipcRendererInternal.sendToAll = function (webContentsId, channel, ...args) {
|
||||
return binding.ipc.sendTo(internal, true, webContentsId, channel, args)
|
||||
return ipc.sendTo(internal, true, webContentsId, channel, args)
|
||||
}
|
||||
|
||||
ipcRendererInternal.invoke = async function<T> (channel: string, ...args: any[]) {
|
||||
const { error, result } = await ipc.invoke<T>(internal, channel, args)
|
||||
if (error) {
|
||||
throw new Error(`Error invoking remote method '${channel}': ${error}`)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user