mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
147 Commits
v8.0.0-nig
...
v8.0.0-nig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f645ca015f | ||
|
|
f808f50fa6 | ||
|
|
5f27c1fa25 | ||
|
|
ba47c9b925 | ||
|
|
b194d5d0e2 | ||
|
|
776cab96b1 | ||
|
|
38711233c5 | ||
|
|
ecd9e1f26e | ||
|
|
1fed1ed577 | ||
|
|
0fe718b1d9 | ||
|
|
dcf6f046d9 | ||
|
|
4240017cb6 | ||
|
|
d8aaaeb378 | ||
|
|
06285f0bf1 | ||
|
|
eaf2c61bef | ||
|
|
bff113760a | ||
|
|
7ae8538847 | ||
|
|
d9b058c694 | ||
|
|
6bcf67e051 | ||
|
|
23ca7e3733 | ||
|
|
b060cbf4ec | ||
|
|
2bbf32a18b | ||
|
|
206e94d20c | ||
|
|
f0b0614dd7 | ||
|
|
dfdf1b54c6 | ||
|
|
3ae3233e65 | ||
|
|
6781d5e3c8 | ||
|
|
b275273044 | ||
|
|
8dfc896cfa | ||
|
|
70fa9ff4c0 | ||
|
|
3d56e13b38 | ||
|
|
0ab9cc30d2 | ||
|
|
c03ed6d3a1 | ||
|
|
5d00494f8d | ||
|
|
79d3901859 | ||
|
|
0f7ebff81e | ||
|
|
375e612ac5 | ||
|
|
baaf058380 | ||
|
|
0c870775c4 | ||
|
|
bd5a5b3ae6 | ||
|
|
b6246dcf12 | ||
|
|
fb8b1fd1c9 | ||
|
|
d0cdd12521 | ||
|
|
0eff02dab9 | ||
|
|
73da4b7215 | ||
|
|
cdff1bde22 | ||
|
|
e8d85b6ded | ||
|
|
8f1ad8a4e5 | ||
|
|
57883eed0f | ||
|
|
4ec6d10969 | ||
|
|
0fe6767d6b | ||
|
|
0e0d4fe990 | ||
|
|
e196a397ad | ||
|
|
510a916f82 | ||
|
|
be955a9721 | ||
|
|
77414813b4 | ||
|
|
cc278cea00 | ||
|
|
fadd7e056d | ||
|
|
6dc101ffec | ||
|
|
467409458e | ||
|
|
f5e5bcd943 | ||
|
|
db4d01c517 | ||
|
|
5abce7ec08 | ||
|
|
18907389da | ||
|
|
ba8f80267c | ||
|
|
2abea22b4b | ||
|
|
cde2aae6b5 | ||
|
|
97fa8c824c | ||
|
|
2658cae3ea | ||
|
|
e3b30a825c | ||
|
|
aa26e8b946 | ||
|
|
9a5cd4c8d2 | ||
|
|
138af75ff8 | ||
|
|
13cb21a684 | ||
|
|
620ac9c2b4 | ||
|
|
417f9e27e1 | ||
|
|
0abbb35c4e | ||
|
|
7b28cd33cb | ||
|
|
4e88633d89 | ||
|
|
eb0e55c514 | ||
|
|
b155ebeeb3 | ||
|
|
820dab295f | ||
|
|
956127ee5e | ||
|
|
43fa9166c9 | ||
|
|
9f23bb581e | ||
|
|
2b890827ef | ||
|
|
3ac3fbdbfb | ||
|
|
0090616f7b | ||
|
|
8099e6137d | ||
|
|
e74b21bff5 | ||
|
|
825e67140e | ||
|
|
19223952a8 | ||
|
|
58115c1cae | ||
|
|
86258c6cff | ||
|
|
b29f0b9348 | ||
|
|
e06b0aa73b | ||
|
|
db91673f23 | ||
|
|
c73e34fb9e | ||
|
|
221ce70699 | ||
|
|
9055b5cf6d | ||
|
|
93788e76e0 | ||
|
|
16d4ace800 | ||
|
|
5273930f76 | ||
|
|
dceabf231b | ||
|
|
1ecfcc8c70 | ||
|
|
6c6bff81ac | ||
|
|
8dad0c7aaa | ||
|
|
eb100cdf9e | ||
|
|
b085dac15b | ||
|
|
a7e26fdc61 | ||
|
|
d67f25ce43 | ||
|
|
dfac05805d | ||
|
|
6025ae7b12 | ||
|
|
8efa45d12d | ||
|
|
1f44f47de1 | ||
|
|
5c2c30142c | ||
|
|
5bd7b6ad50 | ||
|
|
2cf64d0200 | ||
|
|
2aa7ab821c | ||
|
|
1b2c6a33b3 | ||
|
|
67642312f4 | ||
|
|
032353b14f | ||
|
|
d21519ed2e | ||
|
|
50178d4836 | ||
|
|
5d696c5c69 | ||
|
|
01448e1235 | ||
|
|
812de5d3bf | ||
|
|
ec2c1db0e4 | ||
|
|
b92163d226 | ||
|
|
c2e77e4429 | ||
|
|
ec87917f58 | ||
|
|
d1c5c760d0 | ||
|
|
9b4ba7993c | ||
|
|
1dc1ef6091 | ||
|
|
8de925c4c2 | ||
|
|
df1d3156a0 | ||
|
|
c75c3ef616 | ||
|
|
bb2ab01f99 | ||
|
|
2fad53e66b | ||
|
|
c250cd6e7c | ||
|
|
f80a17c5be | ||
|
|
5e11be6898 | ||
|
|
ebd55c1147 | ||
|
|
2ce90f8806 | ||
|
|
b3e7657159 | ||
|
|
23066f8cce | ||
|
|
de3c1fae7f |
@@ -4,7 +4,7 @@ parameters:
|
||||
upload-to-s3:
|
||||
type: string
|
||||
default: '1'
|
||||
|
||||
|
||||
run-lint:
|
||||
type: boolean
|
||||
default: true
|
||||
@@ -28,11 +28,11 @@ parameters:
|
||||
run-linux-arm-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
|
||||
run-linux-arm64-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
|
||||
run-osx-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
@@ -65,12 +65,12 @@ machine-linux-2xlarge: &machine-linux-2xlarge
|
||||
|
||||
machine-mac: &machine-mac
|
||||
macos:
|
||||
xcode: "9.4.1"
|
||||
xcode: "10.3.0"
|
||||
|
||||
machine-mac-large: &machine-mac-large
|
||||
resource_class: large
|
||||
macos:
|
||||
xcode: "9.4.1"
|
||||
xcode: "10.3.0"
|
||||
|
||||
# Build configurations options.
|
||||
env-debug-build: &env-debug-build
|
||||
@@ -136,7 +136,7 @@ env-linux-medium: &env-linux-medium
|
||||
|
||||
env-linux-2xlarge: &env-linux-2xlarge
|
||||
NUMBER_OF_NINJA_PROCESSES: 34
|
||||
|
||||
|
||||
env-linux-2xlarge-release: &env-linux-2xlarge-release
|
||||
NUMBER_OF_NINJA_PROCESSES: 16
|
||||
|
||||
@@ -221,7 +221,7 @@ step-setup-env-for-build: &step-setup-env-for-build
|
||||
echo 'export SCCACHE_PATH="'"$SCCACHE_PATH"'"' >> $BASH_ENV
|
||||
if [ "$CIRCLE_PR_NUMBER" != "" ]; then
|
||||
#if building a fork set readonly access to sccache
|
||||
echo 'export SCCACHE_BUCKET="electronjs-sccache"' >> $BASH_ENV
|
||||
echo 'export SCCACHE_BUCKET="electronjs-sccache-ci"' >> $BASH_ENV
|
||||
echo 'export SCCACHE_TWO_TIER=true' >> $BASH_ENV
|
||||
fi
|
||||
fi
|
||||
@@ -319,6 +319,8 @@ step-gn-check: &step-gn-check
|
||||
gn check out/Default //electron:electron_app
|
||||
gn check out/Default //electron:manifests
|
||||
gn check out/Default //electron/shell/common/api:mojo
|
||||
# Check the hunspell filenames
|
||||
node electron/script/gen-hunspell-filenames.js --check
|
||||
|
||||
step-electron-build: &step-electron-build
|
||||
run:
|
||||
@@ -329,7 +331,7 @@ step-electron-build: &step-electron-build
|
||||
ninja -C out/Default electron -j $NUMBER_OF_NINJA_PROCESSES
|
||||
|
||||
step-native-unittests-build: &step-native-unittests-build
|
||||
run:
|
||||
run:
|
||||
name: Build native test targets
|
||||
no_output_timeout: 30m
|
||||
command: |
|
||||
@@ -534,6 +536,20 @@ step-mksnapshot-store: &step-mksnapshot-store
|
||||
path: src/out/Default/mksnapshot.zip
|
||||
destination: mksnapshot.zip
|
||||
|
||||
step-hunspell-build: &step-hunspell-build
|
||||
run:
|
||||
name: hunspell build
|
||||
command: |
|
||||
cd src
|
||||
if [ "$SKIP_DIST_ZIP" != "1" ]; then
|
||||
ninja -C out/Default electron:hunspell_dictionaries_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
fi
|
||||
|
||||
step-hunspell-store: &step-hunspell-store
|
||||
store_artifacts:
|
||||
path: src/out/Default/hunspell_dictionaries.zip
|
||||
destination: hunspell_dictionaries.zip
|
||||
|
||||
step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
|
||||
run:
|
||||
name: Generate breakpad symbols
|
||||
@@ -553,6 +569,11 @@ step-maybe-zip-symbols: &step-maybe-zip-symbols
|
||||
ninja -C out/Default electron:electron_version
|
||||
electron/script/zip-symbols.py -b $BUILD_PATH
|
||||
|
||||
step-symbols-store: &step-symbols-store
|
||||
store_artifacts:
|
||||
path: src/out/Default/symbols.zip
|
||||
destination: symbols.zip
|
||||
|
||||
step-maybe-cross-arch-snapshot: &step-maybe-cross-arch-snapshot
|
||||
run:
|
||||
name: Generate cross arch snapshot (arm/arm64)
|
||||
@@ -688,7 +709,6 @@ step-minimize-workspace-size-from-checkout: &step-minimize-workspace-size-from-c
|
||||
rm -rf src/ios
|
||||
rm -rf src/third_party/blink/web_tests
|
||||
rm -rf src/third_party/blink/perf_tests
|
||||
rm -rf src/third_party/hunspell_dictionaries
|
||||
rm -rf src/third_party/WebKit/LayoutTests
|
||||
|
||||
# Save the src cache based on the deps hash
|
||||
@@ -699,6 +719,51 @@ step-save-src-cache: &step-save-src-cache
|
||||
key: v5-src-cache-{{ arch }}-{{ checksum "src/electron/.depshash" }}
|
||||
name: Persisting src cache
|
||||
|
||||
# Check for doc only change
|
||||
step-check-for-doc-only-change: &step-check-for-doc-only-change
|
||||
run:
|
||||
name: Check if commit is doc only change
|
||||
command: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
if node script/doc-only-change.js --prNumber=$CIRCLE_PR_NUMBER --prURL=$CIRCLE_PULL_REQUEST; then
|
||||
#PR is doc only change; save file with value true to indicate doc only change
|
||||
echo "true" > .skip-ci-build
|
||||
else
|
||||
#PR is not a doc only change; create empty file to indicate check has been done
|
||||
touch .skip-ci-build
|
||||
fi
|
||||
|
||||
step-persist-doc-only-change: &step-persist-doc-only-change
|
||||
persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- src/electron/.skip-ci-build
|
||||
|
||||
step-maybe-early-exit-doc-only-change: &step-maybe-early-exit-doc-only-change
|
||||
run:
|
||||
name: Shortcircuit build if doc only change
|
||||
command: |
|
||||
if [ -s src/electron/.skip-ci-build ]; then
|
||||
circleci-agent step halt
|
||||
fi
|
||||
|
||||
step-maybe-early-exit-no-doc-change: &step-maybe-early-exit-no-doc-change
|
||||
run:
|
||||
name: Shortcircuit job if change is not doc only
|
||||
command: |
|
||||
if [ ! -s src/electron/.skip-ci-build ]; then
|
||||
circleci-agent step halt
|
||||
fi
|
||||
|
||||
step-ts-compile: &step-ts-compile
|
||||
run:
|
||||
name: Run TS/JS compile on doc only change
|
||||
command: |
|
||||
cd src
|
||||
ninja -C out/Default electron:default_app_js -j $NUMBER_OF_NINJA_PROCESSES
|
||||
ninja -C out/Default electron:atom_js2c -j $NUMBER_OF_NINJA_PROCESSES
|
||||
|
||||
# Lists of steps.
|
||||
steps-lint: &steps-lint
|
||||
steps:
|
||||
@@ -747,6 +812,9 @@ steps-lint: &steps-lint
|
||||
steps-checkout-fast: &steps-checkout-fast
|
||||
steps:
|
||||
- *step-checkout-electron
|
||||
- *step-check-for-doc-only-change
|
||||
- *step-persist-doc-only-change
|
||||
- *step-maybe-early-exit-doc-only-change
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
@@ -778,6 +846,9 @@ steps-checkout-fast: &steps-checkout-fast
|
||||
steps-checkout-and-save-cache: &steps-checkout-and-save-cache
|
||||
steps:
|
||||
- *step-checkout-electron
|
||||
- *step-check-for-doc-only-change
|
||||
- *step-persist-doc-only-change
|
||||
- *step-maybe-early-exit-doc-only-change
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
@@ -808,6 +879,7 @@ steps-electron-gn-check: &steps-electron-gn-check
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- *step-maybe-early-exit-doc-only-change
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-setup-env-for-build
|
||||
- *step-gn-gen-default
|
||||
@@ -817,6 +889,7 @@ steps-electron-build: &steps-electron-build
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- *step-maybe-early-exit-doc-only-change
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-setup-env-for-build
|
||||
- *step-restore-brew-cache
|
||||
@@ -854,11 +927,16 @@ steps-electron-build: &steps-electron-build
|
||||
- *step-ffmpeg-build
|
||||
- *step-ffmpeg-store
|
||||
|
||||
# hunspell
|
||||
- *step-hunspell-build
|
||||
- *step-hunspell-store
|
||||
|
||||
# Save all data needed for a further tests run.
|
||||
- *step-persist-data-for-tests
|
||||
|
||||
- *step-maybe-generate-breakpad-symbols
|
||||
- *step-maybe-zip-symbols
|
||||
- *step-symbols-store
|
||||
|
||||
# Trigger tests on arm hardware if needed
|
||||
- *step-maybe-trigger-arm-test
|
||||
@@ -869,6 +947,9 @@ steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with-
|
||||
steps:
|
||||
# Checkout - Copied ffrom steps-checkout
|
||||
- *step-checkout-electron
|
||||
- *step-check-for-doc-only-change
|
||||
- *step-persist-doc-only-change
|
||||
- *step-maybe-early-exit-doc-only-change
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
@@ -890,7 +971,7 @@ steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with-
|
||||
- *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
|
||||
@@ -928,17 +1009,61 @@ steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with-
|
||||
- *step-ffmpeg-build
|
||||
- *step-ffmpeg-store
|
||||
|
||||
# hunspell
|
||||
- *step-hunspell-build
|
||||
- *step-hunspell-store
|
||||
|
||||
# Save all data needed for a further tests run.
|
||||
- *step-persist-data-for-tests
|
||||
|
||||
- *step-maybe-generate-breakpad-symbols
|
||||
- *step-maybe-zip-symbols
|
||||
- *step-symbols-store
|
||||
|
||||
# Trigger tests on arm hardware if needed
|
||||
- *step-maybe-trigger-arm-test
|
||||
|
||||
- *step-maybe-notify-slack-failure
|
||||
|
||||
steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-change
|
||||
steps:
|
||||
# Checkout - Copied ffrom steps-checkout
|
||||
- *step-checkout-electron
|
||||
- *step-check-for-doc-only-change
|
||||
- *step-maybe-early-exit-no-doc-change
|
||||
- *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
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-install-npm-deps-on-mac
|
||||
- *step-fix-sync-on-mac
|
||||
- *step-gn-gen-default
|
||||
|
||||
#Compile ts/js to verify doc change didn't break anything
|
||||
- *step-ts-compile
|
||||
|
||||
steps-electron-build-for-publish: &steps-electron-build-for-publish
|
||||
steps:
|
||||
- *step-checkout-electron
|
||||
@@ -953,11 +1078,12 @@ steps-electron-build-for-publish: &steps-electron-build-for-publish
|
||||
|
||||
# Electron app
|
||||
- *step-electron-build
|
||||
- *step-maybe-generate-breakpad-symbols
|
||||
- *step-maybe-electron-dist-strip
|
||||
- *step-electron-dist-build
|
||||
- *step-electron-dist-store
|
||||
- *step-maybe-generate-breakpad-symbols
|
||||
- *step-maybe-zip-symbols
|
||||
- *step-symbols-store
|
||||
|
||||
# mksnapshot
|
||||
- *step-mksnapshot-build
|
||||
@@ -976,6 +1102,10 @@ steps-electron-build-for-publish: &steps-electron-build-for-publish
|
||||
- *step-ffmpeg-build
|
||||
- *step-ffmpeg-store
|
||||
|
||||
# hunspell
|
||||
- *step-hunspell-build
|
||||
- *step-hunspell-store
|
||||
|
||||
# typescript defs
|
||||
- *step-maybe-generate-typescript-defs
|
||||
|
||||
@@ -1056,6 +1186,7 @@ steps-tests: &steps-tests
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- *step-maybe-early-exit-doc-only-change
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-electron-dist-unzip
|
||||
- *step-mksnapshot-unzip
|
||||
@@ -1074,7 +1205,7 @@ steps-tests: &steps-tests
|
||||
command: |
|
||||
cd src
|
||||
export ELECTRON_OUT_DIR=Default
|
||||
(cd electron && node script/yarn test -- --ci --enable-logging)
|
||||
(cd electron && node script/yarn test -- --enable-logging)
|
||||
- run:
|
||||
name: Check test results existence
|
||||
command: |
|
||||
@@ -1098,6 +1229,7 @@ steps-test-nan: &steps-test-nan
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- *step-maybe-early-exit-doc-only-change
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-electron-dist-unzip
|
||||
- *step-setup-linux-for-headless-testing
|
||||
@@ -1113,6 +1245,7 @@ steps-test-node: &steps-test-node
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- *step-maybe-early-exit-doc-only-change
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-electron-dist-unzip
|
||||
- *step-setup-linux-for-headless-testing
|
||||
@@ -1138,6 +1271,13 @@ jobs:
|
||||
<<: *env-linux-medium
|
||||
<<: *steps-lint
|
||||
|
||||
ts-compile-doc-change:
|
||||
<<: *machine-linux-medium
|
||||
environment:
|
||||
<<: *env-linux-medium
|
||||
<<: *env-testing-build
|
||||
<<: *steps-electron-ts-compile-for-doc-change
|
||||
|
||||
# Layer 1: Checkout.
|
||||
linux-checkout-fast:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1548,7 +1688,7 @@ jobs:
|
||||
<<: *env-mas
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-ninja-status
|
||||
<<: *env-ninja-status
|
||||
<<: *steps-electron-build
|
||||
|
||||
mas-publish:
|
||||
@@ -1814,7 +1954,7 @@ jobs:
|
||||
workflows:
|
||||
version: 2.1
|
||||
|
||||
# The publish workflows below each contain one job so that they are
|
||||
# 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
|
||||
@@ -1837,13 +1977,13 @@ workflows:
|
||||
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:
|
||||
@@ -1909,6 +2049,7 @@ workflows:
|
||||
- linux-arm64-testing-gn-check:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
- ts-compile-doc-change
|
||||
|
||||
build-mac:
|
||||
when: << pipeline.parameters.run-build-mac >>
|
||||
|
||||
@@ -17,18 +17,29 @@
|
||||
"prefer-const": ["error", {
|
||||
"destructuring": "all"
|
||||
}],
|
||||
"standard/no-callback-literal": "off",
|
||||
"node/no-deprecated-api": 0
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"globals": {
|
||||
"standardScheme": "readonly"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.js",
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.d.ts",
|
||||
"rules": {
|
||||
"no-useless-constructor": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2
.github/config.yml
vendored
2
.github/config.yml
vendored
@@ -29,13 +29,11 @@ firstPRMergeComment: >
|
||||
# Users authorized to run manual trop backports
|
||||
authorizedUsers:
|
||||
- alexeykuzmin
|
||||
- BinaryMuse
|
||||
- ckerr
|
||||
- codebytere
|
||||
- deepak1556
|
||||
- jkleinsc
|
||||
- MarshallOfSound
|
||||
- miniak
|
||||
- nitsakh
|
||||
- nornagon
|
||||
- zcbenz
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -58,7 +58,7 @@ spec/.hash
|
||||
.eslintcache
|
||||
|
||||
# Generated native addon files
|
||||
/spec/fixtures/native-addon/echo/build/
|
||||
/spec-main/fixtures/native-addon/echo/build/
|
||||
|
||||
# If someone runs tsc this is where stuff will end up
|
||||
ts-gen
|
||||
|
||||
44
BUILD.gn
44
BUILD.gn
@@ -1,6 +1,7 @@
|
||||
import("//build/config/locales.gni")
|
||||
import("//build/config/ui.gni")
|
||||
import("//build/config/win/manifest.gni")
|
||||
import("//components/spellcheck/spellcheck_build_features.gni")
|
||||
import("//content/public/app/mac_helpers.gni")
|
||||
import("//pdf/features.gni")
|
||||
import("//printing/buildflags/buildflags.gni")
|
||||
@@ -21,12 +22,14 @@ import("buildflags/buildflags.gni")
|
||||
import("electron_paks.gni")
|
||||
import("filenames.auto.gni")
|
||||
import("filenames.gni")
|
||||
import("filenames.hunspell.gni")
|
||||
|
||||
if (is_mac) {
|
||||
import("//build/config/mac/rules.gni")
|
||||
import("//third_party/icu/config.gni")
|
||||
import("//ui/gl/features.gni")
|
||||
import("//v8/gni/v8.gni")
|
||||
import("build/rules.gni")
|
||||
}
|
||||
|
||||
if (is_linux) {
|
||||
@@ -357,12 +360,12 @@ source_set("electron_lib") {
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//chrome/services/printing/public/mojom",
|
||||
"//components/certificate_transparency",
|
||||
"//components/language/core/browser",
|
||||
"//components/net_log",
|
||||
"//components/network_hints/common",
|
||||
"//components/network_hints/renderer",
|
||||
"//components/network_session_configurator/common",
|
||||
"//components/prefs",
|
||||
"//components/spellcheck/renderer",
|
||||
"//components/viz/host",
|
||||
"//components/viz/service",
|
||||
"//content/public/browser",
|
||||
@@ -477,6 +480,10 @@ source_set("electron_lib") {
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_builtin_spellchecker) {
|
||||
deps += [ "chromium_src:chrome_spellchecker" ]
|
||||
}
|
||||
|
||||
if (is_mac) {
|
||||
deps += [
|
||||
"//components/remote_cocoa/app_shim",
|
||||
@@ -545,6 +552,10 @@ source_set("electron_lib") {
|
||||
]
|
||||
|
||||
sources += filenames.lib_sources_nss
|
||||
sources += [
|
||||
"shell/browser/ui/gtk_util.cc",
|
||||
"shell/browser/ui/gtk_util.h",
|
||||
]
|
||||
}
|
||||
if (is_win) {
|
||||
libs += [ "dwmapi.lib" ]
|
||||
@@ -688,6 +699,12 @@ if (is_mac) {
|
||||
electron_framework_version = "A"
|
||||
electron_version = read_file("ELECTRON_VERSION", "trim string")
|
||||
|
||||
mac_xib_bundle_data("electron_xibs") {
|
||||
sources = [
|
||||
"shell/common/resources/mac/MainMenu.xib",
|
||||
]
|
||||
}
|
||||
|
||||
bundle_data("electron_framework_resources") {
|
||||
public_deps = [
|
||||
":packed_resources",
|
||||
@@ -698,7 +715,6 @@ if (is_mac) {
|
||||
public_deps += [ "//third_party/icu:icudata" ]
|
||||
}
|
||||
if (v8_use_external_startup_data) {
|
||||
sources += [ "$root_out_dir/natives_blob.bin" ]
|
||||
public_deps += [ "//v8" ]
|
||||
if (use_v8_context_snapshot) {
|
||||
sources += [ "$root_out_dir/v8_context_snapshot.bin" ]
|
||||
@@ -803,6 +819,7 @@ if (is_mac) {
|
||||
":electron_framework_libraries",
|
||||
":electron_framework_resources",
|
||||
":electron_swiftshader_library",
|
||||
":electron_xibs",
|
||||
]
|
||||
if (!is_mas_build) {
|
||||
deps += [ ":electron_crashpad_helper" ]
|
||||
@@ -1257,9 +1274,14 @@ template("dist_zip") {
|
||||
"outputs",
|
||||
"testonly",
|
||||
])
|
||||
flatten = false
|
||||
if (defined(invoker.flatten)) {
|
||||
flatten = invoker.flatten
|
||||
}
|
||||
args = rebase_path(outputs + [ _runtime_deps_file ], root_build_dir) + [
|
||||
target_cpu,
|
||||
target_os,
|
||||
"$flatten",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1345,6 +1367,24 @@ dist_zip("electron_mksnapshot_zip") {
|
||||
]
|
||||
}
|
||||
|
||||
copy("hunspell_dictionaries") {
|
||||
sources = hunspell_dictionaries + hunspell_licenses
|
||||
outputs = [
|
||||
"$target_gen_dir/electron_hunspell/{{source_file_part}}",
|
||||
]
|
||||
}
|
||||
|
||||
dist_zip("hunspell_dictionaries_zip") {
|
||||
data_deps = [
|
||||
":hunspell_dictionaries",
|
||||
]
|
||||
flatten = true
|
||||
|
||||
outputs = [
|
||||
"$root_build_dir/hunspell_dictionaries.zip",
|
||||
]
|
||||
}
|
||||
|
||||
group("electron") {
|
||||
public_deps = [
|
||||
":electron_app",
|
||||
|
||||
@@ -20,13 +20,11 @@ Issues are created [here](https://github.com/electron/electron/issues/new).
|
||||
* [Triaging a Bug Report](https://electronjs.org/docs/development/issues#triaging-a-bug-report)
|
||||
* [Resolving a Bug Report](https://electronjs.org/docs/development/issues#resolving-a-bug-report)
|
||||
|
||||
### Issue Maintenance and Closure
|
||||
* If an issue is inactive for 45 days (no activity of any kind), it will be
|
||||
marked for closure with `stale`.
|
||||
* If after this label is applied, no further activity occurs in the next 7 days,
|
||||
the issue will be closed.
|
||||
* If an issue has been closed and you still feel it's relevant, feel free to
|
||||
ping a maintainer or add a comment!
|
||||
### Issue Closure
|
||||
|
||||
Bug reports will be closed if the issue has been inactive and the latest affected version no longer receives support. At the moment, Electron maintains its three latest major versions, with a new major version being released every 12 weeks. (For more information on Electron's release cadence, see [this blog post](https://electronjs.org/blog/12-week-cadence).)
|
||||
|
||||
_If an issue has been closed and you still feel it's relevant, feel free to ping a maintainer or add a comment!_
|
||||
|
||||
### Languages
|
||||
|
||||
|
||||
4
DEPS
4
DEPS
@@ -11,9 +11,9 @@ gclient_gn_args = [
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'6c775c358b709f4353bc693623bf58820adf3918',
|
||||
'62521aaefdef5b345dd470f14eef6e44732ccf23',
|
||||
'node_version':
|
||||
'v12.9.1',
|
||||
'v12.13.0',
|
||||
'nan_version':
|
||||
'2ee313aaca52e2b478965ac50eb5082520380d1b',
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Node.js
|
||||
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
|
||||
RUN curl -sL https://deb.nodesource.com/setup_13.x | bash - \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.0.0-nightly.20191008
|
||||
8.0.0-nightly.20191104
|
||||
12
README.md
12
README.md
@@ -58,13 +58,13 @@ npm start
|
||||
|
||||
## Resources for learning Electron
|
||||
|
||||
- [electronjs.org/docs](https://electronjs.org/docs) - all of Electron's documentation
|
||||
- [electronjs.org/docs](https://electronjs.org/docs) - All of Electron's documentation
|
||||
- [electron/fiddle](https://github.com/electron/fiddle) - A tool to build, run, and package small Electron experiments
|
||||
- [electron/electron-quick-start](https://github.com/electron/electron-quick-start) - a very basic starter Electron app
|
||||
- [electronjs.org/community#boilerplates](https://electronjs.org/community#boilerplates) - sample starter apps created by the community
|
||||
- [electron/simple-samples](https://github.com/electron/simple-samples) - small applications with ideas for taking them further
|
||||
- [electron/electron-api-demos](https://github.com/electron/electron-api-demos) - an Electron app that teaches you how to use Electron
|
||||
- [hokein/electron-sample-apps](https://github.com/hokein/electron-sample-apps) - small demo apps for the various Electron APIs
|
||||
- [electron/electron-quick-start](https://github.com/electron/electron-quick-start) - A very basic starter Electron app
|
||||
- [electronjs.org/community#boilerplates](https://electronjs.org/community#boilerplates) - Sample starter apps created by the community
|
||||
- [electron/simple-samples](https://github.com/electron/simple-samples) - Small applications with ideas for taking them further
|
||||
- [electron/electron-api-demos](https://github.com/electron/electron-api-demos) - An Electron app that teaches you how to use Electron
|
||||
- [hokein/electron-sample-apps](https://github.com/hokein/electron-sample-apps) - Small demo apps for the various Electron APIs
|
||||
|
||||
## Programmatic usage
|
||||
|
||||
|
||||
15
appveyor.yml
15
appveyor.yml
@@ -29,7 +29,7 @@
|
||||
|
||||
version: 1.0.{build}
|
||||
build_cloud: libcc-20
|
||||
image: vs2017-15.9-10.0.18362
|
||||
image: vs2019-16.3-10.0.18362
|
||||
environment:
|
||||
GIT_CACHE_PATH: C:\Users\electron\libcc_cache
|
||||
ELECTRON_OUT_DIR: Default
|
||||
@@ -50,6 +50,12 @@ build_script:
|
||||
- ps: >-
|
||||
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
|
||||
Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild
|
||||
} else {
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
|
||||
if ($(node script/doc-only-change.js --prNumber=$env:APPVEYOR_PULL_REQUEST_NUMBER --prBranch=$env:APPVEYOR_REPO_BRANCH;$LASTEXITCODE -eq 0)) {
|
||||
Write-warning "Skipping build for doc only change"; Exit-AppveyorBuild
|
||||
}
|
||||
}
|
||||
- echo "Building $env:GN_CONFIG build"
|
||||
- git config --global core.longpaths true
|
||||
@@ -73,7 +79,7 @@ build_script:
|
||||
- gclient sync --with_branch_heads --with_tags --reset
|
||||
- cd src
|
||||
- ps: $env:BUILD_CONFIG_PATH="//electron/build/args/%GN_CONFIG%.gn"
|
||||
- gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") %GN_EXTRA_ARGS%"
|
||||
- gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") %GN_EXTRA_ARGS% cc_wrapper=\"%SCCACHE_PATH%\""
|
||||
- gn check out/Default //electron:electron_lib
|
||||
- gn check out/Default //electron:electron_app
|
||||
- gn check out/Default //electron:manifests
|
||||
@@ -85,8 +91,10 @@ build_script:
|
||||
- ninja -C out/Default electron:electron_dist_zip
|
||||
- ninja -C out/Default shell_browser_ui_unittests
|
||||
- ninja -C out/Default electron:electron_mksnapshot_zip
|
||||
- ninja -C out/Default electron:hunspell_dictionaries_zip
|
||||
- ninja -C out/Default electron:electron_chromedriver_zip
|
||||
- ninja -C out/Default third_party/electron_node:headers
|
||||
- cmd /C %SCCACHE_PATH% --show-stats
|
||||
- appveyor PushArtifact out/Default/dist.zip
|
||||
- appveyor PushArtifact out/Default/shell_browser_ui_unittests.exe
|
||||
- appveyor PushArtifact out/Default/chromedriver.zip
|
||||
@@ -94,6 +102,7 @@ build_script:
|
||||
- 7z a node_headers.zip out\Default\gen\node_headers
|
||||
- appveyor PushArtifact node_headers.zip
|
||||
- appveyor PushArtifact out/Default/mksnapshot.zip
|
||||
- appveyor PushArtifact out/Default/hunspell_dictionaries.zip
|
||||
- appveyor PushArtifact out/Default/electron.lib
|
||||
- ps: >-
|
||||
if ($env:GN_CONFIG -eq 'release') {
|
||||
@@ -127,7 +136,7 @@ test_script:
|
||||
echo "Skipping tests for $env:GN_CONFIG build"
|
||||
}
|
||||
- cd electron
|
||||
- if "%RUN_TESTS%"=="true" ( echo Running test suite & node script/yarn test -- --ci --enable-logging)
|
||||
- if "%RUN_TESTS%"=="true" ( echo Running test suite & node script/yarn test -- --enable-logging)
|
||||
- cd ..
|
||||
- if "%RUN_TESTS%"=="true" ( echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg )
|
||||
- echo "About to verify mksnapshot"
|
||||
|
||||
@@ -63,7 +63,7 @@ steps:
|
||||
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
set npm_config_arch=arm64
|
||||
cd electron
|
||||
node script/yarn test -- --ci --enable-logging --verbose
|
||||
node script/yarn test -- --enable-logging --verbose
|
||||
displayName: 'Run Electron tests'
|
||||
env:
|
||||
ELECTRON_OUT_DIR: Default
|
||||
@@ -89,4 +89,4 @@ steps:
|
||||
Get-Process | Where Name –Like "electron*" | Stop-Process
|
||||
Get-Process | Where Name –Like "MicrosoftEdge*" | Stop-Process
|
||||
displayName: 'Kill processes left running from last test run'
|
||||
condition: always()
|
||||
condition: always()
|
||||
|
||||
@@ -21,9 +21,5 @@ angle_enable_vulkan_validation_layers = false
|
||||
|
||||
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
|
||||
|
||||
57
build/rules.gni
Normal file
57
build/rules.gni
Normal file
@@ -0,0 +1,57 @@
|
||||
import("//build/config/mac/mac_sdk.gni")
|
||||
|
||||
# This is imported from /ios becuase this functionality was moved
|
||||
# after Chromium stopped using xib files for macOS menu functionality
|
||||
# See https://chromium-review.googlesource.com/c/chromium/src/+/1648695
|
||||
import("//build/config/ios/rules.gni")
|
||||
|
||||
# Template is copied here from Chromium but was removed in
|
||||
# https://chromium-review.googlesource.com/c/chromium/src/+/1637981
|
||||
# Template to compile and package Mac XIB files as bundle data.
|
||||
# Arguments
|
||||
# sources:
|
||||
# list of string, sources to comiple
|
||||
# output_path:
|
||||
# (optional) string, the path to use for the outputs list in the
|
||||
# bundle_data step. If unspecified, defaults to bundle_resources_dir.
|
||||
template("mac_xib_bundle_data") {
|
||||
_target_name = target_name
|
||||
_compile_target_name = _target_name + "_compile_ibtool"
|
||||
|
||||
compile_ib_files(_compile_target_name) {
|
||||
forward_variables_from(invoker, [ "testonly" ])
|
||||
visibility = [ ":$_target_name" ]
|
||||
sources = invoker.sources
|
||||
output_extension = "nib"
|
||||
ibtool_flags = [
|
||||
"--minimum-deployment-target",
|
||||
mac_deployment_target,
|
||||
|
||||
# TODO(rsesek): Enable this once all the bots are on Xcode 7+.
|
||||
# "--target-device",
|
||||
# "mac",
|
||||
]
|
||||
}
|
||||
|
||||
bundle_data(_target_name) {
|
||||
forward_variables_from(invoker,
|
||||
[
|
||||
"testonly",
|
||||
"visibility",
|
||||
])
|
||||
|
||||
public_deps = [
|
||||
":$_compile_target_name",
|
||||
]
|
||||
sources = get_target_outputs(":$_compile_target_name")
|
||||
|
||||
_output_path = "{{bundle_resources_dir}}"
|
||||
if (defined(invoker.output_path)) {
|
||||
_output_path = invoker.output_path
|
||||
}
|
||||
|
||||
outputs = [
|
||||
"$_output_path/{{source_file_part}}",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -46,13 +46,14 @@ def execute(argv):
|
||||
raise e
|
||||
|
||||
def main(argv):
|
||||
dist_zip, runtime_deps, target_cpu, target_os = argv
|
||||
dist_zip, runtime_deps, target_cpu, target_os, flatten_val = argv
|
||||
should_flatten = flatten_val == "true"
|
||||
dist_files = set()
|
||||
with open(runtime_deps) as f:
|
||||
for dep in f.readlines():
|
||||
dep = dep.strip()
|
||||
dist_files.add(dep)
|
||||
if sys.platform == 'darwin':
|
||||
if sys.platform == 'darwin' and not should_flatten:
|
||||
execute(['zip', '-r', '-y', dist_zip] + list(dist_files))
|
||||
else:
|
||||
with zipfile.ZipFile(dist_zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as z:
|
||||
@@ -67,7 +68,7 @@ def main(argv):
|
||||
basename = os.path.basename(dep)
|
||||
dirname = os.path.dirname(dep)
|
||||
arcname = os.path.join(dirname, 'chrome-sandbox') if basename == 'chrome_sandbox' else dep
|
||||
z.write(dep, arcname)
|
||||
z.write(dep, os.path.basename(arcname) if should_flatten else arcname)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
@@ -19,6 +19,7 @@ buildflag_header("buildflags") {
|
||||
"ENABLE_TTS=$enable_tts",
|
||||
"ENABLE_COLOR_CHOOSER=$enable_color_chooser",
|
||||
"ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions",
|
||||
"ENABLE_BUILTIN_SPELLCHECKER=$enable_builtin_spellchecker",
|
||||
"ENABLE_PICTURE_IN_PICTURE=$enable_picture_in_picture",
|
||||
"OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider",
|
||||
]
|
||||
|
||||
@@ -33,4 +33,7 @@ declare_args() {
|
||||
|
||||
# Enable Chrome extensions support.
|
||||
enable_electron_extensions = false
|
||||
|
||||
# Enable Spellchecker support
|
||||
enable_builtin_spellchecker = true
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//build/config/ui.gni")
|
||||
import("//components/spellcheck/spellcheck_build_features.gni")
|
||||
import("//electron/buildflags/buildflags.gni")
|
||||
import("//printing/buildflags/buildflags.gni")
|
||||
import("//third_party/widevine/cdm/widevine.gni")
|
||||
@@ -43,10 +44,9 @@ 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/ssl/tls_deprecation_config.cc",
|
||||
"//chrome/browser/ui/autofill/popup_view_common.cc",
|
||||
"//chrome/browser/ui/autofill/popup_view_common.h",
|
||||
"//chrome/browser/win/chrome_process_finder.cc",
|
||||
@@ -72,6 +72,14 @@ static_library("chrome") {
|
||||
sources += [
|
||||
"//chrome/browser/extensions/global_shortcut_listener_x11.cc",
|
||||
"//chrome/browser/extensions/global_shortcut_listener_x11.h",
|
||||
"//chrome/browser/ui/views/status_icons/concat_menu_model.cc",
|
||||
"//chrome/browser/ui/views/status_icons/concat_menu_model.h",
|
||||
"//chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc",
|
||||
"//chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h",
|
||||
]
|
||||
public_deps += [
|
||||
"//components/dbus/menu",
|
||||
"//components/dbus/thread_linux",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -164,6 +172,8 @@ 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 += [
|
||||
@@ -216,3 +226,52 @@ static_library("chrome") {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# This source set is just so we don't have to depend on all of //chrome/browser
|
||||
# You may have to add new files here during the upgrade if //chrome/browser/spellchecker
|
||||
# gets more files
|
||||
source_set("chrome_spellchecker") {
|
||||
sources = [
|
||||
"//chrome/browser/spellchecker/spell_check_host_chrome_impl.cc",
|
||||
"//chrome/browser/spellchecker/spell_check_host_chrome_impl.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_custom_dictionary.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_custom_dictionary.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_factory.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_factory.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_policy_handler.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_policy_handler.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_service.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_service.h",
|
||||
]
|
||||
|
||||
if (has_spellcheck_panel) {
|
||||
sources += [
|
||||
"//chrome/browser/spellchecker/spell_check_panel_host_impl.cc",
|
||||
"//chrome/browser/spellchecker/spell_check_panel_host_impl.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (use_browser_spellchecker) {
|
||||
sources += [
|
||||
"//chrome/browser/spellchecker/spelling_request.cc",
|
||||
"//chrome/browser/spellchecker/spelling_request.h",
|
||||
]
|
||||
}
|
||||
|
||||
deps = [
|
||||
"//base:base_static",
|
||||
"//components/language/core/browser",
|
||||
"//components/spellcheck:buildflags",
|
||||
"//components/sync",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
"//components/spellcheck/browser",
|
||||
"//components/spellcheck/common",
|
||||
"//components/spellcheck/renderer",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -37,10 +37,8 @@ net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
|
||||
// public and private slot.
|
||||
// Redirect any slot usage to this persistent slot on Linux.
|
||||
g_nss_cert_database = new net::NSSCertDatabase(
|
||||
crypto::ScopedPK11Slot(
|
||||
crypto::GetPersistentNSSKeySlot()) /* public slot */,
|
||||
crypto::ScopedPK11Slot(
|
||||
crypto::GetPersistentNSSKeySlot()) /* private slot */);
|
||||
crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* public slot */,
|
||||
crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* private slot */);
|
||||
}
|
||||
return g_nss_cert_database;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
<head>
|
||||
<title>Electron</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self'" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-6PH54BfkNq/EMMhUY7nhHf3c+AxloOwfy7hWyT01CM8='; style-src 'self'; img-src 'self'; connect-src 'self'" />
|
||||
<link href="./styles.css" type="text/css" rel="stylesheet" />
|
||||
<link href="./octicon/build.css" type="text/css" rel="stylesheet" />
|
||||
<script defer src="./index.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -84,6 +83,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<script>
|
||||
window.electronDefaultApp.initialize()
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,30 +0,0 @@
|
||||
async function getOcticonSvg (name: string) {
|
||||
try {
|
||||
const response = await fetch(`octicon/${name}.svg`)
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = await response.text()
|
||||
return div
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSVG (element: HTMLSpanElement) {
|
||||
for (const cssClass of element.classList) {
|
||||
if (cssClass.startsWith('octicon-')) {
|
||||
const icon = await getOcticonSvg(cssClass.substr(8))
|
||||
if (icon) {
|
||||
for (const elemClass of element.classList) {
|
||||
icon.classList.add(elemClass)
|
||||
}
|
||||
element.before(icon)
|
||||
element.remove()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) {
|
||||
loadSVG(element)
|
||||
}
|
||||
@@ -1,4 +1,31 @@
|
||||
import { ipcRenderer } from 'electron'
|
||||
import { ipcRenderer, contextBridge } from 'electron'
|
||||
|
||||
async function getOcticonSvg (name: string) {
|
||||
try {
|
||||
const response = await fetch(`octicon/${name}.svg`)
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = await response.text()
|
||||
return div
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSVG (element: HTMLSpanElement) {
|
||||
for (const cssClass of element.classList) {
|
||||
if (cssClass.startsWith('octicon-')) {
|
||||
const icon = await getOcticonSvg(cssClass.substr(8))
|
||||
if (icon) {
|
||||
for (const elemClass of element.classList) {
|
||||
icon.classList.add(elemClass)
|
||||
}
|
||||
element.before(icon)
|
||||
element.remove()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initialize () {
|
||||
const electronPath = await ipcRenderer.invoke('bootstrap')
|
||||
@@ -15,6 +42,12 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initialize)
|
||||
contextBridge.exposeInMainWorld('electronDefaultApp', {
|
||||
initialize
|
||||
})
|
||||
|
||||
@@ -39,6 +39,7 @@ an issue:
|
||||
* [Using Electron's APIs](tutorial/application-architecture.md#using-electron-apis)
|
||||
* [Using Node.js APIs](tutorial/application-architecture.md#using-nodejs-apis)
|
||||
* [Using Native Node.js Modules](tutorial/using-native-node-modules.md)
|
||||
* [Performance Strategies](tutorial/performance.md)
|
||||
* Adding Features to Your App
|
||||
* [Notifications](tutorial/notifications.md)
|
||||
* [Recent Documents](tutorial/recent-documents.md)
|
||||
|
||||
@@ -6,6 +6,59 @@ Breaking changes will be documented here, and deprecation warnings added to JS c
|
||||
|
||||
The `FIXME` string is used in code comments to denote things that should be fixed for future releases. See https://github.com/electron/electron/search?q=fixme
|
||||
|
||||
## Planned Breaking API Changes (8.0)
|
||||
|
||||
### Values sent over IPC are now serialized with Structured Clone Algorithm
|
||||
|
||||
The algorithm used to serialize objects sent over IPC (through
|
||||
`ipcRenderer.send`, `ipcRenderer.sendSync`, `WebContents.send` and related
|
||||
methods) has been switched from a custom algorithm to V8's built-in [Structured
|
||||
Clone Algorithm][SCA], the same algorithm used to serialize messages for
|
||||
`postMessage`. This brings about a 2x performance improvement for large
|
||||
messages, but also brings some breaking changes in behavior.
|
||||
|
||||
- Sending Functions, Promises, WeakMaps, WeakSets, or objects containing any
|
||||
such values, over IPC will now throw an exception, instead of silently
|
||||
converting the functions to `undefined`.
|
||||
```js
|
||||
// Previously:
|
||||
ipcRenderer.send('channel', { value: 3, someFunction: () => {} })
|
||||
// => results in { value: 3 } arriving in the main process
|
||||
|
||||
// From Electron 8:
|
||||
ipcRenderer.send('channel', { value: 3, someFunction: () => {} })
|
||||
// => throws Error("() => {} could not be cloned.")
|
||||
```
|
||||
- `NaN`, `Infinity` and `-Infinity` will now be correctly serialized, instead
|
||||
of being converted to `null`.
|
||||
- Objects containing cyclic references will now be correctly serialized,
|
||||
instead of being converted to `null`.
|
||||
- `Set`, `Map`, `Error` and `RegExp` values will be correctly serialized,
|
||||
instead of being converted to `{}`.
|
||||
- `BigInt` values will be correctly serialized, instead of being converted to
|
||||
`null`.
|
||||
- Sparse arrays will be serialized as such, instead of being converted to dense
|
||||
arrays with `null`s.
|
||||
- `Date` objects will be transferred as `Date` objects, instead of being
|
||||
converted to their ISO string representation.
|
||||
- Typed Arrays (such as `Uint8Array`, `Uint16Array`, `Uint32Array` and so on)
|
||||
will be transferred as such, instead of being converted to Node.js `Buffer`.
|
||||
- Node.js `Buffer` objects will be transferred as `Uint8Array`s. You can
|
||||
convert a `Uint8Array` back to a Node.js `Buffer` by wrapping the underlying
|
||||
`ArrayBuffer`:
|
||||
```js
|
||||
Buffer.from(value.buffer, value.byteOffset, value.byteLength)
|
||||
```
|
||||
|
||||
Sending any objects that aren't native JS types, such as DOM objects (e.g.
|
||||
`Element`, `Location`, `DOMMatrix`), Node.js objects (e.g. `process.env`,
|
||||
`Stream`), or Electron objects (e.g. `WebContents`, `BrowserWindow`,
|
||||
`WebFrame`) is deprecated. In Electron 8, these objects will be serialized as
|
||||
before with a DeprecationWarning message, but starting in Electron 9, sending
|
||||
these kinds of objects will throw a 'could not be cloned' error.
|
||||
|
||||
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
|
||||
## Planned Breaking API Changes (7.0)
|
||||
|
||||
### Node Headers URL
|
||||
|
||||
@@ -385,6 +385,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
* `accessibleTitle` String (optional) - An alternative title string provided only
|
||||
to accessibility tools such as screen readers. This string is not directly
|
||||
visible to users.
|
||||
* `spellcheck` Boolean (optional) - Whether to enable the builtin spellchecker.
|
||||
Default is `true`.
|
||||
|
||||
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
||||
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
||||
@@ -944,11 +946,11 @@ Returns `Boolean` - Whether the window is in simple (pre-Lion) fullscreen mode.
|
||||
|
||||
Returns `Boolean` - Whether the window is in normal state (not maximized, not minimized, not in fullscreen mode).
|
||||
|
||||
#### `win.setAspectRatio(aspectRatio[, extraSize])` _macOS_
|
||||
#### `win.setAspectRatio(aspectRatio[, extraSize])` _macOS_ _Linux_
|
||||
|
||||
* `aspectRatio` Float - The aspect ratio to maintain for some portion of the
|
||||
content view.
|
||||
* `extraSize` [Size](structures/size.md) (optional) - The extra size not to be included while
|
||||
* `extraSize` [Size](structures/size.md) (optional) _macOS_ - The extra size not to be included while
|
||||
maintaining the aspect ratio.
|
||||
|
||||
This will make a window maintain an aspect ratio. The extra size allows a
|
||||
|
||||
111
docs/api/context-bridge.md
Normal file
111
docs/api/context-bridge.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# 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.
|
||||
@@ -119,6 +119,10 @@ Remove a extra parameter from the current set of parameters so that it will not
|
||||
|
||||
See all of the current parameters being passed to the crash reporter.
|
||||
|
||||
### `crashReporter.getCrashesDirectory()`
|
||||
|
||||
Returns `String` - The directory where crashes are temporarily stored before being uploaded.
|
||||
|
||||
## Crash Report Payload
|
||||
|
||||
The crash reporter will send the following data to the `submitURL` as
|
||||
|
||||
@@ -50,7 +50,7 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `method` String - Method name.
|
||||
* `params` unknown - Event parameters defined by the 'parameters'
|
||||
* `params` any - Event parameters defined by the 'parameters'
|
||||
attribute in the remote debugging protocol.
|
||||
|
||||
Emitted whenever the debugging target issues an instrumentation event.
|
||||
|
||||
@@ -44,7 +44,11 @@ Unsupported options are:
|
||||
--use-openssl-ca
|
||||
```
|
||||
|
||||
`NODE_OPTIONS` are explicitly disallowed in packaged apps.
|
||||
`NODE_OPTIONS` are explicitly disallowed in packaged apps, except for the following:
|
||||
|
||||
```sh
|
||||
--max-http-header-size
|
||||
```
|
||||
|
||||
### `GOOGLE_API_KEY`
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ only the next time a message is sent to `channel`, after which it is removed.
|
||||
|
||||
* `channel` String
|
||||
* `listener` Function
|
||||
* `...args` any[]
|
||||
|
||||
Removes the specified `listener` from the listener array for the specified
|
||||
`channel`.
|
||||
|
||||
@@ -55,9 +55,15 @@ Removes all listeners, or those of the specified `channel`.
|
||||
* `channel` String
|
||||
* `...args` any[]
|
||||
|
||||
Send a message to the main process asynchronously via `channel`, you can also
|
||||
send arbitrary arguments. Arguments will be serialized as JSON internally and
|
||||
hence no functions or prototype chain will be included.
|
||||
Send an asynchronous message to the main process via `channel`, along with
|
||||
arguments. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`postMessage`][], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The main process handles it by listening for `channel` with the
|
||||
[`ipcMain`](ipc-main.md) module.
|
||||
@@ -69,9 +75,15 @@ The main process handles it by listening for `channel` with the
|
||||
|
||||
Returns `Promise<any>` - Resolves with the response from the main process.
|
||||
|
||||
Send a message to the main process asynchronously via `channel` and expect an
|
||||
asynchronous result. Arguments will be serialized as JSON internally and
|
||||
hence no functions or prototype chain will be included.
|
||||
Send a message to the main process via `channel` and expect a result
|
||||
asynchronously. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`postMessage`][], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The main process should listen for `channel` with
|
||||
[`ipcMain.handle()`](ipc-main.md#ipcmainhandlechannel-listener).
|
||||
@@ -97,15 +109,23 @@ ipcMain.handle('some-name', async (event, someArgument) => {
|
||||
|
||||
Returns `any` - The value sent back by the [`ipcMain`](ipc-main.md) handler.
|
||||
|
||||
Send a message to the main process synchronously via `channel`, you can also
|
||||
send arbitrary arguments. Arguments will be serialized in JSON internally and
|
||||
hence no functions or prototype chain will be included.
|
||||
Send a message to the main process via `channel` and expect a result
|
||||
synchronously. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`postMessage`][], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The main process handles it by listening for `channel` with [`ipcMain`](ipc-main.md) module,
|
||||
and replies by setting `event.returnValue`.
|
||||
|
||||
**Note:** Sending a synchronous message will block the whole renderer process,
|
||||
unless you know what you are doing you should never use it.
|
||||
> :warning: **WARNING**: Sending a synchronous message will block the whole
|
||||
> renderer process until the reply is received, so use this method only as a
|
||||
> last resort. It's much better to use the asynchronous version,
|
||||
> [`invoke()`](ipc-renderer.md#ipcrendererinvokechannel-args).
|
||||
|
||||
### `ipcRenderer.sendTo(webContentsId, channel, ...args)`
|
||||
|
||||
@@ -129,3 +149,5 @@ The documentation for the `event` object passed to the `callback` can be found
|
||||
in the [`ipc-renderer-event`](structures/ipc-renderer-event.md) structure docs.
|
||||
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
[`postMessage`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
|
||||
|
||||
@@ -35,6 +35,7 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu
|
||||
* `silent` Boolean (optional) - Whether or not to emit an OS notification noise when showing the notification.
|
||||
* `icon` (String | [NativeImage](native-image.md)) (optional) - An icon to use in the notification.
|
||||
* `hasReply` Boolean (optional) _macOS_ - Whether or not to add an inline reply option to the notification.
|
||||
* `timeoutType` String (optional) _Linux_ _Windows_ - The timeout duration of the notification. Can be 'default' or 'never'.
|
||||
* `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'.
|
||||
@@ -151,6 +152,12 @@ A `String` property representing the urgency level of the notification. Can be '
|
||||
|
||||
Default is 'low' - see [NotifyUrgency](https://developer.gnome.org/notification-spec/#urgency-levels) for more information.
|
||||
|
||||
#### `notification.timeoutType` _Linux_ _Windows_
|
||||
|
||||
A `String` property representing the type of timeout duration for the notification. Can be 'default' or 'never'.
|
||||
|
||||
If `timeoutType` is set to 'never', the notification never expires. It stays open until closed by the calling API or the user.
|
||||
|
||||
#### `notification.actions`
|
||||
|
||||
A [`NotificationAction[]`](structures/notification-action.md) property representing the actions of the notification.
|
||||
|
||||
@@ -217,11 +217,15 @@ that all statistics are reported in Kilobytes.
|
||||
|
||||
Returns `String` - The version of the host operating system.
|
||||
|
||||
Examples:
|
||||
Example:
|
||||
|
||||
* `macOS` -> `10.13.6`
|
||||
* `Windows` -> `10.0.17763`
|
||||
* `Linux` -> `4.15.0-45-generic`
|
||||
```js
|
||||
const version = process.getSystemVersion()
|
||||
console.log(version)
|
||||
// On macOS -> '10.13.6'
|
||||
// On Windows -> '10.0.17763'
|
||||
// On Linux -> '4.15.0-45-generic'
|
||||
```
|
||||
|
||||
**Note:** It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`.
|
||||
|
||||
|
||||
@@ -456,10 +456,38 @@ this session just before normal `preload` scripts run.
|
||||
Returns `String[]` an array of paths to preload scripts that have been
|
||||
registered.
|
||||
|
||||
#### `ses.setSpellCheckerLanguages(languages)`
|
||||
|
||||
* `languages` String[] - An array of language codes to enable the spellchecker for.
|
||||
|
||||
The built in spellchecker does not automatically detect what language a user is typing in. In order for the
|
||||
spell checker to correctly check their words you must call this API with an array of language codes. You can
|
||||
get the list of supported language codes with the `ses.availableSpellCheckerLanguages` property.
|
||||
|
||||
#### `ses.getSpellCheckerLanguages()`
|
||||
|
||||
Returns `String[]` - An array of language codes the spellchecker is enabled for. If this list is empty the spellchecker
|
||||
will fallback to using `en-US`. By default on launch if this setting is an empty list Electron will try to populate this
|
||||
setting with the current OS locale. This setting is persisted across restarts.
|
||||
|
||||
#### `ses.setSpellCheckerDictionaryDownloadURL(url)`
|
||||
|
||||
* `url` String - A base URL for Electron to download hunspell dictionaries from.
|
||||
|
||||
By default Electron will download hunspell dictionaries from the Chromium CDN. If you want to override this
|
||||
behavior you can use this API to point the dictionary downloader at your own hosted version of the hunspell
|
||||
dictionaries. We publish a `hunspell_dictionaries.zip` file with each release which contains the files you need
|
||||
to host here.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
The following properties are available on instances of `Session`:
|
||||
|
||||
#### `ses.availableSpellCheckerLanguages` _Readonly_
|
||||
|
||||
A `String[]` array which consists of all the known available spell checker languages. Providing a language
|
||||
code to the `setSpellCheckerLanaguages` API that isn't in this array will result in an error.
|
||||
|
||||
#### `ses.cookies` _Readonly_
|
||||
|
||||
A [`Cookies`](cookies.md) object for this session.
|
||||
|
||||
@@ -291,7 +291,7 @@ This API is only available on macOS 10.14 Mojave or newer.
|
||||
* `window-frame` - Window frame.
|
||||
* `window-text` - Text in windows.
|
||||
* On **macOS**
|
||||
* `alternate-selected-control-text` - The text on a selected surface in a list or table.
|
||||
* `alternate-selected-control-text` - The text on a selected surface in a list or table. _deprecated_
|
||||
* `control-background` - The background of a large interface element, such as a browser or table.
|
||||
* `control` - The surface of a control.
|
||||
* `control-text` -The text of a control that isn’t disabled.
|
||||
@@ -310,7 +310,7 @@ This API is only available on macOS 10.14 Mojave or newer.
|
||||
* `selected-content-background` - The background for selected content in a key window or view.
|
||||
* `selected-control` - The surface of a selected control.
|
||||
* `selected-control-text` - The text of a selected control.
|
||||
* `selected-menu-item` - The text of a selected menu.
|
||||
* `selected-menu-item-text` - The text of a selected menu.
|
||||
* `selected-text-background` - The background of selected text.
|
||||
* `selected-text` - Selected text.
|
||||
* `separator` - A separator between different sections of content.
|
||||
@@ -328,6 +328,8 @@ This API is only available on macOS 10.14 Mojave or newer.
|
||||
Returns `String` - The system color setting in RGB hexadecimal form (`#ABCDEF`).
|
||||
See the [Windows docs][windows-colors] and the [MacOS docs][macos-colors] for more details.
|
||||
|
||||
The following colors are only available on macOS 10.14: `find-highlight`, `selected-content-background`, `separator`, `unemphasized-selected-content-background`, `unemphasized-selected-text-background`, and `unemphasized-selected-text`.
|
||||
|
||||
[windows-colors]:https://msdn.microsoft.com/en-us/library/windows/desktop/ms724371(v=vs.85).aspx
|
||||
[macos-colors]:https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/color#dynamic-system-colors
|
||||
|
||||
|
||||
@@ -570,6 +570,9 @@ Returns:
|
||||
* `titleText` String - Title or alt text of the selection that the context
|
||||
was invoked on.
|
||||
* `misspelledWord` String - The misspelled word under the cursor, if any.
|
||||
* `dictionarySuggestions` String[] - An array of suggested words to show the
|
||||
user to replace the `misspelledWord`. Only available if there is a misspelled
|
||||
word and spellchecker is enabled.
|
||||
* `frameCharset` String - The character encoding of the frame on which the
|
||||
menu was invoked.
|
||||
* `inputFieldType` String - If the context menu was invoked on an input
|
||||
@@ -1479,9 +1482,15 @@ Opens the developer tools for the service worker context.
|
||||
* `channel` String
|
||||
* `...args` any[]
|
||||
|
||||
Send an asynchronous message to renderer process via `channel`, you can also
|
||||
send arbitrary arguments. Arguments will be serialized in JSON internally and
|
||||
hence no functions or prototype chain will be included.
|
||||
Send an asynchronous message to the renderer process via `channel`, along with
|
||||
arguments. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`postMessage`][], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The renderer process can handle the message by listening to `channel` with the
|
||||
[`ipcRenderer`](ipc-renderer.md) module.
|
||||
@@ -1522,8 +1531,14 @@ app.on('ready', () => {
|
||||
* `...args` any[]
|
||||
|
||||
Send an asynchronous message to a specific frame in a renderer process via
|
||||
`channel`. Arguments will be serialized
|
||||
as JSON internally and as such no functions or prototype chains will be included.
|
||||
`channel`, along with arguments. Arguments will be serialized with the
|
||||
[Structured Clone Algorithm][SCA], just like [`postMessage`][], so prototype
|
||||
chains will not be included. Sending Functions, Promises, Symbols, WeakMaps, or
|
||||
WeakSets will throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The renderer process can handle the message by listening to `channel` with the
|
||||
[`ipcRenderer`](ipc-renderer.md) module.
|
||||
@@ -1785,3 +1800,5 @@ A [`Debugger`](debugger.md) instance for this webContents.
|
||||
|
||||
[keyboardevent]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
[`postMessage`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
|
||||
|
||||
@@ -74,6 +74,17 @@ Sets the maximum and minimum layout-based (i.e. non-visual) zoom level.
|
||||
|
||||
Sets a provider for spell checking in input fields and text areas.
|
||||
|
||||
If you want to use this method you must disable the builtin spellchecker when you
|
||||
construct the window.
|
||||
|
||||
```js
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
spellcheck: false
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The `provider` must be an object that has a `spellCheck` method that accepts
|
||||
an array of individual words for spellchecking.
|
||||
The `spellCheck` function runs asynchronously and calls the `callback` function
|
||||
|
||||
@@ -565,7 +565,7 @@ Prints `webview`'s web page. Same as `webContents.print([options])`.
|
||||
* `printSelectionOnly` Boolean (optional) - Whether to print selection only.
|
||||
* `landscape` Boolean (optional) - `true` for landscape, `false` for portrait.
|
||||
|
||||
Returns `Promise<Buffer>` - Resolves with the generated PDF data.
|
||||
Returns `Promise<Uint8Array>` - Resolves with the generated PDF data.
|
||||
|
||||
Prints `webview`'s web page as PDF, Same as `webContents.printToPDF(options)`.
|
||||
|
||||
|
||||
@@ -22,6 +22,5 @@ For guides on Electron app development, see
|
||||
* [Debugging on Windows](debug-instructions-windows.md)
|
||||
* [Debugging on macOS](debugging-instructions-macos.md)
|
||||
* [Setting Up Symbol Server in Debugger](setting-up-symbol-server.md)
|
||||
* [Upgrading Chromium](upgrading-chromium.md)
|
||||
* [Upgrading Crashpad](upgrading-crashpad.md)
|
||||
* [Patches](patches.md)
|
||||
* [Upgrading Node](upgrading-node.md)
|
||||
|
||||
@@ -46,7 +46,7 @@ You can avoid much of the wait by reusing Electron CI's build output via
|
||||
optional steps (listed below) and these two environment variables:
|
||||
|
||||
```sh
|
||||
export SCCACHE_BUCKET="electronjs-sccache"
|
||||
export SCCACHE_BUCKET="electronjs-sccache-ci"
|
||||
export SCCACHE_TWO_TIER=true
|
||||
```
|
||||
|
||||
@@ -236,7 +236,7 @@ the Electron binary:
|
||||
|
||||
```sh
|
||||
$ ./out/Debug/Electron.app/Contents/MacOS/Electron electron/spec \
|
||||
--ci --enable-logging -g 'BrowserWindow module'
|
||||
--enable-logging -g 'BrowserWindow module'
|
||||
```
|
||||
|
||||
## Sharing the git cache between multiple machines
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Issues In Electron
|
||||
|
||||
* [How to Contribute in Issues](#how-to-contribute-in-issues)
|
||||
* [How to Contribute to Issues](#how-to-contribute-to-issues)
|
||||
* [Asking for General Help](#asking-for-general-help)
|
||||
* [Submitting a Bug Report](#submitting-a-bug-report)
|
||||
* [Triaging a Bug Report](#triaging-a-bug-report)
|
||||
* [Resolving a Bug Report](#resolving-a-bug-report)
|
||||
|
||||
## How to Contribute in Issues
|
||||
## How to Contribute to Issues
|
||||
|
||||
For any issue, there are fundamentally three ways an individual can
|
||||
contribute:
|
||||
@@ -75,7 +75,7 @@ If you believe that you have found a bug in Electron, please fill out this
|
||||
form to the best of your ability.
|
||||
|
||||
The two most important pieces of information needed to evaluate the report are
|
||||
a description of the bug and a simple test case to recreate it. It easier to fix
|
||||
a description of the bug and a simple test case to recreate it. It is easier to fix
|
||||
a bug if it can be reproduced.
|
||||
|
||||
See [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve).
|
||||
@@ -92,7 +92,7 @@ are not helpful or professional. To many, such responses are annoying and
|
||||
unfriendly.
|
||||
|
||||
Contributors are encouraged to solve issues collaboratively and help one
|
||||
another make progress. If encounter an issue that you feel is invalid, or
|
||||
another make progress. If you encounter an issue that you feel is invalid, or
|
||||
which contains incorrect information, explain *why* you feel that way with
|
||||
additional supporting context, and be willing to be convinced that you may
|
||||
be wrong. By doing so, we can often reach the correct outcome faster.
|
||||
|
||||
89
docs/development/patches.md
Normal file
89
docs/development/patches.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Patches in Electron
|
||||
|
||||
Electron is built on two major upstream projects: Chromium and Node.js. Each of these projects has several of their own dependencies, too. We try our best to use these dependencies exactly as they are but sometimes we can't achieve our goals without patching those upstream dependencies to fit our use cases.
|
||||
|
||||
## Patch justification
|
||||
|
||||
Every patch in Electron is a maintenance burden. When upstream code changes, patches can break—sometimes without even a patch conflict or a compilation error. It's an ongoing effort to keep our patch set up-to-date and effective. So we strive to keep our patch count at a minimum. To that end, every patch must describe its reason for existence in its commit message. That reason must be one of the following:
|
||||
|
||||
1. The patch is temporary, and is intended to be (or has been) committed upstream or otherwise eventually removed. Include a link to an upstream PR or code review if available, or a procedure for verifying whether the patch is still needed at a later date.
|
||||
2. The patch allows the code to compile in the Electron environment, but cannot be upstreamed because it's Electron-specific (e.g. patching out references to Chrome's `Profile`). Include reasoning about why the change cannot be implemented without a patch (e.g. by subclassing or copying the code).
|
||||
3. The patch makes Electron-specific changes in functionality which are fundamentally incompatible with upstream.
|
||||
|
||||
In general, all the upstream projects we work with are friendly folks and are often happy to accept refactorings that allow the code in question to be compatible with both Electron and the upstream project. (See e.g. [this](https://chromium-review.googlesource.com/c/chromium/src/+/1637040) change in Chromium, which allowed us to remove a patch that did the same thing, or [this](https://github.com/nodejs/node/pull/22110) change in Node, which was a no-op for Node but fixed a bug in Electron.) **We should aim to upstream changes whenever we can, and avoid indefinite-lifetime patches**.
|
||||
|
||||
## Patch system
|
||||
|
||||
If you find yourself in the unfortunate position of having to make a change which can only be made through patching an upstream project, you'll need to know how to manage patches in Electron.
|
||||
|
||||
All patches to upstream projects in Electron are contained in the `patches/` directory. Each subdirectory of `patches/` contains several patch files, along with a `.patches` file which lists the order in which the patches should be applied. Think of these files as making up a series of git commits that are applied on top of the upstream project after we check it out.
|
||||
|
||||
```text
|
||||
patches
|
||||
├── config.json <-- this describes which patchset directory is applied to what project
|
||||
├── chromium
|
||||
│ ├── .patches
|
||||
│ ├── accelerator.patch
|
||||
│ ├── add_contentgpuclient_precreatemessageloop_callback.patch
|
||||
│ ⋮
|
||||
├── node
|
||||
│ ├── .patches
|
||||
│ ├── add_openssl_is_boringssl_guard_to_oaep_hash_check.patch
|
||||
│ ├── build_add_gn_build_files.patch
|
||||
│ ⋮
|
||||
⋮
|
||||
```
|
||||
|
||||
To help manage these patch sets, we provide two tools: `git-import-patches` and `git-export-patches`. `git-import-patches` imports a set of patch files into a git repository by applying each patch in the correct order and creating a commit for each one. `git-export-patches` does the reverse; it exports a series of git commits in a repository into a set of files in a directory and an accompanying `.patches` file.
|
||||
|
||||
> Side note: the reason we use a `.patches` file to maintain the order of applied patches, rather than prepending a number like `001-` to each file, is because it reduces conflicts related to patch ordering. It prevents the situation where two PRs both add a patch at the end of the series with the same numbering and end up both getting merged resulting in a duplicate identifier, and it also reduces churn when a patch is added or deleted in the middle of the series.
|
||||
|
||||
### Usage
|
||||
|
||||
#### Adding a new patch
|
||||
```bash session
|
||||
$ cd src/third_party/electron_node
|
||||
$ vim some/code/file.cc
|
||||
$ git commit
|
||||
$ ../../electron/script/git-export-patches -o ../../electron/patches/node
|
||||
```
|
||||
|
||||
> **NOTE**: `git-export-patches` ignores any uncommitted files, so you must create a commit if you want your changes to be exported. The subject line of the commit message will be used to derive the patch file name, and the body of the commit message should include the reason for the patch's existence.
|
||||
|
||||
Re-exporting patches will sometimes cause shasums in unrelated patches to change. This is generally harmless and can be ignored (but go ahead and add those changes to your PR, it'll stop them from showing up for other people).
|
||||
|
||||
#### Editing an existing patch
|
||||
```bash session
|
||||
$ cd src/v8
|
||||
$ vim some/code/file.cc
|
||||
$ git log
|
||||
# Find the commit sha of the patch you want to edit.
|
||||
$ git commit --fixup [COMMIT_SHA]
|
||||
$ git rebase --autosquash -i [COMMIT_SHA]^
|
||||
$ ../electron/script/git-export-patches -o ../electron/patches/v8
|
||||
```
|
||||
|
||||
#### Removing a patch
|
||||
```bash session
|
||||
$ vim src/electron/patches/node/.patches
|
||||
# Delete the line with the name of the patch you want to remove
|
||||
$ cd src/third_party/electron_node
|
||||
$ git reset --hard refs/patches/upstream-head
|
||||
$ ../../electron/script/git-import-patches ../../electron/patches/node
|
||||
$ ../../electron/script/git-export-patches -o ../../electron/patches/node
|
||||
```
|
||||
|
||||
Note that `git-import-patches` will mark the commit that was `HEAD` when it was run as `refs/patches/upstream-head`. This lets you keep track of which commits are from Electron patches (those that come after `refs/patches/upstream-head`) and which commits are in upstream (those before `refs/patches/upstream-head`).
|
||||
|
||||
#### Resolving conflicts
|
||||
When updating an upstream dependency, patches may fail to apply cleanly. Often, the conflict can be resolved automatically by git with a 3-way merge. You can instruct `git-import-patches` to use the 3-way merge algorithm by passing the `-3` argument:
|
||||
|
||||
```bash session
|
||||
$ cd src/third_party/electron_node
|
||||
# If the patch application failed midway through, you can reset it with:
|
||||
$ git am --abort
|
||||
# And then retry with 3-way merge:
|
||||
$ ../../electron/script/git-import-patches -3 ../../electron/patches/node
|
||||
```
|
||||
|
||||
If `git-import-patches -3` encounters a merge conflict that it can't resolve automatically, it will pause and allow you to resolve the conflict manually. Once you have resolved the conflict, `git add` the resolved files and continue to apply the rest of the patches by running `git am --continue`.
|
||||
@@ -1,166 +0,0 @@
|
||||
# Upgrading Chromium
|
||||
|
||||
This is an overview of the steps needed to upgrade Chromium in Electron.
|
||||
|
||||
- Upgrade libcc to a new Chromium version
|
||||
- Make Electron code compatible with the new libcc
|
||||
- Update Electron dependencies (crashpad, NodeJS, etc.) if needed
|
||||
- Make internal builds of libcc and electron
|
||||
- Update Electron docs if necessary
|
||||
|
||||
|
||||
## Upgrade `libcc` to a new Chromium version
|
||||
|
||||
1. Get the code and initialize the project:
|
||||
```sh
|
||||
$ git clone git@github.com:electron/libchromiumcontent.git
|
||||
$ cd libchromiumcontent
|
||||
$ ./script/bootstrap -v
|
||||
```
|
||||
2. Update the Chromium snapshot
|
||||
- Choose a version number from [OmahaProxy](https://omahaproxy.appspot.com/)
|
||||
and update the `VERSION` file with it
|
||||
- This can be done manually by visiting OmahaProxy in a browser, or automatically:
|
||||
- One-liner for the latest stable mac version: `curl -so- https://omahaproxy.appspot.com/mac > VERSION`
|
||||
- One-liner for the latest win64 beta version: `curl -so- https://omahaproxy.appspot.com/all | grep "win64,beta" | awk -F, 'NR==1{print $3}' > VERSION`
|
||||
- run `$ ./script/update`
|
||||
- Brew some tea -- this may run for 30m or more.
|
||||
- It will probably fail applying patches.
|
||||
3. Fix `*.patch` files in the `patches/` and `patches-mas/` folders.
|
||||
4. (Optional) `script/update` applies patches, but if multiple tries are needed
|
||||
you can manually run the same script that `update` calls:
|
||||
`$ ./script/apply-patches`
|
||||
- There is a second script, `script/patch.py` that may be useful.
|
||||
Read `./script/patch.py -h` for more information.
|
||||
5. Run the build when all patches can be applied without errors
|
||||
- `$ ./script/build`
|
||||
- If some patches are no longer compatible with the Chromium code,
|
||||
fix compilation errors.
|
||||
6. When the build succeeds, create a `dist` for Electron
|
||||
- `$ ./script/create-dist --no_zip`
|
||||
- It will create a `dist/main` folder in the libcc repo's root.
|
||||
You will need this to build Electron.
|
||||
7. (Optional) Update script contents if there are errors resulting from files
|
||||
that were removed or renamed. (`--no_zip` prevents script from create `dist`
|
||||
archives. You don't need them.)
|
||||
|
||||
|
||||
## Update Electron's code
|
||||
|
||||
1. Get the code:
|
||||
```sh
|
||||
$ git clone git@github.com:electron/electron.git
|
||||
$ cd electron
|
||||
```
|
||||
2. If you have libcc built on your machine in its own repo,
|
||||
tell Electron to use it:
|
||||
```sh
|
||||
$ ./script/bootstrap.py -v \
|
||||
--libcc_source_path <libcc_folder>/src \
|
||||
--libcc_shared_library_path <libcc_folder>/shared_library \
|
||||
--libcc_static_library_path <libcc_folder>/static_library
|
||||
```
|
||||
3. If you haven't yet built libcc but it's already supposed to be upgraded
|
||||
to a new Chromium, bootstrap Electron as usual
|
||||
`$ ./script/bootstrap.py -v`
|
||||
- Ensure that libcc submodule (`vendor/libchromiumcontent`) points to the
|
||||
right revision
|
||||
|
||||
4. Set `CLANG_REVISION` in `script/update-clang.sh` to match the version
|
||||
Chromium is using.
|
||||
- Located in `electron/libchromiumcontent/src/tools/clang/scripts/update.py`
|
||||
|
||||
5. Checkout Chromium if you haven't already:
|
||||
- https://chromium.googlesource.com/chromium/src.git/+/{VERSION}/tools/clang/scripts/update.py
|
||||
- (Replace the `{VERSION}` placeholder in the url above to the Chromium
|
||||
version libcc uses.)
|
||||
6. Build Electron.
|
||||
- Try to build Debug version first: `$ ./script/build.py -c D`
|
||||
- You will need it to run tests
|
||||
7. Fix compilation and linking errors
|
||||
8. Ensure that Release build can be built too
|
||||
- `$ ./script/build.py -c R`
|
||||
- Often the Release build will have different linking errors that you'll
|
||||
need to fix.
|
||||
- Some compilation and linking errors are caused by missing source/object
|
||||
files in the libcc `dist`
|
||||
9. Update `./script/create-dist` in the libcc repo, recreate a `dist`, and
|
||||
run Electron bootstrap script once again.
|
||||
|
||||
### Tips for fixing compilation errors
|
||||
- Fix build config errors first
|
||||
- Fix fatal errors first, like missing files and errors related to compiler
|
||||
flags or defines
|
||||
- Try to identify complex errors as soon as possible.
|
||||
- Ask for help if you're not sure how to fix them
|
||||
- Disable all Electron features, fix the build, then enable them one by one
|
||||
- Add more build flags to disable features in build-time.
|
||||
|
||||
When a Debug build of Electron succeeds, run the tests:
|
||||
`$ npm run test`
|
||||
Fix the failing tests.
|
||||
|
||||
Follow all the steps above to fix Electron code on all supported platforms.
|
||||
|
||||
|
||||
## Updating Crashpad
|
||||
|
||||
If there are any compilation errors related to the Crashpad, it probably means
|
||||
you need to update the fork to a newer revision. See
|
||||
[Upgrading Crashpad](upgrading-crashpad.md)
|
||||
for instructions on how to do that.
|
||||
|
||||
|
||||
## Updating NodeJS
|
||||
|
||||
Upgrade `vendor/node` to the Node release that corresponds to the v8 version
|
||||
used in the new Chromium release. See the v8 versions in Node on
|
||||
|
||||
See [Upgrading Node](upgrading-node.md)
|
||||
for instructions on this.
|
||||
|
||||
## Verify ffmpeg support
|
||||
|
||||
Electron ships with a version of `ffmpeg` that includes proprietary codecs by
|
||||
default. A version without these codecs is built and distributed with each
|
||||
release as well. Each Chrome upgrade should verify that switching this version
|
||||
is still supported.
|
||||
|
||||
You can verify Electron's support for multiple `ffmpeg` builds by loading the
|
||||
following page. It should work with the default `ffmpeg` library distributed
|
||||
with Electron and not work with the `ffmpeg` library built without proprietary
|
||||
codecs.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Proprietary Codec Check</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Checking if Electron is using proprietary codecs by loading video from http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4</p>
|
||||
<p id="outcome"></p>
|
||||
<video style="display:none" src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4" autoplay></video>
|
||||
<script>
|
||||
const video = document.querySelector('video')
|
||||
video.addEventListener('error', ({ target }) => {
|
||||
if (target.error.code === target.error.MEDIA_ERR_SRC_NOT_SUPPORTED) {
|
||||
document.querySelector('#outcome').textContent = 'Not using proprietary codecs, video emitted source not supported error event.'
|
||||
} else {
|
||||
document.querySelector('#outcome').textContent = `Unexpected error: ${target.error.code}`
|
||||
}
|
||||
})
|
||||
video.addEventListener('playing', () => {
|
||||
document.querySelector('#outcome').textContent = 'Using proprietary codecs, video started playing.'
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Useful links
|
||||
|
||||
- [Chrome Release Schedule](https://www.chromium.org/developers/calendar)
|
||||
- [OmahaProxy](http://omahaproxy.appspot.com)
|
||||
- [Chromium Issue Tracker](https://bugs.chromium.org/p/chromium)
|
||||
@@ -1,39 +0,0 @@
|
||||
# Upgrading Crashpad
|
||||
|
||||
1. Get the version of crashpad that we're going to use.
|
||||
- `libcc/src/third_party/crashpad/README.chromium` will have a line `Revision:` with a checksum
|
||||
- We need to check out the corresponding branch.
|
||||
- Clone Google's crashpad (https://chromium.googlesource.com/crashpad/crashpad)
|
||||
- `git clone https://chromium.googlesource.com/crashpad/crashpad`
|
||||
- Check out the branch with the revision checksum:
|
||||
- `git checkout <revision checksum>`
|
||||
- Add electron's crashpad fork as a remote
|
||||
- `git remote add electron https://github.com/electron/crashpad`
|
||||
- Check out a new branch for the update
|
||||
- `git checkout -b electron-crashpad-vA.B.C.D`
|
||||
- `A.B.C.D` is the Chromium version found in `libcc/VERSION`
|
||||
and will be something like `62.0.3202.94`
|
||||
|
||||
2. Make a checklist of the Electron patches that need to be applied
|
||||
with `git log --oneline`
|
||||
- Or view https://github.com/electron/crashpad/commits/previous-branch-name
|
||||
|
||||
3. For each patch:
|
||||
- In `electron-crashpad-vA.B.C.D`, cherry-pick the patch's checksum
|
||||
- `git cherry-pick <checksum>`
|
||||
- Resolve any conflicts
|
||||
- Make sure it builds then add, commit, and push work to electron's crashpad fork
|
||||
- `git push electron electron-crashpad-vA.B.C.D`
|
||||
|
||||
4. Update Electron to build the new crashpad:
|
||||
- `cd vendor/crashpad`
|
||||
- `git fetch`
|
||||
- `git checkout electron-crashpad-v62.0.3202.94`
|
||||
5. Regenerate Ninja files against both targets
|
||||
- From Electron root's root, run `script/update.py`
|
||||
- `script/build.py -c D --target=crashpad_client`
|
||||
- `script/build.py -c D --target=crashpad_handler`
|
||||
- Both should build with no errors
|
||||
6. Push changes to submodule reference
|
||||
- (From electron root) `git add vendor/crashpad`
|
||||
- `git push origin upgrade-to-chromium-62`
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Asynchronous messages</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="async-msg">Ping</button>
|
||||
<span id="async-reply"></span>
|
||||
</div>
|
||||
<p>Using <code>ipc</code> to send messages between processes asynchronously is the preferred method since it will return when finished without blocking other operations in the same process.</p>
|
||||
|
||||
<p>This example sends a "ping" from this process (renderer) to the main process. The main process then replies with "pong".</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
const { app, BrowserWindow, ipcMain } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
title: 'Asynchronous messages',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
ipcMain.on('asynchronous-message', (event, arg) => {
|
||||
event.sender.send('asynchronous-reply', 'pong')
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
const asyncMsgBtn = document.getElementById('async-msg')
|
||||
|
||||
asyncMsgBtn.addEventListener('click', () => {
|
||||
ipcRenderer.send('asynchronous-message', 'ping')
|
||||
})
|
||||
|
||||
ipcRenderer.on('asynchronous-reply', (event, arg) => {
|
||||
const message = `Asynchronous message reply: ${arg}`
|
||||
document.getElementById('async-reply').innerHTML = message
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Synchronous messages</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="sync-msg">Ping</button>
|
||||
<span id="sync-reply"></span>
|
||||
</div>
|
||||
<p>You can use the <code>ipc</code> module to send synchronous messages between processes as well, but note that the synchronous nature of this method means that it <b>will block</b> other operations while completing its task.</p>
|
||||
|
||||
<p>This example sends a synchronous message, "ping", from this process (renderer) to the main process. The main process then replies with "pong".</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
const { app, BrowserWindow, ipcMain } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
title: 'Synchronous Messages',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
ipcMain.on('synchronous-message', (event, arg) => {
|
||||
event.returnValue = 'pong'
|
||||
})
|
||||
@@ -0,0 +1,9 @@
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
const syncMsgBtn = document.getElementById('sync-msg')
|
||||
|
||||
syncMsgBtn.addEventListener('click', () => {
|
||||
const reply = ipcRenderer.sendSync('synchronous-message', 'ping')
|
||||
const message = `Synchronous message reply: ${reply}`
|
||||
document.getElementById('sync-reply').innerHTML = message
|
||||
})
|
||||
25
docs/fiddles/media/screenshot/take-screenshot/index.html
Normal file
25
docs/fiddles/media/screenshot/take-screenshot/index.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>Take a Screenshot</h3>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Renderer</i>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<button id="screen-shot">View Demo</button>
|
||||
<span id="screenshot-path"></span>
|
||||
</div>
|
||||
<p>This demo uses the <code>desktopCapturer</code> module to gather screens in use and select the entire screen and take a snapshot of what is visible.</p>
|
||||
<p>Clicking the demo button will take a screenshot of your current screen and open it in your default viewer.</p>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
25
docs/fiddles/media/screenshot/take-screenshot/main.js
Normal file
25
docs/fiddles/media/screenshot/take-screenshot/main.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const { BrowserWindow, app } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 300,
|
||||
title: 'Take a Screenshot',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
43
docs/fiddles/media/screenshot/take-screenshot/renderer.js
Normal file
43
docs/fiddles/media/screenshot/take-screenshot/renderer.js
Normal file
@@ -0,0 +1,43 @@
|
||||
const { desktopCapturer } = require('electron')
|
||||
const { screen, shell } = require('electron').remote
|
||||
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
|
||||
const screenshot = document.getElementById('screen-shot')
|
||||
const screenshotMsg = document.getElementById('screenshot-path')
|
||||
|
||||
screenshot.addEventListener('click', (event) => {
|
||||
screenshotMsg.textContent = 'Gathering screens...'
|
||||
const thumbSize = determineScreenShotSize()
|
||||
const options = { types: ['screen'], thumbnailSize: thumbSize }
|
||||
|
||||
desktopCapturer.getSources(options, (error, sources) => {
|
||||
if (error) return console.log(error)
|
||||
|
||||
sources.forEach((source) => {
|
||||
const sourceName = source.name.toLowerCase()
|
||||
if (sourceName === 'entire screen' || sourceName === 'screen 1') {
|
||||
const screenshotPath = path.join(os.tmpdir(), 'screenshot.png')
|
||||
|
||||
fs.writeFile(screenshotPath, source.thumbnail.toPNG(), (error) => {
|
||||
if (error) return console.log(error)
|
||||
shell.openExternal(`file://${screenshotPath}`)
|
||||
|
||||
const message = `Saved screenshot to: ${screenshotPath}`
|
||||
screenshotMsg.textContent = message
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function determineScreenShotSize () {
|
||||
const screenSize = screen.getPrimaryDisplay().workAreaSize
|
||||
const maxDimension = Math.max(screenSize.width, screenSize.height)
|
||||
return {
|
||||
width: maxDimension * window.devicePixelRatio,
|
||||
height: maxDimension * window.devicePixelRatio
|
||||
}
|
||||
}
|
||||
128
docs/fiddles/menus/customize-menus/index.html
Normal file
128
docs/fiddles/menus/customize-menus/index.html
Normal file
@@ -0,0 +1,128 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Customize Menus</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Customize Menus</h1>
|
||||
|
||||
<h3>
|
||||
The <code>Menu</code> and <code>MenuItem</code> modules can be used to
|
||||
create custom native menus.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
There are two kinds of menus: the application (top) menu and context
|
||||
(right-click) menu.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="http://electron.atom.io/docs/api/menu"
|
||||
>full API documentation<span
|
||||
>(opens in new window)</span
|
||||
></a
|
||||
>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Create an application menu</h2>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
The <code>Menu</code> and <code>MenuItem</code> modules allow you to
|
||||
customize your application menu. If you don't set any menu, Electron
|
||||
will generate a minimal menu for your app by default.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you click the 'View' option in the application menu and then the
|
||||
'App Menu Demo', you'll see an information box displayed.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<h2>ProTip</h2>
|
||||
<strong>Know operating system menu differences.</strong>
|
||||
<p>
|
||||
When designing an app for multiple operating systems it's
|
||||
important to be mindful of the ways application menu conventions
|
||||
differ on each operating system.
|
||||
</p>
|
||||
<p>
|
||||
For instance, on Windows, accelerators are set with an
|
||||
<code>&</code>. Naming conventions also vary, like between
|
||||
"Settings" or "Preferences". Below are resources for learning
|
||||
operating system specific standards.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://developer.apple.com/macos/human-interface-guidelines/menus/menu-anatomy/"
|
||||
>macOS<span
|
||||
>(opens in new window)</span
|
||||
></a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb226797(v=vs.85).aspx"
|
||||
>Windows<span
|
||||
>(opens in new window)</span
|
||||
></a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://developer.gnome.org/hig/stable/menu-bars.html.en"
|
||||
>Linux<span
|
||||
>(opens in new window)</span
|
||||
></a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Create a context menu</h2>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<button id="context-menu">View Demo</button>
|
||||
</div>
|
||||
<p>
|
||||
A context, or right-click, menu can be created with the
|
||||
<code>Menu</code> and <code>MenuItem</code> modules as well. You can
|
||||
right-click anywhere in this app or click the demo button to see an
|
||||
example context menu.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In this demo we use the <code>ipcRenderer</code> module to show the
|
||||
context menu when explicitly calling it from the renderer process.
|
||||
</p>
|
||||
<p>
|
||||
See the full
|
||||
<a
|
||||
href="http://electron.atom.io/docs/api/web-contents/#event-context-menu"
|
||||
>context-menu event documentation</a
|
||||
>
|
||||
for all the available properties.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
360
docs/fiddles/menus/customize-menus/main.js
Normal file
360
docs/fiddles/menus/customize-menus/main.js
Normal file
@@ -0,0 +1,360 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const {
|
||||
BrowserWindow,
|
||||
Menu,
|
||||
MenuItem,
|
||||
ipcMain,
|
||||
app,
|
||||
shell,
|
||||
dialog
|
||||
} = require('electron')
|
||||
|
||||
const menu = new Menu()
|
||||
menu.append(new MenuItem({ label: 'Hello' }))
|
||||
menu.append(new MenuItem({ type: 'separator' }))
|
||||
menu.append(
|
||||
new MenuItem({ label: 'Electron', type: 'checkbox', checked: true })
|
||||
)
|
||||
|
||||
const template = [
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Undo',
|
||||
accelerator: 'CmdOrCtrl+Z',
|
||||
role: 'undo'
|
||||
},
|
||||
{
|
||||
label: 'Redo',
|
||||
accelerator: 'Shift+CmdOrCtrl+Z',
|
||||
role: 'redo'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Cut',
|
||||
accelerator: 'CmdOrCtrl+X',
|
||||
role: 'cut'
|
||||
},
|
||||
{
|
||||
label: 'Copy',
|
||||
accelerator: 'CmdOrCtrl+C',
|
||||
role: 'copy'
|
||||
},
|
||||
{
|
||||
label: 'Paste',
|
||||
accelerator: 'CmdOrCtrl+V',
|
||||
role: 'paste'
|
||||
},
|
||||
{
|
||||
label: 'Select All',
|
||||
accelerator: 'CmdOrCtrl+A',
|
||||
role: 'selectall'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Reload',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
click: (item, focusedWindow) => {
|
||||
if (focusedWindow) {
|
||||
// on reload, start fresh and close any old
|
||||
// open secondary windows
|
||||
if (focusedWindow.id === 1) {
|
||||
BrowserWindow.getAllWindows().forEach(win => {
|
||||
if (win.id > 1) win.close()
|
||||
})
|
||||
}
|
||||
focusedWindow.reload()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: (() => {
|
||||
if (process.platform === 'darwin') {
|
||||
return 'Ctrl+Command+F'
|
||||
} else {
|
||||
return 'F11'
|
||||
}
|
||||
})(),
|
||||
click: (item, focusedWindow) => {
|
||||
if (focusedWindow) {
|
||||
focusedWindow.setFullScreen(!focusedWindow.isFullScreen())
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: (() => {
|
||||
if (process.platform === 'darwin') {
|
||||
return 'Alt+Command+I'
|
||||
} else {
|
||||
return 'Ctrl+Shift+I'
|
||||
}
|
||||
})(),
|
||||
click: (item, focusedWindow) => {
|
||||
if (focusedWindow) {
|
||||
focusedWindow.toggleDevTools()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'App Menu Demo',
|
||||
click: function (item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Application Menu Demo',
|
||||
buttons: ['Ok'],
|
||||
message:
|
||||
'This demo is for the Menu section, showing how to create a clickable menu item in the application menu.'
|
||||
}
|
||||
dialog.showMessageBox(focusedWindow, options, function () {})
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Window',
|
||||
role: 'window',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Minimize',
|
||||
accelerator: 'CmdOrCtrl+M',
|
||||
role: 'minimize'
|
||||
},
|
||||
{
|
||||
label: 'Close',
|
||||
accelerator: 'CmdOrCtrl+W',
|
||||
role: 'close'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Reopen Window',
|
||||
accelerator: 'CmdOrCtrl+Shift+T',
|
||||
enabled: false,
|
||||
key: 'reopenMenuItem',
|
||||
click: () => {
|
||||
app.emit('activate')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click: () => {
|
||||
shell.openExternal('http://electron.atom.io')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
function addUpdateMenuItems (items, position) {
|
||||
if (process.mas) return
|
||||
|
||||
const version = app.getVersion()
|
||||
const updateItems = [
|
||||
{
|
||||
label: `Version ${version}`,
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
label: 'Checking for Update',
|
||||
enabled: false,
|
||||
key: 'checkingForUpdate'
|
||||
},
|
||||
{
|
||||
label: 'Check for Update',
|
||||
visible: false,
|
||||
key: 'checkForUpdate',
|
||||
click: () => {
|
||||
require('electron').autoUpdater.checkForUpdates()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Restart and Install Update',
|
||||
enabled: true,
|
||||
visible: false,
|
||||
key: 'restartToUpdate',
|
||||
click: () => {
|
||||
require('electron').autoUpdater.quitAndInstall()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
items.splice.apply(items, [position, 0].concat(updateItems))
|
||||
}
|
||||
|
||||
function findReopenMenuItem () {
|
||||
const menu = Menu.getApplicationMenu()
|
||||
if (!menu) return
|
||||
|
||||
let reopenMenuItem
|
||||
menu.items.forEach(item => {
|
||||
if (item.submenu) {
|
||||
item.submenu.items.forEach(item => {
|
||||
if (item.key === 'reopenMenuItem') {
|
||||
reopenMenuItem = item
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return reopenMenuItem
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const name = app.getName()
|
||||
template.unshift({
|
||||
label: name,
|
||||
submenu: [
|
||||
{
|
||||
label: `About ${name}`,
|
||||
role: 'about'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Services',
|
||||
role: 'services',
|
||||
submenu: []
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: `Hide ${name}`,
|
||||
accelerator: 'Command+H',
|
||||
role: 'hide'
|
||||
},
|
||||
{
|
||||
label: 'Hide Others',
|
||||
accelerator: 'Command+Alt+H',
|
||||
role: 'hideothers'
|
||||
},
|
||||
{
|
||||
label: 'Show All',
|
||||
role: 'unhide'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'Command+Q',
|
||||
click: () => {
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// Window menu.
|
||||
template[3].submenu.push(
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Bring All to Front',
|
||||
role: 'front'
|
||||
}
|
||||
)
|
||||
|
||||
addUpdateMenuItems(template[0].submenu, 1)
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const helpMenu = template[template.length - 1].submenu
|
||||
addUpdateMenuItems(helpMenu, 0)
|
||||
}
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
const menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(menu)
|
||||
})
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
const reopenMenuItem = findReopenMenuItem()
|
||||
if (reopenMenuItem) reopenMenuItem.enabled = true
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('browser-window-created', (event, win) => {
|
||||
const reopenMenuItem = findReopenMenuItem()
|
||||
if (reopenMenuItem) reopenMenuItem.enabled = false
|
||||
|
||||
win.webContents.on('context-menu', (e, params) => {
|
||||
menu.popup(win, params.x, params.y)
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('show-context-menu', event => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
menu.popup(win)
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
8
docs/fiddles/menus/customize-menus/renderer.js
Normal file
8
docs/fiddles/menus/customize-menus/renderer.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
// Tell main process to show the menu when demo button is clicked
|
||||
const contextMenuBtn = document.getElementById('context-menu')
|
||||
|
||||
contextMenuBtn.addEventListener('click', () => {
|
||||
ipcRenderer.send('show-context-menu')
|
||||
})
|
||||
73
docs/fiddles/menus/shortcuts/index.html
Normal file
73
docs/fiddles/menus/shortcuts/index.html
Normal file
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Keyboard Shortcuts</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Keyboard Shortcuts</h1>
|
||||
|
||||
<h3>The <code>globalShortcut</code> and <code>Menu</code> modules can be used to define keyboard shortcuts.</h3>
|
||||
|
||||
<p>
|
||||
In Electron, keyboard shortcuts are called accelerators.
|
||||
They can be assigned to actions in your application's Menu,
|
||||
or they can be assigned globally so they'll be triggered even when
|
||||
your app doesn't have keyboard focus.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the full documentation for the
|
||||
<a href="http://electron.atom.io/docs/api/menu">Menu</a>,
|
||||
<a href="http://electron.atom.io/docs/api/accelerator">Accelerator</a>,
|
||||
and
|
||||
<a href="http://electron.atom.io/docs/api/global-shortcut">globalShortcut</a>
|
||||
APIs in your browser.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
To try this demo, press <kbd>CommandOrControl+Alt+K</kbd> on your
|
||||
keyboard.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Global shortcuts are detected even when the app doesn't have
|
||||
keyboard focus, and they must be registered after the app's
|
||||
`ready` event is emitted.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<h2>ProTip</h2>
|
||||
<strong>Avoid overriding system-wide keyboard shortcuts.</strong>
|
||||
<p>
|
||||
When registering global shortcuts, it's important to be aware of
|
||||
existing defaults in the target operating system, so as not to
|
||||
override any existing behaviors. For an overview of each
|
||||
operating system's keyboard shortcuts, view these documents:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li><a
|
||||
href="https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/Keyboard.html">macOS</a>
|
||||
</li>
|
||||
<li><a
|
||||
href="http://windows.microsoft.com/en-us/windows-10/keyboard-shortcuts">Windows</a></li>
|
||||
<li><a
|
||||
href="https://developer.gnome.org/hig/stable/keyboard-input.html.en">Linux</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
69
docs/fiddles/menus/shortcuts/main.js
Normal file
69
docs/fiddles/menus/shortcuts/main.js
Normal file
@@ -0,0 +1,69 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, globalShortcut, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
globalShortcut.register('CommandOrControl+Alt+K', () => {
|
||||
dialog.showMessageBox({
|
||||
type: 'info',
|
||||
message: 'Success!',
|
||||
detail: 'You pressed the registered global shortcut keybinding.',
|
||||
buttons: ['OK']
|
||||
})
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('will-quit', function () {
|
||||
globalShortcut.unregisterAll()
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
81
docs/fiddles/native-ui/dialogs/error-dialog/index.html
Normal file
81
docs/fiddles/native-ui/dialogs/error-dialog/index.html
Normal file
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Error Dialog</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Use system dialogs</h1>
|
||||
|
||||
<h3>
|
||||
The <code>dialog</code> module in Electron allows you to use native
|
||||
system dialogs for opening files or directories, saving a file or
|
||||
displaying informational messages.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
This is a main process module because this process is more efficient
|
||||
with native utilities and it allows the call to happen without
|
||||
interrupting the visible elements in your page's renderer process.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="http://electron.atom.io/docs/api/dialog/">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Error Dialog</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="error-dialog">View Demo</button>
|
||||
</div>
|
||||
<p>
|
||||
In this demo, the <code>ipc</code> module is used to send a message
|
||||
from the renderer process instructing the main process to launch the
|
||||
error dialog.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can use an error dialog before the app's
|
||||
<code>ready</code> event, which is useful for showing errors upon
|
||||
startup.
|
||||
</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcRenderer} = require('electron')
|
||||
|
||||
const errorBtn = document.getElementById('error-dialog')
|
||||
|
||||
errorBtn.addEventListener('click', (event) => {
|
||||
ipcRenderer.send('open-error-dialog')
|
||||
})
|
||||
</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcMain, dialog} = require('electron')
|
||||
|
||||
ipcMain.on('open-error-dialog', (event) => {
|
||||
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
95
docs/fiddles/native-ui/dialogs/error-dialog/main.js
Normal file
95
docs/fiddles/native-ui/dialogs/error-dialog/main.js
Normal file
@@ -0,0 +1,95 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('open-error-dialog', event => {
|
||||
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
|
||||
})
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
18
docs/fiddles/native-ui/dialogs/error-dialog/renderer.js
Normal file
18
docs/fiddles/native-ui/dialogs/error-dialog/renderer.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
const errorBtn = document.getElementById('error-dialog')
|
||||
|
||||
errorBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-error-dialog')
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
104
docs/fiddles/native-ui/dialogs/information-dialog/index.html
Normal file
104
docs/fiddles/native-ui/dialogs/information-dialog/index.html
Normal file
@@ -0,0 +1,104 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Information Dialog</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="section-wrapper">
|
||||
<h1>Use system dialogs</h1>
|
||||
|
||||
<h3>
|
||||
The <code>dialog</code> module in Electron allows you to use native
|
||||
system dialogs for opening files or directories, saving a file or
|
||||
displaying informational messages.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
This is a main process module because this process is more efficient
|
||||
with native utilities and it allows the call to happen without
|
||||
interrupting the visible elements in your page's renderer process.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="http://electron.atom.io/docs/api/dialog/">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Information Dialog</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="information-dialog">
|
||||
View Demo
|
||||
</button>
|
||||
<span id="info-selection"></span>
|
||||
</div>
|
||||
<p>
|
||||
In this demo, the <code>ipc</code> module is used to send a message
|
||||
from the renderer process instructing the main process to launch the
|
||||
information dialog. Options may be provided for responses which can
|
||||
then be relayed back to the renderer process.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note: The <code>title</code> property is not displayed in macOS.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An information dialog can contain an icon, your choice of buttons,
|
||||
title and message.
|
||||
</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcRenderer} = require('electron')
|
||||
|
||||
const informationBtn = document.getElementById('information-dialog')
|
||||
|
||||
informationBtn.addEventListener('click', (event) => {
|
||||
ipcRenderer.send('open-information-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('information-dialog-selection', (event, index) => {
|
||||
let message = 'You selected '
|
||||
if (index === 0) message += 'yes.'
|
||||
else message += 'no.'
|
||||
document.getElementById('info-selection').innerHTML = message
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcMain, dialog} = require('electron')
|
||||
|
||||
ipcMain.on('open-information-dialog', (event) => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, (index) => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
70
docs/fiddles/native-ui/dialogs/information-dialog/main.js
Normal file
70
docs/fiddles/native-ui/dialogs/information-dialog/main.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
@@ -0,0 +1,25 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const informationBtn = document.getElementById('information-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
informationBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-information-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('information-dialog-selection', (event, index) => {
|
||||
let message = 'You selected '
|
||||
if (index === 0) message += 'yes.'
|
||||
else message += 'no.'
|
||||
document.getElementById('info-selection').innerHTML = message
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
108
docs/fiddles/native-ui/dialogs/open-file-or-directory/index.html
Normal file
108
docs/fiddles/native-ui/dialogs/open-file-or-directory/index.html
Normal file
@@ -0,0 +1,108 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Open File or Directory</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="section-wrapper">
|
||||
<h1>Use system dialogs</h1>
|
||||
|
||||
<h3>
|
||||
The <code>dialog</code> module in Electron allows you to use native
|
||||
system dialogs for opening files or directories, saving a file or
|
||||
displaying informational messages.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
This is a main process module because this process is more efficient
|
||||
with native utilities and it allows the call to happen without
|
||||
interrupting the visible elements in your page's renderer process.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="http://electron.atom.io/docs/api/dialog/">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Open a File or Directory</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="select-directory">View Demo</button>
|
||||
<span id="selected-file"></span>
|
||||
</div>
|
||||
<p>
|
||||
In this demo, the <code>ipc</code> module is used to send a message
|
||||
from the renderer process instructing the main process to launch the
|
||||
open file (or directory) dialog. If a file is selected, the main
|
||||
process can send that information back to the renderer process.
|
||||
</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcRenderer} = require('electron')
|
||||
|
||||
const selectDirBtn = document.getElementById('select-directory')
|
||||
|
||||
selectDirBtn.addEventListener('click', (event) => {
|
||||
ipcRenderer.send('open-file-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('selected-directory', (event, path) => {
|
||||
document.getElementById('selected-file').innerHTML = `You selected: ${path}`
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcMain, dialog} = require('electron')
|
||||
|
||||
ipcMain.on('open-file-dialog', (event) => {
|
||||
dialog.showOpenDialog({
|
||||
properties: ['openFile', 'openDirectory']
|
||||
}, (files) => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
})
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<div>
|
||||
<h2>ProTip</h2>
|
||||
<strong>The sheet-style dialog on macOS.</strong>
|
||||
<p>
|
||||
On macOS you can choose between a "sheet" dialog or a default
|
||||
dialog. The sheet version descends from the top of the window. To
|
||||
use sheet version, pass the <code>window</code> as the first
|
||||
argument in the dialog method.
|
||||
</p>
|
||||
<pre><code class="language-js">const ipc = require('electron').ipcMain
|
||||
const dialog = require('electron').dialog
|
||||
const BrowserWindow = require('electron').BrowserWindow
|
||||
|
||||
|
||||
ipc.on('open-file-dialog-sheet', function (event) {
|
||||
const window = BrowserWindow.fromWebContents(event.sender)
|
||||
const files = dialog.showOpenDialog(window, { properties: [ 'openFile' ]})
|
||||
})</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,70 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
@@ -0,0 +1,22 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const selectDirBtn = document.getElementById('select-directory')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
selectDirBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-file-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('selected-directory', (event, path) => {
|
||||
document.getElementById('selected-file').innerHTML = `You selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
91
docs/fiddles/native-ui/dialogs/save-dialog/index.html
Normal file
91
docs/fiddles/native-ui/dialogs/save-dialog/index.html
Normal file
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Save Dialog</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Use system dialogs</h1>
|
||||
|
||||
<h3>
|
||||
The <code>dialog</code> module in Electron allows you to use native
|
||||
system dialogs for opening files or directories, saving a file or
|
||||
displaying informational messages.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
This is a main process module because this process is more efficient
|
||||
with native utilities and it allows the call to happen without
|
||||
interrupting the visible elements in your page's renderer process.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="http://electron.atom.io/docs/api/dialog/">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Save Dialog</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button button id="save-dialog">View Demo</button>
|
||||
<span id="file-saved"></span>
|
||||
</div>
|
||||
<p>
|
||||
In this demo, the <code>ipc</code> module is used to send a message
|
||||
from the renderer process instructing the main process to launch the
|
||||
save dialog. It returns the path selected by the user which can be
|
||||
relayed back to the renderer process.
|
||||
</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcRenderer} = require('electron')
|
||||
|
||||
const saveBtn = document.getElementById('save-dialog')
|
||||
|
||||
saveBtn.addEventListener('click', (event) => {
|
||||
ipcRenderer.send('save-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('saved-file', (event, path) => {
|
||||
if (!path) path = 'No path'
|
||||
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcMain, dialog} = require('electron')
|
||||
|
||||
ipcMain.on('save-dialog', (event) => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [
|
||||
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }
|
||||
]
|
||||
}
|
||||
dialog.showSaveDialog(options, (filename) => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
66
docs/fiddles/native-ui/dialogs/save-dialog/main.js
Normal file
66
docs/fiddles/native-ui/dialogs/save-dialog/main.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
23
docs/fiddles/native-ui/dialogs/save-dialog/renderer.js
Normal file
23
docs/fiddles/native-ui/dialogs/save-dialog/renderer.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const saveBtn = document.getElementById('save-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
saveBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('save-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('saved-file', (event, path) => {
|
||||
if (!path) path = 'No path'
|
||||
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
76
docs/fiddles/native-ui/drag-and-drop/index.html
Normal file
76
docs/fiddles/native-ui/drag-and-drop/index.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Drag and drop files</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Drag and drop files</h1>
|
||||
<div>Supports: Win, macOS, Linux <span>|</span> Process: Both</div>
|
||||
<h3>
|
||||
Electron supports dragging files and content out from web content into
|
||||
the operating system's world.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/tutorial/native-file-drag-drop">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Dragging files</h2>
|
||||
<div>
|
||||
<div>
|
||||
<a href="#" id="drag-file-link">Drag Demo</a>
|
||||
</div>
|
||||
<p>
|
||||
Click and drag the link above to copy the renderer process
|
||||
javascript file on to your machine.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In this demo, the <code>webContents.startDrag()</code> API is called
|
||||
in response to the <code>ondragstart</code> event.
|
||||
</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code>
|
||||
const {ipcRenderer} = require('electron')
|
||||
|
||||
const dragFileLink = document.getElementById('drag-file-link')
|
||||
|
||||
dragFileLink.addEventListener('dragstart', (event) => {
|
||||
event.preventDefault()
|
||||
ipcRenderer.send('ondragstart', __filename)
|
||||
})
|
||||
</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const {ipcMain} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
ipcMain.on('ondragstart', (event, filepath) => {
|
||||
const iconName = 'codeIcon.png'
|
||||
event.sender.startDrag({
|
||||
file: filepath,
|
||||
icon: path.join(__dirname, iconName)
|
||||
})
|
||||
})
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
64
docs/fiddles/native-ui/drag-and-drop/main.js
Normal file
64
docs/fiddles/native-ui/drag-and-drop/main.js
Normal file
@@ -0,0 +1,64 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, nativeImage } = require('electron')
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ondragstart', (event, filepath) => {
|
||||
const icon = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAACsZJREFUWAmtWFlsXFcZ/u82++Jt7IyT2Em6ZFHTpAtWIzspEgjEUhA8VNAiIYEQUvuABBIUwUMkQIVKPCIoEiABLShISEBbhFJwIGRpIKRpbNeJ7bh2HHvssR3PPnPnLnzfmRlju6EQqUc+c++c8y/fv54z1uQOh+/7Glh0TD59TE/TND7lnfa4/64OKsM071QoeZpA/y9WWvk/B4XCC06TUC+Xyw8HTXNQ1+Ww6PpOrMebewXxvBueJ6/XHOdMJBL5J9Y97m2R0SS/wweE6JxkGx5dilWr1S/7dXsEa2o4+LyFmcFcaL5zbX3Y9gh5hpeWYpSB9XV5/H678V89BGYDXnHJlCsWn4gHrGc1K9CXxferOdvPOOKUfF8cH7nUyCtklQZXih/VNNlmirk3GdBSoIcRswW7/vVkLPYi5W2Uze8bh7J+4wLfh4dViFx5/nmrUi7/MhGNvrCkBfpeWqnW/7BUdadqntQ8zwr6vhUV34xpYnDynWvcmwQNaclDXsqgLMqkocPDw7fNx7d5qIX+/PmJxKGD6VdDkeh7ztyqOFfrokGCEWiiZ1mp0uITnuKAosaT7+pNxMYTyefutcQfbA+b1XLpH5fnF97/yD335Fu6mqTqsclDINBVmI4fDxw80KPAvJSt1MZtMcLiGxYUu83p4UkgnJZlqcl3LAj3WnTkIS9lUBYNPJjueVWgg7qocyOgliFqjZsg8gq5tRdiieQTf1gq15Y8CUbRZtyWOzZwc8lEqS3PTCtgqd13ieO68BQ2uNl64tXAewktrFuX2mPdkWAxn3sxnmx7sqUTJGqso8MGS9tbXFz8DMH8bblUX3T9QARVi8RV8qljfcJy0zRlaf6mzHEuzEtmekqCoZB4rqp0OmudHtUnlEWZlE0d1EWd1N3EozourcO65pw4eTIZQTW9VazJtbqvw9XwKVFQMsKDBuNhtp4uvGGFI+IDgKnpMjYyIis3ZsQMBIR7pONsIaMsyqRs6ohY1rPUSd3EQFDqo+kdZ3Fh4aupbdu+99uFQr2A1CBs4uEAjZjIFUMHi4dVxMXzCdCXQj4vBrwVCofl0ulTcv/DAxJJJBUPc8mpoyI2JDw7bFyT+ifTcSubyXytJ51+roWBxwG9Q73WWjZ7eSUU3//nXM0NI+x0PBGrTSgsLS9JFuFxHFrvSqIrJV279gi6tjiVspTza3JjZhY+0CQZj0mlWJSeHTslCro6eFqymCcVVN77kkGjs1p4sy2VOoSlOrFwT+XR+PjkgGaZ+ycKVbRTYUdVrmaImCvzk1dlFCEJdHRJ284+ie/ol0h7p7jFvExcvCCXzp2Rqem3pAMAiqWS6JGYhFI9Mjo6KjevXVUyKEuFHrKpY6JQ8TXT3D8+OTkAHBw6o6LCFo9ag3o4JtlCyTHEt5AxKvS6YUi5kJeZG3Py0NAxlLcJ9xti+K7Mjo/JfGZRuvv6Ze+9+yWEhDZAvzg3JyhX2d6/S7q6e+TimdOS7ElLKBZDwqvmj6rztayr1fVI1IoXi4PAcYZY1tPEEO1wEVlXgRFBDcmIXTqJsS+XyhKLJ5A/OpIVXXptWUYv/UvaenfIocEhMQ2EzHHErlXFCgQl3paU1eVl6QAY8sQTCSmVihKJx1V/ogvgIYF/pACdcMBhqONoHhF88/2d+bojyA6cRvje2IdFjoSjUSnBS8hgyS9lZOzKFdmPxO3o6gQIGzwuDn1dVSCtCKPy1pZXlATXqUsVYMLRmKo87vP4Y1ioqwCdCegmMYx3W/VPn8RrSDwwIMMbcEjkYo29JZVOy+ybI7K4eksODx1VSqvligpReSVLgySM/FI5h2q062jNyL3s7FtoAyGJIlx1225UmwJF6aJRJ3XzHXO9bWvsJa3jQFlBJkz6iuXdu32HzM7MyP0PPNgAU6ko4Qzp6b+flr8MD9OYJg9CwtzL5+T65ITs2bsP3mGxN/ZbBcOn0sk20gAkLQ+huXpFi8vkoY9AoyDjxTR1mbo6Ltt275HpN0dlNxQE40mVM8Ajjxx9VAGhAvQR1akZFCq799ADysMuQqOxh2FNmamEaz51ItGLfFD9+oUJoZkLowHoFA2mljUacqOMflKuVmHpfmnfvlMuvXZeStmMBIMhcWEdjgFJtrUjXI0KchAuAg0ilxLJNoRVBxhIBm0TjjKAuqjTqTs3CQZ6QUUMGFW7eiWMUg6w+yo8YMW7DqtqlZLkUDV2ISfd29KyDwk9MjYmMyOXxQIIKuShqo4VGFNBEgeDQYqVam5N5tEePFQgURIUBCsd1EWd1XrtDUUMLARD9bKaK5ytQ2Gb75g8WMiEP6VkfnZGevv6UF1vSBW5E0PFDAweFRvlfun8WVmamhDNrkmweQ0pwaPt6M4m8mgKTTFXqcrV0ZH1FKBg6qAu6qTuJiCV1Cp2Q0NDr9Uq5Ym+oMEDlSewsoRwrVBEaij7AJ4s7zrOpumxEdm15y6558GHJVe1Zezy6zJx6aJkpq5JFB4z6zVZmBiX1VWUP0IY4CFMYcpQdZ3xqIs6oftCE5DHKwd0q/tzOV8svdDb3nk8VnG9qmgQC0ZURz8Ur91alXgSByZ6ES9kZZTr/PR16UOCh+7dq0CWyyXJ4xqCQ0nKt9YQSlPue2gAeYZzD7yNLk0wmqAreb2WYSxAJ8Dget64wxtEBlDaqVOn/K5dB67t6+t5MhoMJuc8w8UPKiQ9CQR9JK5czhZAQxPt7TKF3OiAIisUViAD2Lg5d0P2HDgoKeRaW0enyqVwBJcO5fFG5dqa7h406qaeX8384uTZL5w9+UqxhYHFp0YLIYA9ddfu3T+4UJF6Rg+YAc9D0+RoIGP1ULhpWspr10evyK7+ftWTrk9PS/++A9KZSm26cih2mMOErem6n/ZsZwA2TM/MPHXs2LEftnSTbh0Q36mIIbx44cLvOnu3f+xUwbWLmoHTCUlF6g2jBQo/GnFrnGNqSHdvr+rIKGMW1KahwEBdzHft98aNwMr8zd8/NDDwccihc0hLi3GubRjY0Bm6H19fPvnZI4c/fHd7PJ2peXYZ+WQ26JufZELjQ6lbAQtnWre0d3apY8TFIdtAo+Qri6mupsB49lBMC+QXF0YefObZT8j0eKWlswVjEyCCOXHihPGb575VCvVuf3lvetsH9rXF0rla3cnhpoIGjgsUPhR3I4TMKYJQV1Z6WO02aEjHa5mNe3OPW3OPRHVrbXFh9Ocvv/KR1372owx1Pf3005uc35Ddgtd8rsf06IdS5777zZ+mUqmPzjm6TPpmvayZOq4LyATeCzkanmiy4qEuC/yXiO8CSMRzvLs1x9phepLNZl868sy3Pyen/5hd1/EfRvWmuvSWNeaRS/RkPDI4+NjE1NSXEoXlpaNB1zqo20abi59/vu/UfM2pie7WUDVq8l3wTwnskeZ+zTbIQ17KoCzKpGzq2KqX32/roRbh8ePHdUzl0s9/5Rv9n/7go19MxCKfCkZiu3V06wrO5gocxL7Dgd/IEobEMH6rejg+auXidL5Y/vWv/vTX53/y/e/MkGajTH7fOt4RUJOY1df4RdtY6ICFRzqTySOhUOA+3Ai3o31H1ZbnlXBruFmt2iMrudy5xx9//BzWV7nXDBGN2xpjbt/5oGUEdhtO3iD47xZOvm8a5CHvpsV38wsUaMwBWsz3rbK5xr0mzdv2t9Jv/f5vhsF4J+Q63IUAAAAASUVORK5CYII=')
|
||||
|
||||
event.sender.startDrag({
|
||||
file: filepath,
|
||||
icon
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
21
docs/fiddles/native-ui/drag-and-drop/renderer.js
Normal file
21
docs/fiddles/native-ui/drag-and-drop/renderer.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const { ipcRenderer } = require('electron')
|
||||
const shell = require('electron').shell
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const dragFileLink = document.getElementById('drag-file-link')
|
||||
|
||||
dragFileLink.addEventListener('dragstart', event => {
|
||||
event.preventDefault()
|
||||
ipcRenderer.send('ondragstart', __filename)
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Open Path in File Manager</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<div>
|
||||
<p>This demonstrates using the <code>shell</code> module to open the system file manager at a particular location.</p>
|
||||
<p>Clicking the demo button will open your file manager at the root.</p>
|
||||
<div>
|
||||
<button id="open-file-manager">View Demo</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
title: 'Open Path in File Manager',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
const { shell } = require('electron')
|
||||
const os = require('os')
|
||||
|
||||
const fileManagerBtn = document.getElementById('open-file-manager')
|
||||
|
||||
fileManagerBtn.addEventListener('click', (event) => {
|
||||
shell.showItemInFolder(os.homedir())
|
||||
})
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>Basic notification</h3>
|
||||
<i>Supports: Win 7+, macOS, Linux (that supports libnotify)<span>|</span> Process: Renderer</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="basic-noti">View demo</button>
|
||||
</div>
|
||||
<p>This demo demonstrates a basic notification. Text only.</p>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
const { BrowserWindow, app } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 300,
|
||||
title: 'Basic Notification',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
const notification = {
|
||||
title: 'Basic Notification',
|
||||
body: 'Short message part'
|
||||
}
|
||||
|
||||
const notificationButton = document.getElementById('basic-noti')
|
||||
|
||||
notificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(notification.title, notification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>Notification with image</h3>
|
||||
<i>Supports: Win 7+, macOS, Linux (that supports libnotify)<span>|</span> Process: Renderer</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="advanced-noti">View demo</button>
|
||||
</div>
|
||||
<p>This demo demonstrates an advanced notification. Both text and image.</p>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
const { BrowserWindow, app } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 300,
|
||||
title: 'Advanced Notification',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
const notification = {
|
||||
title: 'Notification with image',
|
||||
body: 'Short message plus a custom image',
|
||||
icon: 'https://raw.githubusercontent.com/electron/electron-api-demos/v2.0.2/assets/img/programming.png'
|
||||
}
|
||||
const notificationButton = document.getElementById('advanced-noti')
|
||||
|
||||
notificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(notification.title, notification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
@@ -17,4 +17,4 @@ app.on('ready', () => {
|
||||
|
||||
mainWindow = new BrowserWindow({ width, height })
|
||||
mainWindow.loadURL('https://electronjs.org')
|
||||
})
|
||||
})
|
||||
|
||||
24
docs/fiddles/system/clipboard/copy/index.html
Normal file
24
docs/fiddles/system/clipboard/copy/index.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Clipboard copy</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="copy-to">Copy</button>
|
||||
<input id="copy-to-input" aria-label="Click copy" placeholder="Click copy."></input>
|
||||
</div>
|
||||
<p>In this example we copy a phrase to the clipboard. After clicking 'Copy' use the text area to paste (CMD + V or CTRL + V) the phrase from the clipboard.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</html>
|
||||
25
docs/fiddles/system/clipboard/copy/main.js
Normal file
25
docs/fiddles/system/clipboard/copy/main.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
title: 'Clipboard copy',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
10
docs/fiddles/system/clipboard/copy/renderer.js
Normal file
10
docs/fiddles/system/clipboard/copy/renderer.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const copyBtn = document.getElementById('copy-to')
|
||||
const copyInput = document.getElementById('copy-to-input')
|
||||
|
||||
copyBtn.addEventListener('click', () => {
|
||||
if (copyInput.value !== '') copyInput.value = ''
|
||||
copyInput.placeholder = 'Copied! Paste here to see.'
|
||||
clipboard.writeText('Electron Demo!')
|
||||
})
|
||||
24
docs/fiddles/system/clipboard/paste/index.html
Normal file
24
docs/fiddles/system/clipboard/paste/index.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Clipboard paste</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="paste-to">Paste</button>
|
||||
<span id="paste-from"></span>
|
||||
</div>
|
||||
<p>In this example we copy a string to the clipboard and then paste the results into a message above.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</html>
|
||||
25
docs/fiddles/system/clipboard/paste/main.js
Normal file
25
docs/fiddles/system/clipboard/paste/main.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
title: 'Clipboard paste',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
9
docs/fiddles/system/clipboard/paste/renderer.js
Normal file
9
docs/fiddles/system/clipboard/paste/renderer.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const pasteBtn = document.getElementById('paste-to')
|
||||
|
||||
pasteBtn.addEventListener('click', () => {
|
||||
clipboard.writeText('What a demo!')
|
||||
const message = `Clipboard contents: ${clipboard.readText()}`
|
||||
document.getElementById('paste-from').innerHTML = message
|
||||
})
|
||||
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>App Information</h1>
|
||||
<div>
|
||||
<div>
|
||||
<button id="app-info">View Demo</button>
|
||||
<span id="got-app-info"></span>
|
||||
</div>
|
||||
<p>The main process <code>app</code> module can be used to get the path at which your app is located on the user's computer.</p>
|
||||
<p>In this example, to get that information from the renderer process, we use the <code>ipc</code> module to send a message to the main process requesting the app's path.</p>
|
||||
<p>See the <a id="electron-docs" href="https://electronjs.org/docs/api/app">app module documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> for more.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
const {app, ipcMain} = require('electron')
|
||||
|
||||
ipcMain.on('get-app-path', (event) => {
|
||||
event.sender.send('got-app-path', app.getAppPath())
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
const {ipcRenderer} = require('electron')
|
||||
|
||||
const appInfoBtn = document.getElementById('app-info')
|
||||
const electron_doc_link = document.querySelectorAll('a[href]')
|
||||
|
||||
appInfoBtn.addEventListener('click', () => {
|
||||
ipcRenderer.send('get-app-path')
|
||||
})
|
||||
|
||||
ipcRenderer.on('got-app-path', (event, path) => {
|
||||
const message = `This app is located at: ${path}`
|
||||
document.getElementById('got-app-info').innerHTML = message
|
||||
})
|
||||
|
||||
electron_doc_link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Get version information</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="version-info">View Demo</button>
|
||||
<span id="got-version-info"></span>
|
||||
</div>
|
||||
<p>The <code>process</code> module is built into Node.js (therefore you can use this in both the main and renderer processes) and in Electron apps this object has a few more useful properties on it.</p>
|
||||
<p>The example below gets the version of Electron in use by the app.</p>
|
||||
<p>See the <a href="http://electron.atom.io/docs/api/process">process documentation <span>(opens in new window)</span></a> for more.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
title: 'Get version information',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
const versionInfoBtn = document.getElementById('version-info')
|
||||
|
||||
const electronVersion = process.versions.electron
|
||||
|
||||
versionInfoBtn.addEventListener('click', () => {
|
||||
const message = `This app is using Electron version: ${electronVersion}`
|
||||
document.getElementById('got-version-info').innerHTML = message
|
||||
})
|
||||
BIN
docs/images/performance-cpu-prof.png
Normal file
BIN
docs/images/performance-cpu-prof.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 868 KiB |
BIN
docs/images/performance-heap-prof.png
Normal file
BIN
docs/images/performance-heap-prof.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -13,4 +13,5 @@
|
||||
| 5.0.0 | 2019-01-22 | 2019-04-24 | M73 | v12 |
|
||||
| 6.0.0 | 2019-05-01 | 2019-07-30 | M76 | v12 |
|
||||
| 7.0.0 | 2019-08-01 | 2019-10-22 | M78 | v12 |
|
||||
| 8.0.0 | TBD | TBD | TBD | TBD |
|
||||
| 8.0.0 | 2019-10-24 | 2020-02-04 | M80 | v12 |
|
||||
| 9.0.0 | TBD | TBD | TBD | TBD |
|
||||
|
||||
431
docs/tutorial/performance.md
Normal file
431
docs/tutorial/performance.md
Normal file
@@ -0,0 +1,431 @@
|
||||
# Performance
|
||||
|
||||
Developers frequently ask about strategies to optimize the performance of
|
||||
Electron applications. Software engineers, consumers, and framework developers
|
||||
do not always agree on one single definition of what "performance" means. This
|
||||
document outlines some of the Electron maintainers' favorite ways to reduce the
|
||||
amount of memory, CPU, and disk resources being used while ensuring that your
|
||||
app is responsive to user input and completes operations as quickly as
|
||||
possible. Furthermore, we want all performance strategies to maintain a high
|
||||
standard for your app's security.
|
||||
|
||||
Wisdom and information about how to build performant websites with JavaScript
|
||||
generally applies to Electron apps, too. To a certain extent, resources
|
||||
discussing how to build performant Node.js applications also apply, but be
|
||||
careful to understand that the term "performance" means different things for
|
||||
a Node.js backend than it does for an application running on a client.
|
||||
|
||||
This list is provided for your convenience – and is, much like our
|
||||
[security checklist][security] – not meant to exhaustive. It is probably possible
|
||||
to build a slow Electron app that follows all the steps outlined below. Electron
|
||||
is a powerful development platform that enables you, the developer, to do more
|
||||
or less whatever you want. All that freedom means that performance is largely
|
||||
your responsibility.
|
||||
|
||||
## Measure, Measure, Measure
|
||||
|
||||
The list below contains a number of steps that are fairly straightforward and
|
||||
easy to implement. However, building the most performant version of your app
|
||||
will require you to go beyond a number of steps. Instead, you will have to
|
||||
closely examine all the code running in your app by carefully profiling and
|
||||
measuring. Where are the bottlenecks? When the user clicks a button, what
|
||||
operations take up the brunt of the time? While the app is simply idling, which
|
||||
objects take up the most memory?
|
||||
|
||||
Time and time again, we have seen that the most successful strategy for building
|
||||
a performant Electron app is to profile the running code, find the most
|
||||
resource-hungry piece of it, and to optimize it. Repeating this seemingly
|
||||
laborious process over and over again will dramatically increase your app's
|
||||
performance. Experience from working with major apps like Visual Studio Code or
|
||||
Slack has shown that this practice is by far the most reliable strategy to
|
||||
improve performance.
|
||||
|
||||
To learn more about how to profile your app's code, familiarize yourself with
|
||||
the Chrome Developer Tools. For advanced analysis looking at multiple processes
|
||||
at once, consider the [Chrome Tracing] tool.
|
||||
|
||||
### Recommended Reading
|
||||
|
||||
* [Get Started With Analyzing Runtime Performance][chrome-devtools-tutorial]
|
||||
* [Talk: "Visual Studio Code - The First Second"][vscode-first-second]
|
||||
|
||||
## Checklist
|
||||
|
||||
Chances are that your app could be a little leaner, faster, and generally less
|
||||
resource-hungry if you attempt these steps.
|
||||
|
||||
1. [Carelessly including modules](#1-carelessly-including-modules)
|
||||
2. [Loading and running code too soon](#2-loading-and-running-code-too-soon)
|
||||
3. [Blocking the main process](#3-blocking-the-main-process)
|
||||
4. [Blocking the renderer process](#4-blocking-the-renderer-process)
|
||||
5. [Unnecessary polyfills](#5-unnecessary-polyfills)
|
||||
6. [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests)
|
||||
7. [Bundle your code](#7-bundle-your-code)
|
||||
|
||||
## 1) Carelessly including modules
|
||||
|
||||
Before adding a Node.js module to your application, examine said module. How
|
||||
many dependencies does that module include? What kind of resources does
|
||||
it need to simply be called in a `require()` statement? You might find
|
||||
that the module with the most downloads on the NPM package registry or the most stars on GitHub
|
||||
is not in fact the leanest or smallest one available.
|
||||
|
||||
### Why?
|
||||
|
||||
The reasoning behind this recommendation is best illustrated with a real-world
|
||||
example. During the early days of Electron, reliable detection of network
|
||||
connectivity was a problem, resulting many apps to use a module that exposed a
|
||||
simple `isOnline()` method.
|
||||
|
||||
That module detected your network connectivity by attempting to reach out to a
|
||||
number of well-known endpoints. For the list of those endpoints, it depended on
|
||||
a different module, which also contained a list of well-known ports. This
|
||||
dependency itself relied on a module containing information about ports, which
|
||||
came in the form of a JSON file with more than 100,000 lines of content.
|
||||
Whenever the module was loaded (usually in a `require('module')` statement),
|
||||
it would load all its dependencies and eventually read and parse this JSON
|
||||
file. Parsing many thousands lines of JSON is a very expensive operation. On
|
||||
a slow machine it can take up whole seconds of time.
|
||||
|
||||
In many server contexts, startup time is virtually irrelevant. A Node.js server
|
||||
that requires information about all ports is likely actually "more performant"
|
||||
if it loads all required information into memory whenever the server boots at
|
||||
the benefit of serving requests faster. The module discussed in this example is
|
||||
not a "bad" module. Electron apps, however, should not be loading, parsing, and
|
||||
storing in memory information that it does not actually need.
|
||||
|
||||
In short, a seemingly excellent module written primarily for Node.js servers
|
||||
running Linux might be bad news for your app's performance. In this particular
|
||||
example, the correct solution was to use no module at all, and to instead use
|
||||
connectivity checks included in later versions of Chromium.
|
||||
|
||||
### How?
|
||||
|
||||
When considering a module, we recommend that you check:
|
||||
|
||||
1. the size of dependencies included
|
||||
2) the resources required to load (`require()`) it
|
||||
3. the resources required to perform the action you're interested in
|
||||
|
||||
Generating a CPU profile and a heap memory profile for loading a module can be done
|
||||
with a single command on the command line. In the example below, we're looking at
|
||||
the popular module `request`.
|
||||
|
||||
```sh
|
||||
node --cpu-prof --heap-prof -e "require('request')"
|
||||
```
|
||||
|
||||
Executing this command results in a `.cpuprofile` file and a `.heapprofile`
|
||||
file in the directory you executed it in. Both files can be analyzed using
|
||||
the Chrome Developer Tools, using the `Performance` and `Memory` tabs
|
||||
respectively.
|
||||
|
||||
![performance-cpu-prof]
|
||||
|
||||
![performance-heap-prof]
|
||||
|
||||
In this example, on the author's machine, we saw that loading `request` took
|
||||
almost half a second, whereas `node-fetch` took dramatically less memory
|
||||
and less than 50ms.
|
||||
|
||||
## 2) Loading and running code too soon
|
||||
|
||||
If you have expensive setup operations, consider deferring those. Inspect all
|
||||
the work being executed right after the application starts. Instead of firing
|
||||
off all operations right away, consider staggering them in a sequence more
|
||||
closely aligned with the user's journey.
|
||||
|
||||
In traditional Node.js development, we're used to putting all our `require()`
|
||||
statements at the top. If you're currently writing your Electron application
|
||||
using the same strategy _and_ are using sizable modules that you do not
|
||||
immediately need, apply the same strategy and defer loading to a more
|
||||
opportune time.
|
||||
|
||||
### Why?
|
||||
|
||||
Loading modules is a surprisingly expensive operation, especially on Windows.
|
||||
When your app starts, it should not make users wait for operations that are
|
||||
currently not necessary.
|
||||
|
||||
This might seem obvious, but many applications tend to do a large amount of
|
||||
work immediately after the app has launched - like checking for updates,
|
||||
downloading content used in a later flow, or performing heavy disk I/O
|
||||
operations.
|
||||
|
||||
Let's consider Visual Studio Code as an example. When you open a file, it will
|
||||
immediately display the file to you without any code highlighting, prioritizing
|
||||
your ability to interact with the text. Once it has done that work, it will
|
||||
move on to code highlighting.
|
||||
|
||||
### How?
|
||||
|
||||
Let's consider an example and assume that your application is parsing files
|
||||
in the fictitious `.foo` format. In order to do that, it relies on the
|
||||
equally fictitious `foo-parser` module. In traditional Node.js development,
|
||||
you might write code that eagerly loads dependencies:
|
||||
|
||||
```js
|
||||
const fs = require('fs')
|
||||
const fooParser = require('foo-parser')
|
||||
|
||||
class Parser {
|
||||
constructor () {
|
||||
this.files = fs.readdirSync('.')
|
||||
}
|
||||
|
||||
getParsedFiles () {
|
||||
return fooParser.parse(this.files)
|
||||
}
|
||||
}
|
||||
|
||||
const parser = new Parser()
|
||||
|
||||
module.exports = { parser }
|
||||
```
|
||||
|
||||
In the above example, we're doing a lot of work that's being executed as soon
|
||||
as the file is loaded. Do we need to get parsed files right away? Could we
|
||||
do this work a little later, when `getParsedFiles()` is actually called?
|
||||
|
||||
```js
|
||||
// "fs" is likely already being loaded, so the `require()` call is cheap
|
||||
const fs = require('fs')
|
||||
|
||||
class Parser {
|
||||
async getFiles () {
|
||||
// Touch the disk as soon as `getFiles` is called, not sooner.
|
||||
// Also, ensure that we're not blocking other operations by using
|
||||
// the asynchronous version.
|
||||
this.files = this.files || await fs.readdir('.')
|
||||
|
||||
return this.files
|
||||
}
|
||||
|
||||
async getParsedFiles () {
|
||||
// Our fictitious foo-parser is a big and expensive module to load, so
|
||||
// defer that work until we actually need to parse files.
|
||||
// Since `require()` comes with a module cache, the `require()` call
|
||||
// will only be expensive once - subsequent calls of `getParsedFiles()`
|
||||
// will be faster.
|
||||
const fooParser = require('foo-parser')
|
||||
const files = await this.getFiles()
|
||||
|
||||
return fooParser.parse(files)
|
||||
}
|
||||
}
|
||||
|
||||
// This operation is now a lot cheaper than in our previous example
|
||||
const parser = new Parser()
|
||||
|
||||
module.exports = { parser }
|
||||
```
|
||||
|
||||
In short, allocate resources "just in time" rather than allocating them all
|
||||
when your app starts.
|
||||
|
||||
## 3) Blocking the main process
|
||||
|
||||
Electron's main process (sometimes called "browser process") is special: It is
|
||||
the parent process to all your app's other processes and the primary process
|
||||
the operating system interacts with. It handles windows, interactions, and the
|
||||
communication between various components inside your app. It also houses the
|
||||
UI thread.
|
||||
|
||||
Under no circumstances should you block this process and the UI thread with
|
||||
long-running operations. Blocking the UI thread means that your entire app
|
||||
will freeze until the main process is ready to continue processing.
|
||||
|
||||
### Why?
|
||||
|
||||
The main process and its UI thread are essentially the control tower for major
|
||||
operations inside your app. When the operating system tells your app about a
|
||||
mouse click, it'll go through the main process before it reaches your window.
|
||||
If your window is rendering a buttery-smooth animation, it'll need to talk to
|
||||
the GPU process about that – once again going through the main process.
|
||||
|
||||
Electron and Chromium are careful to put heavy disk I/O and CPU-bound operations
|
||||
onto new threads to avoid blocking the UI thread. You should do the same.
|
||||
|
||||
### How?
|
||||
|
||||
Electron's powerful multi-process architecture stands ready to assist you with
|
||||
your long-running tasks, but also includes a small number of performance traps.
|
||||
|
||||
1) For long running CPU-heavy tasks, make use of
|
||||
[worker threads][worker-threads], consider moving them to the BrowserWindow, or
|
||||
(as a last resort) spawn a dedicated process.
|
||||
|
||||
2) Avoid using the synchronous IPC and the `remote` module as much as possible.
|
||||
While there are legitimate use cases, it is far too easy to unknowingly block
|
||||
the UI thread using the `remote` module.
|
||||
|
||||
3) Avoid using blocking I/O operations in the main process. In short, whenever
|
||||
core Node.js modules (like `fs` or `child_process`) offer a synchronous or an
|
||||
asynchronous version, you should prefer the asynchronous and non-blocking
|
||||
variant.
|
||||
|
||||
|
||||
## 4) Blocking the renderer process
|
||||
|
||||
Since Electron ships with a current version of Chrome, you can make use of the
|
||||
latest and greatest features the Web Platform offers to defer or offload heavy
|
||||
operations in a way that keeps your app smooth and responsive.
|
||||
|
||||
### Why?
|
||||
|
||||
Your app probably has a lot of JavaScript to run in the renderer process. The
|
||||
trick is to execute operations as quickly as possible without taking away
|
||||
resources needed to keep scrolling smooth, respond to user input, or animations
|
||||
at 60fps.
|
||||
|
||||
Orchestrating the flow of operations in your renderer's code is
|
||||
particularly useful if users complain about your app sometimes "stuttering".
|
||||
|
||||
### How?
|
||||
|
||||
Generally speaking, all advice for building performant web apps for modern
|
||||
browsers apply to Electron's renderers, too. The two primary tools at your
|
||||
disposal are currently `requestIdleCallback()` for small operations and
|
||||
`Web Workers` for long-running operations.
|
||||
|
||||
*`requestIdleCallback()`* allows developers to queue up a function to be
|
||||
executed as soon as the process is entering an idle period. It enables you to
|
||||
perform low-priority or background work without impacting the user experience.
|
||||
For more information about how to use it,
|
||||
[check out its documentation on MDN][request-idle-callback].
|
||||
|
||||
*Web Workers* are a powerful tool to run code on a separate thread. There are
|
||||
some caveats to consider – consult Electron's
|
||||
[multithreading documentation][multithreading] and the
|
||||
[MDN documentation for Web Workers][web-workers]. They're an ideal solution
|
||||
for any operation that requires a lot of CPU power for an extended period of
|
||||
time.
|
||||
|
||||
|
||||
## 5) Unnecessary polyfills
|
||||
|
||||
One of Electron's great benefits is that you know exactly which engine will
|
||||
parse your JavaScript, HTML, and CSS. If you're re-purposing code that was
|
||||
written for the web at large, make sure to not polyfill features included in
|
||||
Electron.
|
||||
|
||||
### Why?
|
||||
|
||||
When building a web application for today's Internet, the oldest environments
|
||||
dictate what features you can and cannot use. Even though Electron supports
|
||||
well-performing CSS filters and animations, an older browser might not. Where
|
||||
you could use WebGL, your developers may have chosen a more resource-hungry
|
||||
solution to support older phones.
|
||||
|
||||
When it comes to JavaScript, you may have included toolkit libraries like
|
||||
jQuery for DOM selectors or polyfills like the `regenerator-runtime` to support
|
||||
`async/await`.
|
||||
|
||||
It is rare for a JavaScript-based polyfill to be faster than the equivalent
|
||||
native feature in Electron. Do not slow down your Electron app by shipping your
|
||||
own version of standard web platform features.
|
||||
|
||||
### How?
|
||||
|
||||
Operate under the assumption that polyfills in current versions of Electron
|
||||
are unnecessary. If you have doubts, check [caniuse.com][https://caniuse.com/]
|
||||
and check if the [version of Chromium used in your Electron version](../api/process.md#processversionschrome-readonly)
|
||||
supports the feature you desire.
|
||||
|
||||
In addition, carefully examine the libraries you use. Are they really necessary?
|
||||
`jQuery`, for example, was such a success that many of its features are now part
|
||||
of the [standard JavaScript feature set available][jquery-need].
|
||||
|
||||
If you're using a transpiler/compiler like TypeScript, examine its configuration
|
||||
and ensure that you're targeting the latest ECMAScript version supported by
|
||||
Electron.
|
||||
|
||||
|
||||
## 6) Unnecessary or blocking network requests
|
||||
|
||||
Avoid fetching rarely changing resources from the internet if they could easily
|
||||
be bundled with your application.
|
||||
|
||||
### Why?
|
||||
|
||||
Many users of Electron start with an entirely web-based app that they're
|
||||
turning into a desktop application. As web developers, we are used to loading
|
||||
resources from a variety of content delivery networks. Now that you are
|
||||
shipping a proper desktop application, attempt to "cut the cord" where possible
|
||||
- and avoid letting your users wait for resources that never change and could
|
||||
easily be included in your app.
|
||||
|
||||
A typical example is Google Fonts. Many developers make use of Google's
|
||||
impressive collection of free fonts, which comes with a content delivery
|
||||
network. The pitch is straightforward: Include a few lines of CSS and Google
|
||||
will take care of the rest.
|
||||
|
||||
When building an Electron app, your users are better served if you download
|
||||
the fonts and include them in your app's bundle.
|
||||
|
||||
### How?
|
||||
|
||||
In an ideal world, your application wouldn't need the network to operate at
|
||||
all. To get there, you must understand what resources your app is downloading
|
||||
\- and how large those resources are.
|
||||
|
||||
To do so, open up the developer tools. Navigate to the `Network` tab and check
|
||||
the `Disable cache` option. Then, reload your renderer. Unless your app
|
||||
prohibits such reloads, you can usually trigger a reload by hitting `Cmd + R`
|
||||
or `Ctrl + R` with the developer tools in focus.
|
||||
|
||||
The tools will now meticulously record all network requests. In a first pass,
|
||||
take stock of all the resources being downloaded, focusing on the larger files
|
||||
first. Are any of them images, fonts, or media files that don't change and
|
||||
could be included with your bundle? If so, include them.
|
||||
|
||||
As a next step, enable `Network Throttling`. Find the drop-down that currently
|
||||
reads `Online` and select a slower speed such as `Fast 3G`. Reload your
|
||||
renderer and see if there are any resources that your app is unnecessarily
|
||||
waiting for. In many cases, an app will wait for a network request to complete
|
||||
despite not actually needing the involved resource.
|
||||
|
||||
As a tip, loading resources from the Internet that you might want to change
|
||||
without shipping an application update is a powerful strategy. For advanced
|
||||
control over how resources are being loaded, consider investing in
|
||||
[Service Workers][service-workers].
|
||||
|
||||
## 7) Bundle your code
|
||||
|
||||
As already pointed out in
|
||||
"[Loading and running code too soon](#2-loading-and-running-code-too-soon)",
|
||||
calling `require()` is an expensive operation. If you are able to do so,
|
||||
bundle your application's code into a single file.
|
||||
|
||||
### Why?
|
||||
|
||||
Modern JavaScript development usually involves many files and modules. While
|
||||
that's perfectly fine for developing with Electron, we heavily recommend that
|
||||
you bundle all your code into one single file to ensure that the overhead
|
||||
included in calling `require()` is only paid once when your application loads.
|
||||
|
||||
### How?
|
||||
|
||||
There are numerous JavaScript bundlers out there and we know better than to
|
||||
anger the community by recommending one tool over another. We do however
|
||||
recommend that you use a bundler that is able to handle Electron's unique
|
||||
environment that needs to handle both Node.js and browser environments.
|
||||
|
||||
As of writing this article, the popular choices include [Webpack][webpack],
|
||||
[Parcel][parcel], and [rollup.js][rollup].
|
||||
|
||||
[security]: ./security.md
|
||||
[performance-cpu-prof]: ../images/performance-cpu-prof.png
|
||||
[performance-heap-prof]: ../images/performance-heap-prof.png
|
||||
[chrome-devtools-tutorial]: https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/
|
||||
[worker-threads]: https://nodejs.org/api/worker_threads.html
|
||||
[web-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
|
||||
[request-idle-callback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
||||
[multithreading]: ./multithreading.md
|
||||
[caniuse]: https://caniuse.com/
|
||||
[jquery-need]: http://youmightnotneedjquery.com/
|
||||
[service-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
|
||||
[webpack]: https://webpack.js.org/
|
||||
[parcel]: https://parceljs.org/
|
||||
[rollup]: https://rollupjs.org/
|
||||
[vscode-first-second]: https://www.youtube.com/watch?v=r0OeHRUCCb4
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user