mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
428 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69fe268d4d | ||
|
|
55738ddac3 | ||
|
|
7916733f5f | ||
|
|
6c37483f2c | ||
|
|
bb6a7d443b | ||
|
|
a3c0d0db3f | ||
|
|
0ffac056c3 | ||
|
|
ae70118ffb | ||
|
|
8c384c13b3 | ||
|
|
91fa68bd32 | ||
|
|
6b67839770 | ||
|
|
fc98a28d67 | ||
|
|
f1c1b22fd4 | ||
|
|
4439b13027 | ||
|
|
1bd2e574a7 | ||
|
|
468c4af661 | ||
|
|
83576caa40 | ||
|
|
36edecf606 | ||
|
|
3f3c0c45f2 | ||
|
|
805674fa8a | ||
|
|
e7ba28f8f9 | ||
|
|
8e0d1af44e | ||
|
|
dde1b500cc | ||
|
|
fefe45e897 | ||
|
|
bd43e652f6 | ||
|
|
bb01e52fb8 | ||
|
|
985b56a68d | ||
|
|
47a2df48b9 | ||
|
|
b5646778e9 | ||
|
|
772bbe775c | ||
|
|
be3b6c1e2b | ||
|
|
cf49565a31 | ||
|
|
415301c477 | ||
|
|
b4742f9f91 | ||
|
|
5868f9ac5e | ||
|
|
6e675c831e | ||
|
|
66432ed9fc | ||
|
|
0ed48378c2 | ||
|
|
42b4744740 | ||
|
|
04e85b4eda | ||
|
|
e42169b7e4 | ||
|
|
67b2739f44 | ||
|
|
a5e78c4a2f | ||
|
|
fe62d46a4a | ||
|
|
beebf99946 | ||
|
|
df0f25dfd9 | ||
|
|
e0e5549ce8 | ||
|
|
1d1ce9c63f | ||
|
|
aa799e2239 | ||
|
|
3ac4532efb | ||
|
|
dc3ce65dd5 | ||
|
|
9faee48070 | ||
|
|
69b8ebc7f4 | ||
|
|
2e8177c839 | ||
|
|
7a3d7b5c95 | ||
|
|
84b863d399 | ||
|
|
149f314e38 | ||
|
|
0c450f8c89 | ||
|
|
3a99edbbdb | ||
|
|
4d371b067b | ||
|
|
84d7f7f071 | ||
|
|
4cefd54143 | ||
|
|
c7fde250bb | ||
|
|
1c7de7e5ee | ||
|
|
4ad7e5f648 | ||
|
|
3bb93c472d | ||
|
|
0412b07612 | ||
|
|
f818ec3295 | ||
|
|
437a0a2c6e | ||
|
|
2d4c150a99 | ||
|
|
31aa0dacee | ||
|
|
39bdf64347 | ||
|
|
1f3161b2ce | ||
|
|
e3f64e7b1f | ||
|
|
6fb688889d | ||
|
|
92371e7231 | ||
|
|
417f2fd3f1 | ||
|
|
e033c294fc | ||
|
|
f138d464b2 | ||
|
|
dfd80db886 | ||
|
|
74d019463a | ||
|
|
a56fbc563a | ||
|
|
00fa1e8404 | ||
|
|
34be316c40 | ||
|
|
cde60dff6a | ||
|
|
7970dc2c0e | ||
|
|
ea2d902de8 | ||
|
|
c697368d14 | ||
|
|
174696e604 | ||
|
|
9b01cedffd | ||
|
|
7e60fc4922 | ||
|
|
e90f6e2e38 | ||
|
|
5c23f17d16 | ||
|
|
a3c129d5d0 | ||
|
|
efa09d8da2 | ||
|
|
d219fcc8b6 | ||
|
|
762ce69066 | ||
|
|
57ce0704e3 | ||
|
|
1f20e4c284 | ||
|
|
b8f05c3648 | ||
|
|
990fb72e35 | ||
|
|
c18e5bae5f | ||
|
|
b71a50e2d3 | ||
|
|
38ce1d68d6 | ||
|
|
eb8dcb52aa | ||
|
|
3d07626ddf | ||
|
|
9caade81c4 | ||
|
|
8c89a771b2 | ||
|
|
b5c8af02f5 | ||
|
|
db7acc9fc0 | ||
|
|
eccc08e653 | ||
|
|
c50975f664 | ||
|
|
20a551ba94 | ||
|
|
8926e4e93d | ||
|
|
bca09956f3 | ||
|
|
5fa67613d9 | ||
|
|
974dbc167c | ||
|
|
1fae9d5ac0 | ||
|
|
6978a722d4 | ||
|
|
b68143a9be | ||
|
|
910cafd785 | ||
|
|
b1223100f5 | ||
|
|
4a20042b5e | ||
|
|
0e5725b4a1 | ||
|
|
95aa5405e7 | ||
|
|
3c75dd1684 | ||
|
|
81fb39f154 | ||
|
|
2448993d1c | ||
|
|
deb0f6ecc0 | ||
|
|
476c925660 | ||
|
|
2b355f8ab3 | ||
|
|
dadb872c3d | ||
|
|
5305596f02 | ||
|
|
9281a34f8b | ||
|
|
35cf190ab9 | ||
|
|
6c7e9b861d | ||
|
|
37df7c5170 | ||
|
|
2fdfa0b662 | ||
|
|
0034868d6c | ||
|
|
1b626b805b | ||
|
|
a597775259 | ||
|
|
71d9362819 | ||
|
|
32eb8c9336 | ||
|
|
1c0936897e | ||
|
|
7e401f4828 | ||
|
|
679f4732bd | ||
|
|
fcab6c6705 | ||
|
|
32d4d01b3c | ||
|
|
cc01109491 | ||
|
|
3505270a2a | ||
|
|
fc5a7dd023 | ||
|
|
1cb343e079 | ||
|
|
11c6f1f684 | ||
|
|
c5e5b13003 | ||
|
|
f44be42478 | ||
|
|
70fe0d9a07 | ||
|
|
cea70deb8a | ||
|
|
31b0daaa91 | ||
|
|
4249800e9c | ||
|
|
2ba9860a5a | ||
|
|
7c65388483 | ||
|
|
96ae5c107e | ||
|
|
6dc122fe5a | ||
|
|
160d48f0f2 | ||
|
|
605d6c9f41 | ||
|
|
b606a98b77 | ||
|
|
4779d565d2 | ||
|
|
a7bb6de466 | ||
|
|
8f9f1cc2fe | ||
|
|
90040360c3 | ||
|
|
b452c7fab4 | ||
|
|
b1cddb0dc8 | ||
|
|
f755b2579f | ||
|
|
7bca7f2c0f | ||
|
|
ed328d8987 | ||
|
|
1a7c546c3a | ||
|
|
748c4bfbcb | ||
|
|
9ec8b1d21c | ||
|
|
32138c6405 | ||
|
|
bc458c97dc | ||
|
|
06ac9d989f | ||
|
|
e8ef5f4ae4 | ||
|
|
6c136d5582 | ||
|
|
7ddb80fbb1 | ||
|
|
3a2306d9e4 | ||
|
|
38d83cfefc | ||
|
|
d61f6309a2 | ||
|
|
126897bd6b | ||
|
|
d3f0970964 | ||
|
|
012774a0eb | ||
|
|
a546545a53 | ||
|
|
742bd735e6 | ||
|
|
a54725b6b5 | ||
|
|
be61126d30 | ||
|
|
7fbc867eaf | ||
|
|
0c4134fefe | ||
|
|
b19a3a65a4 | ||
|
|
563a0622d9 | ||
|
|
6a5bd8dc28 | ||
|
|
b78c30a6d8 | ||
|
|
9ecd8910de | ||
|
|
da87a471fe | ||
|
|
6544df1992 | ||
|
|
c9f7ebf821 | ||
|
|
d3a0a255b1 | ||
|
|
2d3819e2a8 | ||
|
|
bd384b5301 | ||
|
|
1e13475e89 | ||
|
|
8ce23c425d | ||
|
|
c891980848 | ||
|
|
15f4e8ef88 | ||
|
|
b6b4971da0 | ||
|
|
14918767d8 | ||
|
|
f7a16f33a8 | ||
|
|
662fa261da | ||
|
|
8ceb20c75c | ||
|
|
e0e824f069 | ||
|
|
ab0a13eed6 | ||
|
|
7910ace135 | ||
|
|
eb8eb6fae2 | ||
|
|
c9bea8b712 | ||
|
|
71ee04a3e2 | ||
|
|
f7f55d096a | ||
|
|
ad6155f08e | ||
|
|
37caca046f | ||
|
|
ea6a8eea22 | ||
|
|
d314b4e18b | ||
|
|
1bc8549ce5 | ||
|
|
8e6c8cc384 | ||
|
|
5c13da7c40 | ||
|
|
45a5827e09 | ||
|
|
e809a5a043 | ||
|
|
4f2e369bdc | ||
|
|
eef9787c48 | ||
|
|
81fe8993ec | ||
|
|
55b5c85f92 | ||
|
|
a0e6ca8dab | ||
|
|
e30b25269d | ||
|
|
319d77a4ee | ||
|
|
e22767b1e7 | ||
|
|
5008e3ecab | ||
|
|
d69776bfb0 | ||
|
|
b56e4287d3 | ||
|
|
1254a684dd | ||
|
|
99a81e4ef0 | ||
|
|
36d5706ea9 | ||
|
|
2ae9588e1c | ||
|
|
320415edf9 | ||
|
|
dd59115ac6 | ||
|
|
80a378a9de | ||
|
|
8d2530437e | ||
|
|
dde3a0f1ba | ||
|
|
ce079c02bc | ||
|
|
f23df11352 | ||
|
|
abb86a7ea1 | ||
|
|
8f96b2192a | ||
|
|
977a7eb1d0 | ||
|
|
504588c861 | ||
|
|
5bc09dda1a | ||
|
|
6b72837203 | ||
|
|
9f2bef9a65 | ||
|
|
d0273d83ef | ||
|
|
248dc89217 | ||
|
|
a75a867997 | ||
|
|
0d82286a23 | ||
|
|
4553ec2a2a | ||
|
|
62f64f9f03 | ||
|
|
f35adf303b | ||
|
|
f236478b2b | ||
|
|
b69de7c412 | ||
|
|
8c3bbded4f | ||
|
|
d9019fc54b | ||
|
|
7c624c17fa | ||
|
|
3e336d2923 | ||
|
|
95b727ccdb | ||
|
|
382b19f816 | ||
|
|
a1b3b506d7 | ||
|
|
9b25d6b91b | ||
|
|
b113c5d583 | ||
|
|
a36e44c973 | ||
|
|
8ae741102d | ||
|
|
666e8f9647 | ||
|
|
fdceacce44 | ||
|
|
0ad8ffa3d2 | ||
|
|
031283003c | ||
|
|
63fdcba0c8 | ||
|
|
251e567eff | ||
|
|
6bfef67aae | ||
|
|
db27b9f433 | ||
|
|
be32740991 | ||
|
|
b8a21dbcd7 | ||
|
|
58f3c0ee37 | ||
|
|
3f42040a1b | ||
|
|
e9d5c3517c | ||
|
|
91f62ae73f | ||
|
|
4c8583c365 | ||
|
|
0b4ef2b1e5 | ||
|
|
4ba5723aed | ||
|
|
652d603e69 | ||
|
|
f40bd2da23 | ||
|
|
e31f101712 | ||
|
|
4415b7638a | ||
|
|
f7c6545eab | ||
|
|
ef657bdc9d | ||
|
|
6012172187 | ||
|
|
a75b892e90 | ||
|
|
7c4f4c537e | ||
|
|
4ca6fb3c4e | ||
|
|
3fa03977c7 | ||
|
|
75919e28b8 | ||
|
|
ac58607605 | ||
|
|
835ace243c | ||
|
|
8f3ef39f1e | ||
|
|
48e13fde80 | ||
|
|
bb6648b79e | ||
|
|
2b9dae4b06 | ||
|
|
2e1f803f37 | ||
|
|
4c6092e151 | ||
|
|
b72f81ab5b | ||
|
|
97b19a7946 | ||
|
|
b27e4cae21 | ||
|
|
1e106c8aa4 | ||
|
|
fda8ea9277 | ||
|
|
6a6908c4c8 | ||
|
|
42e7cd9b3f | ||
|
|
8cf03f5661 | ||
|
|
916861036d | ||
|
|
52481bc923 | ||
|
|
5023b49713 | ||
|
|
2ac9ff1f20 | ||
|
|
46d3c4e04f | ||
|
|
0f77fec9d9 | ||
|
|
94f701edb8 | ||
|
|
caa5989eed | ||
|
|
e0c348a2f8 | ||
|
|
eb613ef3d4 | ||
|
|
7ed3c7a359 | ||
|
|
64b39dce14 | ||
|
|
5261e08d0c | ||
|
|
9611b641c9 | ||
|
|
095f9067a7 | ||
|
|
b8f970c1c7 | ||
|
|
48d0b09ad9 | ||
|
|
c5fc5efc00 | ||
|
|
6dc46e5bcf | ||
|
|
061e2e5e73 | ||
|
|
bf1cc1aeb2 | ||
|
|
e480cb7103 | ||
|
|
3b69a542fb | ||
|
|
82456c6915 | ||
|
|
e58f0411d9 | ||
|
|
e61728beb9 | ||
|
|
feaf18e371 | ||
|
|
8ee58e18fd | ||
|
|
f33bf2a271 | ||
|
|
77bd80dfb2 | ||
|
|
0d3aee26b9 | ||
|
|
efde7a140b | ||
|
|
4e85bb921b | ||
|
|
512e56baf7 | ||
|
|
c8f715f9a1 | ||
|
|
829fb4f586 | ||
|
|
17ccb3c6ec | ||
|
|
6bd9ee6988 | ||
|
|
76c825d619 | ||
|
|
692876c737 | ||
|
|
c3f06ef037 | ||
|
|
9b20b3a722 | ||
|
|
8f2917db01 | ||
|
|
49df19214e | ||
|
|
5e25d23794 | ||
|
|
8fb0f43030 | ||
|
|
1f390119fe | ||
|
|
87f2a1d572 | ||
|
|
2e03bdb9b6 | ||
|
|
3a5ae28c95 | ||
|
|
ed7b5c44a2 | ||
|
|
edf887bdc5 | ||
|
|
1ff7f098f1 | ||
|
|
fb100e8f49 | ||
|
|
3384908da1 | ||
|
|
73bd9a3155 | ||
|
|
e27905c765 | ||
|
|
a3e3efe4c4 | ||
|
|
46c8b9c728 | ||
|
|
6395898a79 | ||
|
|
32c60b29bb | ||
|
|
868676aa5c | ||
|
|
85cf56d80b | ||
|
|
a92fd2aa05 | ||
|
|
ee966ad6ec | ||
|
|
872d1fe05a | ||
|
|
63f94f2359 | ||
|
|
67a6fbf265 | ||
|
|
f97d68c4bf | ||
|
|
969665eaa2 | ||
|
|
0d8dd61257 | ||
|
|
cf10c19c33 | ||
|
|
fcfbcbc7e1 | ||
|
|
e34cc6f48c | ||
|
|
ee87438d28 | ||
|
|
0de1012280 | ||
|
|
0a5e634736 | ||
|
|
73b7aac6a4 | ||
|
|
ea848bc1c5 | ||
|
|
a44e76fb70 | ||
|
|
3138acc377 | ||
|
|
beed240454 | ||
|
|
fdab0799fe | ||
|
|
17ea7f42fc | ||
|
|
d0cf9c4b08 | ||
|
|
fe7c535a3f | ||
|
|
8eee4f2df1 | ||
|
|
4d6f230d21 | ||
|
|
889859df5b | ||
|
|
cb03c6516b | ||
|
|
2e8114aea3 | ||
|
|
a38b711fb1 | ||
|
|
3972073116 | ||
|
|
8c986ff194 | ||
|
|
71944f2c3b | ||
|
|
8b3e498436 | ||
|
|
cf80994729 | ||
|
|
01f1522cbd | ||
|
|
a37f572388 | ||
|
|
3180312595 | ||
|
|
ff60fe25c1 | ||
|
|
12ed401ca2 |
@@ -51,7 +51,7 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- path-filtering/set-parameters:
|
||||
base-revision: origin/24-x-y
|
||||
base-revision: main
|
||||
mapping: |
|
||||
^((?!docs/).)*$ run-build-mac true
|
||||
^((?!docs/).)*$ run-build-linux true
|
||||
@@ -65,6 +65,9 @@ jobs:
|
||||
curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | DESTDIR=$CIRCLECI_BINARY bash
|
||||
node build.js
|
||||
name: Pack config.yml
|
||||
- run:
|
||||
name: Set params
|
||||
command: node .circleci/config/params.js
|
||||
- continuation/continue:
|
||||
configuration_path: .circleci/config-staging/built.yml
|
||||
parameters: /tmp/pipeline-parameters.json
|
||||
|
||||
@@ -35,6 +35,16 @@ parameters:
|
||||
default: all
|
||||
enum: ["all", "osx-x64", "osx-arm64", "mas-x64", "mas-arm64"]
|
||||
|
||||
medium-linux-executor:
|
||||
type: enum
|
||||
default: electronjs/aks-linux-medium
|
||||
enum: ["electronjs/aks-linux-medium", "medium"]
|
||||
|
||||
large-linux-executor:
|
||||
type: enum
|
||||
default: electronjs/aks-linux-large
|
||||
enum: ["electronjs/aks-linux-large", "2xlarge"]
|
||||
|
||||
# Executors
|
||||
executors:
|
||||
linux-docker:
|
||||
@@ -42,7 +52,9 @@ executors:
|
||||
size:
|
||||
description: "Docker executor size"
|
||||
type: enum
|
||||
enum: ["medium", "xlarge", "2xlarge"]
|
||||
# aks-linux-large === 32 core
|
||||
# 2xlarge should not be used directly, use the pipeline param instead
|
||||
enum: ["medium", "electronjs/aks-linux-medium", "xlarge", "electronjs/aks-linux-large", "2xlarge"]
|
||||
docker:
|
||||
- image: ghcr.io/electron/build:e6bebd08a51a0d78ec23e5b3fd7e7c0846412328
|
||||
resource_class: << parameters.size >>
|
||||
@@ -52,9 +64,14 @@ executors:
|
||||
size:
|
||||
description: "macOS executor size"
|
||||
type: enum
|
||||
enum: ["macos.x86.medium.gen2", "large"]
|
||||
enum: ["macos.x86.medium.gen2"]
|
||||
version:
|
||||
description: "xcode version"
|
||||
type: enum
|
||||
enum: ["14.3.0", "14.0.0"]
|
||||
default: 14.3.0
|
||||
macos:
|
||||
xcode: 14.0.0
|
||||
xcode: << parameters.version >>
|
||||
resource_class: << parameters.size >>
|
||||
|
||||
# Electron Runners
|
||||
@@ -108,7 +125,7 @@ env-unittests: &env-unittests
|
||||
TESTS_CONFIG: src/electron/spec/configs/unittests.yml
|
||||
|
||||
env-arm: &env-arm
|
||||
GN_EXTRA_ARGS: 'target_cpu = "arm" clang_use_chrome_plugins = false'
|
||||
GN_EXTRA_ARGS: 'target_cpu = "arm"'
|
||||
MKSNAPSHOT_TOOLCHAIN: //build/toolchain/linux:clang_arm
|
||||
BUILD_NATIVE_MKSNAPSHOT: 1
|
||||
TARGET_ARCH: arm
|
||||
@@ -247,19 +264,19 @@ step-depot-tools-get: &step-depot-tools-get
|
||||
cd depot_tools
|
||||
cat > gclient.diff \<< 'EOF'
|
||||
diff --git a/gclient.py b/gclient.py
|
||||
index 3a9c5c6..f222043 100755
|
||||
index c305c248..e6e0fbdc 100755
|
||||
--- a/gclient.py
|
||||
+++ b/gclient.py
|
||||
@@ -712,7 +712,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
|
||||
|
||||
if dep_type == 'cipd':
|
||||
cipd_root = self.GetCipdRoot()
|
||||
- for package in dep_value.get('packages', []):
|
||||
+ packages = dep_value.get('packages', [])
|
||||
+ for package in (x for x in packages if "infra/3pp/tools/swift-format" not in x.get('package')):
|
||||
deps_to_add.append(
|
||||
CipdDependency(
|
||||
parent=self,
|
||||
@@ -735,7 +735,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
|
||||
|
||||
if dep_type == 'cipd':
|
||||
cipd_root = self.GetCipdRoot()
|
||||
- for package in dep_value.get('packages', []):
|
||||
+ packages = dep_value.get('packages', [])
|
||||
+ for package in (x for x in packages if "infra/3pp/tools/swift-format" not in x.get('package')):
|
||||
deps_to_add.append(
|
||||
CipdDependency(parent=self,
|
||||
name=name,
|
||||
EOF
|
||||
git apply --3way gclient.diff
|
||||
fi
|
||||
@@ -341,13 +358,13 @@ step-setup-goma-for-build: &step-setup-goma-for-build
|
||||
mkdir third_party
|
||||
node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })"
|
||||
export GOMA_FALLBACK_ON_AUTH_FAILURE=true
|
||||
python3 third_party/goma/goma_ctl.py ensure_start
|
||||
if [ ! -z "$RAW_GOMA_AUTH" ] && [ "`python3 third_party/goma/goma_auth.py info`" != "Login as Fermi Planck" ]; then
|
||||
third_party/goma/goma_ctl.py ensure_start
|
||||
if [ ! -z "$RAW_GOMA_AUTH" ] && [ "`third_party/goma/goma_auth.py info`" != "Login as Fermi Planck" ]; then
|
||||
echo "WARNING!!!!!! Goma authentication is incorrect; please update Goma auth token."
|
||||
exit 1
|
||||
fi
|
||||
echo 'export GN_GOMA_FILE='`node -e "console.log(require('./src/utils/goma.js').gnFilePath)"` >> $BASH_ENV
|
||||
echo 'export LOCAL_GOMA_DIR='`node -e "console.log(require('./src/utils/goma.js').dir)"` >> $BASH_ENV
|
||||
echo 'export GOMA_DIR='`node -e "console.log(require('./src/utils/goma.js').dir)"` >> $BASH_ENV
|
||||
echo 'export GOMA_FALLBACK_ON_AUTH_FAILURE=true' >> $BASH_ENV
|
||||
cd ..
|
||||
touch "${TMPDIR:=/tmp}"/.goma-ready
|
||||
@@ -370,14 +387,14 @@ step-restore-brew-cache: &step-restore-brew-cache
|
||||
- /usr/local/Cellar/gnu-tar
|
||||
- /usr/local/bin/gtar
|
||||
keys:
|
||||
- v7-brew-cache-{{ arch }}
|
||||
- v6-brew-cache-{{ arch }}
|
||||
|
||||
step-save-brew-cache: &step-save-brew-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- /usr/local/Cellar/gnu-tar
|
||||
- /usr/local/bin/gtar
|
||||
key: v7-brew-cache-{{ arch }}
|
||||
key: v6-brew-cache-{{ arch }}
|
||||
name: Persisting brew cache
|
||||
|
||||
step-get-more-space-on-mac: &step-get-more-space-on-mac
|
||||
@@ -537,18 +554,6 @@ step-fix-sync: &step-fix-sync
|
||||
mv .git/config.backup .git/config
|
||||
git fetch
|
||||
|
||||
step-fix-clang: &step-fix-clang
|
||||
run:
|
||||
name: Fix Clang on 32-bit Linux ARM
|
||||
command: |
|
||||
if [ "$TARGET_ARCH" == "arm" ]; then
|
||||
# The following custom clang needed on 32-bit Linux ARM due to https://crbug.com/1431201.
|
||||
# Once https://crbug.com/1425409 has been resolved this workaround can be remove
|
||||
cd src/third_party/llvm-build/Release+Asserts
|
||||
curl -O https://dev-cdn.electronjs.org/clang/clang-llvmorg-17-init-4759-g547e3456-2.tar.xz
|
||||
tar xvf clang-llvmorg-17-init-4759-g547e3456-2.tar.xz
|
||||
fi
|
||||
|
||||
step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac
|
||||
run:
|
||||
name: Import and trust self-signed codesigning cert on MacOS
|
||||
@@ -636,6 +641,7 @@ step-nodejs-headers-build: &step-nodejs-headers-build
|
||||
step-electron-publish: &step-electron-publish
|
||||
run:
|
||||
name: Publish Electron Dist
|
||||
no_output_timeout: 30m
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
rm -rf src/out/Default/obj
|
||||
@@ -730,8 +736,8 @@ step-show-goma-stats: &step-show-goma-stats
|
||||
command: |
|
||||
set +e
|
||||
set +o pipefail
|
||||
$LOCAL_GOMA_DIR/goma_ctl.py stat
|
||||
$LOCAL_GOMA_DIR/diagnose_goma_log.py
|
||||
python3 $GOMA_DIR/goma_ctl.py stat
|
||||
python3 $GOMA_DIR/diagnose_goma_log.py
|
||||
true
|
||||
when: always
|
||||
background: true
|
||||
@@ -883,14 +889,26 @@ step-touch-sync-done: &step-touch-sync-done
|
||||
step-maybe-restore-src-cache: &step-maybe-restore-src-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- v16-src-cache-{{ checksum "src/electron/.depshash" }}
|
||||
- v17-src-cache-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache
|
||||
step-maybe-restore-src-cache-marker: &step-maybe-restore-src-cache-marker
|
||||
restore_cache:
|
||||
keys:
|
||||
- v16-src-cache-marker-{{ checksum "src/electron/.depshash" }}
|
||||
- v17-src-cache-marker-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache marker
|
||||
|
||||
step-maybe-restore-src-cache-aks: &step-maybe-restore-src-cache-aks
|
||||
restore_cache_aks:
|
||||
step-name: Restoring src cache
|
||||
cache_key: v17-src-cache-$(shasum src/electron/.depshash | cut -f1 -d' ')
|
||||
cache_path: /var/portal
|
||||
|
||||
step-maybe-restore-src-cache-marker-aks: &step-maybe-restore-src-cache-marker-aks
|
||||
restore_cache_aks:
|
||||
step-name: Restoring src cache marker
|
||||
cache_key: v17-src-cache-marker-$(shasum src/electron/.depshash | cut -f1 -d' ')
|
||||
cache_path: "."
|
||||
|
||||
# Restore exact or closest git cache based on the hash of DEPS and .circle-sync-done
|
||||
# If the src cache was restored above then this will match an empty cache
|
||||
# If the src cache was not restored above then this will match a close git cache
|
||||
@@ -903,6 +921,12 @@ step-maybe-restore-git-cache: &step-maybe-restore-git-cache
|
||||
- v1-git-cache-{{ checksum "src/electron/.circle-sync-done" }}
|
||||
name: Conditionally restoring git cache
|
||||
|
||||
step-maybe-restore-git-cache-aks: &step-maybe-restore-git-cache-aks
|
||||
restore_cache_aks:
|
||||
step-name: Conditionally restoring git cache (aks)
|
||||
cache_key: v1-git-cache-$(shasum src/electron/.circle-sync-done | cut -f1 -d' ')-$(shasum src/electron/DEPS | cut -f1 -d' ') v1-git-cache-$(shasum src/electron/.circle-sync-done | cut -f1 -d' ')
|
||||
cache_path: git-cache
|
||||
|
||||
step-set-git-cache-path: &step-set-git-cache-path
|
||||
run:
|
||||
name: Set GIT_CACHE_PATH to make gclient to use the cache
|
||||
@@ -920,6 +944,12 @@ step-save-git-cache: &step-save-git-cache
|
||||
key: v1-git-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
name: Persisting git cache
|
||||
|
||||
step-save-git-cache-aks: &step-save-git-cache-aks
|
||||
save_cache_aks:
|
||||
step-name: Persisting git cache (AKS)
|
||||
cache_key: v1-git-cache-$(shasum src/electron/.circle-sync-done | cut -f1 -d' ')-$(shasum src/electron/DEPS | cut -f1 -d' ')
|
||||
cache_path: git-cache
|
||||
|
||||
step-run-electron-only-hooks: &step-run-electron-only-hooks
|
||||
run:
|
||||
name: Run Electron Only Hooks
|
||||
@@ -957,7 +987,7 @@ step-save-src-cache: &step-save-src-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- /var/portal
|
||||
key: v16-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
key: v17-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
name: Persisting src cache
|
||||
step-make-src-cache-marker: &step-make-src-cache-marker
|
||||
run:
|
||||
@@ -967,7 +997,17 @@ step-save-src-cache-marker: &step-save-src-cache-marker
|
||||
save_cache:
|
||||
paths:
|
||||
- .src-cache-marker
|
||||
key: v16-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
key: v17-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
step-save-src-cache-aks: &step-save-src-cache-aks
|
||||
save_cache_aks:
|
||||
step-name: Persisting src cache (aks)
|
||||
cache_key: v17-src-cache-$(shasum /var/portal/src/electron/.depshash | cut -f1 -d' ')
|
||||
cache_path: /var/portal
|
||||
step-save-src-cache-marker-aks: &step-save-src-cache-marker-aks
|
||||
save_cache_aks:
|
||||
step-name: Persisting src cache marker (aks)
|
||||
cache_key: v17-src-cache-marker-$(shasum /var/portal/src/electron/.depshash | cut -f1 -d' ')
|
||||
cache_path: .src-cache-marker
|
||||
|
||||
step-maybe-early-exit-no-doc-change: &step-maybe-early-exit-no-doc-change
|
||||
run:
|
||||
@@ -993,15 +1033,6 @@ step-ts-compile: &step-ts-compile
|
||||
done
|
||||
|
||||
# List of all steps.
|
||||
steps-electron-gn-check: &steps-electron-gn-check
|
||||
steps:
|
||||
- *step-setup-goma-for-build
|
||||
- checkout-from-cache
|
||||
- *step-setup-env-for-build
|
||||
- *step-wait-for-goma
|
||||
- *step-gn-gen-default
|
||||
- *step-gn-check
|
||||
|
||||
steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-change
|
||||
steps:
|
||||
# Checkout - Copied from steps-checkout
|
||||
@@ -1013,11 +1044,92 @@ steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-cha
|
||||
|
||||
# Command Aliases
|
||||
commands:
|
||||
aks-specific-step:
|
||||
parameters:
|
||||
circle:
|
||||
type: steps
|
||||
aks:
|
||||
type: steps
|
||||
could-be-aks:
|
||||
type: boolean
|
||||
description: Only set this to true on linux hosts
|
||||
steps:
|
||||
- when:
|
||||
condition:
|
||||
or:
|
||||
- equal: [<< parameters.could-be-aks >>, false]
|
||||
- equal: [<< pipeline.parameters.large-linux-executor >>, 2xlarge]
|
||||
steps: << parameters.circle >>
|
||||
- when:
|
||||
condition:
|
||||
and:
|
||||
- equal: [<< parameters.could-be-aks >>, true]
|
||||
- equal: [<< pipeline.parameters.large-linux-executor >>, electronjs/aks-linux-large]
|
||||
steps: << parameters.aks >>
|
||||
save_cache_aks:
|
||||
parameters:
|
||||
step-name:
|
||||
type: string
|
||||
cache_key:
|
||||
type: string
|
||||
cache_path:
|
||||
type: string
|
||||
steps:
|
||||
- run:
|
||||
name: << parameters.step-name >>
|
||||
command: |
|
||||
cache_key="<< parameters.cache_key >>"
|
||||
final_cache_path=/mnt/cross-instance-cache/${cache_key}.tar
|
||||
echo "Using cache key: $cache_key"
|
||||
echo "Checking path: $final_cache_path"
|
||||
if [ ! -f "$final_cache_path" ]; then
|
||||
echo "Cache key not founding, storing tarball"
|
||||
tmp_container=/mnt/cross-instance-cache/tmp/$CIRCLE_WORKFLOW_JOB_ID
|
||||
tmp_cache_path=$tmp_container/${cache_key}.tar
|
||||
mkdir -p $tmp_container
|
||||
if [ -f "<< parameters.cache_path >>" ]; then
|
||||
tar -cf $tmp_cache_path -C $(dirname << parameters.cache_path >>) ./$(basename << parameters.cache_path >>)
|
||||
else
|
||||
tar -cf $tmp_cache_path -C << parameters.cache_path >>/ ./
|
||||
fi
|
||||
mv -vn $tmp_cache_path $final_cache_path
|
||||
rm -rf $tmp_container
|
||||
else
|
||||
echo "Cache key already exists, skipping.."
|
||||
fi
|
||||
restore_cache_aks:
|
||||
parameters:
|
||||
step-name:
|
||||
type: string
|
||||
cache_key:
|
||||
type: string
|
||||
cache_path:
|
||||
type: string
|
||||
steps:
|
||||
- run:
|
||||
name: << parameters.step-name >>
|
||||
command: |
|
||||
df -h
|
||||
for cache_key in << parameters.cache_key >>; do
|
||||
cache_path=/mnt/cross-instance-cache/${cache_key}.tar
|
||||
echo "Using cache key: $cache_key"
|
||||
echo "Checking path: $cache_path"
|
||||
if [ ! -f "$cache_path" ]; then
|
||||
echo "Cache key not found, nothing to restore..."
|
||||
else
|
||||
echo "Cache key found, restoring to path..."
|
||||
mkdir -p << parameters.cache_path >>/
|
||||
tar -xf /mnt/cross-instance-cache/${cache_key}.tar -C << parameters.cache_path >>/
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
maybe-restore-portaled-src-cache:
|
||||
parameters:
|
||||
halt-if-successful:
|
||||
type: boolean
|
||||
default: false
|
||||
could-be-aks:
|
||||
type: boolean
|
||||
steps:
|
||||
- run:
|
||||
name: Prepare for cross-OS sync restore
|
||||
@@ -1027,23 +1139,44 @@ commands:
|
||||
- when:
|
||||
condition: << parameters.halt-if-successful >>
|
||||
steps:
|
||||
- *step-maybe-restore-src-cache-marker
|
||||
- aks-specific-step:
|
||||
circle:
|
||||
- *step-maybe-restore-src-cache-marker
|
||||
aks:
|
||||
- *step-maybe-restore-src-cache-marker-aks
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
- run:
|
||||
name: Halt the job early if the src cache exists
|
||||
command: |
|
||||
if [ -f ".src-cache-marker" ]; then
|
||||
circleci-agent step halt
|
||||
fi
|
||||
- *step-maybe-restore-src-cache
|
||||
- aks-specific-step:
|
||||
circle:
|
||||
- *step-maybe-restore-src-cache
|
||||
aks:
|
||||
- *step-maybe-restore-src-cache-aks
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
- run:
|
||||
name: Fix the src cache restore point on macOS
|
||||
name: Fix the src cache restore point
|
||||
command: |
|
||||
if [ -d "/var/portal/src" ]; then
|
||||
echo Relocating Cache
|
||||
rm -rf src
|
||||
mv /var/portal/src ./
|
||||
fi
|
||||
|
||||
run-gn-check:
|
||||
parameters:
|
||||
could-be-aks:
|
||||
type: boolean
|
||||
steps:
|
||||
- *step-setup-goma-for-build
|
||||
- checkout-from-cache:
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
- *step-setup-env-for-build
|
||||
- *step-wait-for-goma
|
||||
- *step-gn-gen-default
|
||||
- *step-gn-check
|
||||
build_and_save_artifacts:
|
||||
parameters:
|
||||
artifact-key:
|
||||
@@ -1055,7 +1188,6 @@ commands:
|
||||
default: true
|
||||
steps:
|
||||
- *step-gn-gen-default
|
||||
- *step-fix-clang
|
||||
- ninja_build_electron:
|
||||
clean-prebuilt-snapshot: false
|
||||
build-type: << parameters.build-type >>
|
||||
@@ -1158,12 +1290,16 @@ commands:
|
||||
mv_if_exist cross-arch-snapshots src
|
||||
|
||||
checkout-from-cache:
|
||||
parameters:
|
||||
could-be-aks:
|
||||
type: boolean
|
||||
steps:
|
||||
- *step-checkout-electron
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-generate-deps-hash
|
||||
- maybe-restore-portaled-src-cache
|
||||
- maybe-restore-portaled-src-cache:
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
- run:
|
||||
name: Ensure src checkout worked
|
||||
command: |
|
||||
@@ -1301,6 +1437,8 @@ commands:
|
||||
after-persist:
|
||||
type: steps
|
||||
default: []
|
||||
could-be-aks:
|
||||
type: boolean
|
||||
steps:
|
||||
- when:
|
||||
condition: << parameters.attach >>
|
||||
@@ -1318,7 +1456,8 @@ commands:
|
||||
- when:
|
||||
condition: << parameters.checkout-and-assume-cache >>
|
||||
steps:
|
||||
- checkout-from-cache
|
||||
- checkout-from-cache:
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
- when:
|
||||
condition: << parameters.checkout >>
|
||||
steps:
|
||||
@@ -1334,8 +1473,15 @@ commands:
|
||||
steps:
|
||||
- maybe-restore-portaled-src-cache:
|
||||
halt-if-successful: << parameters.checkout-to-create-src-cache >>
|
||||
- *step-maybe-restore-git-cache
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
- aks-specific-step:
|
||||
circle:
|
||||
- *step-maybe-restore-git-cache
|
||||
aks:
|
||||
- *step-maybe-restore-git-cache-aks
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
- *step-set-git-cache-path
|
||||
- *step-fix-known-hosts-linux
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
- store_artifacts:
|
||||
@@ -1351,7 +1497,12 @@ commands:
|
||||
- when:
|
||||
condition: << parameters.save-git-cache >>
|
||||
steps:
|
||||
- *step-save-git-cache
|
||||
- aks-specific-step:
|
||||
circle:
|
||||
- *step-save-git-cache
|
||||
aks:
|
||||
- *step-save-git-cache-aks
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
# Mark sync as done _after_ saving the git cache so that it is uploaded
|
||||
# only when the src cache was not present
|
||||
# Their are theoretically two cases for this cache key
|
||||
@@ -1401,9 +1552,19 @@ commands:
|
||||
sudo mkdir -p /var/portal
|
||||
sudo chown -R $(id -u):$(id -g) /var/portal
|
||||
mv ./src /var/portal
|
||||
- *step-save-src-cache
|
||||
- aks-specific-step:
|
||||
circle:
|
||||
- *step-save-src-cache
|
||||
aks:
|
||||
- *step-save-src-cache-aks
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
- *step-make-src-cache-marker
|
||||
- *step-save-src-cache-marker
|
||||
- aks-specific-step:
|
||||
circle:
|
||||
- *step-save-src-cache-marker
|
||||
aks:
|
||||
- *step-save-src-cache-marker-aks
|
||||
could-be-aks: << parameters.could-be-aks >>
|
||||
|
||||
- when:
|
||||
condition: << parameters.build >>
|
||||
@@ -1455,6 +1616,18 @@ commands:
|
||||
condition: << parameters.build >>
|
||||
steps:
|
||||
- *step-maybe-notify-slack-failure
|
||||
- when:
|
||||
condition: << parameters.could-be-aks >>
|
||||
steps:
|
||||
- run:
|
||||
name: Wait for active debug sessions
|
||||
command: |
|
||||
while [ -f /var/.ssh-lock ]
|
||||
do
|
||||
sleep 60
|
||||
done
|
||||
no_output_timeout: 2h
|
||||
when: always
|
||||
|
||||
electron-tests:
|
||||
parameters:
|
||||
@@ -1592,10 +1765,10 @@ commands:
|
||||
- *step-minimize-workspace-size-from-checkout
|
||||
- *step-fix-sync
|
||||
- *step-setup-env-for-build
|
||||
- *step-fix-known-hosts-linux
|
||||
- *step-setup-goma-for-build
|
||||
- *step-wait-for-goma
|
||||
- *step-gn-gen-default
|
||||
- *step-fix-clang
|
||||
|
||||
# Electron app
|
||||
- ninja_build_electron:
|
||||
@@ -1650,7 +1823,7 @@ jobs:
|
||||
linux-make-src-cache:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
@@ -1663,6 +1836,7 @@ jobs:
|
||||
checkout-to-create-src-cache: true
|
||||
artifact-key: 'nil'
|
||||
build-type: 'nil'
|
||||
could-be-aks: true
|
||||
|
||||
mac-checkout:
|
||||
executor:
|
||||
@@ -1682,6 +1856,7 @@ jobs:
|
||||
restore-src-cache: false
|
||||
artifact-key: 'nil'
|
||||
build-type: 'nil'
|
||||
could-be-aks: false
|
||||
|
||||
mac-make-src-cache:
|
||||
executor:
|
||||
@@ -1701,12 +1876,13 @@ jobs:
|
||||
checkout-to-create-src-cache: true
|
||||
artifact-key: 'nil'
|
||||
build-type: 'nil'
|
||||
could-be-aks: false
|
||||
|
||||
# Layer 2: Builds.
|
||||
linux-x64-testing:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-global
|
||||
<<: *env-testing-build
|
||||
@@ -1719,11 +1895,12 @@ jobs:
|
||||
checkout-and-assume-cache: true
|
||||
artifact-key: 'linux-x64'
|
||||
build-type: 'Linux'
|
||||
could-be-aks: true
|
||||
|
||||
linux-x64-testing-asan:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: 2xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-global
|
||||
<<: *env-testing-build
|
||||
@@ -1734,15 +1911,17 @@ jobs:
|
||||
steps:
|
||||
- electron-build:
|
||||
persist: true
|
||||
checkout: true
|
||||
checkout: false
|
||||
checkout-and-assume-cache: true
|
||||
build-nonproprietary-ffmpeg: false
|
||||
artifact-key: 'linux-x64-asan'
|
||||
build-type: 'Linux'
|
||||
could-be-aks: true
|
||||
|
||||
linux-x64-testing-no-run-as-node:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-testing-build
|
||||
@@ -1755,21 +1934,24 @@ jobs:
|
||||
checkout: true
|
||||
artifact-key: 'linux-x64-no-run-as-node'
|
||||
build-type: 'Linux'
|
||||
could-be-aks: true
|
||||
|
||||
linux-x64-testing-gn-check:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: medium
|
||||
size: << pipeline.parameters.medium-linux-executor >>
|
||||
environment:
|
||||
<<: *env-linux-medium
|
||||
<<: *env-testing-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-gn-check
|
||||
steps:
|
||||
- run-gn-check:
|
||||
could-be-aks: true
|
||||
|
||||
linux-x64-publish:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: 2xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-release-build
|
||||
@@ -1792,7 +1974,7 @@ jobs:
|
||||
linux-arm-testing:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: 2xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-global
|
||||
<<: *env-arm
|
||||
@@ -1808,11 +1990,12 @@ jobs:
|
||||
checkout-and-assume-cache: true
|
||||
artifact-key: 'linux-arm'
|
||||
build-type: 'Linux ARM'
|
||||
could-be-aks: true
|
||||
|
||||
linux-arm-publish:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: 2xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm
|
||||
@@ -1837,7 +2020,7 @@ jobs:
|
||||
linux-arm64-testing:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: 2xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-global
|
||||
<<: *env-arm64
|
||||
@@ -1853,22 +2036,25 @@ jobs:
|
||||
checkout-and-assume-cache: true
|
||||
artifact-key: 'linux-arm64'
|
||||
build-type: 'Linux ARM64'
|
||||
could-be-aks: true
|
||||
|
||||
linux-arm64-testing-gn-check:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: medium
|
||||
size: << pipeline.parameters.medium-linux-executor >>
|
||||
environment:
|
||||
<<: *env-linux-medium
|
||||
<<: *env-arm64
|
||||
<<: *env-testing-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-gn-check
|
||||
steps:
|
||||
- run-gn-check:
|
||||
could-be-aks: true
|
||||
|
||||
linux-arm64-publish:
|
||||
executor:
|
||||
name: linux-docker
|
||||
size: 2xlarge
|
||||
size: << pipeline.parameters.large-linux-executor >>
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm64
|
||||
@@ -1923,6 +2109,7 @@ jobs:
|
||||
root: .
|
||||
paths:
|
||||
- generated_artifacts_mas-x64
|
||||
could-be-aks: false
|
||||
|
||||
osx-testing-x64-gn-check:
|
||||
executor:
|
||||
@@ -1932,7 +2119,9 @@ jobs:
|
||||
<<: *env-machine-mac
|
||||
<<: *env-testing-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
|
||||
<<: *steps-electron-gn-check
|
||||
steps:
|
||||
- run-gn-check:
|
||||
could-be-aks: false
|
||||
|
||||
osx-publish-x64:
|
||||
executor:
|
||||
@@ -2015,6 +2204,7 @@ jobs:
|
||||
root: .
|
||||
paths:
|
||||
- generated_artifacts_mas-arm64
|
||||
could-be-aks: false
|
||||
|
||||
mas-publish-x64:
|
||||
executor:
|
||||
@@ -2140,6 +2330,7 @@ jobs:
|
||||
executor:
|
||||
name: macos
|
||||
size: macos.x86.medium.gen2
|
||||
version: 14.0.0
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
<<: *env-stack-dumping
|
||||
@@ -2163,6 +2354,7 @@ jobs:
|
||||
executor:
|
||||
name: macos
|
||||
size: macos.x86.medium.gen2
|
||||
version: 14.0.0
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
<<: *env-stack-dumping
|
||||
|
||||
12
.circleci/config/params.js
Normal file
12
.circleci/config/params.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const PARAMS_PATH = '/tmp/pipeline-parameters.json';
|
||||
|
||||
const content = JSON.parse(fs.readFileSync(PARAMS_PATH, 'utf-8'));
|
||||
|
||||
// Choose resource class for linux hosts
|
||||
const currentBranch = process.env.CIRCLE_BRANCH || '';
|
||||
content['large-linux-executor'] = /^pull\/[0-9-]+$/.test(currentBranch) ? '2xlarge' : 'electronjs/aks-linux-large';
|
||||
content['medium-linux-executor'] = /^pull\/[0-9-]+$/.test(currentBranch) ? 'medium' : 'electronjs/aks-linux-medium';
|
||||
|
||||
fs.writeFileSync(PARAMS_PATH, JSON.stringify(content));
|
||||
@@ -3,5 +3,6 @@
|
||||
set -e
|
||||
|
||||
mkdir -p ~/.ssh
|
||||
echo "|1|B3r+7aO0/x90IdefihIjxIoJrrk=|OJddGDfhbuLFc1bUyy84hhIw57M= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
|
||||
|1|rGlEvW55DtzNZp+pzw9gvyOyKi4=|LLWr+7qlkAlw3YGGVfLHHxB/kR0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" >> ~/.ssh/known_hosts
|
||||
echo "github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
|
||||
github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
|
||||
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=" >> ~/.ssh/known_hosts
|
||||
|
||||
@@ -4,37 +4,6 @@
|
||||
"onCreateCommand": ".devcontainer/on-create-command.sh",
|
||||
"updateContentCommand": ".devcontainer/update-content-command.sh",
|
||||
"workspaceFolder": "/workspaces/gclient/src/electron",
|
||||
"extensions": [
|
||||
"joeleinbinder.mojom-language",
|
||||
"rafaelmaiolla.diff",
|
||||
"surajbarkale.ninja",
|
||||
"ms-vscode.cpptools",
|
||||
"mutantdino.resourcemonitor",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"shakram02.bash-beautify",
|
||||
"marshallofsound.gnls-electron",
|
||||
"CircleCI.circleci"
|
||||
],
|
||||
"settings": {
|
||||
"editor.tabSize": 2,
|
||||
"bashBeautify.tabSize": 2,
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"[gn]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
},
|
||||
"javascript.preferences.quoteStyle": "single",
|
||||
"typescript.preferences.quoteStyle": "single"
|
||||
},
|
||||
"forwardPorts": [8088, 6080, 5901],
|
||||
"portsAttributes": {
|
||||
"8088": {
|
||||
@@ -60,6 +29,38 @@
|
||||
"openFiles": [
|
||||
".devcontainer/README.md"
|
||||
]
|
||||
},
|
||||
"vscode": {
|
||||
"extensions": ["joeleinbinder.mojom-language",
|
||||
"rafaelmaiolla.diff",
|
||||
"surajbarkale.ninja",
|
||||
"ms-vscode.cpptools",
|
||||
"mutantdino.resourcemonitor",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"shakram02.bash-beautify",
|
||||
"marshallofsound.gnls-electron",
|
||||
"CircleCI.circleci"
|
||||
],
|
||||
"settings": {
|
||||
"editor.tabSize": 2,
|
||||
"bashBeautify.tabSize": 2,
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"[gn]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
},
|
||||
"javascript.preferences.quoteStyle": "single",
|
||||
"typescript.preferences.quoteStyle": "single"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ ln -s $buildtools_configs $buildtools/configs
|
||||
|
||||
# Write the gclient config if it does not already exist
|
||||
if [ ! -f $gclient_root/.gclient ]; then
|
||||
echo "Creating gclient config"
|
||||
|
||||
echo "solutions = [
|
||||
{ \"name\" : \"src/electron\",
|
||||
\"url\" : \"https://github.com/electron/electron\",
|
||||
@@ -32,6 +34,8 @@ fi
|
||||
# Write the default buildtools config file if it does
|
||||
# not already exist
|
||||
if [ ! -f $buildtools/configs/evm.testing.json ]; then
|
||||
echo "Creating build-tools testing config"
|
||||
|
||||
write_config() {
|
||||
echo "
|
||||
{
|
||||
@@ -53,7 +57,7 @@ if [ ! -f $buildtools/configs/evm.testing.json ]; then
|
||||
\"CHROMIUM_BUILDTOOLS_PATH\": \"/workspaces/gclient/src/buildtools\",
|
||||
\"GIT_CACHE_PATH\": \"/workspaces/gclient/.git-cache\"
|
||||
},
|
||||
\"$schema\": \"file:///home/builduser/.electron_build_tools/evm-config.schema.json\"
|
||||
\"\$schema\": \"file:///home/builduser/.electron_build_tools/evm-config.schema.json\"
|
||||
}
|
||||
" >$buildtools/configs/evm.testing.json
|
||||
}
|
||||
@@ -67,10 +71,12 @@ if [ ! -f $buildtools/configs/evm.testing.json ]; then
|
||||
# if it works we can use the goma cluster
|
||||
export NOTGOMA_CODESPACES_TOKEN=$GITHUB_TOKEN
|
||||
if e d goma_auth login; then
|
||||
echo "$GITHUB_USER has GOMA access - switching to cluster mode"
|
||||
write_config cluster
|
||||
fi
|
||||
else
|
||||
# Even if the config file existed we still need to re-auth with the goma
|
||||
# cluster
|
||||
echo "build-tools testing config already exists"
|
||||
|
||||
# Re-auth with the goma cluster regardless.
|
||||
NOTGOMA_CODESPACES_TOKEN=$GITHUB_TOKEN e d goma_auth login || true
|
||||
fi
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -11,4 +11,5 @@ patches/**/.patches merge=union
|
||||
*.ts text eol=lf
|
||||
*.py text eol=lf
|
||||
*.ps1 text eol=lf
|
||||
*.html text eol=lf
|
||||
*.md text eol=lf
|
||||
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -8,6 +8,7 @@
|
||||
DEPS @electron/wg-upgrades
|
||||
|
||||
# Releases WG
|
||||
/docs/breaking-changes.md @electron/wg-releases
|
||||
/npm/ @electron/wg-releases
|
||||
/script/release @electron/wg-releases
|
||||
|
||||
|
||||
14
.github/config.yml
vendored
14
.github/config.yml
vendored
@@ -25,17 +25,3 @@ newPRWelcomeComment: |
|
||||
# Comment to be posted to on pull requests merged by a first time user
|
||||
firstPRMergeComment: >
|
||||
Congrats on merging your first pull request! 🎉🎉🎉
|
||||
|
||||
# Users authorized to run manual trop backports
|
||||
authorizedUsers:
|
||||
- alexeykuzmin
|
||||
- ckerr
|
||||
- codebytere
|
||||
- deepak1556
|
||||
- jkleinsc
|
||||
- loc
|
||||
- MarshallOfSound
|
||||
- miniak
|
||||
- mlaurencin
|
||||
- nornagon
|
||||
- zcbenz
|
||||
|
||||
2
.github/workflows/semantic.yml
vendored
2
.github/workflows/semantic.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: "Check Semantic Commit"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
|
||||
13
.github/workflows/update_appveyor_image.yml
vendored
13
.github/workflows/update_appveyor_image.yml
vendored
@@ -40,13 +40,21 @@ jobs:
|
||||
if: ${{ env.APPVEYOR_IMAGE_VERSION }}
|
||||
uses: mikefarah/yq@1c7dc0e88aad311c89889bc5ce5d8f96931a1bd0 # v4.27.2
|
||||
with:
|
||||
cmd: yq '.image = "${{ env.APPVEYOR_IMAGE_VERSION }}"' "appveyor.yml" > "appveyor2.yml"
|
||||
cmd: |
|
||||
yq '.image = "${{ env.APPVEYOR_IMAGE_VERSION }}"' "appveyor.yml" > "appveyor2.yml"
|
||||
yq '.image = "${{ env.APPVEYOR_IMAGE_VERSION }}"' "appveyor-woa.yml" > "appveyor-woa2.yml"
|
||||
- name: (Optionally) Generate Commit Diff
|
||||
if: ${{ env.APPVEYOR_IMAGE_VERSION }}
|
||||
run: |
|
||||
diff -w -B appveyor.yml appveyor2.yml > appveyor.diff || true
|
||||
patch -f appveyor.yml < appveyor.diff
|
||||
rm appveyor2.yml appveyor.diff
|
||||
- name: (Optionally) Generate Commit Diff for WOA
|
||||
if: ${{ env.APPVEYOR_IMAGE_VERSION }}
|
||||
run: |
|
||||
diff -w -B appveyor-woa.yml appveyor-woa2.yml > appveyor-woa.diff || true
|
||||
patch -f appveyor-woa.yml < appveyor-woa.diff
|
||||
rm appveyor-woa2.yml appveyor-woa.diff
|
||||
- name: (Optionally) Commit and Pull Request
|
||||
if: ${{ env.APPVEYOR_IMAGE_VERSION }}
|
||||
uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 # v4.2.3
|
||||
@@ -60,4 +68,5 @@ jobs:
|
||||
delete-branch: true
|
||||
title: 'build: update appveyor image to latest version'
|
||||
body: |
|
||||
This PR updates appveyor.yml to the latest baked image, ${{ env.APPVEYOR_IMAGE_VERSION }}.
|
||||
This PR updates appveyor.yml to the latest baked image, ${{ env.APPVEYOR_IMAGE_VERSION }}.
|
||||
Notes: none
|
||||
6
BUILD.gn
6
BUILD.gn
@@ -1,7 +1,7 @@
|
||||
import("//build/config/locales.gni")
|
||||
import("//build/config/ui.gni")
|
||||
import("//build/config/win/manifest.gni")
|
||||
import("//components/os_crypt/features.gni")
|
||||
import("//components/os_crypt/sync/features.gni")
|
||||
import("//components/spellcheck/spellcheck_build_features.gni")
|
||||
import("//content/public/app/mac_helpers.gni")
|
||||
import("//extensions/buildflags/buildflags.gni")
|
||||
@@ -433,7 +433,7 @@ source_set("electron_lib") {
|
||||
"//components/network_hints/renderer",
|
||||
"//components/network_session_configurator/common",
|
||||
"//components/omnibox/browser:buildflags",
|
||||
"//components/os_crypt",
|
||||
"//components/os_crypt/sync",
|
||||
"//components/pref_registry",
|
||||
"//components/prefs",
|
||||
"//components/security_state/content",
|
||||
@@ -772,6 +772,8 @@ source_set("electron_lib") {
|
||||
sources += [
|
||||
"shell/browser/electron_pdf_web_contents_helper_client.cc",
|
||||
"shell/browser/electron_pdf_web_contents_helper_client.h",
|
||||
"shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc",
|
||||
"shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
5
DEPS
5
DEPS
@@ -2,9 +2,9 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'112.0.5615.204',
|
||||
'114.0.5735.289',
|
||||
'node_version':
|
||||
'v18.14.0',
|
||||
'v18.15.0',
|
||||
'nan_version':
|
||||
'16fa32231e2ccd89d2804b3f765319128b20c4ac',
|
||||
'squirrel.mac_version':
|
||||
@@ -155,5 +155,4 @@ hooks = [
|
||||
|
||||
recursedeps = [
|
||||
'src',
|
||||
'src/third_party/squirrel.mac',
|
||||
]
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
version: 1.0.{build}
|
||||
build_cloud: electronhq-16-core
|
||||
image: e-112.0.5615.29
|
||||
image: e-112.0.5607.0-vs2022
|
||||
environment:
|
||||
GIT_CACHE_PATH: C:\Users\appveyor\libcc_cache
|
||||
ELECTRON_OUT_DIR: Default
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# - "GN_CONFIG" Build type. One of {'testing', 'release'}.
|
||||
# - "GN_EXTRA_ARGS" Additional gn arguments for a build config,
|
||||
# e.g. 'target_cpu="x86"' to build for a 32bit platform.
|
||||
# https://gn.googlesource.com/gn/+/master/docs/reference.md#target_cpu
|
||||
# https://gn.googlesource.com/gn/+/main/docs/reference.md#var_target_cpu
|
||||
# Don't forget to set up "NPM_CONFIG_ARCH" and "TARGET_ARCH" accordingly
|
||||
# if you pass a custom value for 'target_cpu'.
|
||||
# - "ELECTRON_RELEASE" Set it to '1' upload binaries on success.
|
||||
@@ -29,14 +29,14 @@
|
||||
|
||||
version: 1.0.{build}
|
||||
build_cloud: electronhq-16-core
|
||||
image: e-112.0.5615.29
|
||||
image: e-114.0.5735.16-bust-cache
|
||||
environment:
|
||||
GIT_CACHE_PATH: C:\Users\appveyor\libcc_cache
|
||||
ELECTRON_OUT_DIR: Default
|
||||
ELECTRON_ENABLE_STACK_DUMPING: 1
|
||||
ELECTRON_ALSO_LOG_TO_STDERR: 1
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap
|
||||
MOCHA_MULTI_REPORTERS: "@marshallofsound/mocha-appveyor-reporter, tap"
|
||||
GOMA_FALLBACK_ON_AUTH_FAILURE: true
|
||||
DEPOT_TOOLS_WIN_TOOLCHAIN: 0
|
||||
PYTHONIOENCODING: UTF-8
|
||||
@@ -49,6 +49,11 @@ environment:
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: base-woa
|
||||
APPVEYOR_BUILD_WORKER_CLOUD: electronhq-woa
|
||||
|
||||
clone_script:
|
||||
- ps: git clone -q $("--branch=" + $Env:APPVEYOR_REPO_BRANCH) $("https://github.com/" + $Env:APPVEYOR_REPO_NAME + ".git") $Env:APPVEYOR_BUILD_FOLDER
|
||||
- ps: if (!$Env:APPVEYOR_PULL_REQUEST_NUMBER) {$("git checkout -qf " + $Env:APPVEYOR_REPO_COMMIT)}
|
||||
- ps: if ($Env:APPVEYOR_PULL_REQUEST_NUMBER) {git fetch -q origin +refs/pull/$($Env:APPVEYOR_PULL_REQUEST_NUMBER)/head; git checkout -qf FETCH_HEAD}
|
||||
|
||||
clone_folder: C:\projects\src\electron
|
||||
|
||||
skip_branch_with_pr: true
|
||||
@@ -67,8 +72,10 @@ for:
|
||||
- ps: |
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
node script/doc-only-change.js --prNumber=$env:APPVEYOR_PULL_REQUEST_NUMBER
|
||||
$env:SHOULD_SKIP_ARTIFACT_VALIDATION = "false"
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-warning "Skipping build for doc only change"
|
||||
Write-warning "Skipping build for doc-only change"
|
||||
$env:SHOULD_SKIP_ARTIFACT_VALIDATION = "true"
|
||||
Exit-AppveyorBuild
|
||||
} else {
|
||||
$global:LASTEXITCODE = 0
|
||||
@@ -85,6 +92,8 @@ for:
|
||||
Remove-Item -Recurse -Force $pwd\build-tools
|
||||
}
|
||||
- git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
- ps: New-Item -Name depot_tools\.disable_auto_update -ItemType File
|
||||
- depot_tools\bootstrap\win_tools.bat
|
||||
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"
|
||||
- ps: >-
|
||||
if (Test-Path -Path "$pwd\src\electron") {
|
||||
@@ -107,7 +116,7 @@ for:
|
||||
- ps: .\src\electron\script\start-goma.ps1 -gomaDir $env:LOCAL_GOMA_DIR
|
||||
- ps: >-
|
||||
if (Test-Path 'env:RAW_GOMA_AUTH') {
|
||||
$goma_login = python $env:LOCAL_GOMA_DIR\goma_auth.py info
|
||||
$goma_login = python3 $env:LOCAL_GOMA_DIR\goma_auth.py info
|
||||
if ($goma_login -eq 'Login as Fermi Planck') {
|
||||
Write-warning "Goma authentication is correct";
|
||||
} else {
|
||||
@@ -157,7 +166,7 @@ for:
|
||||
- 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
|
||||
- python %LOCAL_GOMA_DIR%\goma_ctl.py stat
|
||||
- python3 %LOCAL_GOMA_DIR%\goma_ctl.py stat
|
||||
- ps: >-
|
||||
Get-CimInstance -Namespace root\cimv2 -Class Win32_product | Select vendor, description, @{l='install_location';e='InstallLocation'}, @{l='install_date';e='InstallDate'}, @{l='install_date_2';e='InstallDate2'}, caption, version, name, @{l='sku_number';e='SKUNumber'} | ConvertTo-Json | Out-File -Encoding utf8 -FilePath .\installed_software.json
|
||||
- python3 electron/build/profile_toolchain.py --output-json=out/Default/windows_toolchain_profile.json
|
||||
@@ -178,6 +187,30 @@ for:
|
||||
7z a pdb.zip out\Default\*.pdb
|
||||
}
|
||||
- python3 electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
|
||||
- ps: |
|
||||
cd C:\projects\src
|
||||
$missing_artifacts = $false
|
||||
if ($env:SHOULD_SKIP_ARTIFACT_VALIDATION -eq 'true') {
|
||||
Write-warning "Skipping artifact validation for doc-only PR"
|
||||
} else {
|
||||
$artifacts_to_validate = 'dist.zip','windows_toolchain_profile.json','shell_browser_ui_unittests.exe','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib','hunspell_dictionaries.zip'
|
||||
foreach($artifact_name in $artifacts_to_validate) {
|
||||
if ($artifact_name -eq 'ffmpeg.zip') {
|
||||
$artifact_file = "out\ffmpeg\ffmpeg.zip"
|
||||
} elseif ($artifact_name -eq 'node_headers.zip') {
|
||||
$artifact_file = $artifact_name
|
||||
} else {
|
||||
$artifact_file = "out\Default\$artifact_name"
|
||||
}
|
||||
if (-not(Test-Path $artifact_file)) {
|
||||
Write-warning "$artifact_name is missing and cannot be added to artifacts"
|
||||
$missing_artifacts = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($missing_artifacts) {
|
||||
throw "Build failed due to missing artifacts"
|
||||
}
|
||||
|
||||
deploy_script:
|
||||
- cd electron
|
||||
@@ -203,7 +236,7 @@ for:
|
||||
- if exist node_headers.zip (appveyor-retry appveyor PushArtifact node_headers.zip)
|
||||
- if exist out\Default\mksnapshot.zip (appveyor-retry appveyor PushArtifact out\Default\mksnapshot.zip)
|
||||
- if exist out\Default\hunspell_dictionaries.zip (appveyor-retry appveyor PushArtifact out\Default\hunspell_dictionaries.zip)
|
||||
- if exist out\Default\electron.lib (appveyor-retry appveyor PushArtifact out\Default\electron.lib)
|
||||
- if exist out\Default\electron.lib (appveyor-retry appveyor PushArtifact out\Default\electron.lib)
|
||||
- ps: >-
|
||||
if ((Test-Path "pdb.zip") -And ($env:GN_CONFIG -ne 'release')) {
|
||||
appveyor-retry appveyor PushArtifact pdb.zip
|
||||
@@ -236,7 +269,7 @@ for:
|
||||
# Download build artifacts
|
||||
$apiUrl = 'https://ci.appveyor.com/api'
|
||||
$build_info = Invoke-RestMethod -Method Get -Uri "$apiUrl/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/builds/$env:APPVEYOR_BUILD_ID"
|
||||
$artifacts_to_download = @('dist.zip','ffmpeg.zip','node_headers.zip','pdb.zip','electron.lib')
|
||||
$artifacts_to_download = @('dist.zip','ffmpeg.zip','node_headers.zip','electron.lib')
|
||||
foreach ($job in $build_info.build.jobs) {
|
||||
if ($job.name -eq "Build Arm on X64 Windows") {
|
||||
$jobId = $job.jobId
|
||||
@@ -248,10 +281,13 @@ for:
|
||||
}
|
||||
Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/$artifact_name" -OutFile $outfile
|
||||
}
|
||||
# Uncomment the following lines to download the pdb.zip to show real stacktraces when crashes happen during testing
|
||||
# Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/pdb.zip" -OutFile pdb.zip
|
||||
# 7z x -y -osrc pdb.zip
|
||||
}
|
||||
}
|
||||
- ps: |
|
||||
$out_default_zips = @('dist.zip','pdb.zip')
|
||||
$out_default_zips = @('dist.zip')
|
||||
foreach($zip_name in $out_default_zips) {
|
||||
7z x -y -osrc\out\Default $zip_name
|
||||
}
|
||||
|
||||
50
appveyor.yml
50
appveyor.yml
@@ -6,7 +6,7 @@
|
||||
# - "GN_CONFIG" Build type. One of {'testing', 'release'}.
|
||||
# - "GN_EXTRA_ARGS" Additional gn arguments for a build config,
|
||||
# e.g. 'target_cpu="x86"' to build for a 32bit platform.
|
||||
# https://gn.googlesource.com/gn/+/master/docs/reference.md#target_cpu
|
||||
# https://gn.googlesource.com/gn/+/main/docs/reference.md#var_target_cpu
|
||||
# Don't forget to set up "NPM_CONFIG_ARCH" and "TARGET_ARCH" accordingly
|
||||
# if you pass a custom value for 'target_cpu'.
|
||||
# - "ELECTRON_RELEASE" Set it to '1' upload binaries on success.
|
||||
@@ -29,14 +29,14 @@
|
||||
|
||||
version: 1.0.{build}
|
||||
build_cloud: electronhq-16-core
|
||||
image: e-112.0.5615.29
|
||||
image: e-114.0.5735.16-bust-cache
|
||||
environment:
|
||||
GIT_CACHE_PATH: C:\Users\appveyor\libcc_cache
|
||||
ELECTRON_OUT_DIR: Default
|
||||
ELECTRON_ENABLE_STACK_DUMPING: 1
|
||||
ELECTRON_ALSO_LOG_TO_STDERR: 1
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap
|
||||
MOCHA_MULTI_REPORTERS: "@marshallofsound/mocha-appveyor-reporter, tap"
|
||||
GOMA_FALLBACK_ON_AUTH_FAILURE: true
|
||||
DEPOT_TOOLS_WIN_TOOLCHAIN: 0
|
||||
PYTHONIOENCODING: UTF-8
|
||||
@@ -47,6 +47,11 @@ environment:
|
||||
- job_name: Test
|
||||
job_depends_on: Build
|
||||
|
||||
clone_script:
|
||||
- ps: git clone -q $("--branch=" + $Env:APPVEYOR_REPO_BRANCH) $("https://github.com/" + $Env:APPVEYOR_REPO_NAME + ".git") $Env:APPVEYOR_BUILD_FOLDER
|
||||
- ps: if (!$Env:APPVEYOR_PULL_REQUEST_NUMBER) {$("git checkout -qf " + $Env:APPVEYOR_REPO_COMMIT)}
|
||||
- ps: if ($Env:APPVEYOR_PULL_REQUEST_NUMBER) {git fetch -q origin +refs/pull/$($Env:APPVEYOR_PULL_REQUEST_NUMBER)/head; git checkout -qf FETCH_HEAD}
|
||||
|
||||
clone_folder: C:\projects\src\electron
|
||||
|
||||
skip_branch_with_pr: true
|
||||
@@ -65,8 +70,10 @@ for:
|
||||
- ps: |
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
node script/doc-only-change.js --prNumber=$env:APPVEYOR_PULL_REQUEST_NUMBER
|
||||
$env:SHOULD_SKIP_ARTIFACT_VALIDATION = "false"
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-warning "Skipping build for doc only change"
|
||||
Write-warning "Skipping build for doc-only change"
|
||||
$env:SHOULD_SKIP_ARTIFACT_VALIDATION = "true"
|
||||
Exit-AppveyorBuild
|
||||
} else {
|
||||
$global:LASTEXITCODE = 0
|
||||
@@ -83,6 +90,8 @@ for:
|
||||
Remove-Item -Recurse -Force $pwd\build-tools
|
||||
}
|
||||
- git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
- ps: New-Item -Name depot_tools\.disable_auto_update -ItemType File
|
||||
- depot_tools\bootstrap\win_tools.bat
|
||||
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"
|
||||
- ps: >-
|
||||
if (Test-Path -Path "$pwd\src\electron") {
|
||||
@@ -105,7 +114,7 @@ for:
|
||||
- ps: .\src\electron\script\start-goma.ps1 -gomaDir $env:LOCAL_GOMA_DIR
|
||||
- ps: >-
|
||||
if (Test-Path 'env:RAW_GOMA_AUTH') {
|
||||
$goma_login = python $env:LOCAL_GOMA_DIR\goma_auth.py info
|
||||
$goma_login = python3 $env:LOCAL_GOMA_DIR\goma_auth.py info
|
||||
if ($goma_login -eq 'Login as Fermi Planck') {
|
||||
Write-warning "Goma authentication is correct";
|
||||
} else {
|
||||
@@ -155,7 +164,7 @@ for:
|
||||
- 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
|
||||
- python %LOCAL_GOMA_DIR%\goma_ctl.py stat
|
||||
- python3 %LOCAL_GOMA_DIR%\goma_ctl.py stat
|
||||
- ps: >-
|
||||
Get-CimInstance -Namespace root\cimv2 -Class Win32_product | Select vendor, description, @{l='install_location';e='InstallLocation'}, @{l='install_date';e='InstallDate'}, @{l='install_date_2';e='InstallDate2'}, caption, version, name, @{l='sku_number';e='SKUNumber'} | ConvertTo-Json | Out-File -Encoding utf8 -FilePath .\installed_software.json
|
||||
- python3 electron/build/profile_toolchain.py --output-json=out/Default/windows_toolchain_profile.json
|
||||
@@ -176,6 +185,30 @@ for:
|
||||
7z a pdb.zip out\Default\*.pdb
|
||||
}
|
||||
- python3 electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
|
||||
- ps: |
|
||||
cd C:\projects\src
|
||||
$missing_artifacts = $false
|
||||
if ($env:SHOULD_SKIP_ARTIFACT_VALIDATION -eq 'true') {
|
||||
Write-warning "Skipping artifact validation for doc-only PR"
|
||||
} else {
|
||||
$artifacts_to_validate = 'dist.zip','windows_toolchain_profile.json','shell_browser_ui_unittests.exe','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib','hunspell_dictionaries.zip'
|
||||
foreach($artifact_name in $artifacts_to_validate) {
|
||||
if ($artifact_name -eq 'ffmpeg.zip') {
|
||||
$artifact_file = "out\ffmpeg\ffmpeg.zip"
|
||||
} elseif ($artifact_name -eq 'node_headers.zip') {
|
||||
$artifact_file = $artifact_name
|
||||
} else {
|
||||
$artifact_file = "out\Default\$artifact_name"
|
||||
}
|
||||
if (-not(Test-Path $artifact_file)) {
|
||||
Write-warning "$artifact_name is missing and cannot be added to artifacts"
|
||||
$missing_artifacts = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($missing_artifacts) {
|
||||
throw "Build failed due to missing artifacts"
|
||||
}
|
||||
|
||||
deploy_script:
|
||||
- cd electron
|
||||
@@ -201,7 +234,7 @@ for:
|
||||
- if exist node_headers.zip (appveyor-retry appveyor PushArtifact node_headers.zip)
|
||||
- if exist out\Default\mksnapshot.zip (appveyor-retry appveyor PushArtifact out\Default\mksnapshot.zip)
|
||||
- if exist out\Default\hunspell_dictionaries.zip (appveyor-retry appveyor PushArtifact out\Default\hunspell_dictionaries.zip)
|
||||
- if exist out\Default\electron.lib (appveyor-retry appveyor PushArtifact out\Default\electron.lib)
|
||||
- if exist out\Default\electron.lib (appveyor-retry appveyor PushArtifact out\Default\electron.lib)
|
||||
- ps: >-
|
||||
if ((Test-Path "pdb.zip") -And ($env:GN_CONFIG -ne 'release')) {
|
||||
appveyor-retry appveyor PushArtifact pdb.zip
|
||||
@@ -244,6 +277,9 @@ for:
|
||||
}
|
||||
Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/$artifact_name" -OutFile $outfile
|
||||
}
|
||||
# Uncomment the following lines to download the pdb.zip to show real stacktraces when crashes happen during testing
|
||||
# Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/pdb.zip" -OutFile pdb.zip
|
||||
# 7z x -y -osrc pdb.zip
|
||||
}
|
||||
}
|
||||
- ps: |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
is_electron_build = true
|
||||
root_extra_deps = [ "//electron" ]
|
||||
|
||||
# Registry of NMVs --> https://github.com/nodejs/node/blob/master/doc/abi_version_registry.json
|
||||
node_module_version = 114
|
||||
# Registry of NMVs --> https://github.com/nodejs/node/blob/main/doc/abi_version_registry.json
|
||||
node_module_version = 116
|
||||
|
||||
v8_promise_internal_field_count = 1
|
||||
v8_embedder_string = "-electron.0"
|
||||
@@ -45,3 +45,13 @@ is_cfi = false
|
||||
|
||||
# TODO: fix this once sysroots have been updated.
|
||||
use_qt = false
|
||||
|
||||
# https://chromium-review.googlesource.com/c/chromium/src/+/4365718
|
||||
# TODO(codebytere): fix perfetto incompatibility with Node.js.
|
||||
use_perfetto_client_library = false
|
||||
|
||||
# Ref: https://chromium-review.googlesource.com/c/chromium/src/+/4402277
|
||||
enable_check_raw_ptr_fields = false
|
||||
|
||||
# Disables the builtins PGO for V8
|
||||
v8_builtins_profiling_log_file = ""
|
||||
|
||||
@@ -174,6 +174,10 @@ static_library("chrome") {
|
||||
"//chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc",
|
||||
"//chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h",
|
||||
]
|
||||
sources += [
|
||||
"//chrome/browser/ui/views/dark_mode_manager_linux.cc",
|
||||
"//chrome/browser/ui/views/dark_mode_manager_linux.h",
|
||||
]
|
||||
public_deps += [
|
||||
"//components/dbus/menu",
|
||||
"//components/dbus/thread_linux",
|
||||
@@ -336,6 +340,34 @@ static_library("chrome") {
|
||||
"//components/pdf/renderer",
|
||||
]
|
||||
}
|
||||
} else {
|
||||
# These are required by the webRequest module.
|
||||
sources += [
|
||||
"//extensions/browser/api/declarative_net_request/request_action.cc",
|
||||
"//extensions/browser/api/declarative_net_request/request_action.h",
|
||||
"//extensions/browser/api/web_request/form_data_parser.cc",
|
||||
"//extensions/browser/api/web_request/form_data_parser.h",
|
||||
"//extensions/browser/api/web_request/upload_data_presenter.cc",
|
||||
"//extensions/browser/api/web_request/upload_data_presenter.h",
|
||||
"//extensions/browser/api/web_request/web_request_api_constants.cc",
|
||||
"//extensions/browser/api/web_request/web_request_api_constants.h",
|
||||
"//extensions/browser/api/web_request/web_request_info.cc",
|
||||
"//extensions/browser/api/web_request/web_request_info.h",
|
||||
"//extensions/browser/api/web_request/web_request_resource_type.cc",
|
||||
"//extensions/browser/api/web_request/web_request_resource_type.h",
|
||||
"//extensions/browser/extension_api_frame_id_map.cc",
|
||||
"//extensions/browser/extension_api_frame_id_map.h",
|
||||
"//extensions/browser/extension_navigation_ui_data.cc",
|
||||
"//extensions/browser/extension_navigation_ui_data.h",
|
||||
"//extensions/browser/extensions_browser_client.cc",
|
||||
"//extensions/browser/extensions_browser_client.h",
|
||||
"//extensions/browser/guest_view/web_view/web_view_renderer_state.cc",
|
||||
"//extensions/browser/guest_view/web_view/web_view_renderer_state.h",
|
||||
]
|
||||
|
||||
public_deps += [
|
||||
"//extensions/browser/api/declarative_net_request/flat:extension_ruleset",
|
||||
]
|
||||
}
|
||||
|
||||
if (!is_mas_build) {
|
||||
|
||||
@@ -55,7 +55,7 @@ The `Super` (or `Meta`) key is mapped to the `Windows` key on Windows and Linux
|
||||
* `0` to `9`
|
||||
* `A` to `Z`
|
||||
* `F1` to `F24`
|
||||
* Punctuation like `~`, `!`, `@`, `#`, `$`, etc.
|
||||
* Various Punctuation: `)`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `(`, `:`, `;`, `:`, `+`, `=`, `<`, `,`, `_`, `-`, `>`, `.`, `?`, `/`, `~`, `` ` ``, `{`, `]`, `[`, `|`, `\`, `}`, `"`
|
||||
* `Plus`
|
||||
* `Space`
|
||||
* `Tab`
|
||||
|
||||
@@ -150,9 +150,20 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted when mac application become active. Difference from `activate` event is
|
||||
Emitted when the application becomes active. This differs from the `activate` event in
|
||||
that `did-become-active` is emitted every time the app becomes active, not only
|
||||
when Dock icon is clicked or application is re-launched.
|
||||
when Dock icon is clicked or application is re-launched. It is also emitted when a user
|
||||
switches to the app via the macOS App Switcher.
|
||||
|
||||
### Event: 'did-resign-active' _macOS_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted when the app is no longer active and doesn’t have focus. This can be triggered,
|
||||
for example, by clicking on another application or by using the macOS App Switcher to
|
||||
switch to another application.
|
||||
|
||||
### Event: 'continue-activity' _macOS_
|
||||
|
||||
@@ -402,18 +413,7 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `details` Object
|
||||
* `reason` string - The reason the render process is gone. Possible values:
|
||||
* `clean-exit` - Process exited with an exit code of zero
|
||||
* `abnormal-exit` - Process exited with a non-zero exit code
|
||||
* `killed` - Process was sent a SIGTERM or otherwise killed externally
|
||||
* `crashed` - Process crashed
|
||||
* `oom` - Process ran out of memory
|
||||
* `launch-failed` - Process never successfully launched
|
||||
* `integrity-failure` - Windows code integrity checks failed
|
||||
* `exitCode` Integer - The exit code of the process, unless `reason` is
|
||||
`launch-failed`, in which case `exitCode` will be a platform-specific
|
||||
launch failure error code.
|
||||
* `details` [RenderProcessGoneDetails](structures/render-process-gone-details.md)
|
||||
|
||||
Emitted when the renderer process unexpectedly disappears. This is normally
|
||||
because it was crashed or killed.
|
||||
@@ -1135,11 +1135,11 @@ indicates success while any other value indicates failure according to Chromium
|
||||
resolver will attempt to use the system's DNS settings to do DNS lookups
|
||||
itself. Enabled by default on macOS, disabled by default on Windows and
|
||||
Linux.
|
||||
* `secureDnsMode` string (optional) - Can be "off", "automatic" or "secure".
|
||||
Configures the DNS-over-HTTP mode. When "off", no DoH lookups will be
|
||||
performed. When "automatic", DoH lookups will be performed first if DoH is
|
||||
* `secureDnsMode` string (optional) - Can be 'off', 'automatic' or 'secure'.
|
||||
Configures the DNS-over-HTTP mode. When 'off', no DoH lookups will be
|
||||
performed. When 'automatic', DoH lookups will be performed first if DoH is
|
||||
available, and insecure DNS lookups will be performed as a fallback. When
|
||||
"secure", only DoH lookups will be performed. Defaults to "automatic".
|
||||
'secure', only DoH lookups will be performed. Defaults to 'automatic'.
|
||||
* `secureDnsServers` string[] (optional) - A list of DNS-over-HTTP
|
||||
server templates. See [RFC8484 § 3][] for details on the template format.
|
||||
Most servers support the POST method; the template for such servers is
|
||||
|
||||
@@ -1110,10 +1110,14 @@ win.setBounds({ width: 100 })
|
||||
console.log(win.getBounds())
|
||||
```
|
||||
|
||||
**Note:** On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray.
|
||||
|
||||
#### `win.getBounds()`
|
||||
|
||||
Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window as `Object`.
|
||||
|
||||
**Note:** On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`.
|
||||
|
||||
#### `win.getBackgroundColor()`
|
||||
|
||||
Returns `string` - Gets the background color of the window in Hex (`#RRGGBB`) format.
|
||||
@@ -1854,17 +1858,6 @@ will remove the vibrancy effect on the window.
|
||||
Note that `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` have been
|
||||
deprecated and will be removed in an upcoming version of macOS.
|
||||
|
||||
#### `win.setTrafficLightPosition(position)` _macOS_
|
||||
|
||||
* `position` [Point](structures/point.md)
|
||||
|
||||
Set a custom position for the traffic light buttons in frameless window.
|
||||
|
||||
#### `win.getTrafficLightPosition()` _macOS_
|
||||
|
||||
Returns `Point` - The custom position for the traffic light buttons in
|
||||
frameless window.
|
||||
|
||||
#### `win.setBackgroundMaterial(material)` _Windows_
|
||||
|
||||
* `material` string
|
||||
@@ -1880,6 +1873,37 @@ See the [Windows documentation](https://learn.microsoft.com/en-us/windows/win32/
|
||||
|
||||
**Note:** This method is only supported on Windows 11 22H2 and up.
|
||||
|
||||
#### `win.setWindowButtonPosition(position)` _macOS_
|
||||
|
||||
* `position` [Point](structures/point.md) | null
|
||||
|
||||
Set a custom position for the traffic light buttons in frameless window.
|
||||
Passing `null` will reset the position to default.
|
||||
|
||||
#### `win.getWindowButtonPosition()` _macOS_
|
||||
|
||||
Returns `Point | null` - The custom position for the traffic light buttons in
|
||||
frameless window, `null` will be returned when there is no custom position.
|
||||
|
||||
#### `win.setTrafficLightPosition(position)` _macOS_ _Deprecated_
|
||||
|
||||
* `position` [Point](structures/point.md)
|
||||
|
||||
Set a custom position for the traffic light buttons in frameless window.
|
||||
Passing `{ x: 0, y: 0 }` will reset the position to default.
|
||||
|
||||
> **Note**
|
||||
> This function is deprecated. Use [setWindowButtonPosition](#winsetwindowbuttonpositionposition-macos) instead.
|
||||
|
||||
#### `win.getTrafficLightPosition()` _macOS_ _Deprecated_
|
||||
|
||||
Returns `Point` - The custom position for the traffic light buttons in
|
||||
frameless window, `{ x: 0, y: 0 }` will be returned when there is no custom
|
||||
position.
|
||||
|
||||
> **Note**
|
||||
> This function is deprecated. Use [getWindowButtonPosition](#wingetwindowbuttonposition-macos) instead.
|
||||
|
||||
#### `win.setTouchBar(touchBar)` _macOS_
|
||||
|
||||
* `touchBar` TouchBar | null
|
||||
|
||||
@@ -23,12 +23,14 @@ following properties:
|
||||
with which the request is associated. Defaults to the empty string. The
|
||||
`session` option supersedes `partition`. Thus if a `session` is explicitly
|
||||
specified, `partition` is ignored.
|
||||
* `credentials` string (optional) - Can be `include` or `omit`. Whether to
|
||||
send [credentials](https://fetch.spec.whatwg.org/#credentials) with this
|
||||
* `credentials` string (optional) - Can be `include`, `omit` or
|
||||
`same-origin`. Whether to send
|
||||
[credentials](https://fetch.spec.whatwg.org/#credentials) with this
|
||||
request. If set to `include`, credentials from the session associated with
|
||||
the request will be used. If set to `omit`, credentials will not be sent
|
||||
with the request (and the `'login'` event will not be triggered in the
|
||||
event of a 401). This matches the behavior of the
|
||||
event of a 401). If set to `same-origin`, `origin` must also be specified.
|
||||
This matches the behavior of the
|
||||
[fetch](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
|
||||
option of the same name. If this option is not specified, authentication
|
||||
data from the session will be sent, and cookies will not be sent (unless
|
||||
@@ -49,6 +51,13 @@ following properties:
|
||||
[`request.followRedirect`](#requestfollowredirect) is invoked synchronously
|
||||
during the [`redirect`](#event-redirect) event. Defaults to `follow`.
|
||||
* `origin` string (optional) - The origin URL of the request.
|
||||
* `referrerPolicy` string (optional) - can be `""`, `no-referrer`,
|
||||
`no-referrer-when-downgrade`, `origin`, `origin-when-cross-origin`,
|
||||
`unsafe-url`, `same-origin`, `strict-origin`, or
|
||||
`strict-origin-when-cross-origin`. Defaults to
|
||||
`strict-origin-when-cross-origin`.
|
||||
* `cache` string (optional) - can be `default`, `no-store`, `reload`,
|
||||
`no-cache`, `force-cache` or `only-if-cached`.
|
||||
|
||||
`options` properties such as `protocol`, `host`, `hostname`, `port` and `path`
|
||||
strictly follow the Node.js model as described in the
|
||||
@@ -234,6 +243,8 @@ it is not allowed to add or remove a custom header.
|
||||
* `encoding` string (optional)
|
||||
* `callback` Function (optional)
|
||||
|
||||
Returns `this`.
|
||||
|
||||
Sends the last chunk of the request data. Subsequent write or end operations
|
||||
will not be allowed. The `finish` event is emitted just after the end operation.
|
||||
|
||||
|
||||
@@ -116,14 +116,20 @@ Ignore the connections limit for `domains` list separated by `,`.
|
||||
|
||||
### --js-flags=`flags`
|
||||
|
||||
Specifies the flags passed to the Node.js engine. It has to be passed when starting
|
||||
Electron if you want to enable the `flags` in the main process.
|
||||
Specifies the flags passed to the [V8 engine](https://v8.dev). In order to enable the `flags` in the main process,
|
||||
this switch must be passed on startup.
|
||||
|
||||
```sh
|
||||
$ electron --js-flags="--harmony_proxies --harmony_collections" your-app
|
||||
```
|
||||
|
||||
See the [Node.js documentation][node-cli] or run `node --help` in your terminal for a list of available flags. Additionally, run `node --v8-options` to see a list of flags that specifically refer to Node.js's V8 JavaScript engine.
|
||||
Run `node --v8-options` or `electron --js-flags="--help"` in your terminal for the list of available flags. These can be used to enable early-stage JavaScript features, or log and manipulate garbage collection, among other things.
|
||||
|
||||
For example, to trace V8 optimization and deoptimization:
|
||||
|
||||
```sh
|
||||
$ electron --js-flags="--trace-opt --trace-deopt" your-app
|
||||
```
|
||||
|
||||
### --lang
|
||||
|
||||
@@ -241,19 +247,25 @@ Electron supports some of the [CLI flags][node-cli] supported by Node.js.
|
||||
|
||||
**Note:** Passing unsupported command line switches to Electron when it is not running in `ELECTRON_RUN_AS_NODE` will have no effect.
|
||||
|
||||
### --inspect-brk\[=\[host:]port]
|
||||
### `--inspect-brk\[=\[host:]port]`
|
||||
|
||||
Activate inspector on host:port and break at start of user script. Default host:port is 127.0.0.1:9229.
|
||||
|
||||
Aliased to `--debug-brk=[host:]port`.
|
||||
|
||||
### --inspect-port=\[host:]port
|
||||
#### `--inspect-brk-node[=[host:]port]`
|
||||
|
||||
Activate inspector on `host:port` and break at start of the first internal
|
||||
JavaScript script executed when the inspector is available.
|
||||
Default `host:port` is `127.0.0.1:9229`.
|
||||
|
||||
### `--inspect-port=\[host:]port`
|
||||
|
||||
Set the `host:port` to be used when the inspector is activated. Useful when activating the inspector by sending the SIGUSR1 signal. Default host is `127.0.0.1`.
|
||||
|
||||
Aliased to `--debug-port=[host:]port`.
|
||||
|
||||
### --inspect\[=\[host:]port]
|
||||
### `--inspect\[=\[host:]port]`
|
||||
|
||||
Activate inspector on `host:port`. Default is `127.0.0.1:9229`.
|
||||
|
||||
@@ -263,12 +275,37 @@ See the [Debugging the Main Process][debugging-main-process] guide for more deta
|
||||
|
||||
Aliased to `--debug[=[host:]port`.
|
||||
|
||||
### --inspect-publish-uid=stderr,http
|
||||
### `--inspect-publish-uid=stderr,http`
|
||||
|
||||
Specify ways of the inspector web socket url exposure.
|
||||
|
||||
By default inspector websocket url is available in stderr and under /json/list endpoint on http://host:port/json/list.
|
||||
|
||||
### `--no-deprecation`
|
||||
|
||||
Silence deprecation warnings.
|
||||
|
||||
### `--throw-deprecation`
|
||||
|
||||
Throw errors for deprecations.
|
||||
|
||||
### `--trace-deprecation`
|
||||
|
||||
Print stack traces for deprecations.
|
||||
|
||||
### `--trace-warnings`
|
||||
|
||||
Print stack traces for process warnings (including deprecations).
|
||||
|
||||
### `--dns-result-order=order`
|
||||
|
||||
Set the default value of the `verbatim` parameter in the Node.js [`dns.lookup()`](https://nodejs.org/api/dns.html#dnslookuphostname-options-callback) and [`dnsPromises.lookup()`](https://nodejs.org/api/dns.html#dnspromiseslookuphostname-options) functions. The value could be:
|
||||
|
||||
* `ipv4first`: sets default `verbatim` `false`.
|
||||
* `verbatim`: sets default `verbatim` `true`.
|
||||
|
||||
The default is `verbatim` and `dns.setDefaultResultOrder()` have higher priority than `--dns-result-order`.
|
||||
|
||||
[app]: app.md
|
||||
[append-switch]: command-line.md#commandlineappendswitchswitch-value
|
||||
[debugging-main-process]: ../tutorial/debugging-main-process.md
|
||||
|
||||
@@ -119,4 +119,8 @@ Removes the cookies matching `url` and `name`
|
||||
|
||||
Returns `Promise<void>` - A promise which resolves when the cookie store has been flushed
|
||||
|
||||
Writes any unwritten cookies data to disk.
|
||||
Writes any unwritten cookies data to disk
|
||||
|
||||
Cookies written by any method will not be written to disk immediately, but will be written every 30 seconds or 512 operations
|
||||
|
||||
Calling this method can cause the cookie to be written to disk immediately.
|
||||
|
||||
@@ -44,14 +44,20 @@ provisional and may be removed.
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/devtools_inspectedWindow) for more information.
|
||||
|
||||
### `chrome.devtools.network`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/devtools_network) for more information.
|
||||
|
||||
### `chrome.devtools.panels`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/devtools_panels) for more information.
|
||||
|
||||
### `chrome.extension`
|
||||
|
||||
The following properties of `chrome.extension` are supported:
|
||||
@@ -63,6 +69,25 @@ The following methods of `chrome.extension` are supported:
|
||||
- `chrome.extension.getURL`
|
||||
- `chrome.extension.getBackgroundPage`
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/extension) for more information.
|
||||
|
||||
### `chrome.management`
|
||||
|
||||
The following methods of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.getAll`
|
||||
- `chrome.management.get`
|
||||
- `chrome.management.getSelf`
|
||||
- `chrome.management.getPermissionWarningsById`
|
||||
- `chrome.management.getPermissionWarningsByManifest`
|
||||
|
||||
The following events of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.onEnabled`
|
||||
- `chrome.management.onDisabled`
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/management) for more information.
|
||||
|
||||
### `chrome.runtime`
|
||||
|
||||
The following properties of `chrome.runtime` are supported:
|
||||
@@ -89,10 +114,23 @@ The following events of `chrome.runtime` are supported:
|
||||
- `chrome.runtime.onConnect`
|
||||
- `chrome.runtime.onMessage`
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/runtime) for more information.
|
||||
|
||||
### `chrome.scripting`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/scripting) for more information.
|
||||
|
||||
### `chrome.storage`
|
||||
|
||||
Only `chrome.storage.local` is supported; `chrome.storage.sync` and
|
||||
`chrome.storage.managed` are not.
|
||||
The following methods of `chrome.storage` are supported:
|
||||
|
||||
- `chrome.storage.local`
|
||||
|
||||
`chrome.storage.sync` and `chrome.storage.managed` are **not** supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/storage) for more information.
|
||||
|
||||
### `chrome.tabs`
|
||||
|
||||
@@ -101,6 +139,8 @@ The following methods of `chrome.tabs` are supported:
|
||||
- `chrome.tabs.sendMessage`
|
||||
- `chrome.tabs.reload`
|
||||
- `chrome.tabs.executeScript`
|
||||
- `chrome.tabs.query` (partial support)
|
||||
- supported properties: `url`, `title`, `audible`, `active`, `muted`.
|
||||
- `chrome.tabs.update` (partial support)
|
||||
- supported properties: `url`, `muted`.
|
||||
|
||||
@@ -108,20 +148,12 @@ The following methods of `chrome.tabs` are supported:
|
||||
> tab". Since Electron has no such concept, passing `-1` as a tab ID is not
|
||||
> supported and will raise an error.
|
||||
|
||||
### `chrome.management`
|
||||
|
||||
The following methods of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.getAll`
|
||||
- `chrome.management.get`
|
||||
- `chrome.management.getSelf`
|
||||
- `chrome.management.getPermissionWarningsById`
|
||||
- `chrome.management.getPermissionWarningsByManifest`
|
||||
- `chrome.management.onEnabled`
|
||||
- `chrome.management.onDisabled`
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/tabs) for more information.
|
||||
|
||||
### `chrome.webRequest`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
> **NOTE:** Electron's [`webRequest`](web-request.md) module takes precedence over `chrome.webRequest` if there are conflicting handlers.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/webRequest) for more information.
|
||||
|
||||
@@ -72,7 +72,7 @@ Removes listeners of the specified `channel`.
|
||||
### `ipcMain.handle(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function<Promise\<void> | any>
|
||||
* `listener` Function<Promise\<any> | any>
|
||||
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
|
||||
* `...args` any[]
|
||||
|
||||
@@ -109,8 +109,8 @@ provided to the renderer process. Please refer to
|
||||
### `ipcMain.handleOnce(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function<Promise\<void> | any>
|
||||
* `event` IpcMainInvokeEvent
|
||||
* `listener` Function<Promise\<any> | any>
|
||||
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
|
||||
* `...args` any[]
|
||||
|
||||
Handles a single `invoke`able IPC message, then removes the listener. See
|
||||
@@ -122,17 +122,6 @@ Handles a single `invoke`able IPC message, then removes the listener. See
|
||||
|
||||
Removes any handler for `channel`, if present.
|
||||
|
||||
## IpcMainEvent object
|
||||
|
||||
The documentation for the `event` object passed to the `callback` can be found
|
||||
in the [`ipc-main-event`][ipc-main-event] structure docs.
|
||||
|
||||
## IpcMainInvokeEvent object
|
||||
|
||||
The documentation for the `event` object passed to `handle` callbacks can be
|
||||
found in the [`ipc-main-invoke-event`][ipc-main-invoke-event]
|
||||
structure docs.
|
||||
|
||||
[IPC tutorial]: ../tutorial/ipc.md
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[web-contents-send]: ../api/web-contents.md#contentssendchannel-args
|
||||
|
||||
@@ -26,7 +26,7 @@ The `ipcRenderer` module has the following method to listen for events and send
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function
|
||||
* `event` IpcRendererEvent
|
||||
* `event` [IpcRendererEvent][ipc-renderer-event]
|
||||
* `...args` any[]
|
||||
|
||||
Listens to `channel`, when a new message arrives `listener` would be called with
|
||||
@@ -36,7 +36,7 @@ Listens to `channel`, when a new message arrives `listener` would be called with
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function
|
||||
* `event` IpcRendererEvent
|
||||
* `event` [IpcRendererEvent][ipc-renderer-event]
|
||||
* `...args` any[]
|
||||
|
||||
Adds a one time `listener` function for the event. This `listener` is invoked
|
||||
@@ -208,12 +208,8 @@ Sends a message to a window with `webContentsId` via `channel`.
|
||||
Like `ipcRenderer.send` but the event will be sent to the `<webview>` element in
|
||||
the host page instead of the main process.
|
||||
|
||||
## Event object
|
||||
|
||||
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
|
||||
[`window.postMessage`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
|
||||
[`MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
|
||||
[ipc-renderer-event]: ./structures/ipc-renderer-event.md
|
||||
|
||||
@@ -63,6 +63,62 @@ Creates a [`ClientRequest`](./client-request.md) instance using the provided
|
||||
The `net.request` method would be used to issue both secure and insecure HTTP
|
||||
requests according to the specified protocol scheme in the `options` object.
|
||||
|
||||
### `net.fetch(input[, init])`
|
||||
|
||||
* `input` string | [GlobalRequest](https://nodejs.org/api/globals.html#request)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) & { bypassCustomProtocolHandlers?: boolean } (optional)
|
||||
|
||||
Returns `Promise<GlobalResponse>` - see [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
||||
|
||||
Sends a request, similarly to how `fetch()` works in the renderer, using
|
||||
Chrome's network stack. This differs from Node's `fetch()`, which uses
|
||||
Node.js's HTTP stack.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
async function example () {
|
||||
const response = await net.fetch('https://my.app')
|
||||
if (response.ok) {
|
||||
const body = await response.json()
|
||||
// ... use the result.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This method will issue requests from the [default
|
||||
session](session.md#sessiondefaultsession). To send a `fetch` request from
|
||||
another session, use [ses.fetch()](session.md#sesfetchinput-init).
|
||||
|
||||
See the MDN documentation for
|
||||
[`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) for more
|
||||
details.
|
||||
|
||||
Limitations:
|
||||
|
||||
* `net.fetch()` does not support the `data:` or `blob:` schemes.
|
||||
* The value of the `integrity` option is ignored.
|
||||
* The `.type` and `.url` values of the returned `Response` object are
|
||||
incorrect.
|
||||
|
||||
By default, requests made with `net.fetch` can be made to [custom
|
||||
protocols](protocol.md) as well as `file:`, and will trigger
|
||||
[webRequest](web-request.md) handlers if present. When the non-standard
|
||||
`bypassCustomProtocolHandlers` option is set in RequestInit, custom protocol
|
||||
handlers will not be called for this request. This allows forwarding an
|
||||
intercepted request to the built-in handler. [webRequest](web-request.md)
|
||||
handlers will still be triggered when bypassing custom protocols.
|
||||
|
||||
```js
|
||||
protocol.handle('https', (req) => {
|
||||
if (req.url === 'https://my-app.com') {
|
||||
return new Response('<body>my app</body>')
|
||||
} else {
|
||||
return net.fetch(req, { bypassCustomProtocolHandlers: true })
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `net.isOnline()`
|
||||
|
||||
Returns `boolean` - Whether there is currently internet connection.
|
||||
|
||||
@@ -49,6 +49,8 @@ is used.
|
||||
|
||||
Stops the specified power save blocker.
|
||||
|
||||
Returns `boolean` - Whether the specified `powerSaveBlocker` has been stopped.
|
||||
|
||||
### `powerSaveBlocker.isStarted(id)`
|
||||
|
||||
* `id` Integer - The power save blocker id returned by `powerSaveBlocker.start`.
|
||||
|
||||
@@ -49,8 +49,11 @@ beginning to load the web page or the main script.
|
||||
|
||||
### `process.defaultApp` _Readonly_
|
||||
|
||||
A `boolean`. When app is started by being passed as parameter to the default app, this
|
||||
A `boolean`. When the app is started by being passed as parameter to the default Electron executable, this
|
||||
property is `true` in the main process, otherwise it is `undefined`.
|
||||
For example when running the app with `electron .`, it is `true`,
|
||||
even if the app is packaged ([`isPackaged`](app.md#appispackaged-readonly)) is `true`.
|
||||
This can be useful to determine how many arguments will need to be sliced off from `process.argv`.
|
||||
|
||||
### `process.isMainFrame` _Readonly_
|
||||
|
||||
|
||||
@@ -8,15 +8,11 @@ An example of implementing a protocol that has the same effect as the
|
||||
`file://` protocol:
|
||||
|
||||
```javascript
|
||||
const { app, protocol } = require('electron')
|
||||
const path = require('path')
|
||||
const url = require('url')
|
||||
const { app, protocol, net } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
protocol.registerFileProtocol('atom', (request, callback) => {
|
||||
const filePath = url.fileURLToPath('file://' + request.url.slice('atom://'.length))
|
||||
callback(filePath)
|
||||
})
|
||||
protocol.handle('atom', (request) =>
|
||||
net.fetch('file://' + request.url.slice('atom://'.length)))
|
||||
})
|
||||
```
|
||||
|
||||
@@ -38,14 +34,15 @@ to register it to that session explicitly.
|
||||
```javascript
|
||||
const { app, BrowserWindow, net, protocol, session } = require('electron')
|
||||
const path = require('path')
|
||||
const url = require('url')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const partition = 'persist:example'
|
||||
const ses = session.fromPartition(partition)
|
||||
|
||||
ses.protocol.registerFileProtocol('atom', (request, callback) => {
|
||||
const url = request.url.substr(7)
|
||||
callback({ path: path.normalize(path.join(__dirname, url)) })
|
||||
ses.protocol.handle('atom', (request) => {
|
||||
const filePath = request.url.slice('atom://'.length)
|
||||
return net.fetch(url.pathToFileURL(path.join(__dirname, filePath)).toString())
|
||||
})
|
||||
|
||||
const mainWindow = new BrowserWindow({ webPreferences: { partition } })
|
||||
@@ -109,7 +106,74 @@ The `<video>` and `<audio>` HTML elements expect protocols to buffer their
|
||||
responses by default. The `stream` flag configures those elements to correctly
|
||||
expect streaming responses.
|
||||
|
||||
### `protocol.registerFileProtocol(scheme, handler)`
|
||||
### `protocol.handle(scheme, handler)`
|
||||
|
||||
* `scheme` string - scheme to handle, for example `https` or `my-app`. This is
|
||||
the bit before the `:` in a URL.
|
||||
* `handler` Function<[GlobalResponse](https://nodejs.org/api/globals.html#response) | Promise<GlobalResponse>>
|
||||
* `request` [GlobalRequest](https://nodejs.org/api/globals.html#request)
|
||||
|
||||
Register a protocol handler for `scheme`. Requests made to URLs with this
|
||||
scheme will delegate to this handler to determine what response should be sent.
|
||||
|
||||
Either a `Response` or a `Promise<Response>` can be returned.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
const { app, net, protocol } = require('electron')
|
||||
const { join } = require('path')
|
||||
const { pathToFileURL } = require('url')
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme: 'app',
|
||||
privileges: {
|
||||
standard: true,
|
||||
secure: true,
|
||||
supportFetchAPI: true
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
app.whenReady().then(() => {
|
||||
protocol.handle('app', (req) => {
|
||||
const { host, pathname } = new URL(req.url)
|
||||
if (host === 'bundle') {
|
||||
if (pathname === '/') {
|
||||
return new Response('<h1>hello, world</h1>', {
|
||||
headers: { 'content-type': 'text/html' }
|
||||
})
|
||||
}
|
||||
// NB, this does not check for paths that escape the bundle, e.g.
|
||||
// app://bundle/../../secret_file.txt
|
||||
return net.fetch(pathToFileURL(join(__dirname, pathname)).toString())
|
||||
} else if (host === 'api') {
|
||||
return net.fetch('https://api.my-server.com/' + pathname, {
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
body: req.body
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
See the MDN docs for [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) for more details.
|
||||
|
||||
### `protocol.unhandle(scheme)`
|
||||
|
||||
* `scheme` string - scheme for which to remove the handler.
|
||||
|
||||
Removes a protocol handler registered with `protocol.handle`.
|
||||
|
||||
### `protocol.isProtocolHandled(scheme)`
|
||||
|
||||
* `scheme` string
|
||||
|
||||
Returns `boolean` - Whether `scheme` is already handled.
|
||||
|
||||
### `protocol.registerFileProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -130,7 +194,7 @@ path or an object that has a `path` property, e.g. `callback(filePath)` or
|
||||
By default the `scheme` is treated like `http:`, which is parsed differently
|
||||
from protocols that follow the "generic URI syntax" like `file:`.
|
||||
|
||||
### `protocol.registerBufferProtocol(scheme, handler)`
|
||||
### `protocol.registerBufferProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -154,7 +218,7 @@ protocol.registerBufferProtocol('atom', (request, callback) => {
|
||||
})
|
||||
```
|
||||
|
||||
### `protocol.registerStringProtocol(scheme, handler)`
|
||||
### `protocol.registerStringProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -170,7 +234,7 @@ The usage is the same with `registerFileProtocol`, except that the `callback`
|
||||
should be called with either a `string` or an object that has the `data`
|
||||
property.
|
||||
|
||||
### `protocol.registerHttpProtocol(scheme, handler)`
|
||||
### `protocol.registerHttpProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -185,7 +249,7 @@ Registers a protocol of `scheme` that will send an HTTP request as a response.
|
||||
The usage is the same with `registerFileProtocol`, except that the `callback`
|
||||
should be called with an object that has the `url` property.
|
||||
|
||||
### `protocol.registerStreamProtocol(scheme, handler)`
|
||||
### `protocol.registerStreamProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -234,7 +298,7 @@ protocol.registerStreamProtocol('atom', (request, callback) => {
|
||||
})
|
||||
```
|
||||
|
||||
### `protocol.unregisterProtocol(scheme)`
|
||||
### `protocol.unregisterProtocol(scheme)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
|
||||
@@ -242,13 +306,13 @@ Returns `boolean` - Whether the protocol was successfully unregistered
|
||||
|
||||
Unregisters the custom protocol of `scheme`.
|
||||
|
||||
### `protocol.isProtocolRegistered(scheme)`
|
||||
### `protocol.isProtocolRegistered(scheme)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
|
||||
Returns `boolean` - Whether `scheme` is already registered.
|
||||
|
||||
### `protocol.interceptFileProtocol(scheme, handler)`
|
||||
### `protocol.interceptFileProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -261,7 +325,7 @@ Returns `boolean` - Whether the protocol was successfully intercepted
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a file as a response.
|
||||
|
||||
### `protocol.interceptStringProtocol(scheme, handler)`
|
||||
### `protocol.interceptStringProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -274,7 +338,7 @@ Returns `boolean` - Whether the protocol was successfully intercepted
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a `string` as a response.
|
||||
|
||||
### `protocol.interceptBufferProtocol(scheme, handler)`
|
||||
### `protocol.interceptBufferProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -287,7 +351,7 @@ Returns `boolean` - Whether the protocol was successfully intercepted
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a `Buffer` as a response.
|
||||
|
||||
### `protocol.interceptHttpProtocol(scheme, handler)`
|
||||
### `protocol.interceptHttpProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -300,7 +364,7 @@ Returns `boolean` - Whether the protocol was successfully intercepted
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a new HTTP request as a response.
|
||||
|
||||
### `protocol.interceptStreamProtocol(scheme, handler)`
|
||||
### `protocol.interceptStreamProtocol(scheme, handler)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
* `handler` Function
|
||||
@@ -313,7 +377,7 @@ Returns `boolean` - Whether the protocol was successfully intercepted
|
||||
Same as `protocol.registerStreamProtocol`, except that it replaces an existing
|
||||
protocol handler.
|
||||
|
||||
### `protocol.uninterceptProtocol(scheme)`
|
||||
### `protocol.uninterceptProtocol(scheme)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
|
||||
@@ -321,7 +385,7 @@ Returns `boolean` - Whether the protocol was successfully unintercepted
|
||||
|
||||
Remove the interceptor installed for `scheme` and restore its original handler.
|
||||
|
||||
### `protocol.isProtocolIntercepted(scheme)`
|
||||
### `protocol.isProtocolIntercepted(scheme)` _Deprecated_
|
||||
|
||||
* `scheme` string
|
||||
|
||||
|
||||
@@ -38,3 +38,31 @@ Returns `string` - the decrypted string. Decrypts the encrypted buffer
|
||||
obtained with `safeStorage.encryptString` back into a string.
|
||||
|
||||
This function will throw an error if decryption fails.
|
||||
|
||||
### `safeStorage.setUsePlainTextEncryption(usePlainText)`
|
||||
|
||||
* `usePlainText` boolean
|
||||
|
||||
This function on Linux will force the module to use an in memory password for creating
|
||||
symmetric key that is used for encrypt/decrypt functions when a valid OS password
|
||||
manager cannot be determined for the current active desktop environment. This function
|
||||
is a no-op on Windows and MacOS.
|
||||
|
||||
### `safeStorage.getSelectedStorageBackend()` _Linux_
|
||||
|
||||
Returns `string` - User friendly name of the password manager selected on Linux.
|
||||
|
||||
This function will return one of the following values:
|
||||
|
||||
* `basic_text` - When the desktop environment is not recognised or if the following
|
||||
command line flag is provided `--password-store="basic"`.
|
||||
* `gnome_any` - When the desktop environment is `X-Cinnamon`, `Deepin`, `GNOME`, `Pantheon`, `XFCE`, `UKUI`, `unity` or if the following command line flag is provided `--password-store="gnome"`. When this value is present the application
|
||||
will first try to use `libsecret` backend and if it fails will attempt to use `libgnome_keyring`.
|
||||
* `gnome_libsecret` - When the following command line flag is provided `--password-store="gnome-libsecret"`.
|
||||
* `gnome_keyring` - When the following command line flag is provided `--password-store="gnome-keyring"`.
|
||||
* `kwallet` - When the desktop session is `kde4` or if the following command line flag
|
||||
is provided `--password-store="kwallet"`.
|
||||
* `kwallet5` - When the desktop session is `kde5` or if the following command line flag
|
||||
is provided `--password-store="kwallet5"`.
|
||||
* `kwallet6` - When the desktop session is `kde6`.
|
||||
* `unknown` - When the function is called before app has emitted the `ready` event.
|
||||
|
||||
@@ -42,6 +42,22 @@ To create a `Session` with `options`, you have to ensure the `Session` with the
|
||||
`partition` has never been used before. There is no way to change the `options`
|
||||
of an existing `Session` object.
|
||||
|
||||
### `session.fromPath(path[, options])`
|
||||
|
||||
* `path` string
|
||||
* `options` Object (optional)
|
||||
* `cache` boolean - Whether to enable cache.
|
||||
|
||||
Returns `Session` - A session instance from the absolute path as specified by the `path`
|
||||
string. When there is an existing `Session` with the same absolute path, it
|
||||
will be returned; otherwise a new `Session` instance will be created with `options`. The
|
||||
call will throw an error if the path is not an absolute path. Additionally, an error will
|
||||
be thrown if an empty string is provided.
|
||||
|
||||
To create a `Session` with `options`, you have to ensure the `Session` with the
|
||||
`path` has never been used before. There is no way to change the `options`
|
||||
of an existing `Session` object.
|
||||
|
||||
## Properties
|
||||
|
||||
The `session` module has the following properties:
|
||||
@@ -503,9 +519,8 @@ app.whenReady().then(() => {
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Object
|
||||
* `device` [USBDevice](structures/usb-device.md)
|
||||
* `frame` [WebFrameMain](web-frame-main.md)
|
||||
* `device` [USBDevice](structures/usb-device.md)
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
|
||||
Emitted after `navigator.usb.requestDevice` has been called and
|
||||
`select-usb-device` has fired if a new device becomes available before
|
||||
@@ -518,9 +533,8 @@ with the newly added device.
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Object
|
||||
* `device` [USBDevice](structures/usb-device.md)
|
||||
* `frame` [WebFrameMain](web-frame-main.md)
|
||||
* `device` [USBDevice](structures/usb-device.md)
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
|
||||
Emitted after `navigator.usb.requestDevice` has been called and
|
||||
`select-usb-device` has fired if a device has been removed before the callback
|
||||
@@ -534,7 +548,7 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Object
|
||||
* `device` [USBDevice[]](structures/usb-device.md)
|
||||
* `device` [USBDevice](structures/usb-device.md)
|
||||
* `origin` string (optional) - The origin that the device has been revoked from.
|
||||
|
||||
Emitted after `USBDevice.forget()` has been called. This event can be used
|
||||
@@ -768,6 +782,61 @@ Returns `Promise<void>` - Resolves when all connections are closed.
|
||||
|
||||
**Note:** It will terminate / fail all requests currently in flight.
|
||||
|
||||
#### `ses.fetch(input[, init])`
|
||||
|
||||
* `input` string | [GlobalRequest](https://nodejs.org/api/globals.html#request)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) & { bypassCustomProtocolHandlers?: boolean } (optional)
|
||||
|
||||
Returns `Promise<GlobalResponse>` - see [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
||||
|
||||
Sends a request, similarly to how `fetch()` works in the renderer, using
|
||||
Chrome's network stack. This differs from Node's `fetch()`, which uses
|
||||
Node.js's HTTP stack.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
async function example () {
|
||||
const response = await net.fetch('https://my.app')
|
||||
if (response.ok) {
|
||||
const body = await response.json()
|
||||
// ... use the result.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See also [`net.fetch()`](net.md#netfetchinput-init), a convenience method which
|
||||
issues requests from the [default session](#sessiondefaultsession).
|
||||
|
||||
See the MDN documentation for
|
||||
[`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) for more
|
||||
details.
|
||||
|
||||
Limitations:
|
||||
|
||||
* `net.fetch()` does not support the `data:` or `blob:` schemes.
|
||||
* The value of the `integrity` option is ignored.
|
||||
* The `.type` and `.url` values of the returned `Response` object are
|
||||
incorrect.
|
||||
|
||||
By default, requests made with `net.fetch` can be made to [custom
|
||||
protocols](protocol.md) as well as `file:`, and will trigger
|
||||
[webRequest](web-request.md) handlers if present. When the non-standard
|
||||
`bypassCustomProtocolHandlers` option is set in RequestInit, custom protocol
|
||||
handlers will not be called for this request. This allows forwarding an
|
||||
intercepted request to the built-in handler. [webRequest](web-request.md)
|
||||
handlers will still be triggered when bypassing custom protocols.
|
||||
|
||||
```js
|
||||
protocol.handle('https', (req) => {
|
||||
if (req.url === 'https://my-app.com') {
|
||||
return new Response('<body>my app</body>')
|
||||
} else {
|
||||
return net.fetch(req, { bypassCustomProtocolHandlers: true })
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### `ses.disableNetworkEmulation()`
|
||||
|
||||
Disables any network emulation already active for the `session`. Resets to
|
||||
@@ -822,18 +891,19 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||
* `permission` string - The type of requested permission.
|
||||
* `clipboard-read` - Request access to read from the clipboard.
|
||||
* `clipboard-sanitized-write` - Request access to write to the clipboard.
|
||||
* `display-capture` - Request access to capture the screen via the [Screen Capture API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API).
|
||||
* `fullscreen` - Request control of the app's fullscreen state via the [Fullscreen API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API).
|
||||
* `geolocation` - Request access to the user's location via the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API)
|
||||
* `idle-detection` - Request access to the user's idle state via the [IdleDetector API](https://developer.mozilla.org/en-US/docs/Web/API/IdleDetector).
|
||||
* `media` - Request access to media devices such as camera, microphone and speakers.
|
||||
* `display-capture` - Request access to capture the screen.
|
||||
* `mediaKeySystem` - Request access to DRM protected content.
|
||||
* `geolocation` - Request access to user's current location.
|
||||
* `notifications` - Request notification creation and the ability to display them in the user's system tray.
|
||||
* `midi` - Request MIDI access in the `webmidi` API.
|
||||
* `midiSysex` - Request the use of system exclusive messages in the `webmidi` API.
|
||||
* `pointerLock` - Request to directly interpret mouse movements as an input method. Click [here](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API) to know more. These requests always appear to originate from the main frame.
|
||||
* `fullscreen` - Request for the app to enter fullscreen mode.
|
||||
* `midi` - Request MIDI access in the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API).
|
||||
* `midiSysex` - Request the use of system exclusive messages in the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API).
|
||||
* `notifications` - Request notification creation and the ability to display them in the user's system tray using the [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/notification)
|
||||
* `pointerLock` - Request to directly interpret mouse movements as an input method via the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API). These requests always appear to originate from the main frame.
|
||||
* `openExternal` - Request to open links in external applications.
|
||||
* `window-management` - Request access to enumerate screens using the [`getScreenDetails`](https://developer.chrome.com/en/articles/multi-screen-window-placement/) API.
|
||||
* `unknown` - An unrecognized permission request
|
||||
* `unknown` - An unrecognized permission request.
|
||||
* `callback` Function
|
||||
* `permissionGranted` boolean - Allow or deny the permission.
|
||||
* `details` Object - Some properties are only available on certain permission types.
|
||||
@@ -865,7 +935,22 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents
|
||||
|
||||
* `handler` Function\<boolean> | null
|
||||
* `webContents` ([WebContents](web-contents.md) | null) - WebContents checking the permission. Please note that if the request comes from a subframe you should use `requestingUrl` to check the request origin. All cross origin sub frames making permission checks will pass a `null` webContents to this handler, while certain other permission checks such as `notifications` checks will always pass `null`. You should use `embeddingOrigin` and `requestingOrigin` to determine what origin the owning frame and the requesting frame are on respectively.
|
||||
* `permission` string - Type of permission check. Valid values are `midiSysex`, `notifications`, `geolocation`, `media`,`mediaKeySystem`,`midi`, `pointerLock`, `fullscreen`, `openExternal`, `hid`, `serial`, or `usb`.
|
||||
* `permission` string - Type of permission check.
|
||||
* `clipboard-read` - Request access to read from the clipboard.
|
||||
* `clipboard-sanitized-write` - Request access to write to the clipboard.
|
||||
* `geolocation` - Access the user's geolocation data via the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API)
|
||||
* `fullscreen` - Control of the app's fullscreen state via the [Fullscreen API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API).
|
||||
* `hid` - Access the HID protocol to manipulate HID devices via the [WebHID API](https://developer.mozilla.org/en-US/docs/Web/API/WebHID_API).
|
||||
* `idle-detection` - Access the user's idle state via the [IdleDetector API](https://developer.mozilla.org/en-US/docs/Web/API/IdleDetector).
|
||||
* `media` - Access to media devices such as camera, microphone and speakers.
|
||||
* `mediaKeySystem` - Access to DRM protected content.
|
||||
* `midi` - Enable MIDI access in the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API).
|
||||
* `midiSysex` - Use system exclusive messages in the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API).
|
||||
* `notifications` - Configure and display desktop notifications to the user with the [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/notification).
|
||||
* `openExternal` - Open links in external applications.
|
||||
* `pointerLock` - Directly interpret mouse movements as an input method via the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API). These requests always appear to originate from the main frame.
|
||||
* `serial` - Read from and write to serial devices with the [Web Serial API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API).
|
||||
* `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
|
||||
* `requestingOrigin` string - The origin URL of the permission check
|
||||
* `details` Object - Some properties are only available on certain permission types.
|
||||
* `embeddingOrigin` string (optional) - The origin of the frame embedding the frame that made the permission check. Only set for cross-origin sub frames making permission checks.
|
||||
@@ -1215,16 +1300,18 @@ reused for new connections.
|
||||
|
||||
Returns `Promise<Buffer>` - resolves with blob data.
|
||||
|
||||
#### `ses.downloadURL(url)`
|
||||
#### `ses.downloadURL(url[, options])`
|
||||
|
||||
* `url` string
|
||||
* `options` Object (optional)
|
||||
* `headers` Record<string, string> (optional) - HTTP request headers.
|
||||
|
||||
Initiates a download of the resource at `url`.
|
||||
The API will generate a [DownloadItem](download-item.md) that can be accessed
|
||||
with the [will-download](#event-will-download) event.
|
||||
|
||||
**Note:** This does not perform any security checks that relate to a page's origin,
|
||||
unlike [`webContents.downloadURL`](web-contents.md#contentsdownloadurlurl).
|
||||
unlike [`webContents.downloadURL`](web-contents.md#contentsdownloadurlurl-options).
|
||||
|
||||
#### `ses.createInterruptedDownload(options)`
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Event Object extends `GlobalEvent`
|
||||
|
||||
* `preventDefault` VoidFunction
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
* `sender` [IpcRenderer](../ipc-renderer.md) - The `IpcRenderer` instance that emitted the event originally
|
||||
* `senderId` Integer - The `webContents.id` that sent the message, you can call `event.sender.sendTo(event.senderId, ...)` to reply to the message, see [ipcRenderer.sendTo][ipc-renderer-sendto] for more information. This only applies to messages sent from a different renderer. Messages sent directly from the main process set `event.senderId` to `0`.
|
||||
* `senderIsMainFrame` boolean (optional) - Whether the message sent via [ipcRenderer.sendTo][ipc-renderer-sendto] was sent by the main frame. This is relevant when `nodeIntegrationInSubFrames` is enabled in the originating `webContents`.
|
||||
* `ports` [MessagePort][][] - A list of MessagePorts that were transferred with this message
|
||||
|
||||
[ipc-renderer-sendto]: ../ipc-renderer.md#ipcrenderersendtowebcontentsid-channel-args
|
||||
|
||||
13
docs/api/structures/render-process-gone-details.md
Normal file
13
docs/api/structures/render-process-gone-details.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# RenderProcessGoneDetails Object
|
||||
|
||||
* `reason` string - The reason the render process is gone. Possible values:
|
||||
* `clean-exit` - Process exited with an exit code of zero
|
||||
* `abnormal-exit` - Process exited with a non-zero exit code
|
||||
* `killed` - Process was sent a SIGTERM or otherwise killed externally
|
||||
* `crashed` - Process crashed
|
||||
* `oom` - Process ran out of memory
|
||||
* `launch-failed` - Process never successfully launched
|
||||
* `integrity-failure` - Windows code integrity checks failed
|
||||
* `exitCode` Integer - The exit code of the process, unless `reason` is
|
||||
`launch-failed`, in which case `exitCode` will be a platform-specific
|
||||
launch failure error code.
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
* `type` 'file' - `file`.
|
||||
* `filePath` string - Path of file to be uploaded.
|
||||
* `offset` Integer - Defaults to `0`.
|
||||
* `length` Integer - Number of bytes to read from `offset`.
|
||||
* `offset` Integer (optional) - Defaults to `0`.
|
||||
* `length` Integer (optional) - Number of bytes to read from `offset`.
|
||||
Defaults to `0`.
|
||||
* `modificationTime` Double - Last Modification time in
|
||||
number of seconds since the UNIX epoch.
|
||||
* `modificationTime` Double (optional) - Last Modification time in
|
||||
number of seconds since the UNIX epoch. Defaults to `0`.
|
||||
|
||||
@@ -6,7 +6,7 @@ Process: [Main](../glossary.md#main-process)
|
||||
|
||||
```javascript
|
||||
const { systemPreferences } = require('electron')
|
||||
console.log(systemPreferences.isDarkMode())
|
||||
console.log(systemPreferences.isAeroGlassEnabled())
|
||||
```
|
||||
|
||||
## Events
|
||||
@@ -47,12 +47,6 @@ Returns:
|
||||
|
||||
## Methods
|
||||
|
||||
### `systemPreferences.isDarkMode()` _macOS_ _Windows_ _Deprecated_
|
||||
|
||||
Returns `boolean` - Whether the system is in Dark Mode.
|
||||
|
||||
**Deprecated:** Should use the new [`nativeTheme.shouldUseDarkColors`](native-theme.md#nativethemeshouldusedarkcolors-readonly) API.
|
||||
|
||||
### `systemPreferences.isSwipeTrackingFromScrollEventsEnabled()` _macOS_
|
||||
|
||||
Returns `boolean` - Whether the Swipe between pages setting is on.
|
||||
@@ -297,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. _deprecated_
|
||||
* `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.
|
||||
@@ -356,18 +350,6 @@ Returns `string` - The standard system color formatted as `#RRGGBBAA`.
|
||||
|
||||
Returns one of several standard system colors that automatically adapt to vibrancy and changes in accessibility settings like 'Increase contrast' and 'Reduce transparency'. See [Apple Documentation](https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/color#system-colors) for more details.
|
||||
|
||||
### `systemPreferences.isInvertedColorScheme()` _Windows_ _Deprecated_
|
||||
|
||||
Returns `boolean` - `true` if an inverted color scheme (a high contrast color scheme with light text and dark backgrounds) is active, `false` otherwise.
|
||||
|
||||
**Deprecated:** Should use the new [`nativeTheme.shouldUseInvertedColorScheme`](native-theme.md#nativethemeshoulduseinvertedcolorscheme-macos-windows-readonly) API.
|
||||
|
||||
### `systemPreferences.isHighContrastColorScheme()` _macOS_ _Windows_ _Deprecated_
|
||||
|
||||
Returns `boolean` - `true` if a high contrast theme is active, `false` otherwise.
|
||||
|
||||
**Deprecated:** Should use the new [`nativeTheme.shouldUseHighContrastColors`](native-theme.md#nativethemeshouldusehighcontrastcolors-macos-windows-readonly) API.
|
||||
|
||||
### `systemPreferences.getEffectiveAppearance()` _macOS_
|
||||
|
||||
Returns `string` - Can be `dark`, `light` or `unknown`.
|
||||
|
||||
@@ -29,7 +29,7 @@ Process: [Main](../glossary.md#main-process)<br />
|
||||
* `inherit`: equivalent to \['ignore', 'inherit', 'inherit']
|
||||
* `serviceName` string (optional) - Name of the process that will appear in `name` property of
|
||||
[`child-process-gone` event of `app`](app.md#event-child-process-gone).
|
||||
Default is `node.mojom.NodeService`.
|
||||
Default is `Node Utility Process`.
|
||||
* `allowLoadingUnsignedLibraries` boolean (optional) _macOS_ - With this flag, the utility process will be
|
||||
launched via the `Electron Helper (Plugin).app` helper executable on macOS, which can be
|
||||
codesigned with `com.apple.security.cs.disable-library-validation` and
|
||||
|
||||
@@ -19,6 +19,36 @@ const contents = win.webContents
|
||||
console.log(contents)
|
||||
```
|
||||
|
||||
## Navigation Events
|
||||
|
||||
Several events can be used to monitor navigations as they occur within a `webContents`.
|
||||
|
||||
### Document Navigations
|
||||
|
||||
When a `webContents` navigates to another page (as opposed to an [in-page navigation](web-contents.md#in-page-navigation)), the following events will be fired.
|
||||
|
||||
* [`did-start-navigation`](web-contents.md#event-did-start-navigation)
|
||||
* [`will-frame-navigate`](web-contents.md#event-will-frame-navigate)
|
||||
* [`will-navigate`](web-contents.md#event-will-navigate) (only fired when main frame navigates)
|
||||
* [`will-redirect`](web-contents.md#event-will-redirect) (only fired when a redirect happens during navigation)
|
||||
* [`did-redirect-navigation`](web-contents.md#event-did-redirect-navigation) (only fired when a redirect happens during navigation)
|
||||
* [`did-frame-navigate`](web-contents.md#event-did-frame-navigate)
|
||||
* [`did-navigate`](web-contents.md#event-did-navigate) (only fired when main frame navigates)
|
||||
|
||||
Subsequent events will not fire if `event.preventDefault()` is called on any of the cancellable events.
|
||||
|
||||
### In-page Navigation
|
||||
|
||||
In-page navigations don't cause the page to reload, but instead navigate to a location within the current page. These events are not cancellable. For an in-page navigations, the following events will fire in this order:
|
||||
|
||||
* [`did-start-navigation`](web-contents.md#event-did-start-navigation)
|
||||
* [`did-navigate-in-page`](web-contents.md#event-did-navigate-in-page)
|
||||
|
||||
### Frame Navigation
|
||||
|
||||
The [`will-navigate`](web-contents.md#event-will-navigate) and [`did-navigate`](web-contents.md#event-did-navigate) events only fire when the [mainFrame](web-contents.md#contentsmainframe-readonly) navigates.
|
||||
If you want to also observe navigations in `<iframe>`s, use [`will-frame-navigate`](web-contents.md#event-will-frame-navigate) and [`did-frame-navigate`](web-contents.md#event-did-frame-navigate) events.
|
||||
|
||||
## Methods
|
||||
|
||||
These methods can be accessed from the `webContents` module:
|
||||
@@ -207,10 +237,25 @@ See [`window.open()`](window-open.md) for more details and how to use this in co
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `url` string
|
||||
* `details` Event<>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||
document. Examples of same document navigations are reference fragment
|
||||
navigations, pushState/replaceState, and same page history navigation.
|
||||
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||
* `frame` WebFrameMain - The frame to be navigated.
|
||||
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||
frame's name), or null if the navigation was not initiated by a frame. This
|
||||
can also be null if the initiating frame was deleted before the event was
|
||||
emitted.
|
||||
* `url` string _Deprecated_
|
||||
* `isInPlace` boolean _Deprecated_
|
||||
* `isMainFrame` boolean _Deprecated_
|
||||
* `frameProcessId` Integer _Deprecated_
|
||||
* `frameRoutingId` Integer _Deprecated_
|
||||
|
||||
Emitted when a user or the page wants to start navigation. It can happen when
|
||||
Emitted when a user or the page wants to start navigation on the main frame. It can happen when
|
||||
the `window.location` object is changed or a user clicks a link in the page.
|
||||
|
||||
This event will not emit when the navigation is started programmatically with
|
||||
@@ -222,30 +267,79 @@ this purpose.
|
||||
|
||||
Calling `event.preventDefault()` will prevent the navigation.
|
||||
|
||||
#### Event: 'will-frame-navigate'
|
||||
|
||||
Returns:
|
||||
|
||||
* `details` Event<>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||
* `frame` WebFrameMain - The frame to be navigated.
|
||||
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||
frame's name), or null if the navigation was not initiated by a frame. This
|
||||
can also be null if the initiating frame was deleted before the event was
|
||||
emitted.
|
||||
|
||||
Emitted when a user or the page wants to start navigation in any frame. It can happen when
|
||||
the `window.location` object is changed or a user clicks a link in the page.
|
||||
|
||||
Unlike `will-navigate`, `will-frame-navigate` is fired when the main frame or any of its subframes attempts to navigate. When the navigation event comes from the main frame, `isMainFrame` will be `true`.
|
||||
|
||||
This event will not emit when the navigation is started programmatically with
|
||||
APIs like `webContents.loadURL` and `webContents.back`.
|
||||
|
||||
It is also not emitted for in-page navigations, such as clicking anchor links
|
||||
or updating the `window.location.hash`. Use `did-navigate-in-page` event for
|
||||
this purpose.
|
||||
|
||||
Calling `event.preventDefault()` will prevent the navigation.
|
||||
|
||||
#### Event: 'did-start-navigation'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `url` string
|
||||
* `isInPlace` boolean
|
||||
* `isMainFrame` boolean
|
||||
* `frameProcessId` Integer
|
||||
* `frameRoutingId` Integer
|
||||
* `details` Event<>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||
document. Examples of same document navigations are reference fragment
|
||||
navigations, pushState/replaceState, and same page history navigation.
|
||||
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||
* `frame` WebFrameMain - The frame to be navigated.
|
||||
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||
frame's name), or null if the navigation was not initiated by a frame. This
|
||||
can also be null if the initiating frame was deleted before the event was
|
||||
emitted.
|
||||
* `url` string _Deprecated_
|
||||
* `isInPlace` boolean _Deprecated_
|
||||
* `isMainFrame` boolean _Deprecated_
|
||||
* `frameProcessId` Integer _Deprecated_
|
||||
* `frameRoutingId` Integer _Deprecated_
|
||||
|
||||
Emitted when any frame (including main) starts navigating. `isInPlace` will be
|
||||
`true` for in-page navigations.
|
||||
Emitted when any frame (including main) starts navigating.
|
||||
|
||||
#### Event: 'will-redirect'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `url` string
|
||||
* `isInPlace` boolean
|
||||
* `isMainFrame` boolean
|
||||
* `frameProcessId` Integer
|
||||
* `frameRoutingId` Integer
|
||||
* `details` Event<>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||
document. Examples of same document navigations are reference fragment
|
||||
navigations, pushState/replaceState, and same page history navigation.
|
||||
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||
* `frame` WebFrameMain - The frame to be navigated.
|
||||
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||
frame's name), or null if the navigation was not initiated by a frame. This
|
||||
can also be null if the initiating frame was deleted before the event was
|
||||
emitted.
|
||||
* `url` string _Deprecated_
|
||||
* `isInPlace` boolean _Deprecated_
|
||||
* `isMainFrame` boolean _Deprecated_
|
||||
* `frameProcessId` Integer _Deprecated_
|
||||
* `frameRoutingId` Integer _Deprecated_
|
||||
|
||||
Emitted when a server side redirect occurs during navigation. For example a 302
|
||||
redirect.
|
||||
@@ -260,12 +354,23 @@ redirect).
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `url` string
|
||||
* `isInPlace` boolean
|
||||
* `isMainFrame` boolean
|
||||
* `frameProcessId` Integer
|
||||
* `frameRoutingId` Integer
|
||||
* `details` Event<>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||
document. Examples of same document navigations are reference fragment
|
||||
navigations, pushState/replaceState, and same page history navigation.
|
||||
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||
* `frame` WebFrameMain - The frame to be navigated.
|
||||
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||
frame's name), or null if the navigation was not initiated by a frame. This
|
||||
can also be null if the initiating frame was deleted before the event was
|
||||
emitted.
|
||||
* `url` string _Deprecated_
|
||||
* `isInPlace` boolean _Deprecated_
|
||||
* `isMainFrame` boolean _Deprecated_
|
||||
* `frameProcessId` Integer _Deprecated_
|
||||
* `frameRoutingId` Integer _Deprecated_
|
||||
|
||||
Emitted after a server side redirect occurs during navigation. For example a 302
|
||||
redirect.
|
||||
@@ -373,18 +478,7 @@ checking `reason === 'killed'` when you switch to that event.
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Object
|
||||
* `reason` string - The reason the render process is gone. Possible values:
|
||||
* `clean-exit` - Process exited with an exit code of zero
|
||||
* `abnormal-exit` - Process exited with a non-zero exit code
|
||||
* `killed` - Process was sent a SIGTERM or otherwise killed externally
|
||||
* `crashed` - Process crashed
|
||||
* `oom` - Process ran out of memory
|
||||
* `launch-failed` - Process never successfully launched
|
||||
* `integrity-failure` - Windows code integrity checks failed
|
||||
* `exitCode` Integer - The exit code of the process, unless `reason` is
|
||||
`launch-failed`, in which case `exitCode` will be a platform-specific
|
||||
launch failure error code.
|
||||
* `details` [RenderProcessGoneDetails](structures/render-process-gone-details.md)
|
||||
|
||||
Emitted when the renderer process unexpectedly disappears. This is normally
|
||||
because it was crashed or killed.
|
||||
@@ -589,6 +683,15 @@ Emitted when media starts playing.
|
||||
|
||||
Emitted when media is paused or done playing.
|
||||
|
||||
#### Event: 'audio-state-changed'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event<>
|
||||
* `audible` boolean - True if one or more frames or child `webContents` are emitting audio.
|
||||
|
||||
Emitted when media becomes audible or inaudible.
|
||||
|
||||
#### Event: 'did-change-theme-color'
|
||||
|
||||
Returns:
|
||||
@@ -841,7 +944,7 @@ Emitted when the preload script `preloadPath` throws an unhandled exception `err
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `event` [IpcMainEvent](structures/ipc-main-event.md)
|
||||
* `channel` string
|
||||
* `...args` any[]
|
||||
|
||||
@@ -853,7 +956,7 @@ See also [`webContents.ipc`](#contentsipc-readonly), which provides an [`IpcMain
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `event` [IpcMainEvent](structures/ipc-main-event.md)
|
||||
* `channel` string
|
||||
* `...args` any[]
|
||||
|
||||
@@ -942,9 +1045,11 @@ const win = new BrowserWindow()
|
||||
win.loadFile('src/index.html')
|
||||
```
|
||||
|
||||
#### `contents.downloadURL(url)`
|
||||
#### `contents.downloadURL(url[, options])`
|
||||
|
||||
* `url` string
|
||||
* `options` Object (optional)
|
||||
* `headers` Record<string, string> (optional) - HTTP request headers.
|
||||
|
||||
Initiates a download of the resource at `url` without navigating. The
|
||||
`will-download` event of `session` will be triggered.
|
||||
@@ -1105,7 +1210,7 @@ Returns `string` - The user agent for this web page.
|
||||
|
||||
* `css` string
|
||||
* `options` Object (optional)
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
* `cssOrigin` string (optional) - Can be 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
|
||||
Returns `Promise<string>` - A promise that resolves with a key for the inserted CSS that can later be used to remove the CSS via `contents.removeInsertedCSS(key)`.
|
||||
|
||||
@@ -1287,6 +1392,10 @@ Executes the editing command `cut` in web page.
|
||||
|
||||
Executes the editing command `copy` in web page.
|
||||
|
||||
#### `contents.centerSelection()`
|
||||
|
||||
Centers the current text selection in web page.
|
||||
|
||||
#### `contents.copyImageAt(x, y)`
|
||||
|
||||
* `x` Integer
|
||||
@@ -1314,6 +1423,46 @@ Executes the editing command `selectAll` in web page.
|
||||
|
||||
Executes the editing command `unselect` in web page.
|
||||
|
||||
#### `contents.scrollToTop()`
|
||||
|
||||
Scrolls to the top of the current `webContents`.
|
||||
|
||||
#### `contents.scrollToBottom()`
|
||||
|
||||
Scrolls to the bottom of the current `webContents`.
|
||||
|
||||
#### `contents.adjustSelection(options)`
|
||||
|
||||
* `options` Object
|
||||
* `start` Number (optional) - Amount to shift the start index of the current selection.
|
||||
* `end` Number (optional) - Amount to shift the end index of the current selection.
|
||||
|
||||
Adjusts the current text selection starting and ending points in the focused frame by the given amounts. A negative amount moves the selection towards the beginning of the document, and a positive amount moves the selection towards the end of the document.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
const win = new BrowserWindow()
|
||||
|
||||
// Adjusts the beginning of the selection 1 letter forward,
|
||||
// and the end of the selection 5 letters forward.
|
||||
win.webContents.adjustSelection({ start: 1, end: 5 })
|
||||
|
||||
// Adjusts the beginning of the selection 2 letters forward,
|
||||
// and the end of the selection 3 letters backward.
|
||||
win.webContents.adjustSelection({ start: 2, end: -3 })
|
||||
```
|
||||
|
||||
For a call of `win.webContents.adjustSelection({ start: 1, end: 5 })`
|
||||
|
||||
Before:
|
||||
|
||||
<img width="487" alt="Image Before Text Selection Adjustment" src="../images/web-contents-text-selection-before.png"/>
|
||||
|
||||
After:
|
||||
|
||||
<img width="487" alt="Image After Text Selection Adjustment" src="../images/web-contents-text-selection-after.png"/>
|
||||
|
||||
#### `contents.replace(text)`
|
||||
|
||||
* `text` string
|
||||
|
||||
@@ -113,7 +113,7 @@ webFrame.setSpellCheckProvider('en-US', {
|
||||
|
||||
* `css` string
|
||||
* `options` Object (optional)
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
* `cssOrigin` string (optional) - Can be 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
|
||||
Returns `string` - A key for the inserted CSS that can later be used to remove
|
||||
the CSS via `webFrame.removeInsertedCSS(key)`.
|
||||
|
||||
@@ -280,9 +280,11 @@ if the page fails to load (see
|
||||
Loads the `url` in the webview, the `url` must contain the protocol prefix,
|
||||
e.g. the `http://` or `file://`.
|
||||
|
||||
### `<webview>.downloadURL(url)`
|
||||
### `<webview>.downloadURL(url[, options])`
|
||||
|
||||
* `url` string
|
||||
* `options` Object (optional)
|
||||
* `headers` Record<string, string> (optional) - HTTP request headers.
|
||||
|
||||
Initiates a download of the resource at `url` without navigating.
|
||||
|
||||
@@ -465,6 +467,10 @@ Executes editing command `cut` in page.
|
||||
|
||||
Executes editing command `copy` in page.
|
||||
|
||||
#### `<webview>.centerSelection()`
|
||||
|
||||
Centers the current text selection in page.
|
||||
|
||||
### `<webview>.paste()`
|
||||
|
||||
Executes editing command `paste` in page.
|
||||
@@ -485,6 +491,25 @@ Executes editing command `selectAll` in page.
|
||||
|
||||
Executes editing command `unselect` in page.
|
||||
|
||||
#### `<webview>.scrollToTop()`
|
||||
|
||||
Scrolls to the top of the current `<webview>`.
|
||||
|
||||
#### `<webview>.scrollToBottom()`
|
||||
|
||||
Scrolls to the bottom of the current `<webview>`.
|
||||
|
||||
#### `<webview>.adjustSelection(options)`
|
||||
|
||||
* `options` Object
|
||||
* `start` Number (optional) - Amount to shift the start index of the current selection.
|
||||
* `end` Number (optional) - Amount to shift the end index of the current selection.
|
||||
|
||||
Adjusts the current text selection starting and ending points in the focused frame by the given amounts. A negative amount moves the selection towards the beginning of the document, and a positive amount moves the selection towards the end of the document.
|
||||
|
||||
See [`webContents.adjustSelection`](web-contents.md#contentsadjustselectionoptions) for
|
||||
examples.
|
||||
|
||||
### `<webview>.replace(text)`
|
||||
|
||||
* `text` string
|
||||
@@ -825,6 +850,28 @@ this purpose.
|
||||
|
||||
Calling `event.preventDefault()` does **NOT** have any effect.
|
||||
|
||||
### Event: 'will-frame-navigate'
|
||||
|
||||
Returns:
|
||||
|
||||
* `url` string
|
||||
* `isMainFrame` boolean
|
||||
* `frameProcessId` Integer
|
||||
* `frameRoutingId` Integer
|
||||
|
||||
Emitted when a user or the page wants to start navigation anywhere in the `<webview>`
|
||||
or any frames embedded within. It can happen when the `window.location` object is
|
||||
changed or a user clicks a link in the page.
|
||||
|
||||
This event will not emit when the navigation is started programmatically with
|
||||
APIs like `<webview>.loadURL` and `<webview>.back`.
|
||||
|
||||
It is also not emitted during in-page navigation, such as clicking anchor links
|
||||
or updating the `window.location.hash`. Use `did-navigate-in-page` event for
|
||||
this purpose.
|
||||
|
||||
Calling `event.preventDefault()` does **NOT** have any effect.
|
||||
|
||||
### Event: 'did-start-navigation'
|
||||
|
||||
Returns:
|
||||
@@ -938,9 +985,22 @@ ipcRenderer.on('ping', () => {
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'crashed'
|
||||
### Event: 'crashed' _Deprecated_
|
||||
|
||||
Fired when the renderer process is crashed.
|
||||
Fired when the renderer process crashes or is killed.
|
||||
|
||||
**Deprecated:** This event is superceded by the `render-process-gone` event
|
||||
which contains more information about why the render process disappeared. It
|
||||
isn't always because it crashed.
|
||||
|
||||
### Event: 'render-process-gone'
|
||||
|
||||
Returns:
|
||||
|
||||
* `details` [RenderProcessGoneDetails](structures/render-process-gone-details.md)
|
||||
|
||||
Fired when the renderer process unexpectedly disappears. This is normally
|
||||
because it was crashed or killed.
|
||||
|
||||
### Event: 'plugin-crashed'
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ window.open('https://github.com', '_blank', 'top=500,left=200,frame=false,nodeIn
|
||||
* Non-standard features (that are not handled by Chromium or Electron) given in
|
||||
`features` will be passed to any registered `webContents`'s
|
||||
`did-create-window` event handler in the `options` argument.
|
||||
* `frameName` follows the specification of `windowName` located in the [native documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window/open#parameters).
|
||||
* `frameName` follows the specification of `target` located in the [native documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window/open#parameters).
|
||||
* When opening `about:blank`, the child window's `WebPreferences` will be copied
|
||||
from the parent window, and there is no way to override it because Chromium
|
||||
skips browser side navigation in this case.
|
||||
|
||||
@@ -12,6 +12,102 @@ This document uses the following convention to categorize breaking changes:
|
||||
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
|
||||
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
|
||||
|
||||
## Planned Breaking API Changes (27.0)
|
||||
|
||||
### Removed: macOS 10.13 / 10.14 support
|
||||
|
||||
macOS 10.13 (High Sierra) and macOS 10.14 (Mojave) are no longer supported by [Chromium](https://chromium-review.googlesource.com/c/chromium/src/+/4629466).
|
||||
|
||||
Older versions of Electron will continue to run on these operating systems, but macOS 10.15 (Catalina)
|
||||
or later will be required to run Electron v27.0.0 and higher.
|
||||
|
||||
## Planned Breaking API Changes (25.0)
|
||||
|
||||
### Deprecated: `protocol.{register,intercept}{Buffer,String,Stream,File,Http}Protocol`
|
||||
|
||||
The `protocol.register*Protocol` and `protocol.intercept*Protocol` methods have
|
||||
been replaced with [`protocol.handle`](api/protocol.md#protocolhandlescheme-handler).
|
||||
|
||||
The new method can either register a new protocol or intercept an existing
|
||||
protocol, and responses can be of any type.
|
||||
|
||||
```js
|
||||
// Deprecated in Electron 25
|
||||
protocol.registerBufferProtocol('some-protocol', () => {
|
||||
callback({ mimeType: 'text/html', data: Buffer.from('<h5>Response</h5>') })
|
||||
})
|
||||
|
||||
// Replace with
|
||||
protocol.handle('some-protocol', () => {
|
||||
return new Response(
|
||||
Buffer.from('<h5>Response</h5>'), // Could also be a string or ReadableStream.
|
||||
{ headers: { 'content-type': 'text/html' } }
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
```js
|
||||
// Deprecated in Electron 25
|
||||
protocol.registerHttpProtocol('some-protocol', () => {
|
||||
callback({ url: 'https://electronjs.org' })
|
||||
})
|
||||
|
||||
// Replace with
|
||||
protocol.handle('some-protocol', () => {
|
||||
return net.fetch('https://electronjs.org')
|
||||
})
|
||||
```
|
||||
|
||||
```js
|
||||
// Deprecated in Electron 25
|
||||
protocol.registerFileProtocol('some-protocol', () => {
|
||||
callback({ filePath: '/path/to/my/file' })
|
||||
})
|
||||
|
||||
// Replace with
|
||||
protocol.handle('some-protocol', () => {
|
||||
return net.fetch('file:///path/to/my/file')
|
||||
})
|
||||
```
|
||||
|
||||
### Deprecated: `BrowserWindow.setTrafficLightPosition(position)`
|
||||
|
||||
`BrowserWindow.setTrafficLightPosition(position)` has been deprecated, the
|
||||
`BrowserWindow.setWindowButtonPosition(position)` API should be used instead
|
||||
which accepts `null` instead of `{ x: 0, y: 0 }` to reset the position to
|
||||
system default.
|
||||
|
||||
```js
|
||||
// Deprecated in Electron 25
|
||||
win.setTrafficLightPosition({ x: 10, y: 10 })
|
||||
win.setTrafficLightPosition({ x: 0, y: 0 })
|
||||
|
||||
// Replace with
|
||||
win.setWindowButtonPosition({ x: 10, y: 10 })
|
||||
win.setWindowButtonPosition(null)
|
||||
```
|
||||
|
||||
### Deprecated: `BrowserWindow.getTrafficLightPosition()`
|
||||
|
||||
`BrowserWindow.getTrafficLightPosition()` has been deprecated, the
|
||||
`BrowserWindow.getWindowButtonPosition()` API should be used instead
|
||||
which returns `null` instead of `{ x: 0, y: 0 }` when there is no custom
|
||||
position.
|
||||
|
||||
```js
|
||||
// Deprecated in Electron 25
|
||||
const pos = win.getTrafficLightPosition()
|
||||
if (pos.x === 0 && pos.y === 0) {
|
||||
// No custom position.
|
||||
}
|
||||
|
||||
// Replace with
|
||||
const ret = win.getWindowButtonPosition()
|
||||
if (ret === null) {
|
||||
// No custom position.
|
||||
}
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (24.0)
|
||||
|
||||
### API Changed: `nativeImage.createThumbnailFromPath(path, size)`
|
||||
@@ -446,6 +542,18 @@ to open synchronously scriptable child windows, among other incompatibilities.
|
||||
See the documentation for [window.open in Electron](api/window-open.md)
|
||||
for more details.
|
||||
|
||||
### Deprecated: `app.runningUnderRosettaTranslation`
|
||||
|
||||
The `app.runningUnderRosettaTranslation` property has been deprecated.
|
||||
Use `app.runningUnderARM64Translation` instead.
|
||||
|
||||
```js
|
||||
// Deprecated
|
||||
console.log(app.runningUnderRosettaTranslation)
|
||||
// Replace with
|
||||
console.log(app.runningUnderARM64Translation)
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (14.0)
|
||||
|
||||
### Removed: `remote` module
|
||||
|
||||
@@ -1,128 +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="https://electronjs.org/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://learn.microsoft.com/en-us/previous-versions/windows/desktop/bb226797"
|
||||
>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="https://electronjs.org/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>
|
||||
<!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="https://electronjs.org/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://learn.microsoft.com/en-us/previous-versions/windows/desktop/bb226797"
|
||||
>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="https://electronjs.org/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>
|
||||
|
||||
@@ -1,73 +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="https://electronjs.org/docs/api/menu">Menu</a>,
|
||||
<a href="https://electronjs.org/docs/api/accelerator">Accelerator</a>,
|
||||
and
|
||||
<a href="https://electronjs.org/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>
|
||||
<!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="https://electronjs.org/docs/api/menu">Menu</a>,
|
||||
<a href="https://electronjs.org/docs/api/accelerator">Accelerator</a>,
|
||||
and
|
||||
<a href="https://electronjs.org/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>
|
||||
|
||||
@@ -1,81 +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="https://electronjs.org/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>
|
||||
<!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="https://electronjs.org/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>
|
||||
|
||||
@@ -1,104 +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="https://electronjs.org/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>
|
||||
<!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="https://electronjs.org/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>
|
||||
|
||||
@@ -1,108 +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="https://electronjs.org/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>
|
||||
<!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="https://electronjs.org/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>
|
||||
|
||||
@@ -1,91 +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="https://electronjs.org/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>
|
||||
<!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="https://electronjs.org/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>
|
||||
|
||||
@@ -1,76 +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>
|
||||
<!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>
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Open external links and the file manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>
|
||||
Open external links and the file manager
|
||||
</h1>
|
||||
<h3>
|
||||
The <code>shell</code> module in Electron allows you to access certain
|
||||
native elements like the file manager and default web browser.
|
||||
</h3>
|
||||
|
||||
<p>This module works in both the main and renderer process.</p>
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/shell">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Open Path in File Manager</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="open-file-manager">
|
||||
View Demo
|
||||
</button>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Open External Links</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="open-ex-links">View Demo</button>
|
||||
</div>
|
||||
<p>
|
||||
If you do not want your app to open website links
|
||||
<em>within</em> the app, you can use the <code>shell</code> module
|
||||
to open them externally. When clicked, the links will open outside
|
||||
of your app and in the user's default web browser.
|
||||
</p>
|
||||
<p>
|
||||
When the demo button is clicked, the electron website will open in
|
||||
your browser.
|
||||
</p>
|
||||
<p></p>
|
||||
|
||||
<div>
|
||||
<h2>ProTip</h2>
|
||||
<strong>Open all outbound links externally.</strong>
|
||||
<p>
|
||||
You may want to open all <code>http</code> and
|
||||
<code>https</code> links outside of your app. To do this, query
|
||||
the document and loop through each link and add a listener. This
|
||||
app uses the code below which is located in
|
||||
<code>assets/ex-links.js</code>.
|
||||
</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Open external links and the file manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>
|
||||
Open external links and the file manager
|
||||
</h1>
|
||||
<h3>
|
||||
The <code>shell</code> module in Electron allows you to access certain
|
||||
native elements like the file manager and default web browser.
|
||||
</h3>
|
||||
|
||||
<p>This module works in both the main and renderer process.</p>
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/shell">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Open Path in File Manager</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="open-file-manager">
|
||||
View Demo
|
||||
</button>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Open External Links</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="open-ex-links">View Demo</button>
|
||||
</div>
|
||||
<p>
|
||||
If you do not want your app to open website links
|
||||
<em>within</em> the app, you can use the <code>shell</code> module
|
||||
to open them externally. When clicked, the links will open outside
|
||||
of your app and in the user's default web browser.
|
||||
</p>
|
||||
<p>
|
||||
When the demo button is clicked, the electron website will open in
|
||||
your browser.
|
||||
</p>
|
||||
<p></p>
|
||||
|
||||
<div>
|
||||
<h2>ProTip</h2>
|
||||
<strong>Open all outbound links externally.</strong>
|
||||
<p>
|
||||
You may want to open all <code>http</code> and
|
||||
<code>https</code> links outside of your app. To do this, query
|
||||
the document and loop through each link and add a listener. This
|
||||
app uses the code below which is located in
|
||||
<code>assets/ex-links.js</code>.
|
||||
</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,67 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Desktop notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>Desktop notifications</h1>
|
||||
<h3>
|
||||
The <code>notification</code> module in Electron allows you to add basic
|
||||
desktop notifications.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Electron conveniently allows developers to send notifications with the
|
||||
<a href="https://notifications.spec.whatwg.org/">HTML5 Notification API</a>,
|
||||
using the currently running operating system’s native notification
|
||||
APIs to display it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Note:</b> Since this is an HTML5 API it is only available in the
|
||||
renderer process.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/all/#notifications-windows-linux-macos">
|
||||
full API documentation<span>(opens in new window)</span>
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Basic notification</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="basic-noti">View demo</button>
|
||||
</div>
|
||||
<p>This demo demonstrates a basic notification. Text only.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Notification with image</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="advanced-noti">View demo</button>
|
||||
</div>
|
||||
<p>
|
||||
This demo demonstrates a basic notification. Both text and a image
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Desktop notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>Desktop notifications</h1>
|
||||
<h3>
|
||||
The <code>notification</code> module in Electron allows you to add basic
|
||||
desktop notifications.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Electron conveniently allows developers to send notifications with the
|
||||
<a href="https://notifications.spec.whatwg.org/">HTML5 Notification API</a>,
|
||||
using the currently running operating system’s native notification
|
||||
APIs to display it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Note:</b> Since this is an HTML5 API it is only available in the
|
||||
renderer process.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/all/#notifications-windows-linux-macos">
|
||||
full API documentation<span>(opens in new window)</span>
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Basic notification</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="basic-noti">View demo</button>
|
||||
</div>
|
||||
<p>This demo demonstrates a basic notification. Text only.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Notification with image</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="advanced-noti">View demo</button>
|
||||
</div>
|
||||
<p>
|
||||
This demo demonstrates a basic notification. Both text and a image
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Tray</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>Tray</h1>
|
||||
<h3>
|
||||
The <code>tray</code> module allows you to create an icon in the
|
||||
operating system's notification area.
|
||||
</h3>
|
||||
<p>This icon can also have a context menu attached.</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/tray">
|
||||
full API documentation
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<h2>ProTip</h2>
|
||||
<strong>Tray support in Linux.</strong>
|
||||
<p>
|
||||
On Linux distributions that only have app indicator support, users
|
||||
will need to install <code>libappindicator1</code> to make the
|
||||
tray icon work. See the
|
||||
<a href="https://electronjs.org/docs/api/tray">
|
||||
full API documentation
|
||||
</a>
|
||||
for more details about using Tray on Linux.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Tray</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>Tray</h1>
|
||||
<h3>
|
||||
The <code>tray</code> module allows you to create an icon in the
|
||||
operating system's notification area.
|
||||
</h3>
|
||||
<p>This icon can also have a context menu attached.</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/tray">
|
||||
full API documentation
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<h2>ProTip</h2>
|
||||
<strong>Tray support in Linux.</strong>
|
||||
<p>
|
||||
On Linux distributions that only have app indicator support, users
|
||||
will need to install <code>libappindicator1</code> to make the
|
||||
tray icon work. See the
|
||||
<a href="https://electronjs.org/docs/api/tray">
|
||||
full API documentation
|
||||
</a>
|
||||
for more details about using Tray on Linux.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Frameless window</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Create and Manage Windows</h1>
|
||||
|
||||
<h3>
|
||||
The <code>BrowserWindow</code> module in Electron allows you to create a
|
||||
new browser window or manage an existing one.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Each browser window is a separate process, known as the renderer
|
||||
process. This process, like the main process that controls the life
|
||||
cycle of the app, has full access to the Node.js APIs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Create a frameless window</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="frameless-window">View Demo</button>
|
||||
</div>
|
||||
<p>
|
||||
A frameless window is a window that has no
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Glossary/Chrome">
|
||||
"chrome"
|
||||
</a>
|
||||
, such as toolbars, title bars, status bars, borders, etc. You can
|
||||
make a browser window frameless by setting <code>frame</code> to
|
||||
<code>false</code> when creating the window.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Windows can have a transparent background, too. By setting the
|
||||
<code>transparent</code> option to <code>true</code>, you can also
|
||||
make your frameless window transparent:
|
||||
</p>
|
||||
<pre>
|
||||
<code class="language-js">var win = new BrowserWindow({
|
||||
transparent: true,
|
||||
frame: false
|
||||
})</code></pre>
|
||||
|
||||
<p>
|
||||
For more details, see the
|
||||
<a href="https://electronjs.org/docs/tutorial/window-customization/">
|
||||
Window Customization
|
||||
|
||||
</a>
|
||||
documentation.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Frameless window</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Create and Manage Windows</h1>
|
||||
|
||||
<h3>
|
||||
The <code>BrowserWindow</code> module in Electron allows you to create a
|
||||
new browser window or manage an existing one.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Each browser window is a separate process, known as the renderer
|
||||
process. This process, like the main process that controls the life
|
||||
cycle of the app, has full access to the Node.js APIs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Create a frameless window</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="frameless-window">View Demo</button>
|
||||
</div>
|
||||
<p>
|
||||
A frameless window is a window that has no
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Glossary/Chrome">
|
||||
"chrome"
|
||||
</a>
|
||||
, such as toolbars, title bars, status bars, borders, etc. You can
|
||||
make a browser window frameless by setting <code>frame</code> to
|
||||
<code>false</code> when creating the window.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Windows can have a transparent background, too. By setting the
|
||||
<code>transparent</code> option to <code>true</code>, you can also
|
||||
make your frameless window transparent:
|
||||
</p>
|
||||
<pre>
|
||||
<code class="language-js">var win = new BrowserWindow({
|
||||
transparent: true,
|
||||
frame: false
|
||||
})</code></pre>
|
||||
|
||||
<p>
|
||||
For more details, see the
|
||||
<a href="https://electronjs.org/docs/tutorial/window-customization/">
|
||||
Window Customization
|
||||
|
||||
</a>
|
||||
documentation.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Manage window state</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Create and Manage Windows</h1>
|
||||
|
||||
<h3>
|
||||
The <code>BrowserWindow</code> module in Electron allows you to create a
|
||||
new browser window or manage an existing one.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Each browser window is a separate process, known as the renderer
|
||||
process. This process, like the main process that controls the life
|
||||
cycle of the app, has full access to the Node.js APIs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Manage window state</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="manage-window">View Demo</button>
|
||||
<span id="manage-window-reply"></span>
|
||||
</div>
|
||||
<p>
|
||||
In this demo we create a new window and listen for
|
||||
<code>move</code> and <code>resize</code> events on it. Click the
|
||||
demo button, change the new window and see the dimensions and
|
||||
position update here, above.
|
||||
</p>
|
||||
<p>
|
||||
There are a lot of methods for controlling the state of the window
|
||||
such as the size, location, and focus status as well as events to
|
||||
listen to for window changes. Visit the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
documentation (opens in new window)
|
||||
</a>
|
||||
for the full list.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Manage window state</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Create and Manage Windows</h1>
|
||||
|
||||
<h3>
|
||||
The <code>BrowserWindow</code> module in Electron allows you to create a
|
||||
new browser window or manage an existing one.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Each browser window is a separate process, known as the renderer
|
||||
process. This process, like the main process that controls the life
|
||||
cycle of the app, has full access to the Node.js APIs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Manage window state</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="manage-window">View Demo</button>
|
||||
<span id="manage-window-reply"></span>
|
||||
</div>
|
||||
<p>
|
||||
In this demo we create a new window and listen for
|
||||
<code>move</code> and <code>resize</code> events on it. Click the
|
||||
demo button, change the new window and see the dimensions and
|
||||
position update here, above.
|
||||
</p>
|
||||
<p>
|
||||
There are a lot of methods for controlling the state of the window
|
||||
such as the size, location, and focus status as well as events to
|
||||
listen to for window changes. Visit the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
documentation (opens in new window)
|
||||
</a>
|
||||
for the full list.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Window events</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Create and Manage Windows</h1>
|
||||
|
||||
<h3>
|
||||
The <code>BrowserWindow</code> module in Electron allows you to create a
|
||||
new browser window or manage an existing one.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Each browser window is a separate process, known as the renderer
|
||||
process. This process, like the main process that controls the life
|
||||
cycle of the app, has full access to the Node.js APIs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Window events</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="listen-to-window">View Demo</button>
|
||||
<button id="focus-on-modal-window">
|
||||
Focus on Demo
|
||||
</button>
|
||||
</div>
|
||||
<p>
|
||||
In this demo, we create a new window and listen for
|
||||
<code>blur</code> event on it. Click the demo button to create a new
|
||||
modal window, and switch focus back to the parent window by clicking
|
||||
on it. You can click the <i>Focus on Demo</i> button to switch focus
|
||||
to the modal window again.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Window events</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Create and Manage Windows</h1>
|
||||
|
||||
<h3>
|
||||
The <code>BrowserWindow</code> module in Electron allows you to create a
|
||||
new browser window or manage an existing one.
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
Each browser window is a separate process, known as the renderer
|
||||
process. This process, like the main process that controls the life
|
||||
cycle of the app, has full access to the Node.js APIs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h2>Window events</h2>
|
||||
<div>
|
||||
<div>
|
||||
<button id="listen-to-window">View Demo</button>
|
||||
<button id="focus-on-modal-window">
|
||||
Focus on Demo
|
||||
</button>
|
||||
</div>
|
||||
<p>
|
||||
In this demo, we create a new window and listen for
|
||||
<code>blur</code> event on it. Click the demo button to create a new
|
||||
modal window, and switch focus back to the parent window by clicking
|
||||
on it. You can click the <i>Focus on Demo</i> button to switch focus
|
||||
to the modal window again.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require("./renderer.js");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
BIN
docs/images/web-contents-text-selection-after.png
Normal file
BIN
docs/images/web-contents-text-selection-after.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/images/web-contents-text-selection-before.png
Normal file
BIN
docs/images/web-contents-text-selection-before.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -41,7 +41,9 @@ Valid `algorithm` values are currently `SHA256` only. The `hash` is a hash of t
|
||||
ASAR integrity checking is currently disabled by default and can be enabled by toggling a fuse. See [Electron Fuses](fuses.md) for more information on what Electron Fuses are and how they work. When enabling this fuse you typically also want to enable the `onlyLoadAppFromAsar` fuse otherwise the validity checking can be bypassed via the Electron app code search path.
|
||||
|
||||
```js @ts-nocheck
|
||||
require('@electron/fuses').flipFuses(
|
||||
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')
|
||||
|
||||
flipFuses(
|
||||
// E.g. /a/b/Foo.app
|
||||
pathToPackagedApp,
|
||||
{
|
||||
|
||||
@@ -9,10 +9,11 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
|
||||
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
|
||||
| 25.0.0 | 2023-Apr-10 | 2023-May-02 | 2023-May-30 | 2023-Dec-05 | M114 | TBD | ✅ |
|
||||
| 24.0.0 | 2022-Feb-09 | 2023-Mar-07 | 2023-Apr-04 | 2023-Oct-03 | M112 | v18.14 | ✅ |
|
||||
| 23.0.0 | 2022-Dec-01 | 2023-Jan-10 | 2023-Feb-07 | 2023-Aug-08 | M110 | v18.12 | ✅ |
|
||||
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | 2023-May-30 | M108 | v16.17 | ✅ |
|
||||
| 26.0.0 | 2023-Jun-01 | 2023-Jun-27 | 2023-Aug-15 | TBD | M116 | TBD | ✅ |
|
||||
| 25.0.0 | 2023-Apr-10 | 2023-May-02 | 2023-May-30 | 2024-Jan-02 | M114 | v18.15 | ✅ |
|
||||
| 24.0.0 | 2022-Feb-09 | 2023-Mar-07 | 2023-Apr-04 | 2023-Oct-10 | M112 | v18.14 | ✅ |
|
||||
| 23.0.0 | 2022-Dec-01 | 2023-Jan-10 | 2023-Feb-07 | 2023-Aug-15 | M110 | v18.12 | ✅ |
|
||||
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | 2023-Oct-10 | M108 | v16.17 | ✅ |
|
||||
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | 2023-Apr-04 | M106 | v16.16 | 🚫 |
|
||||
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | 2023-Feb-07 | M104 | v16.15 | 🚫 |
|
||||
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | 2022-Nov-29 | M102 | v16.14 | 🚫 |
|
||||
@@ -56,12 +57,12 @@ Chromium has the own public release schedule [here](https://chromiumdash.appspot
|
||||
|
||||
:::info
|
||||
|
||||
Beginning in September 2021 (Electron 15), the Electron team
|
||||
will temporarily support the latest **four** stable major versions. This
|
||||
extended support is intended to help Electron developers transition to
|
||||
the [new 8-week release cadence](https://electronjs.org/blog/8-week-cadence),
|
||||
and will continue until the release of Electron 19. At that time,
|
||||
the Electron team will drop support back to the latest three stable major versions.
|
||||
The Electron team will temporarily support Electron 22 until October 10, 2023.
|
||||
This extended support is intended to help Electron developers who still need
|
||||
support for Windows 7/8/8.1, which ended support in Electron 23. The October
|
||||
support date follows the extended support dates from both Chromium and Microsoft.
|
||||
On October 11, the Electron team will drop support back to the latest three
|
||||
stable major versions.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -68,7 +68,9 @@ The loadBrowserProcessSpecificV8Snapshot fuse changes which V8 snapshot file is
|
||||
We've made a handy module, [`@electron/fuses`](https://npmjs.com/package/@electron/fuses), to make flipping these fuses easy. Check out the README of that module for more details on usage and potential error cases.
|
||||
|
||||
```js @ts-nocheck
|
||||
require('@electron/fuses').flipFuses(
|
||||
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')
|
||||
|
||||
flipFuses(
|
||||
// Path to electron
|
||||
require('electron'),
|
||||
// Fuses to flip
|
||||
@@ -82,7 +84,7 @@ require('@electron/fuses').flipFuses(
|
||||
You can validate the fuses have been flipped or check the fuse status of an arbitrary Electron app using the fuses CLI.
|
||||
|
||||
```bash
|
||||
npx @electron/fuses read --app /Applications/Foo.app
|
||||
npx @electron/fuses read --app /Applications/Foo.app
|
||||
```
|
||||
|
||||
### The hard way
|
||||
|
||||
@@ -63,9 +63,9 @@ const createWindow = () => {
|
||||
|
||||
In this next step, we will create our `BrowserWindow` and tell our application how to handle an event in which an external protocol is clicked.
|
||||
|
||||
This code will be different in Windows compared to MacOS and Linux. This is due to Windows requiring additional code in order to open the contents of the protocol link within the same Electron instance. Read more about this [here](../api/app.md#apprequestsingleinstancelockadditionaldata).
|
||||
This code will be different in Windows and Linux compared to MacOS. This is due to both platforms emitting the `second-instance` event rather than the `open-url` event and Windows requiring additional code in order to open the contents of the protocol link within the same Electron instance. Read more about this [here](../api/app.md#apprequestsingleinstancelockadditionaldata).
|
||||
|
||||
#### Windows code:
|
||||
#### Windows and Linux code:
|
||||
|
||||
```javascript @ts-type={mainWindow:Electron.BrowserWindow} @ts-type={createWindow:()=>void}
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
@@ -80,8 +80,7 @@ if (!gotTheLock) {
|
||||
mainWindow.focus()
|
||||
}
|
||||
// the commandLine is array of strings in which last element is deep link url
|
||||
// the url str ends with /
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${commandLine.pop().slice(0, -1)}`)
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${commandLine.pop()}`)
|
||||
})
|
||||
|
||||
// Create mainWindow, load the rest of the app, etc...
|
||||
@@ -91,7 +90,7 @@ if (!gotTheLock) {
|
||||
}
|
||||
```
|
||||
|
||||
#### MacOS and Linux code:
|
||||
#### MacOS code:
|
||||
|
||||
```javascript @ts-type={createWindow:()=>void}
|
||||
// This method will be called when Electron has finished
|
||||
|
||||
@@ -150,7 +150,7 @@ renderer.
|
||||
|
||||
```js title='renderer.js (Renderer Process)' @ts-nocheck
|
||||
// elsewhere in your code to send a message to the other renderers message handler
|
||||
window.electronMessagePort.postmessage('ping')
|
||||
window.electronMessagePort.postMessage('ping')
|
||||
```
|
||||
|
||||
### Worker process
|
||||
|
||||
@@ -202,7 +202,7 @@ Then, set up your `handle` listener in the main process. We do this _before_
|
||||
loading the HTML file so that the handler is guaranteed to be ready before
|
||||
you send out the `invoke` call from the renderer.
|
||||
|
||||
```js {1,12} title="main.js"
|
||||
```js {1,15} title="main.js"
|
||||
const { app, BrowserWindow, ipcMain } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
@@ -214,10 +214,12 @@ const createWindow = () => {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
ipcMain.handle('ping', () => 'pong')
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
app.whenReady().then(createWindow)
|
||||
app.whenReady().then(() => {
|
||||
ipcMain.handle('ping', () => 'pong')
|
||||
createWindow()
|
||||
})
|
||||
```
|
||||
|
||||
Once you have the sender and receiver set up, you can now send messages from the renderer
|
||||
|
||||
@@ -115,7 +115,7 @@ const win = new BrowserWindow({
|
||||
})
|
||||
```
|
||||
|
||||
On either platform `titleBarOverlay` can also be an object. On both macOS and Windows, the height of the overlay can be specified with the `height` property. On Windows, the color of the overlay and its symbols can be specified using the `color` and `symbolColor` properties respectively.
|
||||
On either platform `titleBarOverlay` can also be an object. On both macOS and Windows, the height of the overlay can be specified with the `height` property. On Windows, the color of the overlay and its symbols can be specified using the `color` and `symbolColor` properties respectively. `rgba()`, `hsla()`, and `#RRGGBBAA` color formats are supported to apply transparency.
|
||||
|
||||
If a color option is not specified, the color will default to its system color for the window control buttons. Similarly, if the height option is not specified it will default to the default height:
|
||||
|
||||
@@ -136,10 +136,6 @@ const win = new BrowserWindow({
|
||||
> color and dimension values from a renderer using a set of readonly
|
||||
> [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars].
|
||||
|
||||
### Limitations
|
||||
|
||||
* Transparent colors are currently not supported. Progress updates for this feature can be found in PR [#33567](https://github.com/electron/electron/issues/33567).
|
||||
|
||||
## Create transparent windows
|
||||
|
||||
By setting the `transparent` option to `true`, you can make a fully transparent window.
|
||||
|
||||
@@ -61,6 +61,7 @@ template("electron_extra_paks") {
|
||||
"$root_gen_dir/content/browser/webrtc/resources/webrtc_internals_resources.pak",
|
||||
"$root_gen_dir/content/content_resources.pak",
|
||||
"$root_gen_dir/content/dev_ui_content_resources.pak",
|
||||
"$root_gen_dir/content/gpu_resources.pak",
|
||||
"$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
|
||||
"$root_gen_dir/net/net_resources.pak",
|
||||
"$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
|
||||
@@ -74,6 +75,7 @@ template("electron_extra_paks") {
|
||||
"//components/resources",
|
||||
"//content:content_resources",
|
||||
"//content:dev_ui_content_resources",
|
||||
"//content/browser/resources/gpu:resources",
|
||||
"//content/browser/resources/media:resources",
|
||||
"//content/browser/tracing:resources",
|
||||
"//content/browser/webrtc/resources",
|
||||
|
||||
@@ -78,7 +78,6 @@ auto_filenames = {
|
||||
"docs/api/structures/custom-scheme.md",
|
||||
"docs/api/structures/desktop-capturer-source.md",
|
||||
"docs/api/structures/display.md",
|
||||
"docs/api/structures/event.md",
|
||||
"docs/api/structures/extension-info.md",
|
||||
"docs/api/structures/extension.md",
|
||||
"docs/api/structures/file-filter.md",
|
||||
@@ -115,6 +114,7 @@ auto_filenames = {
|
||||
"docs/api/structures/protocol-response.md",
|
||||
"docs/api/structures/rectangle.md",
|
||||
"docs/api/structures/referrer.md",
|
||||
"docs/api/structures/render-process-gone-details.md",
|
||||
"docs/api/structures/resolved-endpoint.md",
|
||||
"docs/api/structures/resolved-host.md",
|
||||
"docs/api/structures/scrubber-item.md",
|
||||
@@ -210,6 +210,8 @@ auto_filenames = {
|
||||
"lib/browser/api/message-channel.ts",
|
||||
"lib/browser/api/module-list.ts",
|
||||
"lib/browser/api/native-theme.ts",
|
||||
"lib/browser/api/net-client-request.ts",
|
||||
"lib/browser/api/net-fetch.ts",
|
||||
"lib/browser/api/net-log.ts",
|
||||
"lib/browser/api/net.ts",
|
||||
"lib/browser/api/notification.ts",
|
||||
|
||||
@@ -265,7 +265,6 @@ filenames = {
|
||||
"shell/browser/api/electron_api_dialog.cc",
|
||||
"shell/browser/api/electron_api_download_item.cc",
|
||||
"shell/browser/api/electron_api_download_item.h",
|
||||
"shell/browser/api/electron_api_event.cc",
|
||||
"shell/browser/api/electron_api_event_emitter.cc",
|
||||
"shell/browser/api/electron_api_event_emitter.h",
|
||||
"shell/browser/api/electron_api_global_shortcut.cc",
|
||||
@@ -318,8 +317,6 @@ filenames = {
|
||||
"shell/browser/api/electron_api_web_request.cc",
|
||||
"shell/browser/api/electron_api_web_request.h",
|
||||
"shell/browser/api/electron_api_web_view_manager.cc",
|
||||
"shell/browser/api/event.cc",
|
||||
"shell/browser/api/event.h",
|
||||
"shell/browser/api/frame_subscriber.cc",
|
||||
"shell/browser/api/frame_subscriber.h",
|
||||
"shell/browser/api/gpu_info_enumerator.cc",
|
||||
@@ -380,7 +377,6 @@ filenames = {
|
||||
"shell/browser/electron_web_contents_utility_handler_impl.h",
|
||||
"shell/browser/electron_web_ui_controller_factory.cc",
|
||||
"shell/browser/electron_web_ui_controller_factory.h",
|
||||
"shell/browser/event_emitter_mixin.cc",
|
||||
"shell/browser/event_emitter_mixin.h",
|
||||
"shell/browser/extended_web_contents_observer.h",
|
||||
"shell/browser/feature_list.cc",
|
||||
@@ -586,6 +582,7 @@ filenames = {
|
||||
"shell/common/gin_converters/native_window_converter.h",
|
||||
"shell/common/gin_converters/net_converter.cc",
|
||||
"shell/common/gin_converters/net_converter.h",
|
||||
"shell/common/gin_converters/optional_converter.h",
|
||||
"shell/common/gin_converters/serial_port_info_converter.h",
|
||||
"shell/common/gin_converters/std_converter.h",
|
||||
"shell/common/gin_converters/time_converter.cc",
|
||||
@@ -606,10 +603,13 @@ filenames = {
|
||||
"shell/common/gin_helper/dictionary.h",
|
||||
"shell/common/gin_helper/error_thrower.cc",
|
||||
"shell/common/gin_helper/error_thrower.h",
|
||||
"shell/common/gin_helper/event_emitter.cc",
|
||||
"shell/common/gin_helper/event.cc",
|
||||
"shell/common/gin_helper/event.h",
|
||||
"shell/common/gin_helper/event_emitter.h",
|
||||
"shell/common/gin_helper/event_emitter_caller.cc",
|
||||
"shell/common/gin_helper/event_emitter_caller.h",
|
||||
"shell/common/gin_helper/event_emitter_template.cc",
|
||||
"shell/common/gin_helper/event_emitter_template.h",
|
||||
"shell/common/gin_helper/function_template.cc",
|
||||
"shell/common/gin_helper/function_template.h",
|
||||
"shell/common/gin_helper/function_template_extensions.h",
|
||||
@@ -697,6 +697,8 @@ filenames = {
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.h",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h",
|
||||
"shell/browser/extensions/api/scripting/scripting_api.cc",
|
||||
"shell/browser/extensions/api/scripting/scripting_api.h",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.cc",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.h",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.cc",
|
||||
|
||||
@@ -226,10 +226,15 @@ libcxx_headers = [
|
||||
"//buildtools/third_party/libc++/trunk/include/__bsd_locale_defaults.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__bsd_locale_fallbacks.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/chars_format.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/from_chars_integral.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/from_chars_result.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/tables.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/to_chars.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/to_chars_base_10.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/to_chars_floating_point.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/to_chars_integral.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/to_chars_result.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__charconv/traits.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__chrono/calendar.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__chrono/concepts.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__chrono/convert_to_timespec.h",
|
||||
@@ -290,6 +295,7 @@ libcxx_headers = [
|
||||
"//buildtools/third_party/libc++/trunk/include/__concepts/semiregular.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__concepts/swappable.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__concepts/totally_ordered.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__condition_variable/condition_variable.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__config",
|
||||
"//buildtools/third_party/libc++/trunk/include/__config_site.in",
|
||||
"//buildtools/third_party/libc++/trunk/include/__coroutine/coroutine_handle.h",
|
||||
@@ -298,7 +304,11 @@ libcxx_headers = [
|
||||
"//buildtools/third_party/libc++/trunk/include/__coroutine/trivial_awaitables.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__debug",
|
||||
"//buildtools/third_party/libc++/trunk/include/__debug_utils/randomize_range.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__errc",
|
||||
"//buildtools/third_party/libc++/trunk/include/__exception/exception.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__exception/exception_ptr.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__exception/nested_exception.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__exception/operations.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__exception/terminate.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__expected/bad_expected_access.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__expected/expected.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__expected/unexpect.h",
|
||||
@@ -466,7 +476,10 @@ libcxx_headers = [
|
||||
"//buildtools/third_party/libc++/trunk/include/__memory_resource/pool_options.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__memory_resource/synchronized_pool_resource.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__memory_resource/unsynchronized_pool_resource.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__mutex_base",
|
||||
"//buildtools/third_party/libc++/trunk/include/__mutex/lock_guard.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__mutex/mutex.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__mutex/tag_types.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__mutex/unique_lock.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__node_handle",
|
||||
"//buildtools/third_party/libc++/trunk/include/__numeric/accumulate.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__numeric/adjacent_difference.h",
|
||||
@@ -557,7 +570,6 @@ libcxx_headers = [
|
||||
"//buildtools/third_party/libc++/trunk/include/__ranges/views.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__ranges/zip_view.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__split_buffer",
|
||||
"//buildtools/third_party/libc++/trunk/include/__std_stream",
|
||||
"//buildtools/third_party/libc++/trunk/include/__string/char_traits.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__string/constexpr_c_functions.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__string/extern_template_lists.h",
|
||||
@@ -577,20 +589,25 @@ libcxx_headers = [
|
||||
"//buildtools/third_party/libc++/trunk/include/__support/xlocale/__nop_locale_mgmt.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__support/xlocale/__posix_l_fallback.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__support/xlocale/__strtonum_fallback.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__system_error/errc.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__system_error/error_category.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__system_error/error_code.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__system_error/error_condition.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__system_error/system_error.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__thread/poll_with_backoff.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__thread/timed_backoff_policy.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__threading_support",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tree",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/apply_cv.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/make_tuple_types.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/pair_like.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/sfinae_helpers.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/tuple_element.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/tuple_indices.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/tuple_like.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/tuple_like_ext.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/tuple_size.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple_dir/tuple_types.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/apply_cv.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/make_tuple_types.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/pair_like.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/sfinae_helpers.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/tuple_element.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/tuple_indices.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/tuple_like.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/tuple_like_ext.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/tuple_size.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__tuple/tuple_types.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__type_traits/add_const.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__type_traits/add_cv.h",
|
||||
"//buildtools/third_party/libc++/trunk/include/__type_traits/add_lvalue_reference.h",
|
||||
@@ -794,10 +811,8 @@ libcxx_headers = [
|
||||
"//buildtools/third_party/libc++/trunk/include/expected",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/__config",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/__memory",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/algorithm",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/deque",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/forward_list",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/functional",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/iterator",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/list",
|
||||
"//buildtools/third_party/libc++/trunk/include/experimental/map",
|
||||
|
||||
@@ -27,7 +27,7 @@ const cachedArchives = new Map<string, NodeJS.AsarArchive>();
|
||||
const getOrCreateArchive = (archivePath: string) => {
|
||||
const isCached = cachedArchives.has(archivePath);
|
||||
if (isCached) {
|
||||
return cachedArchives.get(archivePath);
|
||||
return cachedArchives.get(archivePath)!;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -39,6 +39,8 @@ const getOrCreateArchive = (archivePath: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
process._getOrCreateArchive = getOrCreateArchive;
|
||||
|
||||
const asarRe = /\.asar/i;
|
||||
|
||||
// Separate asar package's path from full path.
|
||||
@@ -839,7 +841,7 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
const originalModuleLoad = Module._load;
|
||||
Module._load = (request: string, ...args: any[]) => {
|
||||
const loadResult = originalModuleLoad(request, ...args);
|
||||
if (request === 'child_process') {
|
||||
if (request === 'child_process' || request === 'node:child_process') {
|
||||
if (!asarReady.has(loadResult)) {
|
||||
asarReady.add(loadResult);
|
||||
// Just to make it obvious what we are dealing with here
|
||||
|
||||
@@ -114,5 +114,11 @@ for (const name of events) {
|
||||
}
|
||||
|
||||
// Deprecation.
|
||||
deprecate.event(app, 'gpu-process-crashed', 'child-process-gone');
|
||||
deprecate.event(app, 'renderer-process-crashed', 'render-process-gone');
|
||||
deprecate.event(app, 'gpu-process-crashed', 'child-process-gone', () => {
|
||||
// the old event is still emitted by App::OnGpuProcessCrashed()
|
||||
return undefined;
|
||||
});
|
||||
|
||||
deprecate.event(app, 'renderer-process-crashed', 'render-process-gone', (event: Electron.Event, webContents: Electron.WebContents, details: Electron.RenderProcessGoneDetails) => {
|
||||
return [event, webContents, details.reason === 'killed'];
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import type { BaseWindow as TLWT } from 'electron/main';
|
||||
import * as deprecate from '@electron/internal/common/deprecate';
|
||||
const { BaseWindow } = process._linkedBinding('electron_browser_base_window') as { BaseWindow: typeof TLWT };
|
||||
|
||||
Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);
|
||||
@@ -15,6 +16,25 @@ BaseWindow.prototype._init = function () {
|
||||
}
|
||||
};
|
||||
|
||||
// Deprecation.
|
||||
const setTrafficLightPositionDeprecated = deprecate.warnOnce('setTrafficLightPosition', 'setWindowButtonPosition');
|
||||
// Converting to any as the methods are defined under BrowserWindow in our docs.
|
||||
(BaseWindow as any).prototype.setTrafficLightPosition = function (pos: Electron.Point) {
|
||||
setTrafficLightPositionDeprecated();
|
||||
if (typeof pos === 'object' && pos.x === 0 && pos.y === 0) {
|
||||
this.setWindowButtonPosition(null);
|
||||
} else {
|
||||
this.setWindowButtonPosition(pos);
|
||||
}
|
||||
};
|
||||
|
||||
const getTrafficLightPositionDeprecated = deprecate.warnOnce('getTrafficLightPosition', 'getWindowButtonPosition');
|
||||
(BaseWindow as any).prototype.getTrafficLightPosition = function () {
|
||||
getTrafficLightPositionDeprecated();
|
||||
const pos = this.getWindowButtonPosition();
|
||||
return pos === null ? { x: 0, y: 0 } : pos;
|
||||
};
|
||||
|
||||
// Properties
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'autoHideMenuBar', {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseWindow, WebContents, Event, BrowserView, TouchBar } from 'electron/main';
|
||||
import { BaseWindow, WebContents, BrowserView, TouchBar } from 'electron/main';
|
||||
import type { BrowserWindow as BWT } from 'electron/main';
|
||||
import * as deprecate from '@electron/internal/common/deprecate';
|
||||
const { BrowserWindow } = process._linkedBinding('electron_browser_window') as { BrowserWindow: typeof BWT };
|
||||
@@ -30,10 +30,10 @@ BrowserWindow.prototype._init = function (this: BWT) {
|
||||
};
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event: Event) => {
|
||||
this.on('blur', (event: Electron.Event) => {
|
||||
app.emit('browser-window-blur', event, this);
|
||||
});
|
||||
this.on('focus', (event: Event) => {
|
||||
this.on('focus', (event: Electron.Event) => {
|
||||
app.emit('browser-window-focus', event, this);
|
||||
});
|
||||
|
||||
@@ -76,8 +76,7 @@ BrowserWindow.prototype._init = function (this: BWT) {
|
||||
});
|
||||
|
||||
// Notify the creation of the window.
|
||||
const event = process._linkedBinding('electron_browser_event').createEmpty();
|
||||
app.emit('browser-window-created', event, this);
|
||||
app.emit('browser-window-created', { preventDefault () {} }, this);
|
||||
|
||||
Object.defineProperty(this, 'devToolsWebContents', {
|
||||
enumerable: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as roles from '@electron/internal/browser/api/menu-item-roles';
|
||||
import { Menu, Event, BrowserWindow, WebContents } from 'electron/main';
|
||||
import { Menu, BrowserWindow, WebContents, KeyboardEvent } from 'electron/main';
|
||||
|
||||
let nextCommandId = 0;
|
||||
|
||||
@@ -53,7 +53,7 @@ const MenuItem = function (this: any, options: any) {
|
||||
});
|
||||
|
||||
const click = options.click;
|
||||
this.click = (event: Event, focusedWindow: BrowserWindow, focusedWebContents: WebContents) => {
|
||||
this.click = (event: KeyboardEvent, focusedWindow: BrowserWindow, focusedWebContents: WebContents) => {
|
||||
// Manually flip the checked flags when clicked.
|
||||
if (!roles.shouldOverrideCheckStatus(this.role) &&
|
||||
(this.type === 'checkbox' || this.type === 'radio')) {
|
||||
|
||||
537
lib/browser/api/net-client-request.ts
Normal file
537
lib/browser/api/net-client-request.ts
Normal file
@@ -0,0 +1,537 @@
|
||||
import * as url from 'url';
|
||||
import { Readable, Writable } from 'stream';
|
||||
import { app } from 'electron/main';
|
||||
import type { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
|
||||
|
||||
const {
|
||||
isValidHeaderName,
|
||||
isValidHeaderValue,
|
||||
createURLLoader
|
||||
} = process._linkedBinding('electron_browser_net');
|
||||
const { Session } = process._linkedBinding('electron_browser_session');
|
||||
|
||||
const kHttpProtocols = new Set(['http:', 'https:']);
|
||||
|
||||
// set of headers that Node.js discards duplicates for
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
const discardableDuplicateHeaders = new Set([
|
||||
'content-type',
|
||||
'content-length',
|
||||
'user-agent',
|
||||
'referer',
|
||||
'host',
|
||||
'authorization',
|
||||
'proxy-authorization',
|
||||
'if-modified-since',
|
||||
'if-unmodified-since',
|
||||
'from',
|
||||
'location',
|
||||
'max-forwards',
|
||||
'retry-after',
|
||||
'etag',
|
||||
'last-modified',
|
||||
'server',
|
||||
'age',
|
||||
'expires'
|
||||
]);
|
||||
|
||||
class IncomingMessage extends Readable {
|
||||
_shouldPush: boolean = false;
|
||||
_data: (Buffer | null)[] = [];
|
||||
_responseHead: NodeJS.ResponseHead;
|
||||
_resume: (() => void) | null = null;
|
||||
|
||||
constructor (responseHead: NodeJS.ResponseHead) {
|
||||
super();
|
||||
this._responseHead = responseHead;
|
||||
}
|
||||
|
||||
get statusCode () {
|
||||
return this._responseHead.statusCode;
|
||||
}
|
||||
|
||||
get statusMessage () {
|
||||
return this._responseHead.statusMessage;
|
||||
}
|
||||
|
||||
get headers () {
|
||||
const filteredHeaders: Record<string, string | string[]> = {};
|
||||
const { headers, rawHeaders } = this._responseHead;
|
||||
for (const [name, values] of Object.entries(headers)) {
|
||||
filteredHeaders[name] = discardableDuplicateHeaders.has(name) ? values[0] : values.join(', ');
|
||||
}
|
||||
const cookies = rawHeaders.filter(({ key }) => key.toLowerCase() === 'set-cookie').map(({ value }) => value);
|
||||
// keep set-cookie as an array per Node.js rules
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
if (cookies.length) { filteredHeaders['set-cookie'] = cookies; }
|
||||
return filteredHeaders;
|
||||
}
|
||||
|
||||
get rawHeaders () {
|
||||
const rawHeadersArr: string[] = [];
|
||||
const { rawHeaders } = this._responseHead;
|
||||
rawHeaders.forEach(header => {
|
||||
rawHeadersArr.push(header.key, header.value);
|
||||
});
|
||||
return rawHeadersArr;
|
||||
}
|
||||
|
||||
get httpVersion () {
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
|
||||
}
|
||||
|
||||
get httpVersionMajor () {
|
||||
return this._responseHead.httpVersion.major;
|
||||
}
|
||||
|
||||
get httpVersionMinor () {
|
||||
return this._responseHead.httpVersion.minor;
|
||||
}
|
||||
|
||||
get rawTrailers () {
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
}
|
||||
|
||||
get trailers () {
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
}
|
||||
|
||||
_storeInternalData (chunk: Buffer | null, resume: (() => void) | null) {
|
||||
// save the network callback for use in _pushInternalData
|
||||
this._resume = resume;
|
||||
this._data.push(chunk);
|
||||
this._pushInternalData();
|
||||
}
|
||||
|
||||
_pushInternalData () {
|
||||
while (this._shouldPush && this._data.length > 0) {
|
||||
const chunk = this._data.shift();
|
||||
this._shouldPush = this.push(chunk);
|
||||
}
|
||||
if (this._shouldPush && this._resume) {
|
||||
// Reset the callback, so that a new one is used for each
|
||||
// batch of throttled data. Do this before calling resume to avoid a
|
||||
// potential race-condition
|
||||
const resume = this._resume;
|
||||
this._resume = null;
|
||||
|
||||
resume();
|
||||
}
|
||||
}
|
||||
|
||||
_read () {
|
||||
this._shouldPush = true;
|
||||
this._pushInternalData();
|
||||
}
|
||||
}
|
||||
|
||||
/** Writable stream that buffers up everything written to it. */
|
||||
class SlurpStream extends Writable {
|
||||
_data: Buffer;
|
||||
constructor () {
|
||||
super();
|
||||
this._data = Buffer.alloc(0);
|
||||
}
|
||||
|
||||
_write (chunk: Buffer, encoding: string, callback: () => void) {
|
||||
this._data = Buffer.concat([this._data, chunk]);
|
||||
callback();
|
||||
}
|
||||
|
||||
data () { return this._data; }
|
||||
}
|
||||
|
||||
class ChunkedBodyStream extends Writable {
|
||||
_pendingChunk: Buffer | undefined;
|
||||
_downstream?: NodeJS.DataPipe;
|
||||
_pendingCallback?: (error?: Error) => void;
|
||||
_clientRequest: ClientRequest;
|
||||
|
||||
constructor (clientRequest: ClientRequest) {
|
||||
super();
|
||||
this._clientRequest = clientRequest;
|
||||
}
|
||||
|
||||
_write (chunk: Buffer, encoding: string, callback: () => void) {
|
||||
if (this._downstream) {
|
||||
this._downstream.write(chunk).then(callback, callback);
|
||||
} else {
|
||||
// the contract of _write is that we won't be called again until we call
|
||||
// the callback, so we're good to just save a single chunk.
|
||||
this._pendingChunk = chunk;
|
||||
this._pendingCallback = callback;
|
||||
|
||||
// The first write to a chunked body stream begins the request.
|
||||
this._clientRequest._startRequest();
|
||||
}
|
||||
}
|
||||
|
||||
_final (callback: () => void) {
|
||||
this._downstream!.done();
|
||||
callback();
|
||||
}
|
||||
|
||||
startReading (pipe: NodeJS.DataPipe) {
|
||||
if (this._downstream) {
|
||||
throw new Error('two startReading calls???');
|
||||
}
|
||||
this._downstream = pipe;
|
||||
if (this._pendingChunk) {
|
||||
const doneWriting = (maybeError: Error | void) => {
|
||||
// If the underlying request has been aborted, we honestly don't care about the error
|
||||
// all work should cease as soon as we abort anyway, this error is probably a
|
||||
// "mojo pipe disconnected" error (code=9)
|
||||
if (this._clientRequest._aborted) return;
|
||||
|
||||
const cb = this._pendingCallback!;
|
||||
delete this._pendingCallback;
|
||||
delete this._pendingChunk;
|
||||
cb(maybeError || undefined);
|
||||
};
|
||||
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type RedirectPolicy = 'manual' | 'follow' | 'error';
|
||||
|
||||
const kAllowNonHttpProtocols = Symbol('kAllowNonHttpProtocols');
|
||||
export function allowAnyProtocol (opts: ClientRequestConstructorOptions): ClientRequestConstructorOptions {
|
||||
return {
|
||||
...opts,
|
||||
[kAllowNonHttpProtocols]: true
|
||||
} as any;
|
||||
}
|
||||
|
||||
type ExtraURLLoaderOptions = {
|
||||
redirectPolicy: RedirectPolicy;
|
||||
headers: Record<string, { name: string, value: string | string[] }>;
|
||||
allowNonHttpProtocols: boolean;
|
||||
}
|
||||
function parseOptions (optionsIn: ClientRequestConstructorOptions | string): NodeJS.CreateURLLoaderOptions & ExtraURLLoaderOptions {
|
||||
const options: any = typeof optionsIn === 'string' ? url.parse(optionsIn) : { ...optionsIn };
|
||||
|
||||
let urlStr: string = options.url;
|
||||
|
||||
if (!urlStr) {
|
||||
const urlObj: url.UrlObject = {};
|
||||
const protocol = options.protocol || 'http:';
|
||||
urlObj.protocol = protocol;
|
||||
|
||||
if (options.host) {
|
||||
urlObj.host = options.host;
|
||||
} else {
|
||||
if (options.hostname) {
|
||||
urlObj.hostname = options.hostname;
|
||||
} else {
|
||||
urlObj.hostname = 'localhost';
|
||||
}
|
||||
|
||||
if (options.port) {
|
||||
urlObj.port = options.port;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.path && / /.test(options.path)) {
|
||||
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
|
||||
// with an additional rule for ignoring percentage-escaped characters
|
||||
// but that's a) hard to capture in a regular expression that performs
|
||||
// well, and b) possibly too restrictive for real-world usage. That's
|
||||
// why it only scans for spaces because those are guaranteed to create
|
||||
// an invalid request.
|
||||
throw new TypeError('Request path contains unescaped characters');
|
||||
}
|
||||
const pathObj = url.parse(options.path || '/');
|
||||
urlObj.pathname = pathObj.pathname;
|
||||
urlObj.search = pathObj.search;
|
||||
urlObj.hash = pathObj.hash;
|
||||
urlStr = url.format(urlObj);
|
||||
}
|
||||
|
||||
const redirectPolicy = options.redirect || 'follow';
|
||||
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
|
||||
throw new Error('redirect mode should be one of follow, error or manual');
|
||||
}
|
||||
|
||||
if (options.headers != null && typeof options.headers !== 'object') {
|
||||
throw new TypeError('headers must be an object');
|
||||
}
|
||||
|
||||
const urlLoaderOptions: NodeJS.CreateURLLoaderOptions & { redirectPolicy: RedirectPolicy, headers: Record<string, { name: string, value: string | string[] }>, allowNonHttpProtocols: boolean } = {
|
||||
method: (options.method || 'GET').toUpperCase(),
|
||||
url: urlStr,
|
||||
redirectPolicy,
|
||||
headers: {},
|
||||
body: null as any,
|
||||
useSessionCookies: options.useSessionCookies,
|
||||
credentials: options.credentials,
|
||||
origin: options.origin,
|
||||
referrerPolicy: options.referrerPolicy,
|
||||
cache: options.cache,
|
||||
allowNonHttpProtocols: Object.prototype.hasOwnProperty.call(options, kAllowNonHttpProtocols)
|
||||
};
|
||||
const headers: Record<string, string | string[]> = options.headers || {};
|
||||
for (const [name, value] of Object.entries(headers)) {
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
const key = name.toLowerCase();
|
||||
urlLoaderOptions.headers[key] = { name, value };
|
||||
}
|
||||
if (options.session) {
|
||||
if (!(options.session instanceof Session)) { throw new TypeError('`session` should be an instance of the Session class'); }
|
||||
urlLoaderOptions.session = options.session;
|
||||
} else if (options.partition) {
|
||||
if (typeof options.partition === 'string') {
|
||||
urlLoaderOptions.partition = options.partition;
|
||||
} else {
|
||||
throw new TypeError('`partition` should be a string');
|
||||
}
|
||||
}
|
||||
return urlLoaderOptions;
|
||||
}
|
||||
|
||||
export class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
_started: boolean = false;
|
||||
_firstWrite: boolean = false;
|
||||
_aborted: boolean = false;
|
||||
_chunkedEncoding: boolean | undefined;
|
||||
_body: Writable | undefined;
|
||||
_urlLoaderOptions: NodeJS.CreateURLLoaderOptions & { headers: Record<string, { name: string, value: string | string[] }> };
|
||||
_redirectPolicy: RedirectPolicy;
|
||||
_followRedirectCb?: () => void;
|
||||
_uploadProgress?: { active: boolean, started: boolean, current: number, total: number };
|
||||
_urlLoader?: NodeJS.URLLoader;
|
||||
_response?: IncomingMessage;
|
||||
|
||||
constructor (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
super({ autoDestroy: true });
|
||||
|
||||
if (!app.isReady()) {
|
||||
throw new Error('net module can only be used after app is ready');
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
this.once('response', callback);
|
||||
}
|
||||
|
||||
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options);
|
||||
const urlObj = new URL(urlLoaderOptions.url);
|
||||
if (!urlLoaderOptions.allowNonHttpProtocols && !kHttpProtocols.has(urlObj.protocol)) {
|
||||
throw new Error('ClientRequest only supports http: and https: protocols');
|
||||
}
|
||||
if (urlLoaderOptions.credentials === 'same-origin' && !urlLoaderOptions.origin) { throw new Error('credentials: same-origin requires origin to be set'); }
|
||||
this._urlLoaderOptions = urlLoaderOptions;
|
||||
this._redirectPolicy = redirectPolicy;
|
||||
}
|
||||
|
||||
get chunkedEncoding () {
|
||||
return this._chunkedEncoding || false;
|
||||
}
|
||||
|
||||
set chunkedEncoding (value: boolean) {
|
||||
if (this._started) {
|
||||
throw new Error('chunkedEncoding can only be set before the request is started');
|
||||
}
|
||||
if (typeof this._chunkedEncoding !== 'undefined') {
|
||||
throw new Error('chunkedEncoding can only be set once');
|
||||
}
|
||||
this._chunkedEncoding = !!value;
|
||||
if (this._chunkedEncoding) {
|
||||
this._body = new ChunkedBodyStream(this);
|
||||
this._urlLoaderOptions.body = (pipe: NodeJS.DataPipe) => {
|
||||
(this._body! as ChunkedBodyStream).startReading(pipe);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
setHeader (name: string, value: string) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)');
|
||||
}
|
||||
if (value == null) {
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)');
|
||||
}
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t set headers after they are sent');
|
||||
}
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
this._urlLoaderOptions.headers[key] = { name, value };
|
||||
}
|
||||
|
||||
getHeader (name: string) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for getHeader(name)');
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
const header = this._urlLoaderOptions.headers[key];
|
||||
return header && header.value as any;
|
||||
}
|
||||
|
||||
removeHeader (name: string) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for removeHeader(name)');
|
||||
}
|
||||
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t remove headers after they are sent');
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
delete this._urlLoaderOptions.headers[key];
|
||||
}
|
||||
|
||||
_write (chunk: Buffer, encoding: BufferEncoding, callback: () => void) {
|
||||
this._firstWrite = true;
|
||||
if (!this._body) {
|
||||
this._body = new SlurpStream();
|
||||
this._body.on('finish', () => {
|
||||
this._urlLoaderOptions.body = (this._body as SlurpStream).data();
|
||||
this._startRequest();
|
||||
});
|
||||
}
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.write(chunk, encoding, callback);
|
||||
}
|
||||
|
||||
_final (callback: () => void) {
|
||||
if (this._body) {
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.end(callback);
|
||||
} else {
|
||||
// end() called without a body, go ahead and start the request
|
||||
this._startRequest();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
_startRequest () {
|
||||
this._started = true;
|
||||
const stringifyValues = (obj: Record<string, { name: string, value: string | string[] }>) => {
|
||||
const ret: Record<string, string> = {};
|
||||
for (const k of Object.keys(obj)) {
|
||||
const kv = obj[k];
|
||||
ret[kv.name] = kv.value.toString();
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
this._urlLoaderOptions.referrer = this.getHeader('referer') || '';
|
||||
this._urlLoaderOptions.origin = this._urlLoaderOptions.origin || this.getHeader('origin') || '';
|
||||
this._urlLoaderOptions.hasUserActivation = this.getHeader('sec-fetch-user') === '?1';
|
||||
this._urlLoaderOptions.mode = this.getHeader('sec-fetch-mode') || '';
|
||||
this._urlLoaderOptions.destination = this.getHeader('sec-fetch-dest') || '';
|
||||
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.headers) };
|
||||
this._urlLoader = createURLLoader(opts);
|
||||
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
|
||||
const response = this._response = new IncomingMessage(responseHead);
|
||||
this.emit('response', response);
|
||||
});
|
||||
this._urlLoader.on('data', (event, data, resume) => {
|
||||
this._response!._storeInternalData(Buffer.from(data), resume);
|
||||
});
|
||||
this._urlLoader.on('complete', () => {
|
||||
if (this._response) { this._response._storeInternalData(null, null); }
|
||||
});
|
||||
this._urlLoader.on('error', (event, netErrorString) => {
|
||||
const error = new Error(netErrorString);
|
||||
if (this._response) this._response.destroy(error);
|
||||
this._die(error);
|
||||
});
|
||||
|
||||
this._urlLoader.on('login', (event, authInfo, callback) => {
|
||||
const handled = this.emit('login', authInfo, callback);
|
||||
if (!handled) {
|
||||
// If there were no listeners, cancel the authentication request.
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
||||
this._urlLoader.on('redirect', (event, redirectInfo, headers) => {
|
||||
const { statusCode, newMethod, newUrl } = redirectInfo;
|
||||
if (this._redirectPolicy === 'error') {
|
||||
this._die(new Error('Attempted to redirect, but redirect policy was \'error\''));
|
||||
} else if (this._redirectPolicy === 'manual') {
|
||||
let _followRedirect = false;
|
||||
this._followRedirectCb = () => { _followRedirect = true; };
|
||||
try {
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers);
|
||||
} finally {
|
||||
this._followRedirectCb = undefined;
|
||||
if (!_followRedirect && !this._aborted) {
|
||||
this._die(new Error('Redirect was cancelled'));
|
||||
}
|
||||
}
|
||||
} else if (this._redirectPolicy === 'follow') {
|
||||
// Calling followRedirect() when the redirect policy is 'follow' is
|
||||
// allowed but does nothing. (Perhaps it should throw an error
|
||||
// though...? Since the redirect will happen regardless.)
|
||||
try {
|
||||
this._followRedirectCb = () => {};
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers);
|
||||
} finally {
|
||||
this._followRedirectCb = undefined;
|
||||
}
|
||||
} else {
|
||||
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`));
|
||||
}
|
||||
});
|
||||
|
||||
this._urlLoader.on('upload-progress', (event, position, total) => {
|
||||
this._uploadProgress = { active: true, started: true, current: position, total };
|
||||
this.emit('upload-progress', position, total); // Undocumented, for now
|
||||
});
|
||||
|
||||
this._urlLoader.on('download-progress', (event, current) => {
|
||||
if (this._response) {
|
||||
this._response.emit('download-progress', current); // Undocumented, for now
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
followRedirect () {
|
||||
if (this._followRedirectCb) {
|
||||
this._followRedirectCb();
|
||||
} else {
|
||||
throw new Error('followRedirect() called, but was not waiting for a redirect');
|
||||
}
|
||||
}
|
||||
|
||||
abort () {
|
||||
if (!this._aborted) {
|
||||
process.nextTick(() => { this.emit('abort'); });
|
||||
}
|
||||
this._aborted = true;
|
||||
this._die();
|
||||
}
|
||||
|
||||
_die (err?: Error) {
|
||||
// Node.js assumes that any stream which is ended is no longer capable of emitted events
|
||||
// which is a faulty assumption for the case of an object that is acting like a stream
|
||||
// (our urlRequest). If we don't emit here, this causes errors since we *do* expect
|
||||
// that error events can be emitted after urlRequest.end().
|
||||
if ((this as any)._writableState.destroyed && err) {
|
||||
this.emit('error', err);
|
||||
}
|
||||
|
||||
this.destroy(err);
|
||||
if (this._urlLoader) {
|
||||
this._urlLoader.cancel();
|
||||
if (this._response) this._response.destroy(err);
|
||||
}
|
||||
}
|
||||
|
||||
getUploadProgress (): UploadProgress {
|
||||
return this._uploadProgress ? { ...this._uploadProgress } : { active: false, started: false, current: 0, total: 0 };
|
||||
}
|
||||
}
|
||||
120
lib/browser/api/net-fetch.ts
Normal file
120
lib/browser/api/net-fetch.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { net, IncomingMessage, Session as SessionT } from 'electron/main';
|
||||
import { Readable, Writable, isReadable } from 'stream';
|
||||
import { allowAnyProtocol } from '@electron/internal/browser/api/net-client-request';
|
||||
|
||||
function createDeferredPromise<T, E extends Error = Error> (): { promise: Promise<T>; resolve: (x: T) => void; reject: (e: E) => void; } {
|
||||
let res: (x: T) => void;
|
||||
let rej: (e: E) => void;
|
||||
const promise = new Promise<T>((resolve, reject) => {
|
||||
res = resolve;
|
||||
rej = reject;
|
||||
});
|
||||
|
||||
return { promise, resolve: res!, reject: rej! };
|
||||
}
|
||||
|
||||
export function fetchWithSession (input: RequestInfo, init: (RequestInit & {bypassCustomProtocolHandlers?: boolean}) | undefined, session: SessionT): Promise<Response> {
|
||||
const p = createDeferredPromise<Response>();
|
||||
let req: Request;
|
||||
try {
|
||||
req = new Request(input, init);
|
||||
} catch (e: any) {
|
||||
p.reject(e);
|
||||
return p.promise;
|
||||
}
|
||||
|
||||
if (req.signal.aborted) {
|
||||
// 1. Abort the fetch() call with p, request, null, and
|
||||
// requestObject’s signal’s abort reason.
|
||||
const error = (req.signal as any).reason ?? new DOMException('The operation was aborted.', 'AbortError');
|
||||
p.reject(error);
|
||||
|
||||
if (req.body != null && isReadable(req.body as unknown as NodeJS.ReadableStream)) {
|
||||
req.body.cancel(error).catch((err) => {
|
||||
if (err.code === 'ERR_INVALID_STATE') {
|
||||
// Node bug?
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Return p.
|
||||
return p.promise;
|
||||
}
|
||||
|
||||
let locallyAborted = false;
|
||||
req.signal.addEventListener(
|
||||
'abort',
|
||||
() => {
|
||||
// 1. Set locallyAborted to true.
|
||||
locallyAborted = true;
|
||||
|
||||
// 2. Abort the fetch() call with p, request, responseObject,
|
||||
// and requestObject’s signal’s abort reason.
|
||||
const error = (req.signal as any).reason ?? new DOMException('The operation was aborted.', 'AbortError');
|
||||
p.reject(error);
|
||||
if (req.body != null && isReadable(req.body as unknown as NodeJS.ReadableStream)) {
|
||||
req.body.cancel(error).catch((err) => {
|
||||
if (err.code === 'ERR_INVALID_STATE') {
|
||||
// Node bug?
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
r.abort();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
|
||||
const origin = req.headers.get('origin') ?? undefined;
|
||||
// We can't set credentials to same-origin unless there's an origin set.
|
||||
const credentials = req.credentials === 'same-origin' && !origin ? 'include' : req.credentials;
|
||||
|
||||
const r = net.request(allowAnyProtocol({
|
||||
session,
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
origin,
|
||||
credentials,
|
||||
cache: req.cache,
|
||||
referrerPolicy: req.referrerPolicy,
|
||||
redirect: req.redirect
|
||||
}));
|
||||
|
||||
(r as any)._urlLoaderOptions.bypassCustomProtocolHandlers = !!init?.bypassCustomProtocolHandlers;
|
||||
|
||||
// cors is the default mode, but we can't set mode=cors without an origin.
|
||||
if (req.mode && (req.mode !== 'cors' || origin)) {
|
||||
r.setHeader('Sec-Fetch-Mode', req.mode);
|
||||
}
|
||||
|
||||
for (const [k, v] of req.headers) {
|
||||
r.setHeader(k, v);
|
||||
}
|
||||
|
||||
r.on('response', (resp: IncomingMessage) => {
|
||||
if (locallyAborted) return;
|
||||
const headers = new Headers();
|
||||
for (const [k, v] of Object.entries(resp.headers)) { headers.set(k, Array.isArray(v) ? v.join(', ') : v); }
|
||||
const nullBodyStatus = [101, 204, 205, 304];
|
||||
const body = nullBodyStatus.includes(resp.statusCode) || req.method === 'HEAD' ? null : Readable.toWeb(resp as unknown as Readable) as ReadableStream;
|
||||
const rResp = new Response(body, {
|
||||
headers,
|
||||
status: resp.statusCode,
|
||||
statusText: resp.statusMessage
|
||||
});
|
||||
(rResp as any).__original_resp = resp;
|
||||
p.resolve(rResp);
|
||||
});
|
||||
|
||||
r.on('error', (err) => {
|
||||
p.reject(err);
|
||||
});
|
||||
|
||||
if (!req.body?.pipeTo(Writable.toWeb(r as unknown as Writable)).then(() => r.end())) { r.end(); }
|
||||
|
||||
return p.promise;
|
||||
}
|
||||
@@ -1,531 +1,17 @@
|
||||
import * as url from 'url';
|
||||
import { Readable, Writable } from 'stream';
|
||||
import { app, session } from 'electron/main';
|
||||
import type { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
|
||||
import { IncomingMessage, session } from 'electron/main';
|
||||
import type { ClientRequestConstructorOptions } from 'electron/main';
|
||||
import { ClientRequest } from '@electron/internal/browser/api/net-client-request';
|
||||
|
||||
const {
|
||||
isOnline,
|
||||
isValidHeaderName,
|
||||
isValidHeaderValue,
|
||||
createURLLoader
|
||||
} = process._linkedBinding('electron_browser_net');
|
||||
|
||||
const kSupportedProtocols = new Set(['http:', 'https:']);
|
||||
|
||||
// set of headers that Node.js discards duplicates for
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
const discardableDuplicateHeaders = new Set([
|
||||
'content-type',
|
||||
'content-length',
|
||||
'user-agent',
|
||||
'referer',
|
||||
'host',
|
||||
'authorization',
|
||||
'proxy-authorization',
|
||||
'if-modified-since',
|
||||
'if-unmodified-since',
|
||||
'from',
|
||||
'location',
|
||||
'max-forwards',
|
||||
'retry-after',
|
||||
'etag',
|
||||
'last-modified',
|
||||
'server',
|
||||
'age',
|
||||
'expires'
|
||||
]);
|
||||
|
||||
class IncomingMessage extends Readable {
|
||||
_shouldPush: boolean = false;
|
||||
_data: (Buffer | null)[] = [];
|
||||
_responseHead: NodeJS.ResponseHead;
|
||||
_resume: (() => void) | null = null;
|
||||
|
||||
constructor (responseHead: NodeJS.ResponseHead) {
|
||||
super();
|
||||
this._responseHead = responseHead;
|
||||
}
|
||||
|
||||
get statusCode () {
|
||||
return this._responseHead.statusCode;
|
||||
}
|
||||
|
||||
get statusMessage () {
|
||||
return this._responseHead.statusMessage;
|
||||
}
|
||||
|
||||
get headers () {
|
||||
const filteredHeaders: Record<string, string | string[]> = {};
|
||||
const { headers, rawHeaders } = this._responseHead;
|
||||
for (const [name, values] of Object.entries(headers)) {
|
||||
filteredHeaders[name] = discardableDuplicateHeaders.has(name) ? values[0] : values.join(', ');
|
||||
}
|
||||
const cookies = rawHeaders.filter(({ key }) => key.toLowerCase() === 'set-cookie').map(({ value }) => value);
|
||||
// keep set-cookie as an array per Node.js rules
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
if (cookies.length) { filteredHeaders['set-cookie'] = cookies; }
|
||||
return filteredHeaders;
|
||||
}
|
||||
|
||||
get rawHeaders () {
|
||||
const rawHeadersArr: string[] = [];
|
||||
const { rawHeaders } = this._responseHead;
|
||||
rawHeaders.forEach(header => {
|
||||
rawHeadersArr.push(header.key, header.value);
|
||||
});
|
||||
return rawHeadersArr;
|
||||
}
|
||||
|
||||
get httpVersion () {
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
|
||||
}
|
||||
|
||||
get httpVersionMajor () {
|
||||
return this._responseHead.httpVersion.major;
|
||||
}
|
||||
|
||||
get httpVersionMinor () {
|
||||
return this._responseHead.httpVersion.minor;
|
||||
}
|
||||
|
||||
get rawTrailers () {
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
}
|
||||
|
||||
get trailers () {
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
}
|
||||
|
||||
_storeInternalData (chunk: Buffer | null, resume: (() => void) | null) {
|
||||
// save the network callback for use in _pushInternalData
|
||||
this._resume = resume;
|
||||
this._data.push(chunk);
|
||||
this._pushInternalData();
|
||||
}
|
||||
|
||||
_pushInternalData () {
|
||||
while (this._shouldPush && this._data.length > 0) {
|
||||
const chunk = this._data.shift();
|
||||
this._shouldPush = this.push(chunk);
|
||||
}
|
||||
if (this._shouldPush && this._resume) {
|
||||
// Reset the callback, so that a new one is used for each
|
||||
// batch of throttled data. Do this before calling resume to avoid a
|
||||
// potential race-condition
|
||||
const resume = this._resume;
|
||||
this._resume = null;
|
||||
|
||||
resume();
|
||||
}
|
||||
}
|
||||
|
||||
_read () {
|
||||
this._shouldPush = true;
|
||||
this._pushInternalData();
|
||||
}
|
||||
}
|
||||
|
||||
/** Writable stream that buffers up everything written to it. */
|
||||
class SlurpStream extends Writable {
|
||||
_data: Buffer;
|
||||
constructor () {
|
||||
super();
|
||||
this._data = Buffer.alloc(0);
|
||||
}
|
||||
|
||||
_write (chunk: Buffer, encoding: string, callback: () => void) {
|
||||
this._data = Buffer.concat([this._data, chunk]);
|
||||
callback();
|
||||
}
|
||||
|
||||
data () { return this._data; }
|
||||
}
|
||||
|
||||
class ChunkedBodyStream extends Writable {
|
||||
_pendingChunk: Buffer | undefined;
|
||||
_downstream?: NodeJS.DataPipe;
|
||||
_pendingCallback?: (error?: Error) => void;
|
||||
_clientRequest: ClientRequest;
|
||||
|
||||
constructor (clientRequest: ClientRequest) {
|
||||
super();
|
||||
this._clientRequest = clientRequest;
|
||||
}
|
||||
|
||||
_write (chunk: Buffer, encoding: string, callback: () => void) {
|
||||
if (this._downstream) {
|
||||
this._downstream.write(chunk).then(callback, callback);
|
||||
} else {
|
||||
// the contract of _write is that we won't be called again until we call
|
||||
// the callback, so we're good to just save a single chunk.
|
||||
this._pendingChunk = chunk;
|
||||
this._pendingCallback = callback;
|
||||
|
||||
// The first write to a chunked body stream begins the request.
|
||||
this._clientRequest._startRequest();
|
||||
}
|
||||
}
|
||||
|
||||
_final (callback: () => void) {
|
||||
this._downstream!.done();
|
||||
callback();
|
||||
}
|
||||
|
||||
startReading (pipe: NodeJS.DataPipe) {
|
||||
if (this._downstream) {
|
||||
throw new Error('two startReading calls???');
|
||||
}
|
||||
this._downstream = pipe;
|
||||
if (this._pendingChunk) {
|
||||
const doneWriting = (maybeError: Error | void) => {
|
||||
// If the underlying request has been aborted, we honestly don't care about the error
|
||||
// all work should cease as soon as we abort anyway, this error is probably a
|
||||
// "mojo pipe disconnected" error (code=9)
|
||||
if (this._clientRequest._aborted) return;
|
||||
|
||||
const cb = this._pendingCallback!;
|
||||
delete this._pendingCallback;
|
||||
delete this._pendingChunk;
|
||||
cb(maybeError || undefined);
|
||||
};
|
||||
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type RedirectPolicy = 'manual' | 'follow' | 'error';
|
||||
|
||||
function parseOptions (optionsIn: ClientRequestConstructorOptions | string): NodeJS.CreateURLLoaderOptions & { redirectPolicy: RedirectPolicy, headers: Record<string, { name: string, value: string | string[] }> } {
|
||||
const options: any = typeof optionsIn === 'string' ? url.parse(optionsIn) : { ...optionsIn };
|
||||
|
||||
let urlStr: string = options.url;
|
||||
|
||||
if (!urlStr) {
|
||||
const urlObj: url.UrlObject = {};
|
||||
const protocol = options.protocol || 'http:';
|
||||
if (!kSupportedProtocols.has(protocol)) {
|
||||
throw new Error('Protocol "' + protocol + '" not supported');
|
||||
}
|
||||
urlObj.protocol = protocol;
|
||||
|
||||
if (options.host) {
|
||||
urlObj.host = options.host;
|
||||
} else {
|
||||
if (options.hostname) {
|
||||
urlObj.hostname = options.hostname;
|
||||
} else {
|
||||
urlObj.hostname = 'localhost';
|
||||
}
|
||||
|
||||
if (options.port) {
|
||||
urlObj.port = options.port;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.path && / /.test(options.path)) {
|
||||
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
|
||||
// with an additional rule for ignoring percentage-escaped characters
|
||||
// but that's a) hard to capture in a regular expression that performs
|
||||
// well, and b) possibly too restrictive for real-world usage. That's
|
||||
// why it only scans for spaces because those are guaranteed to create
|
||||
// an invalid request.
|
||||
throw new TypeError('Request path contains unescaped characters');
|
||||
}
|
||||
const pathObj = url.parse(options.path || '/');
|
||||
urlObj.pathname = pathObj.pathname;
|
||||
urlObj.search = pathObj.search;
|
||||
urlObj.hash = pathObj.hash;
|
||||
urlStr = url.format(urlObj);
|
||||
}
|
||||
|
||||
const redirectPolicy = options.redirect || 'follow';
|
||||
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
|
||||
throw new Error('redirect mode should be one of follow, error or manual');
|
||||
}
|
||||
|
||||
if (options.headers != null && typeof options.headers !== 'object') {
|
||||
throw new TypeError('headers must be an object');
|
||||
}
|
||||
|
||||
const urlLoaderOptions: NodeJS.CreateURLLoaderOptions & { redirectPolicy: RedirectPolicy, headers: Record<string, { name: string, value: string | string[] }> } = {
|
||||
method: (options.method || 'GET').toUpperCase(),
|
||||
url: urlStr,
|
||||
redirectPolicy,
|
||||
headers: {},
|
||||
body: null as any,
|
||||
useSessionCookies: options.useSessionCookies,
|
||||
credentials: options.credentials,
|
||||
origin: options.origin
|
||||
};
|
||||
const headers: Record<string, string | string[]> = options.headers || {};
|
||||
for (const [name, value] of Object.entries(headers)) {
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
const key = name.toLowerCase();
|
||||
urlLoaderOptions.headers[key] = { name, value };
|
||||
}
|
||||
if (options.session) {
|
||||
// Weak check, but it should be enough to catch 99% of accidental misuses.
|
||||
if (options.session.constructor && options.session.constructor.name === 'Session') {
|
||||
urlLoaderOptions.session = options.session;
|
||||
} else {
|
||||
throw new TypeError('`session` should be an instance of the Session class');
|
||||
}
|
||||
} else if (options.partition) {
|
||||
if (typeof options.partition === 'string') {
|
||||
urlLoaderOptions.partition = options.partition;
|
||||
} else {
|
||||
throw new TypeError('`partition` should be a string');
|
||||
}
|
||||
}
|
||||
return urlLoaderOptions;
|
||||
}
|
||||
|
||||
export class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
_started: boolean = false;
|
||||
_firstWrite: boolean = false;
|
||||
_aborted: boolean = false;
|
||||
_chunkedEncoding: boolean | undefined;
|
||||
_body: Writable | undefined;
|
||||
_urlLoaderOptions: NodeJS.CreateURLLoaderOptions & { headers: Record<string, { name: string, value: string | string[] }> };
|
||||
_redirectPolicy: RedirectPolicy;
|
||||
_followRedirectCb?: () => void;
|
||||
_uploadProgress?: { active: boolean, started: boolean, current: number, total: number };
|
||||
_urlLoader?: NodeJS.URLLoader;
|
||||
_response?: IncomingMessage;
|
||||
|
||||
constructor (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
super({ autoDestroy: true });
|
||||
|
||||
if (!app.isReady()) {
|
||||
throw new Error('net module can only be used after app is ready');
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
this.once('response', callback);
|
||||
}
|
||||
|
||||
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options);
|
||||
this._urlLoaderOptions = urlLoaderOptions;
|
||||
this._redirectPolicy = redirectPolicy;
|
||||
}
|
||||
|
||||
get chunkedEncoding () {
|
||||
return this._chunkedEncoding || false;
|
||||
}
|
||||
|
||||
set chunkedEncoding (value: boolean) {
|
||||
if (this._started) {
|
||||
throw new Error('chunkedEncoding can only be set before the request is started');
|
||||
}
|
||||
if (typeof this._chunkedEncoding !== 'undefined') {
|
||||
throw new Error('chunkedEncoding can only be set once');
|
||||
}
|
||||
this._chunkedEncoding = !!value;
|
||||
if (this._chunkedEncoding) {
|
||||
this._body = new ChunkedBodyStream(this);
|
||||
this._urlLoaderOptions.body = (pipe: NodeJS.DataPipe) => {
|
||||
(this._body! as ChunkedBodyStream).startReading(pipe);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
setHeader (name: string, value: string) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)');
|
||||
}
|
||||
if (value == null) {
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)');
|
||||
}
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t set headers after they are sent');
|
||||
}
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
this._urlLoaderOptions.headers[key] = { name, value };
|
||||
}
|
||||
|
||||
getHeader (name: string) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for getHeader(name)');
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
const header = this._urlLoaderOptions.headers[key];
|
||||
return header && header.value as any;
|
||||
}
|
||||
|
||||
removeHeader (name: string) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for removeHeader(name)');
|
||||
}
|
||||
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t remove headers after they are sent');
|
||||
}
|
||||
|
||||
const key = name.toLowerCase();
|
||||
delete this._urlLoaderOptions.headers[key];
|
||||
}
|
||||
|
||||
_write (chunk: Buffer, encoding: BufferEncoding, callback: () => void) {
|
||||
this._firstWrite = true;
|
||||
if (!this._body) {
|
||||
this._body = new SlurpStream();
|
||||
this._body.on('finish', () => {
|
||||
this._urlLoaderOptions.body = (this._body as SlurpStream).data();
|
||||
this._startRequest();
|
||||
});
|
||||
}
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.write(chunk, encoding, callback);
|
||||
}
|
||||
|
||||
_final (callback: () => void) {
|
||||
if (this._body) {
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.end(callback);
|
||||
} else {
|
||||
// end() called without a body, go ahead and start the request
|
||||
this._startRequest();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
_startRequest () {
|
||||
this._started = true;
|
||||
const stringifyValues = (obj: Record<string, { name: string, value: string | string[] }>) => {
|
||||
const ret: Record<string, string> = {};
|
||||
for (const k of Object.keys(obj)) {
|
||||
const kv = obj[k];
|
||||
ret[kv.name] = kv.value.toString();
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
this._urlLoaderOptions.referrer = this.getHeader('referer') || '';
|
||||
this._urlLoaderOptions.origin = this._urlLoaderOptions.origin || this.getHeader('origin') || '';
|
||||
this._urlLoaderOptions.hasUserActivation = this.getHeader('sec-fetch-user') === '?1';
|
||||
this._urlLoaderOptions.mode = this.getHeader('sec-fetch-mode') || '';
|
||||
this._urlLoaderOptions.destination = this.getHeader('sec-fetch-dest') || '';
|
||||
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.headers) };
|
||||
this._urlLoader = createURLLoader(opts);
|
||||
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
|
||||
const response = this._response = new IncomingMessage(responseHead);
|
||||
this.emit('response', response);
|
||||
});
|
||||
this._urlLoader.on('data', (event, data, resume) => {
|
||||
this._response!._storeInternalData(Buffer.from(data), resume);
|
||||
});
|
||||
this._urlLoader.on('complete', () => {
|
||||
if (this._response) { this._response._storeInternalData(null, null); }
|
||||
});
|
||||
this._urlLoader.on('error', (event, netErrorString) => {
|
||||
const error = new Error(netErrorString);
|
||||
if (this._response) this._response.destroy(error);
|
||||
this._die(error);
|
||||
});
|
||||
|
||||
this._urlLoader.on('login', (event, authInfo, callback) => {
|
||||
const handled = this.emit('login', authInfo, callback);
|
||||
if (!handled) {
|
||||
// If there were no listeners, cancel the authentication request.
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
||||
this._urlLoader.on('redirect', (event, redirectInfo, headers) => {
|
||||
const { statusCode, newMethod, newUrl } = redirectInfo;
|
||||
if (this._redirectPolicy === 'error') {
|
||||
this._die(new Error('Attempted to redirect, but redirect policy was \'error\''));
|
||||
} else if (this._redirectPolicy === 'manual') {
|
||||
let _followRedirect = false;
|
||||
this._followRedirectCb = () => { _followRedirect = true; };
|
||||
try {
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers);
|
||||
} finally {
|
||||
this._followRedirectCb = undefined;
|
||||
if (!_followRedirect && !this._aborted) {
|
||||
this._die(new Error('Redirect was cancelled'));
|
||||
}
|
||||
}
|
||||
} else if (this._redirectPolicy === 'follow') {
|
||||
// Calling followRedirect() when the redirect policy is 'follow' is
|
||||
// allowed but does nothing. (Perhaps it should throw an error
|
||||
// though...? Since the redirect will happen regardless.)
|
||||
try {
|
||||
this._followRedirectCb = () => {};
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers);
|
||||
} finally {
|
||||
this._followRedirectCb = undefined;
|
||||
}
|
||||
} else {
|
||||
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`));
|
||||
}
|
||||
});
|
||||
|
||||
this._urlLoader.on('upload-progress', (event, position, total) => {
|
||||
this._uploadProgress = { active: true, started: true, current: position, total };
|
||||
this.emit('upload-progress', position, total); // Undocumented, for now
|
||||
});
|
||||
|
||||
this._urlLoader.on('download-progress', (event, current) => {
|
||||
if (this._response) {
|
||||
this._response.emit('download-progress', current); // Undocumented, for now
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
followRedirect () {
|
||||
if (this._followRedirectCb) {
|
||||
this._followRedirectCb();
|
||||
} else {
|
||||
throw new Error('followRedirect() called, but was not waiting for a redirect');
|
||||
}
|
||||
}
|
||||
|
||||
abort () {
|
||||
if (!this._aborted) {
|
||||
process.nextTick(() => { this.emit('abort'); });
|
||||
}
|
||||
this._aborted = true;
|
||||
this._die();
|
||||
}
|
||||
|
||||
_die (err?: Error) {
|
||||
// Node.js assumes that any stream which is ended is no longer capable of emitted events
|
||||
// which is a faulty assumption for the case of an object that is acting like a stream
|
||||
// (our urlRequest). If we don't emit here, this causes errors since we *do* expect
|
||||
// that error events can be emitted after urlRequest.end().
|
||||
if ((this as any)._writableState.destroyed && err) {
|
||||
this.emit('error', err);
|
||||
}
|
||||
|
||||
this.destroy(err);
|
||||
if (this._urlLoader) {
|
||||
this._urlLoader.cancel();
|
||||
if (this._response) this._response.destroy(err);
|
||||
}
|
||||
}
|
||||
|
||||
getUploadProgress (): UploadProgress {
|
||||
return this._uploadProgress ? { ...this._uploadProgress } : { active: false, started: false, current: 0, total: 0 };
|
||||
}
|
||||
}
|
||||
const { isOnline } = process._linkedBinding('electron_browser_net');
|
||||
|
||||
export function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
return new ClientRequest(options, callback);
|
||||
}
|
||||
|
||||
export function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
return session.defaultSession.fetch(input, init);
|
||||
}
|
||||
|
||||
export function resolveHost (host: string, options?: Electron.ResolveHostOptions): Promise<Electron.ResolvedHost> {
|
||||
return session.defaultSession.resolveHost(host, options);
|
||||
}
|
||||
|
||||
@@ -1,33 +1,151 @@
|
||||
import { app, session } from 'electron/main';
|
||||
import { ProtocolRequest, session } from 'electron/main';
|
||||
import { createReadStream } from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
import { ReadableStream } from 'stream/web';
|
||||
|
||||
// Global protocol APIs.
|
||||
const protocol = process._linkedBinding('electron_browser_protocol');
|
||||
const { registerSchemesAsPrivileged, getStandardSchemes, Protocol } = process._linkedBinding('electron_browser_protocol');
|
||||
|
||||
// Fallback protocol APIs of default session.
|
||||
Object.setPrototypeOf(protocol, new Proxy({}, {
|
||||
get (_target, property) {
|
||||
if (!app.isReady()) return;
|
||||
const ERR_FAILED = -2;
|
||||
const ERR_UNEXPECTED = -9;
|
||||
|
||||
const protocol = session.defaultSession!.protocol;
|
||||
if (!Object.prototype.hasOwnProperty.call(protocol, property)) return;
|
||||
const isBuiltInScheme = (scheme: string) => ['http', 'https', 'file'].includes(scheme);
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args);
|
||||
},
|
||||
function makeStreamFromPipe (pipe: any): ReadableStream {
|
||||
const buf = new Uint8Array(1024 * 1024 /* 1 MB */);
|
||||
return new ReadableStream({
|
||||
async pull (controller) {
|
||||
try {
|
||||
const rv = await pipe.read(buf);
|
||||
if (rv > 0) {
|
||||
controller.enqueue(buf.subarray(0, rv));
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
} catch (e) {
|
||||
controller.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ownKeys () {
|
||||
if (!app.isReady()) return [];
|
||||
return Reflect.ownKeys(session.defaultSession!.protocol);
|
||||
},
|
||||
function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): RequestInit['body'] {
|
||||
if (!uploadData) return null;
|
||||
// Optimization: skip creating a stream if the request is just a single buffer.
|
||||
if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') return uploadData[0].bytes;
|
||||
|
||||
has: (target, property: string) => {
|
||||
if (!app.isReady()) return false;
|
||||
return Reflect.has(session.defaultSession!.protocol, property);
|
||||
},
|
||||
const chunks = [...uploadData] as any[]; // TODO: types are wrong
|
||||
let current: ReadableStreamDefaultReader | null = null;
|
||||
return new ReadableStream({
|
||||
pull (controller) {
|
||||
if (current) {
|
||||
current.read().then(({ done, value }) => {
|
||||
controller.enqueue(value);
|
||||
if (done) current = null;
|
||||
}, (err) => {
|
||||
controller.error(err);
|
||||
});
|
||||
} else {
|
||||
if (!chunks.length) { return controller.close(); }
|
||||
const chunk = chunks.shift()!;
|
||||
if (chunk.type === 'rawData') { controller.enqueue(chunk.bytes); } else if (chunk.type === 'file') {
|
||||
current = Readable.toWeb(createReadStream(chunk.filePath, { start: chunk.offset ?? 0, end: chunk.length >= 0 ? chunk.offset + chunk.length : undefined })).getReader();
|
||||
this.pull!(controller);
|
||||
} else if (chunk.type === 'stream') {
|
||||
current = makeStreamFromPipe(chunk.body).getReader();
|
||||
this.pull!(controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}) as RequestInit['body'];
|
||||
}
|
||||
|
||||
getOwnPropertyDescriptor () {
|
||||
return { configurable: true, enumerable: true };
|
||||
// TODO(codebytere): Use Object.hasOwn() once we update to ECMAScript 2022.
|
||||
function validateResponse (res: Response) {
|
||||
if (!res || typeof res !== 'object') return false;
|
||||
|
||||
if (res.type === 'error') return true;
|
||||
|
||||
const exists = (key: string) => Object.prototype.hasOwnProperty.call(res, key);
|
||||
|
||||
if (exists('status') && typeof res.status !== 'number') return false;
|
||||
if (exists('statusText') && typeof res.statusText !== 'string') return false;
|
||||
if (exists('headers') && typeof res.headers !== 'object') return false;
|
||||
|
||||
if (exists('body')) {
|
||||
if (typeof res.body !== 'object') return false;
|
||||
if (res.body !== null && !(res.body instanceof ReadableStream)) return false;
|
||||
}
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Protocol.prototype.handle = function (this: Electron.Protocol, scheme: string, handler: (req: Request) => Response | Promise<Response>) {
|
||||
const register = isBuiltInScheme(scheme) ? this.interceptProtocol : this.registerProtocol;
|
||||
const success = register.call(this, scheme, async (preq: ProtocolRequest, cb: any) => {
|
||||
try {
|
||||
const body = convertToRequestBody(preq.uploadData);
|
||||
const req = new Request(preq.url, {
|
||||
headers: preq.headers,
|
||||
method: preq.method,
|
||||
referrer: preq.referrer,
|
||||
body,
|
||||
duplex: body instanceof ReadableStream ? 'half' : undefined
|
||||
} as any);
|
||||
const res = await handler(req);
|
||||
if (!validateResponse(res)) {
|
||||
return cb({ error: ERR_UNEXPECTED });
|
||||
} else if (res.type === 'error') {
|
||||
cb({ error: ERR_FAILED });
|
||||
} else {
|
||||
cb({
|
||||
data: res.body ? Readable.fromWeb(res.body as ReadableStream<ArrayBufferView>) : null,
|
||||
headers: res.headers ? Object.fromEntries(res.headers) : {},
|
||||
statusCode: res.status,
|
||||
statusText: res.statusText,
|
||||
mimeType: (res as any).__original_resp?._responseHead?.mimeType
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
cb({ error: ERR_UNEXPECTED });
|
||||
}
|
||||
});
|
||||
if (!success) throw new Error(`Failed to register protocol: ${scheme}`);
|
||||
};
|
||||
|
||||
Protocol.prototype.unhandle = function (this: Electron.Protocol, scheme: string) {
|
||||
const unregister = isBuiltInScheme(scheme) ? this.uninterceptProtocol : this.unregisterProtocol;
|
||||
if (!unregister.call(this, scheme)) { throw new Error(`Failed to unhandle protocol: ${scheme}`); }
|
||||
};
|
||||
|
||||
Protocol.prototype.isProtocolHandled = function (this: Electron.Protocol, scheme: string) {
|
||||
const isRegistered = isBuiltInScheme(scheme) ? this.isProtocolIntercepted : this.isProtocolRegistered;
|
||||
return isRegistered.call(this, scheme);
|
||||
};
|
||||
|
||||
const protocol = {
|
||||
registerSchemesAsPrivileged,
|
||||
getStandardSchemes,
|
||||
registerStringProtocol: (...args) => session.defaultSession.protocol.registerStringProtocol(...args),
|
||||
registerBufferProtocol: (...args) => session.defaultSession.protocol.registerBufferProtocol(...args),
|
||||
registerStreamProtocol: (...args) => session.defaultSession.protocol.registerStreamProtocol(...args),
|
||||
registerFileProtocol: (...args) => session.defaultSession.protocol.registerFileProtocol(...args),
|
||||
registerHttpProtocol: (...args) => session.defaultSession.protocol.registerHttpProtocol(...args),
|
||||
registerProtocol: (...args) => session.defaultSession.protocol.registerProtocol(...args),
|
||||
unregisterProtocol: (...args) => session.defaultSession.protocol.unregisterProtocol(...args),
|
||||
isProtocolRegistered: (...args) => session.defaultSession.protocol.isProtocolRegistered(...args),
|
||||
interceptStringProtocol: (...args) => session.defaultSession.protocol.interceptStringProtocol(...args),
|
||||
interceptBufferProtocol: (...args) => session.defaultSession.protocol.interceptBufferProtocol(...args),
|
||||
interceptStreamProtocol: (...args) => session.defaultSession.protocol.interceptStreamProtocol(...args),
|
||||
interceptFileProtocol: (...args) => session.defaultSession.protocol.interceptFileProtocol(...args),
|
||||
interceptHttpProtocol: (...args) => session.defaultSession.protocol.interceptHttpProtocol(...args),
|
||||
interceptProtocol: (...args) => session.defaultSession.protocol.interceptProtocol(...args),
|
||||
uninterceptProtocol: (...args) => session.defaultSession.protocol.uninterceptProtocol(...args),
|
||||
isProtocolIntercepted: (...args) => session.defaultSession.protocol.isProtocolIntercepted(...args),
|
||||
handle: (...args) => session.defaultSession.protocol.handle(...args),
|
||||
unhandle: (...args) => session.defaultSession.protocol.unhandle(...args),
|
||||
isProtocolHandled: (...args) => session.defaultSession.protocol.isProtocolHandled(...args)
|
||||
} as typeof Electron.protocol;
|
||||
|
||||
export default protocol;
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
const { fromPartition } = process._linkedBinding('electron_browser_session');
|
||||
import { fetchWithSession } from '@electron/internal/browser/api/net-fetch';
|
||||
const { fromPartition, fromPath, Session } = process._linkedBinding('electron_browser_session');
|
||||
|
||||
Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
|
||||
return fetchWithSession(input, init, this);
|
||||
};
|
||||
|
||||
export default {
|
||||
fromPartition,
|
||||
fromPath,
|
||||
get defaultSession () {
|
||||
return fromPartition('');
|
||||
}
|
||||
|
||||
@@ -337,40 +337,54 @@ WebContents.prototype.printToPDF = async function (options) {
|
||||
|
||||
// TODO(codebytere): deduplicate argument sanitization by moving rest of
|
||||
// print param logic into new file shared between printToPDF and print
|
||||
WebContents.prototype.print = function (printOptions: ElectronInternal.WebContentsPrintOptions, callback) {
|
||||
const options = printOptions ?? {};
|
||||
if (options.pageSize) {
|
||||
const pageSize = options.pageSize;
|
||||
if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
WebContents.prototype.print = function (options: ElectronInternal.WebContentsPrintOptions, callback) {
|
||||
if (typeof options !== 'object') {
|
||||
throw new Error('webContents.print(): Invalid print settings specified.');
|
||||
}
|
||||
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
const height = Math.ceil(pageSize.height);
|
||||
const width = Math.ceil(pageSize.width);
|
||||
if (!isValidCustomPageSize(width, height)) {
|
||||
throw new Error('height and width properties must be minimum 352 microns.');
|
||||
}
|
||||
const printSettings: Record<string, any> = { ...options };
|
||||
|
||||
options.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: height,
|
||||
width_microns: width
|
||||
};
|
||||
} else if (PDFPageSizes[pageSize]) {
|
||||
options.mediaSize = PDFPageSizes[pageSize];
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
const pageSize = options.pageSize ?? 'A4';
|
||||
if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
const height = Math.ceil(pageSize.height);
|
||||
const width = Math.ceil(pageSize.width);
|
||||
if (!isValidCustomPageSize(width, height)) {
|
||||
throw new Error('height and width properties must be minimum 352 microns.');
|
||||
}
|
||||
|
||||
printSettings.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: height,
|
||||
width_microns: width,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: width,
|
||||
imageable_area_top_microns: height
|
||||
};
|
||||
} else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) {
|
||||
const mediaSize = PDFPageSizes[pageSize];
|
||||
printSettings.mediaSize = {
|
||||
...mediaSize,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: mediaSize.width_microns,
|
||||
imageable_area_top_microns: mediaSize.height_microns
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
}
|
||||
|
||||
if (this._print) {
|
||||
if (callback) {
|
||||
this._print(options, callback);
|
||||
this._print(printSettings, callback);
|
||||
} else {
|
||||
this._print(options);
|
||||
this._print(printSettings);
|
||||
}
|
||||
} else {
|
||||
console.error('Error: Printing feature is disabled.');
|
||||
@@ -416,10 +430,6 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
|
||||
};
|
||||
|
||||
WebContents.prototype.loadURL = function (url, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
const p = new Promise<void>((resolve, reject) => {
|
||||
const resolveAndCleanup = () => {
|
||||
removeListeners();
|
||||
@@ -441,6 +451,7 @@ WebContents.prototype.loadURL = function (url, options) {
|
||||
};
|
||||
|
||||
let navigationStarted = false;
|
||||
let browserInitiatedInPageNavigation = false;
|
||||
const navigationListener = (event: Electron.Event, url: string, isSameDocument: boolean, isMainFrame: boolean) => {
|
||||
if (isMainFrame) {
|
||||
if (navigationStarted && !isSameDocument) {
|
||||
@@ -455,6 +466,7 @@ WebContents.prototype.loadURL = function (url, options) {
|
||||
// as the routing does not leave the document
|
||||
return rejectAndCleanup(-3, 'ERR_ABORTED', url);
|
||||
}
|
||||
browserInitiatedInPageNavigation = navigationStarted && isSameDocument;
|
||||
navigationStarted = true;
|
||||
}
|
||||
};
|
||||
@@ -469,24 +481,29 @@ WebContents.prototype.loadURL = function (url, options) {
|
||||
// would be more appropriate.
|
||||
rejectAndCleanup(-2, 'ERR_FAILED', url);
|
||||
};
|
||||
const finishListenerWhenUserInitiatedNavigation = () => {
|
||||
if (!browserInitiatedInPageNavigation) {
|
||||
finishListener();
|
||||
}
|
||||
};
|
||||
const removeListeners = () => {
|
||||
this.removeListener('did-finish-load', finishListener);
|
||||
this.removeListener('did-fail-load', failListener);
|
||||
this.removeListener('did-navigate-in-page', finishListener);
|
||||
this.removeListener('did-navigate-in-page', finishListenerWhenUserInitiatedNavigation);
|
||||
this.removeListener('did-start-navigation', navigationListener);
|
||||
this.removeListener('did-stop-loading', stopLoadingListener);
|
||||
this.removeListener('destroyed', stopLoadingListener);
|
||||
};
|
||||
this.on('did-finish-load', finishListener);
|
||||
this.on('did-fail-load', failListener);
|
||||
this.on('did-navigate-in-page', finishListener);
|
||||
this.on('did-navigate-in-page', finishListenerWhenUserInitiatedNavigation);
|
||||
this.on('did-start-navigation', navigationListener);
|
||||
this.on('did-stop-loading', stopLoadingListener);
|
||||
this.on('destroyed', stopLoadingListener);
|
||||
});
|
||||
// Add a no-op rejection handler to silence the unhandled rejection error.
|
||||
p.catch(() => {});
|
||||
this._loadURL(url, options);
|
||||
this._loadURL(url, options ?? {});
|
||||
return p;
|
||||
};
|
||||
|
||||
@@ -539,7 +556,8 @@ const addReplyToEvent = (event: Electron.IpcMainEvent) => {
|
||||
};
|
||||
};
|
||||
|
||||
const addSenderFrameToEvent = (event: Electron.IpcMainEvent | Electron.IpcMainInvokeEvent) => {
|
||||
const addSenderToEvent = (event: Electron.IpcMainEvent | Electron.IpcMainInvokeEvent, sender: Electron.WebContents) => {
|
||||
event.sender = sender;
|
||||
const { processId, frameId } = event;
|
||||
Object.defineProperty(event, 'senderFrame', {
|
||||
get: () => webFrameMain.fromId(processId, frameId)
|
||||
@@ -548,7 +566,7 @@ const addSenderFrameToEvent = (event: Electron.IpcMainEvent | Electron.IpcMainIn
|
||||
|
||||
const addReturnValueToEvent = (event: Electron.IpcMainEvent) => {
|
||||
Object.defineProperty(event, 'returnValue', {
|
||||
set: (value) => event.sendReply(value),
|
||||
set: (value) => event._replyChannel.sendReply(value),
|
||||
get: () => {}
|
||||
});
|
||||
};
|
||||
@@ -589,7 +607,7 @@ WebContents.prototype._init = function () {
|
||||
|
||||
// Dispatch IPC messages to the ipc module.
|
||||
this.on('-ipc-message' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
addSenderToEvent(event, this);
|
||||
if (internal) {
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
} else {
|
||||
@@ -602,25 +620,30 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-invoke' as any, function (event: Electron.IpcMainInvokeEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
event._reply = (result: any) => event.sendReply({ result });
|
||||
event._throw = (error: Error) => {
|
||||
this.on('-ipc-invoke' as any, async function (this: Electron.WebContents, event: Electron.IpcMainInvokeEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderToEvent(event, this);
|
||||
const replyWithResult = (result: any) => event._replyChannel.sendReply({ result });
|
||||
const replyWithError = (error: Error) => {
|
||||
console.error(`Error occurred in handler for '${channel}':`, error);
|
||||
event.sendReply({ error: error.toString() });
|
||||
event._replyChannel.sendReply({ error: error.toString() });
|
||||
};
|
||||
const maybeWebFrame = getWebFrameForEvent(event);
|
||||
const targets: (ElectronInternal.IpcMainInternal| undefined)[] = internal ? [ipcMainInternal] : [maybeWebFrame?.ipc, ipc, ipcMain];
|
||||
const target = targets.find(target => target && (target as any)._invokeHandlers.has(channel));
|
||||
if (target) {
|
||||
(target as any)._invokeHandlers.get(channel)(event, ...args);
|
||||
const handler = (target as any)._invokeHandlers.get(channel);
|
||||
try {
|
||||
replyWithResult(await Promise.resolve(handler(event, ...args)));
|
||||
} catch (err) {
|
||||
replyWithError(err as Error);
|
||||
}
|
||||
} else {
|
||||
event._throw(`No handler registered for '${channel}'`);
|
||||
replyWithError(new Error(`No handler registered for '${channel}'`));
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-message-sync' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
addSenderToEvent(event, this);
|
||||
addReturnValueToEvent(event);
|
||||
if (internal) {
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
@@ -637,8 +660,8 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-ports' as any, function (event: Electron.IpcMainEvent, internal: boolean, channel: string, message: any, ports: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
this.on('-ipc-ports' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, message: any, ports: any[]) {
|
||||
addSenderToEvent(event, this);
|
||||
event.ports = ports.map(p => new MessagePortMain(p));
|
||||
const maybeWebFrame = getWebFrameForEvent(event);
|
||||
maybeWebFrame && maybeWebFrame.ipc.emit(channel, event, message);
|
||||
@@ -646,8 +669,8 @@ WebContents.prototype._init = function () {
|
||||
ipcMain.emit(channel, event, message);
|
||||
});
|
||||
|
||||
this.on('crashed', (event, ...args) => {
|
||||
app.emit('renderer-process-crashed', event, this, ...args);
|
||||
deprecate.event(this, 'crashed', 'render-process-gone', (event: Electron.Event, details: Electron.RenderProcessGoneDetails) => {
|
||||
return [event, details.reason === 'killed'];
|
||||
});
|
||||
|
||||
this.on('render-process-gone', (event, details) => {
|
||||
@@ -666,7 +689,7 @@ WebContents.prototype._init = function () {
|
||||
|
||||
if (this.getType() !== 'remote') {
|
||||
// Make new windows requested by links behave like "window.open".
|
||||
this.on('-new-window' as any, (event: ElectronInternal.Event, url: string, frameName: string, disposition: Electron.HandlerDetails['disposition'],
|
||||
this.on('-new-window' as any, (event: Electron.Event, url: string, frameName: string, disposition: Electron.HandlerDetails['disposition'],
|
||||
rawFeatures: string, referrer: Electron.Referrer, postData: PostData) => {
|
||||
const postBody = postData ? {
|
||||
data: postData,
|
||||
@@ -692,7 +715,7 @@ WebContents.prototype._init = function () {
|
||||
const options = result.browserWindowConstructorOptions;
|
||||
if (!event.defaultPrevented) {
|
||||
openGuestWindow({
|
||||
embedder: event.sender,
|
||||
embedder: this,
|
||||
disposition,
|
||||
referrer,
|
||||
postData,
|
||||
@@ -705,7 +728,7 @@ WebContents.prototype._init = function () {
|
||||
|
||||
let windowOpenOverriddenOptions: BrowserWindowConstructorOptions | null = null;
|
||||
let windowOpenOutlivesOpenerOption: boolean = false;
|
||||
this.on('-will-add-new-contents' as any, (event: ElectronInternal.Event, url: string, frameName: string, rawFeatures: string, disposition: Electron.HandlerDetails['disposition'], referrer: Electron.Referrer, postData: PostData) => {
|
||||
this.on('-will-add-new-contents' as any, (event: Electron.Event, url: string, frameName: string, rawFeatures: string, disposition: Electron.HandlerDetails['disposition'], referrer: Electron.Referrer, postData: PostData) => {
|
||||
const postBody = postData ? {
|
||||
data: postData,
|
||||
...parseContentTypeFormat(postData)
|
||||
@@ -740,7 +763,7 @@ WebContents.prototype._init = function () {
|
||||
} : undefined;
|
||||
const { webPreferences: parsedWebPreferences } = parseFeatures(rawFeatures);
|
||||
const webPreferences = makeWebPreferences({
|
||||
embedder: event.sender,
|
||||
embedder: this,
|
||||
insecureParsedWebPreferences: parsedWebPreferences,
|
||||
secureOverrideWebPreferences
|
||||
});
|
||||
@@ -753,7 +776,7 @@ WebContents.prototype._init = function () {
|
||||
});
|
||||
|
||||
// Create a new browser window for "window.open"
|
||||
this.on('-add-new-contents' as any, (event: ElectronInternal.Event, webContents: Electron.WebContents, disposition: string,
|
||||
this.on('-add-new-contents' as any, (event: Electron.Event, webContents: Electron.WebContents, disposition: string,
|
||||
_userGesture: boolean, _left: number, _top: number, _width: number, _height: number, url: string, frameName: string,
|
||||
referrer: Electron.Referrer, rawFeatures: string, postData: PostData) => {
|
||||
const overriddenOptions = windowOpenOverriddenOptions || undefined;
|
||||
@@ -769,7 +792,7 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
|
||||
openGuestWindow({
|
||||
embedder: event.sender,
|
||||
embedder: this,
|
||||
guest: webContents,
|
||||
overrideBrowserWindowOptions: overriddenOptions,
|
||||
disposition,
|
||||
@@ -806,8 +829,7 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
const event = process._linkedBinding('electron_browser_event').createEmpty();
|
||||
app.emit('web-contents-created', event, this);
|
||||
app.emit('web-contents-created', { sender: this, preventDefault () {}, get defaultPrevented () { return false; } }, this);
|
||||
|
||||
// Properties
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ interface GuestInstance {
|
||||
}
|
||||
|
||||
const webViewManager = process._linkedBinding('electron_browser_web_view_manager');
|
||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
||||
const netBinding = process._linkedBinding('electron_browser_net');
|
||||
|
||||
const supportedWebViewEvents = Object.keys(webViewEvents);
|
||||
@@ -82,7 +81,13 @@ function makeLoadURLOptions (params: Record<string, any>) {
|
||||
// Create a new guest instance.
|
||||
const createGuest = function (embedder: Electron.WebContents, embedderFrameId: number, elementInstanceId: number, params: Record<string, any>) {
|
||||
const webPreferences = makeWebPreferences(embedder, params);
|
||||
const event = eventBinding.createWithSender(embedder);
|
||||
const event = {
|
||||
sender: embedder,
|
||||
preventDefault () {
|
||||
this.defaultPrevented = true;
|
||||
},
|
||||
defaultPrevented: false
|
||||
};
|
||||
|
||||
const { instanceId } = params;
|
||||
|
||||
@@ -158,6 +163,16 @@ const createGuest = function (embedder: Electron.WebContents, embedderFrameId: n
|
||||
});
|
||||
});
|
||||
|
||||
// Dispatch guest's frame navigation event to embedder.
|
||||
guest.on('will-frame-navigate', function (event: Electron.WebContentsWillFrameNavigateEventParams) {
|
||||
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'will-frame-navigate', {
|
||||
url: event.url,
|
||||
isMainFrame: event.isMainFrame,
|
||||
frameProcessId: event.frame.processId,
|
||||
frameRoutingId: event.frame.routingId
|
||||
});
|
||||
});
|
||||
|
||||
// Notify guest of embedder window visibility when it is ready
|
||||
// FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed
|
||||
guest.on('dom-ready', function () {
|
||||
|
||||
@@ -84,11 +84,20 @@ const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
let packagePath = null;
|
||||
let packageJson = null;
|
||||
const searchPaths: string[] = v8Util.getHiddenValue(global, 'appSearchPaths');
|
||||
const searchPathsOnlyLoadASAR: boolean = v8Util.getHiddenValue(global, 'appSearchPathsOnlyLoadASAR');
|
||||
// Borrow the _getOrCreateArchive asar helper
|
||||
const getOrCreateArchive = process._getOrCreateArchive;
|
||||
delete process._getOrCreateArchive;
|
||||
|
||||
if (process.resourcesPath) {
|
||||
for (packagePath of searchPaths) {
|
||||
try {
|
||||
packagePath = path.join(process.resourcesPath, packagePath);
|
||||
if (searchPathsOnlyLoadASAR) {
|
||||
if (!getOrCreateArchive?.(packagePath)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
packageJson = Module._load(path.join(packagePath, 'package.json'));
|
||||
break;
|
||||
} catch {
|
||||
|
||||
@@ -18,13 +18,7 @@ export class IpcMainImpl extends EventEmitter {
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`);
|
||||
}
|
||||
this._invokeHandlers.set(method, async (e, ...args) => {
|
||||
try {
|
||||
e._reply(await Promise.resolve(fn(e, ...args)));
|
||||
} catch (err) {
|
||||
e._throw(err as Error);
|
||||
}
|
||||
});
|
||||
this._invokeHandlers.set(method, fn);
|
||||
}
|
||||
|
||||
handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => {
|
||||
|
||||
@@ -66,14 +66,17 @@ export function renameFunction<T extends Function> (fn: T, newName: string): T {
|
||||
}
|
||||
|
||||
// change the name of an event
|
||||
export function event (emitter: NodeJS.EventEmitter, oldName: string, newName: string) {
|
||||
export function event (emitter: NodeJS.EventEmitter, oldName: string, newName: string, transformer: (...args: any[]) => any[] | undefined = (...args) => args) {
|
||||
const warn = newName.startsWith('-') /* internal event */
|
||||
? warnOnce(`${oldName} event`)
|
||||
: warnOnce(`${oldName} event`, `${newName} event`);
|
||||
return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) {
|
||||
if (this.listenerCount(oldName) !== 0) {
|
||||
warn();
|
||||
this.emit(oldName, ...args);
|
||||
const transformedArgs = transformer(...args);
|
||||
if (transformedArgs) {
|
||||
this.emit(oldName, ...transformedArgs);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,11 +31,15 @@ export const syncMethods = new Set([
|
||||
'redo',
|
||||
'cut',
|
||||
'copy',
|
||||
'centerSelection',
|
||||
'paste',
|
||||
'pasteAndMatchStyle',
|
||||
'delete',
|
||||
'selectAll',
|
||||
'unselect',
|
||||
'scrollToTop',
|
||||
'scrollToBottom',
|
||||
'adjustSelection',
|
||||
'replace',
|
||||
'replaceMisspelling',
|
||||
'findInPage',
|
||||
|
||||
@@ -17,13 +17,13 @@ const isWebView = mainFrame.getWebPreference('isWebView');
|
||||
// ElectronApiServiceImpl will look for the "ipcNative" hidden object when
|
||||
// invoking the 'onMessage' callback.
|
||||
v8Util.setHiddenValue(global, 'ipcNative', {
|
||||
onMessage (internal: boolean, channel: string, ports: MessagePort[], args: any[], senderId: number) {
|
||||
onMessage (internal: boolean, channel: string, ports: MessagePort[], args: any[], senderId: number, senderIsMainFrame: boolean) {
|
||||
if (internal && senderId !== 0) {
|
||||
console.error(`Message ${channel} sent by unexpected WebContents (${senderId})`);
|
||||
return;
|
||||
}
|
||||
const sender = internal ? ipcRendererInternal : ipcRenderer;
|
||||
sender.emit(channel, { sender, senderId, ports }, ...args);
|
||||
sender.emit(channel, { sender, senderId, ...(senderId ? { senderIsMainFrame } : {}), ports }, ...args);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"devDependencies": {
|
||||
"@azure/storage-blob": "^12.9.0",
|
||||
"@electron/asar": "^3.2.1",
|
||||
"@electron/docs-parser": "^1.0.0",
|
||||
"@electron/docs-parser": "^1.1.0",
|
||||
"@electron/fiddle-core": "^1.0.4",
|
||||
"@electron/github-app-auth": "^2.0.0",
|
||||
"@electron/lint-roller": "^1.5.0",
|
||||
"@electron/typescript-definitions": "^8.10.0",
|
||||
"@electron/typescript-definitions": "^8.14.0",
|
||||
"@octokit/rest": "^19.0.7",
|
||||
"@primer/octicons": "^10.0.0",
|
||||
"@types/basic-auth": "^1.1.3",
|
||||
@@ -73,7 +73,7 @@
|
||||
"ts-loader": "^8.0.2",
|
||||
"ts-node": "6.2.0",
|
||||
"typescript": "^4.5.5",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"wrapper-webpack-plugin": "^2.2.0"
|
||||
},
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
fix_rename_webswapcgllayer_to_webswapcgllayerchromium.patch
|
||||
cherry-pick-d0ee0197ddff.patch
|
||||
cherry-pick-285c7712c506.patch
|
||||
cherry-pick-2bf945775fe6.patch
|
||||
cherry-pick-cafe56b591ed.patch
|
||||
cherry-pick-5486190be556.patch
|
||||
cherry-pick-e4669a74888d.patch
|
||||
|
||||
153
patches/angle/cherry-pick-285c7712c506.patch
Normal file
153
patches/angle/cherry-pick-285c7712c506.patch
Normal file
@@ -0,0 +1,153 @@
|
||||
From 285c7712c50654e3d7238b059c4631bc91285514 Mon Sep 17 00:00:00 2001
|
||||
From: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Date: Thu, 13 Jul 2023 15:23:49 -0400
|
||||
Subject: [PATCH] M116: Translator: Unconditionally limit variable sizes
|
||||
|
||||
... instead of just for WebGL. This is to avoid hitting driver bugs
|
||||
that were prevented with this check for WebGL on a compromised renderer
|
||||
that can create non-WebGL contexts.
|
||||
|
||||
Bug: chromium:1464682
|
||||
Change-Id: I2b1c5a8c51f06225f5f850109d30778d97e574c7
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4717371
|
||||
Reviewed-by: Roman Lavrov <romanl@google.com>
|
||||
---
|
||||
|
||||
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
|
||||
index 7b1ac4e..383feeb 100644
|
||||
--- a/src/compiler/translator/Compiler.cpp
|
||||
+++ b/src/compiler/translator/Compiler.cpp
|
||||
@@ -397,9 +397,10 @@
|
||||
|
||||
bool TCompiler::shouldLimitTypeSizes() const
|
||||
{
|
||||
- // WebGL shaders limit the size of variables' types in shaders,
|
||||
- // including arrays, structs and interface blocks.
|
||||
- return IsWebGLBasedSpec(mShaderSpec);
|
||||
+ // Prevent unrealistically large variable sizes in shaders. This works around driver bugs
|
||||
+ // around int-size limits (such as 2GB). The limits are generously large enough that no real
|
||||
+ // shader should ever hit it.
|
||||
+ return true;
|
||||
}
|
||||
|
||||
bool TCompiler::Init(const ShBuiltInResources &resources)
|
||||
diff --git a/src/compiler/translator/ValidateTypeSizeLimitations.cpp b/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
index 2a033ad..19a4821 100644
|
||||
--- a/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
+++ b/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
@@ -23,10 +23,10 @@
|
||||
// Arbitrarily enforce that all types declared with a size in bytes of over 2 GB will cause
|
||||
// compilation failure.
|
||||
//
|
||||
-// For local and global variables, the limit is much lower (1MB) as that much memory won't fit in
|
||||
+// For local and global variables, the limit is much lower (16MB) as that much memory won't fit in
|
||||
// the GPU registers anyway.
|
||||
constexpr size_t kMaxVariableSizeInBytes = static_cast<size_t>(2) * 1024 * 1024 * 1024;
|
||||
-constexpr size_t kMaxPrivateVariableSizeInBytes = static_cast<size_t>(1) * 1024 * 1024;
|
||||
+constexpr size_t kMaxPrivateVariableSizeInBytes = static_cast<size_t>(16) * 1024 * 1024;
|
||||
|
||||
// Traverses intermediate tree to ensure that the shader does not
|
||||
// exceed certain implementation-defined limits on the sizes of types.
|
||||
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
|
||||
index a91f8b0..a866b25 100644
|
||||
--- a/src/compiler/translator/util.cpp
|
||||
+++ b/src/compiler/translator/util.cpp
|
||||
@@ -282,6 +282,9 @@
|
||||
|
||||
return kBoolGLType[type.getNominalSize() - 1];
|
||||
|
||||
+ case EbtYuvCscStandardEXT:
|
||||
+ return GL_UNSIGNED_INT;
|
||||
+
|
||||
case EbtSampler2D:
|
||||
return GL_SAMPLER_2D;
|
||||
case EbtSampler3D:
|
||||
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
index 9ae56f5..a8d2ce4 100644
|
||||
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
@@ -5284,8 +5284,8 @@
|
||||
|
||||
constexpr char kVSArrayTooLarge[] =
|
||||
R"(varying vec4 color;
|
||||
-// 1 MB / 32 aligned bytes per mat2 = 32768
|
||||
-const int array_size = 32769;
|
||||
+// 16 MB / 32 aligned bytes per mat2 = 524288
|
||||
+const int array_size = 524289;
|
||||
void main()
|
||||
{
|
||||
mat2 array[array_size];
|
||||
@@ -5297,7 +5297,7 @@
|
||||
|
||||
constexpr char kVSArrayMuchTooLarge[] =
|
||||
R"(varying vec4 color;
|
||||
-const int array_size = 55600;
|
||||
+const int array_size = 757000;
|
||||
void main()
|
||||
{
|
||||
mat2 array[array_size];
|
||||
@@ -5361,9 +5361,9 @@
|
||||
constexpr char kTooLargeGlobalMemory1[] =
|
||||
R"(precision mediump float;
|
||||
|
||||
-// 1 MB / 16 bytes per vec4 = 65536
|
||||
-vec4 array[32768];
|
||||
-vec4 array2[32769];
|
||||
+// 16 MB / 16 bytes per vec4 = 1048576
|
||||
+vec4 array[524288];
|
||||
+vec4 array2[524289];
|
||||
|
||||
void main()
|
||||
{
|
||||
@@ -5376,9 +5376,9 @@
|
||||
constexpr char kTooLargeGlobalMemory2[] =
|
||||
R"(precision mediump float;
|
||||
|
||||
-// 1 MB / 16 bytes per vec4 = 65536
|
||||
-vec4 array[32767];
|
||||
-vec4 array2[32767];
|
||||
+// 16 MB / 16 bytes per vec4 = 1048576
|
||||
+vec4 array[524287];
|
||||
+vec4 array2[524287];
|
||||
vec4 x, y, z;
|
||||
|
||||
void main()
|
||||
@@ -5392,12 +5392,12 @@
|
||||
constexpr char kTooLargeGlobalAndLocalMemory1[] =
|
||||
R"(precision mediump float;
|
||||
|
||||
-// 1 MB / 16 bytes per vec4 = 65536
|
||||
-vec4 array[32768];
|
||||
+// 16 MB / 16 bytes per vec4 = 1048576
|
||||
+vec4 array[524288];
|
||||
|
||||
void main()
|
||||
{
|
||||
- vec4 array2[32769];
|
||||
+ vec4 array2[524289];
|
||||
if (array[0].x + array[1].x == 2.0)
|
||||
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
else
|
||||
@@ -5408,18 +5408,18 @@
|
||||
constexpr char kTooLargeGlobalAndLocalMemory2[] =
|
||||
R"(precision mediump float;
|
||||
|
||||
-// 1 MB / 16 bytes per vec4 = 65536
|
||||
-vec4 array[32768];
|
||||
+// 16 MB / 16 bytes per vec4 = 1048576
|
||||
+vec4 array[524288];
|
||||
|
||||
float f()
|
||||
{
|
||||
- vec4 array2[16384];
|
||||
+ vec4 array2[524288];
|
||||
return array2[0].x;
|
||||
}
|
||||
|
||||
float g()
|
||||
{
|
||||
- vec4 array3[16383];
|
||||
+ vec4 array3[524287];
|
||||
return array3[0].x;
|
||||
}
|
||||
|
||||
197
patches/angle/cherry-pick-2bf945775fe6.patch
Normal file
197
patches/angle/cherry-pick-2bf945775fe6.patch
Normal file
@@ -0,0 +1,197 @@
|
||||
From 2bf945775fe634eb9e420c2263dae6043bbb5ece Mon Sep 17 00:00:00 2001
|
||||
From: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Date: Fri, 14 Jul 2023 12:30:15 -0400
|
||||
Subject: [PATCH] M116: Translator: Limit variable sizes vs uint overflow
|
||||
|
||||
Bug: chromium:1464680
|
||||
Change-Id: Iee41a2da7a7a330e6cc4d6da59a6e9836ee9dd36
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4717372
|
||||
Reviewed-by: Roman Lavrov <romanl@google.com>
|
||||
---
|
||||
|
||||
diff --git a/src/compiler/translator/ValidateTypeSizeLimitations.cpp b/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
index 19a4821..f0ff9cb 100644
|
||||
--- a/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
+++ b/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "compiler/translator/ValidateTypeSizeLimitations.h"
|
||||
|
||||
#include "angle_gl.h"
|
||||
+#include "common/mathutil.h"
|
||||
#include "compiler/translator/Diagnostics.h"
|
||||
#include "compiler/translator/Symbol.h"
|
||||
#include "compiler/translator/SymbolTable.h"
|
||||
@@ -113,7 +114,8 @@
|
||||
|
||||
void validateTotalPrivateVariableSize()
|
||||
{
|
||||
- if (mTotalPrivateVariablesSize > kMaxPrivateVariableSizeInBytes)
|
||||
+ if (mTotalPrivateVariablesSize.ValueOrDefault(std::numeric_limits<size_t>::max()) >
|
||||
+ kMaxPrivateVariableSizeInBytes)
|
||||
{
|
||||
mDiagnostics->error(
|
||||
TSourceLoc{},
|
||||
@@ -231,7 +233,7 @@
|
||||
TDiagnostics *mDiagnostics;
|
||||
std::vector<int> mLoopSymbolIds;
|
||||
|
||||
- size_t mTotalPrivateVariablesSize;
|
||||
+ angle::base::CheckedNumeric<size_t> mTotalPrivateVariablesSize;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
index a8d2ce4..542d49f 100644
|
||||
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
@@ -5426,7 +5426,7 @@
|
||||
float h()
|
||||
{
|
||||
vec4 value;
|
||||
- float value2
|
||||
+ float value2;
|
||||
return value.x + value2;
|
||||
}
|
||||
|
||||
@@ -5438,6 +5438,131 @@
|
||||
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
})";
|
||||
|
||||
+ constexpr char kTooLargeGlobalMemoryOverflow[] =
|
||||
+ R"(precision mediump float;
|
||||
+
|
||||
+// 16 MB / 16 bytes per vec4 = 1048576
|
||||
+// Create 256 arrays so each is small, but the total overflows a 32-bit number
|
||||
+vec4 array[1048576], array2[1048576], array3[1048576], array4[1048576], array5[1048576];
|
||||
+vec4 array6[1048576], array7[1048576], array8[1048576], array9[1048576], array10[1048576];
|
||||
+vec4 array11[1048576], array12[1048576], array13[1048576], array14[1048576], array15[1048576];
|
||||
+vec4 array16[1048576], array17[1048576], array18[1048576], array19[1048576], array20[1048576];
|
||||
+vec4 array21[1048576], array22[1048576], array23[1048576], array24[1048576], array25[1048576];
|
||||
+vec4 array26[1048576], array27[1048576], array28[1048576], array29[1048576], array30[1048576];
|
||||
+vec4 array31[1048576], array32[1048576], array33[1048576], array34[1048576], array35[1048576];
|
||||
+vec4 array36[1048576], array37[1048576], array38[1048576], array39[1048576], array40[1048576];
|
||||
+vec4 array41[1048576], array42[1048576], array43[1048576], array44[1048576], array45[1048576];
|
||||
+vec4 array46[1048576], array47[1048576], array48[1048576], array49[1048576], array50[1048576];
|
||||
+vec4 array51[1048576], array52[1048576], array53[1048576], array54[1048576], array55[1048576];
|
||||
+vec4 array56[1048576], array57[1048576], array58[1048576], array59[1048576], array60[1048576];
|
||||
+vec4 array61[1048576], array62[1048576], array63[1048576], array64[1048576], array65[1048576];
|
||||
+vec4 array66[1048576], array67[1048576], array68[1048576], array69[1048576], array70[1048576];
|
||||
+vec4 array71[1048576], array72[1048576], array73[1048576], array74[1048576], array75[1048576];
|
||||
+vec4 array76[1048576], array77[1048576], array78[1048576], array79[1048576], array80[1048576];
|
||||
+vec4 array81[1048576], array82[1048576], array83[1048576], array84[1048576], array85[1048576];
|
||||
+vec4 array86[1048576], array87[1048576], array88[1048576], array89[1048576], array90[1048576];
|
||||
+vec4 array91[1048576], array92[1048576], array93[1048576], array94[1048576], array95[1048576];
|
||||
+vec4 array96[1048576], array97[1048576], array98[1048576], array99[1048576], array100[1048576];
|
||||
+vec4 array101[1048576], array102[1048576], array103[1048576], array104[1048576], array105[1048576];
|
||||
+vec4 array106[1048576], array107[1048576], array108[1048576], array109[1048576], array110[1048576];
|
||||
+vec4 array111[1048576], array112[1048576], array113[1048576], array114[1048576], array115[1048576];
|
||||
+vec4 array116[1048576], array117[1048576], array118[1048576], array119[1048576], array120[1048576];
|
||||
+vec4 array121[1048576], array122[1048576], array123[1048576], array124[1048576], array125[1048576];
|
||||
+vec4 array126[1048576], array127[1048576], array128[1048576], array129[1048576], array130[1048576];
|
||||
+vec4 array131[1048576], array132[1048576], array133[1048576], array134[1048576], array135[1048576];
|
||||
+vec4 array136[1048576], array137[1048576], array138[1048576], array139[1048576], array140[1048576];
|
||||
+vec4 array141[1048576], array142[1048576], array143[1048576], array144[1048576], array145[1048576];
|
||||
+vec4 array146[1048576], array147[1048576], array148[1048576], array149[1048576], array150[1048576];
|
||||
+vec4 array151[1048576], array152[1048576], array153[1048576], array154[1048576], array155[1048576];
|
||||
+vec4 array156[1048576], array157[1048576], array158[1048576], array159[1048576], array160[1048576];
|
||||
+vec4 array161[1048576], array162[1048576], array163[1048576], array164[1048576], array165[1048576];
|
||||
+vec4 array166[1048576], array167[1048576], array168[1048576], array169[1048576], array170[1048576];
|
||||
+vec4 array171[1048576], array172[1048576], array173[1048576], array174[1048576], array175[1048576];
|
||||
+vec4 array176[1048576], array177[1048576], array178[1048576], array179[1048576], array180[1048576];
|
||||
+vec4 array181[1048576], array182[1048576], array183[1048576], array184[1048576], array185[1048576];
|
||||
+vec4 array186[1048576], array187[1048576], array188[1048576], array189[1048576], array190[1048576];
|
||||
+vec4 array191[1048576], array192[1048576], array193[1048576], array194[1048576], array195[1048576];
|
||||
+vec4 array196[1048576], array197[1048576], array198[1048576], array199[1048576], array200[1048576];
|
||||
+vec4 array201[1048576], array202[1048576], array203[1048576], array204[1048576], array205[1048576];
|
||||
+vec4 array206[1048576], array207[1048576], array208[1048576], array209[1048576], array210[1048576];
|
||||
+vec4 array211[1048576], array212[1048576], array213[1048576], array214[1048576], array215[1048576];
|
||||
+vec4 array216[1048576], array217[1048576], array218[1048576], array219[1048576], array220[1048576];
|
||||
+vec4 array221[1048576], array222[1048576], array223[1048576], array224[1048576], array225[1048576];
|
||||
+vec4 array226[1048576], array227[1048576], array228[1048576], array229[1048576], array230[1048576];
|
||||
+vec4 array231[1048576], array232[1048576], array233[1048576], array234[1048576], array235[1048576];
|
||||
+vec4 array236[1048576], array237[1048576], array238[1048576], array239[1048576], array240[1048576];
|
||||
+vec4 array241[1048576], array242[1048576], array243[1048576], array244[1048576], array245[1048576];
|
||||
+vec4 array246[1048576], array247[1048576], array248[1048576], array249[1048576], array250[1048576];
|
||||
+vec4 array251[1048576], array252[1048576], array253[1048576], array254[1048576], array255[1048576];
|
||||
+vec4 array256[1048576];
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ float f = array[0].x; f += array2[0].x; f += array3[0].x; f += array4[0].x; f += array5[0].x;
|
||||
+ f += array6[0].x; f += array7[0].x; f += array8[0].x; f += array9[0].x; f += array10[0].x;
|
||||
+ f += array11[0].x; f += array12[0].x; f += array13[0].x; f += array14[0].x; f += array15[0].x;
|
||||
+ f += array16[0].x; f += array17[0].x; f += array18[0].x; f += array19[0].x; f += array20[0].x;
|
||||
+ f += array21[0].x; f += array22[0].x; f += array23[0].x; f += array24[0].x; f += array25[0].x;
|
||||
+ f += array26[0].x; f += array27[0].x; f += array28[0].x; f += array29[0].x; f += array30[0].x;
|
||||
+ f += array31[0].x; f += array32[0].x; f += array33[0].x; f += array34[0].x; f += array35[0].x;
|
||||
+ f += array36[0].x; f += array37[0].x; f += array38[0].x; f += array39[0].x; f += array40[0].x;
|
||||
+ f += array41[0].x; f += array42[0].x; f += array43[0].x; f += array44[0].x; f += array45[0].x;
|
||||
+ f += array46[0].x; f += array47[0].x; f += array48[0].x; f += array49[0].x; f += array50[0].x;
|
||||
+ f += array51[0].x; f += array52[0].x; f += array53[0].x; f += array54[0].x; f += array55[0].x;
|
||||
+ f += array56[0].x; f += array57[0].x; f += array58[0].x; f += array59[0].x; f += array60[0].x;
|
||||
+ f += array61[0].x; f += array62[0].x; f += array63[0].x; f += array64[0].x; f += array65[0].x;
|
||||
+ f += array66[0].x; f += array67[0].x; f += array68[0].x; f += array69[0].x; f += array70[0].x;
|
||||
+ f += array71[0].x; f += array72[0].x; f += array73[0].x; f += array74[0].x; f += array75[0].x;
|
||||
+ f += array76[0].x; f += array77[0].x; f += array78[0].x; f += array79[0].x; f += array80[0].x;
|
||||
+ f += array81[0].x; f += array82[0].x; f += array83[0].x; f += array84[0].x; f += array85[0].x;
|
||||
+ f += array86[0].x; f += array87[0].x; f += array88[0].x; f += array89[0].x; f += array90[0].x;
|
||||
+ f += array91[0].x; f += array92[0].x; f += array93[0].x; f += array94[0].x; f += array95[0].x;
|
||||
+ f += array96[0].x; f += array97[0].x; f += array98[0].x; f += array99[0].x; f += array100[0].x;
|
||||
+ f += array101[0].x; f += array102[0].x; f += array103[0].x; f += array104[0].x;
|
||||
+ f += array105[0].x; f += array106[0].x; f += array107[0].x; f += array108[0].x;
|
||||
+ f += array109[0].x; f += array110[0].x; f += array111[0].x; f += array112[0].x;
|
||||
+ f += array113[0].x; f += array114[0].x; f += array115[0].x; f += array116[0].x;
|
||||
+ f += array117[0].x; f += array118[0].x; f += array119[0].x; f += array120[0].x;
|
||||
+ f += array121[0].x; f += array122[0].x; f += array123[0].x; f += array124[0].x;
|
||||
+ f += array125[0].x; f += array126[0].x; f += array127[0].x; f += array128[0].x;
|
||||
+ f += array129[0].x; f += array130[0].x; f += array131[0].x; f += array132[0].x;
|
||||
+ f += array133[0].x; f += array134[0].x; f += array135[0].x; f += array136[0].x;
|
||||
+ f += array137[0].x; f += array138[0].x; f += array139[0].x; f += array140[0].x;
|
||||
+ f += array141[0].x; f += array142[0].x; f += array143[0].x; f += array144[0].x;
|
||||
+ f += array145[0].x; f += array146[0].x; f += array147[0].x; f += array148[0].x;
|
||||
+ f += array149[0].x; f += array150[0].x; f += array151[0].x; f += array152[0].x;
|
||||
+ f += array153[0].x; f += array154[0].x; f += array155[0].x; f += array156[0].x;
|
||||
+ f += array157[0].x; f += array158[0].x; f += array159[0].x; f += array160[0].x;
|
||||
+ f += array161[0].x; f += array162[0].x; f += array163[0].x; f += array164[0].x;
|
||||
+ f += array165[0].x; f += array166[0].x; f += array167[0].x; f += array168[0].x;
|
||||
+ f += array169[0].x; f += array170[0].x; f += array171[0].x; f += array172[0].x;
|
||||
+ f += array173[0].x; f += array174[0].x; f += array175[0].x; f += array176[0].x;
|
||||
+ f += array177[0].x; f += array178[0].x; f += array179[0].x; f += array180[0].x;
|
||||
+ f += array181[0].x; f += array182[0].x; f += array183[0].x; f += array184[0].x;
|
||||
+ f += array185[0].x; f += array186[0].x; f += array187[0].x; f += array188[0].x;
|
||||
+ f += array189[0].x; f += array190[0].x; f += array191[0].x; f += array192[0].x;
|
||||
+ f += array193[0].x; f += array194[0].x; f += array195[0].x; f += array196[0].x;
|
||||
+ f += array197[0].x; f += array198[0].x; f += array199[0].x; f += array200[0].x;
|
||||
+ f += array201[0].x; f += array202[0].x; f += array203[0].x; f += array204[0].x;
|
||||
+ f += array205[0].x; f += array206[0].x; f += array207[0].x; f += array208[0].x;
|
||||
+ f += array209[0].x; f += array210[0].x; f += array211[0].x; f += array212[0].x;
|
||||
+ f += array213[0].x; f += array214[0].x; f += array215[0].x; f += array216[0].x;
|
||||
+ f += array217[0].x; f += array218[0].x; f += array219[0].x; f += array220[0].x;
|
||||
+ f += array221[0].x; f += array222[0].x; f += array223[0].x; f += array224[0].x;
|
||||
+ f += array225[0].x; f += array226[0].x; f += array227[0].x; f += array228[0].x;
|
||||
+ f += array229[0].x; f += array230[0].x; f += array231[0].x; f += array232[0].x;
|
||||
+ f += array233[0].x; f += array234[0].x; f += array235[0].x; f += array236[0].x;
|
||||
+ f += array237[0].x; f += array238[0].x; f += array239[0].x; f += array240[0].x;
|
||||
+ f += array241[0].x; f += array242[0].x; f += array243[0].x; f += array244[0].x;
|
||||
+ f += array245[0].x; f += array246[0].x; f += array247[0].x; f += array248[0].x;
|
||||
+ f += array249[0].x; f += array250[0].x; f += array251[0].x; f += array252[0].x;
|
||||
+ f += array253[0].x; f += array254[0].x; f += array255[0].x; f += array256[0].x;
|
||||
+ if (f == 2.0)
|
||||
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
+ else
|
||||
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
+})";
|
||||
+
|
||||
GLuint program = CompileProgram(essl1_shaders::vs::Simple(), kTooLargeGlobalMemory1);
|
||||
EXPECT_EQ(0u, program);
|
||||
|
||||
@@ -5449,6 +5574,9 @@
|
||||
|
||||
program = CompileProgram(essl1_shaders::vs::Simple(), kTooLargeGlobalAndLocalMemory2);
|
||||
EXPECT_EQ(0u, program);
|
||||
+
|
||||
+ program = CompileProgram(essl1_shaders::vs::Simple(), kTooLargeGlobalMemoryOverflow);
|
||||
+ EXPECT_EQ(0u, program);
|
||||
}
|
||||
|
||||
// Linking should fail when corresponding vertex/fragment uniform blocks have different precision
|
||||
42
patches/angle/cherry-pick-5486190be556.patch
Normal file
42
patches/angle/cherry-pick-5486190be556.patch
Normal file
@@ -0,0 +1,42 @@
|
||||
From 5486190be5565f9b5567fbd252a10425af3d59ee Mon Sep 17 00:00:00 2001
|
||||
From: Geoff Lang <geofflang@chromium.org>
|
||||
Date: Fri, 21 Jul 2023 13:45:52 -0400
|
||||
Subject: [PATCH] [M114-LTS] Fix read size validation for RGBX formats.
|
||||
|
||||
GL_RGBX8_ANGLE is the only format where the upload format is 3-channel
|
||||
RGB, whilethe download format is 4-channel RGBX. As such, the internal
|
||||
format corresponding to format+type expects 3-byte input/output. The
|
||||
format is fixed here for readPixels to output 4 bytes per pixel.
|
||||
|
||||
Bug: chromium:1458046
|
||||
Change-Id: Iec737ed64bade003cfab50dc5f595eb4875e81e4
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4706957
|
||||
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
(cherry picked from commit 430a4f559cbc2bcd5d026e8b36ee46ddd80e9651)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4765136
|
||||
Commit-Queue: Daniel Yip <danielyip@google.com>
|
||||
Auto-Submit: Daniel Yip <danielyip@google.com>
|
||||
(cherry picked from commit 4a372ad49ceddea6c13f79adb212a777ec770a66)
|
||||
---
|
||||
|
||||
diff --git a/src/libANGLE/formatutils.cpp b/src/libANGLE/formatutils.cpp
|
||||
index f2845dc..bb1389aaa 100644
|
||||
--- a/src/libANGLE/formatutils.cpp
|
||||
+++ b/src/libANGLE/formatutils.cpp
|
||||
@@ -1702,7 +1702,15 @@
|
||||
GLuint InternalFormat::computePixelBytes(GLenum formatType) const
|
||||
{
|
||||
const auto &typeInfo = GetTypeInfo(formatType);
|
||||
- GLuint components = typeInfo.specialInterpretation ? 1u : componentCount;
|
||||
+ GLuint components = componentCount;
|
||||
+ if (sizedInternalFormat == GL_RGBX8_ANGLE)
|
||||
+ {
|
||||
+ components = 4;
|
||||
+ }
|
||||
+ else if (typeInfo.specialInterpretation)
|
||||
+ {
|
||||
+ components = 1;
|
||||
+ }
|
||||
return components * typeInfo.bytes;
|
||||
}
|
||||
|
||||
286
patches/angle/cherry-pick-cafe56b591ed.patch
Normal file
286
patches/angle/cherry-pick-cafe56b591ed.patch
Normal file
@@ -0,0 +1,286 @@
|
||||
From cafe56b591edb77f041be70b58cac3a61565644a Mon Sep 17 00:00:00 2001
|
||||
From: Geoff Lang <geofflang@chromium.org>
|
||||
Date: Fri, 23 Jun 2023 14:46:28 -0400
|
||||
Subject: [PATCH] M116: GL: Ensure all instanced attributes have a buffer with data
|
||||
|
||||
Apple OpenGL drivers sometimes crash when given an instanced draw with
|
||||
a buffer that has never been given data.
|
||||
|
||||
It's not efficient to check if the attribute is both zero-sized and
|
||||
instanced so just ensure that every time a zero-sized buffer is bound
|
||||
to an attribute, it gets initialized with some data.
|
||||
|
||||
Bug: chromium:1456243
|
||||
Change-Id: I66b7c7017843153db2df3bc50010cba765d03c5f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4642048
|
||||
Commit-Queue: Geoff Lang <geofflang@chromium.org>
|
||||
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
(cherry picked from commit 4e6124dae892690204f8e5996aeaad14f45e0a97)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4727452
|
||||
---
|
||||
|
||||
diff --git a/include/platform/FeaturesGL_autogen.h b/include/platform/FeaturesGL_autogen.h
|
||||
index aa0565c..2f6e094 100644
|
||||
--- a/include/platform/FeaturesGL_autogen.h
|
||||
+++ b/include/platform/FeaturesGL_autogen.h
|
||||
@@ -501,6 +501,12 @@
|
||||
"supportsShaderPixelLocalStorageEXT", FeatureCategory::OpenGLFeatures,
|
||||
"Backend GL context supports EXT_shader_pixel_local_storage extension", &members,
|
||||
"http://anglebug.com/7279"};
|
||||
+
|
||||
+ FeatureInfo ensureNonEmptyBufferIsBoundForDraw = {
|
||||
+ "ensureNonEmptyBufferIsBoundForDraw", FeatureCategory::OpenGLFeatures,
|
||||
+ "Apple OpenGL drivers crash when drawing with a zero-sized buffer bound using a non-zero "
|
||||
+ "divisor.",
|
||||
+ &members, "http://crbug.com/1456243"};
|
||||
};
|
||||
|
||||
inline FeaturesGL::FeaturesGL() = default;
|
||||
diff --git a/include/platform/gl_features.json b/include/platform/gl_features.json
|
||||
index 032f29a..b358cea 100644
|
||||
--- a/include/platform/gl_features.json
|
||||
+++ b/include/platform/gl_features.json
|
||||
@@ -699,6 +699,14 @@
|
||||
"Backend GL context supports EXT_shader_pixel_local_storage extension"
|
||||
],
|
||||
"issue": "http://anglebug.com/7279"
|
||||
+ },
|
||||
+ {
|
||||
+ "name": "ensure_non_empty_buffer_is_bound_for_draw",
|
||||
+ "category": "Features",
|
||||
+ "description": [
|
||||
+ "Apple OpenGL drivers crash when drawing with a zero-sized buffer bound using a non-zero divisor."
|
||||
+ ],
|
||||
+ "issue": "http://crbug.com/1456243"
|
||||
}
|
||||
]
|
||||
}
|
||||
diff --git a/scripts/code_generation_hashes/ANGLE_features.json b/scripts/code_generation_hashes/ANGLE_features.json
|
||||
index d4576c2..503001c 100644
|
||||
--- a/scripts/code_generation_hashes/ANGLE_features.json
|
||||
+++ b/scripts/code_generation_hashes/ANGLE_features.json
|
||||
@@ -2,7 +2,7 @@
|
||||
"include/platform/FeaturesD3D_autogen.h":
|
||||
"9923fb44d0a6f31948d0c8f46ee1d9e2",
|
||||
"include/platform/FeaturesGL_autogen.h":
|
||||
- "a795a806d71b0e6d1f9e6d95c6e11971",
|
||||
+ "fef16ab3946346a2a7d5b76bb39471a4",
|
||||
"include/platform/FeaturesMtl_autogen.h":
|
||||
"407426c8874de9295482ace9c94bd812",
|
||||
"include/platform/FeaturesVk_autogen.h":
|
||||
@@ -16,13 +16,13 @@
|
||||
"include/platform/gen_features.py":
|
||||
"062989f7a8f3ff3b383f98fc8908dc33",
|
||||
"include/platform/gl_features.json":
|
||||
- "3335055a70e35ebb7bf74c6d7c58897b",
|
||||
+ "c9aead89696e7fd0c8bfe5c5ca85ca63",
|
||||
"include/platform/mtl_features.json":
|
||||
"c66d170e7a8eb3448030f4c423ed0133",
|
||||
"include/platform/vk_features.json":
|
||||
"416bbb28b9fa1a3c4ef141f243c0a9e6",
|
||||
"util/angle_features_autogen.cpp":
|
||||
- "73169f63c755192c3b4bd27d6f4096ca",
|
||||
+ "288daaec490eb816883d744f108d74c9",
|
||||
"util/angle_features_autogen.h":
|
||||
- "7aa8120eb8f8fd335946b8c27074745d"
|
||||
+ "daf25d3e4ffea143d1c082416513f7e7"
|
||||
}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/libANGLE/renderer/gl/BufferGL.cpp b/src/libANGLE/renderer/gl/BufferGL.cpp
|
||||
index c99fd5d..9651838 100644
|
||||
--- a/src/libANGLE/renderer/gl/BufferGL.cpp
|
||||
+++ b/src/libANGLE/renderer/gl/BufferGL.cpp
|
||||
@@ -296,6 +296,11 @@
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
+size_t BufferGL::getBufferSize() const
|
||||
+{
|
||||
+ return mBufferSize;
|
||||
+}
|
||||
+
|
||||
GLuint BufferGL::getBufferID() const
|
||||
{
|
||||
return mBufferID;
|
||||
diff --git a/src/libANGLE/renderer/gl/BufferGL.h b/src/libANGLE/renderer/gl/BufferGL.h
|
||||
index 7b57594..fe9138e 100644
|
||||
--- a/src/libANGLE/renderer/gl/BufferGL.h
|
||||
+++ b/src/libANGLE/renderer/gl/BufferGL.h
|
||||
@@ -56,6 +56,7 @@
|
||||
bool primitiveRestartEnabled,
|
||||
gl::IndexRange *outRange) override;
|
||||
|
||||
+ size_t getBufferSize() const;
|
||||
GLuint getBufferID() const;
|
||||
|
||||
private:
|
||||
diff --git a/src/libANGLE/renderer/gl/VertexArrayGL.cpp b/src/libANGLE/renderer/gl/VertexArrayGL.cpp
|
||||
index dc981de..fda9099 100644
|
||||
--- a/src/libANGLE/renderer/gl/VertexArrayGL.cpp
|
||||
+++ b/src/libANGLE/renderer/gl/VertexArrayGL.cpp
|
||||
@@ -646,6 +646,7 @@
|
||||
|
||||
angle::Result VertexArrayGL::updateAttribPointer(const gl::Context *context, size_t attribIndex)
|
||||
{
|
||||
+ const angle::FeaturesGL &features = GetFeaturesGL(context);
|
||||
|
||||
const VertexAttribute &attrib = mState.getVertexAttribute(attribIndex);
|
||||
|
||||
@@ -687,8 +688,16 @@
|
||||
// is not NULL.
|
||||
|
||||
StateManagerGL *stateManager = GetStateManagerGL(context);
|
||||
- GLuint bufferId = GetNativeBufferID(arrayBuffer);
|
||||
+ BufferGL *bufferGL = GetImplAs<BufferGL>(arrayBuffer);
|
||||
+ GLuint bufferId = bufferGL->getBufferID();
|
||||
stateManager->bindBuffer(gl::BufferBinding::Array, bufferId);
|
||||
+ if (features.ensureNonEmptyBufferIsBoundForDraw.enabled && bufferGL->getBufferSize() == 0)
|
||||
+ {
|
||||
+ constexpr uint32_t data = 0;
|
||||
+ ANGLE_TRY(bufferGL->setData(context, gl::BufferBinding::Array, &data, sizeof(data),
|
||||
+ gl::BufferUsage::StaticDraw));
|
||||
+ ASSERT(bufferGL->getBufferSize() > 0);
|
||||
+ }
|
||||
ANGLE_TRY(callVertexAttribPointer(context, static_cast<GLuint>(attribIndex), attrib,
|
||||
binding.getStride(), binding.getOffset()));
|
||||
|
||||
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
|
||||
index 6911247..ab2a608 100644
|
||||
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
|
||||
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
|
||||
@@ -2465,6 +2465,9 @@
|
||||
// EXT_shader_pixel_local_storage
|
||||
ANGLE_FEATURE_CONDITION(features, supportsShaderPixelLocalStorageEXT,
|
||||
functions->hasGLESExtension("GL_EXT_shader_pixel_local_storage"));
|
||||
+
|
||||
+ // http://crbug.com/1456243
|
||||
+ ANGLE_FEATURE_CONDITION(features, ensureNonEmptyBufferIsBoundForDraw, IsApple() || IsAndroid());
|
||||
}
|
||||
|
||||
void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features)
|
||||
diff --git a/src/tests/angle_end2end_tests_expectations.txt b/src/tests/angle_end2end_tests_expectations.txt
|
||||
index 59ec7c2..44ff3e4 100644
|
||||
--- a/src/tests/angle_end2end_tests_expectations.txt
|
||||
+++ b/src/tests/angle_end2end_tests_expectations.txt
|
||||
@@ -380,6 +380,7 @@
|
||||
7294 WIN D3D11 : StateChangeTestES3.StencilWriteMask/* = SKIP
|
||||
7316 WIN D3D11 : StateChangeTestES3.StencilTestAndFunc/* = SKIP
|
||||
7329 WIN D3D11 : StateChangeTestES3.PrimitiveRestart/* = SKIP
|
||||
+1456243 WIN D3D11 : WebGL2CompatibilityTest.DrawWithZeroSizedBuffer/* = SKIP
|
||||
|
||||
// Android
|
||||
6095 ANDROID GLES : GLSLTest_ES3.InitGlobalComplexConstant/* = SKIP
|
||||
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
index 7dc56cd..bd7ecd1 100644
|
||||
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
@@ -1632,10 +1632,10 @@
|
||||
|
||||
constexpr GLuint kMaxIntAsGLuint = static_cast<GLuint>(std::numeric_limits<GLint>::max());
|
||||
constexpr GLuint kIndexData[] = {
|
||||
- kMaxIntAsGLuint,
|
||||
- kMaxIntAsGLuint + 1,
|
||||
- kMaxIntAsGLuint + 2,
|
||||
- kMaxIntAsGLuint + 3,
|
||||
+ kMaxIntAsGLuint,
|
||||
+ kMaxIntAsGLuint + 1,
|
||||
+ kMaxIntAsGLuint + 2,
|
||||
+ kMaxIntAsGLuint + 3,
|
||||
};
|
||||
|
||||
GLBuffer indexBuffer;
|
||||
@@ -3687,8 +3687,8 @@
|
||||
|
||||
constexpr float readPixelsData[] = {-5000.0f, 0.0f, 0.0f, 1.0f};
|
||||
const GLushort textureData[] = {
|
||||
- gl::float32ToFloat16(readPixelsData[0]), gl::float32ToFloat16(readPixelsData[1]),
|
||||
- gl::float32ToFloat16(readPixelsData[2]), gl::float32ToFloat16(readPixelsData[3])};
|
||||
+ gl::float32ToFloat16(readPixelsData[0]), gl::float32ToFloat16(readPixelsData[1]),
|
||||
+ gl::float32ToFloat16(readPixelsData[2]), gl::float32ToFloat16(readPixelsData[3])};
|
||||
|
||||
for (auto extension : FloatingPointTextureExtensions)
|
||||
{
|
||||
@@ -3748,8 +3748,8 @@
|
||||
|
||||
constexpr float readPixelsData[] = {7108.0f, -10.0f, 0.0f, 1.0f};
|
||||
const GLushort textureData[] = {
|
||||
- gl::float32ToFloat16(readPixelsData[0]), gl::float32ToFloat16(readPixelsData[1]),
|
||||
- gl::float32ToFloat16(readPixelsData[2]), gl::float32ToFloat16(readPixelsData[3])};
|
||||
+ gl::float32ToFloat16(readPixelsData[0]), gl::float32ToFloat16(readPixelsData[1]),
|
||||
+ gl::float32ToFloat16(readPixelsData[2]), gl::float32ToFloat16(readPixelsData[3])};
|
||||
|
||||
for (auto extension : FloatingPointTextureExtensions)
|
||||
{
|
||||
@@ -3811,8 +3811,8 @@
|
||||
|
||||
constexpr float readPixelsData[] = {7000.0f, 100.0f, 33.0f, 1.0f};
|
||||
const GLushort textureData[] = {
|
||||
- gl::float32ToFloat16(readPixelsData[0]), gl::float32ToFloat16(readPixelsData[1]),
|
||||
- gl::float32ToFloat16(readPixelsData[2]), gl::float32ToFloat16(readPixelsData[3])};
|
||||
+ gl::float32ToFloat16(readPixelsData[0]), gl::float32ToFloat16(readPixelsData[1]),
|
||||
+ gl::float32ToFloat16(readPixelsData[2]), gl::float32ToFloat16(readPixelsData[3])};
|
||||
|
||||
for (auto extension : FloatingPointTextureExtensions)
|
||||
{
|
||||
@@ -3874,8 +3874,8 @@
|
||||
|
||||
constexpr float readPixelsData[] = {7000.0f, 100.0f, 33.0f, -1.0f};
|
||||
const GLushort textureData[] = {
|
||||
- gl::float32ToFloat16(readPixelsData[0]), gl::float32ToFloat16(readPixelsData[1]),
|
||||
- gl::float32ToFloat16(readPixelsData[2]), gl::float32ToFloat16(readPixelsData[3])};
|
||||
+ gl::float32ToFloat16(readPixelsData[0]), gl::float32ToFloat16(readPixelsData[1]),
|
||||
+ gl::float32ToFloat16(readPixelsData[2]), gl::float32ToFloat16(readPixelsData[3])};
|
||||
|
||||
for (auto extension : FloatingPointTextureExtensions)
|
||||
{
|
||||
@@ -5803,6 +5803,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
+// Test for a mishandling of instanced vertex attributes with zero-sized buffers bound on Apple
|
||||
+// OpenGL drivers.
|
||||
+TEST_P(WebGL2CompatibilityTest, DrawWithZeroSizedBuffer)
|
||||
+{
|
||||
+ ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
|
||||
+ glUseProgram(program);
|
||||
+
|
||||
+ GLBuffer buffer;
|
||||
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
+
|
||||
+ GLint posLocation = glGetAttribLocation(program, essl3_shaders::PositionAttrib());
|
||||
+ glEnableVertexAttribArray(posLocation);
|
||||
+
|
||||
+ glVertexAttribDivisor(posLocation, 1);
|
||||
+ glVertexAttribPointer(posLocation, 1, GL_UNSIGNED_BYTE, GL_FALSE, 9,
|
||||
+ reinterpret_cast<void *>(0x41424344));
|
||||
+
|
||||
+ glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
+}
|
||||
+
|
||||
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(WebGLCompatibilityTest);
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2CompatibilityTest);
|
||||
diff --git a/util/angle_features_autogen.cpp b/util/angle_features_autogen.cpp
|
||||
index adb9610..a060dd2 100644
|
||||
--- a/util/angle_features_autogen.cpp
|
||||
+++ b/util/angle_features_autogen.cpp
|
||||
@@ -118,6 +118,7 @@
|
||||
{Feature::EnablePrecisionQualifiers, "enablePrecisionQualifiers"},
|
||||
{Feature::EnablePreRotateSurfaces, "enablePreRotateSurfaces"},
|
||||
{Feature::EnableProgramBinaryForCapture, "enableProgramBinaryForCapture"},
|
||||
+ {Feature::EnsureNonEmptyBufferIsBoundForDraw, "ensureNonEmptyBufferIsBoundForDraw"},
|
||||
{Feature::ExpandIntegerPowExpressions, "expandIntegerPowExpressions"},
|
||||
{Feature::ExplicitlyEnablePerSampleShading, "explicitlyEnablePerSampleShading"},
|
||||
{Feature::ExposeNonConformantExtensionsAndVersions, "exposeNonConformantExtensionsAndVersions"},
|
||||
diff --git a/util/angle_features_autogen.h b/util/angle_features_autogen.h
|
||||
index 3d8c47f..4064425 100644
|
||||
--- a/util/angle_features_autogen.h
|
||||
+++ b/util/angle_features_autogen.h
|
||||
@@ -112,6 +112,7 @@
|
||||
EnablePrecisionQualifiers,
|
||||
EnablePreRotateSurfaces,
|
||||
EnableProgramBinaryForCapture,
|
||||
+ EnsureNonEmptyBufferIsBoundForDraw,
|
||||
ExpandIntegerPowExpressions,
|
||||
ExplicitlyEnablePerSampleShading,
|
||||
ExposeNonConformantExtensionsAndVersions,
|
||||
208
patches/angle/cherry-pick-d0ee0197ddff.patch
Normal file
208
patches/angle/cherry-pick-d0ee0197ddff.patch
Normal file
@@ -0,0 +1,208 @@
|
||||
From d0ee0197ddff25fe1a9876511c07542ac483702d Mon Sep 17 00:00:00 2001
|
||||
From: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Date: Wed, 03 May 2023 13:41:36 -0400
|
||||
Subject: [PATCH] WebGL: Limit total size of private data
|
||||
|
||||
... not just individual arrays.
|
||||
|
||||
Bug: chromium:1431761
|
||||
Change-Id: I721e29aeceeaf12c3f6a67b668abffb8dfbc89b0
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4503753
|
||||
Reviewed-by: Kenneth Russell <kbr@chromium.org>
|
||||
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
---
|
||||
|
||||
diff --git a/src/compiler/translator/ValidateTypeSizeLimitations.cpp b/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
index 6097b6d..2a033ad 100644
|
||||
--- a/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
+++ b/src/compiler/translator/ValidateTypeSizeLimitations.cpp
|
||||
@@ -35,7 +35,9 @@
|
||||
{
|
||||
public:
|
||||
ValidateTypeSizeLimitationsTraverser(TSymbolTable *symbolTable, TDiagnostics *diagnostics)
|
||||
- : TIntermTraverser(true, false, false, symbolTable), mDiagnostics(diagnostics)
|
||||
+ : TIntermTraverser(true, false, false, symbolTable),
|
||||
+ mDiagnostics(diagnostics),
|
||||
+ mTotalPrivateVariablesSize(0)
|
||||
{
|
||||
ASSERT(diagnostics);
|
||||
}
|
||||
@@ -93,18 +95,33 @@
|
||||
const bool isPrivate = variableType.getQualifier() == EvqTemporary ||
|
||||
variableType.getQualifier() == EvqGlobal ||
|
||||
variableType.getQualifier() == EvqConst;
|
||||
- if (layoutEncoder.getCurrentOffset() > kMaxPrivateVariableSizeInBytes && isPrivate)
|
||||
+ if (isPrivate)
|
||||
{
|
||||
- error(asSymbol->getLine(),
|
||||
- "Size of declared private variable exceeds implementation-defined limit",
|
||||
- asSymbol->getName());
|
||||
- return false;
|
||||
+ if (layoutEncoder.getCurrentOffset() > kMaxPrivateVariableSizeInBytes)
|
||||
+ {
|
||||
+ error(asSymbol->getLine(),
|
||||
+ "Size of declared private variable exceeds implementation-defined limit",
|
||||
+ asSymbol->getName());
|
||||
+ return false;
|
||||
+ }
|
||||
+ mTotalPrivateVariablesSize += layoutEncoder.getCurrentOffset();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+ void validateTotalPrivateVariableSize()
|
||||
+ {
|
||||
+ if (mTotalPrivateVariablesSize > kMaxPrivateVariableSizeInBytes)
|
||||
+ {
|
||||
+ mDiagnostics->error(
|
||||
+ TSourceLoc{},
|
||||
+ "Total size of declared private variables exceeds implementation-defined limit",
|
||||
+ "");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
private:
|
||||
void error(TSourceLoc loc, const char *reason, const ImmutableString &token)
|
||||
{
|
||||
@@ -213,6 +230,8 @@
|
||||
|
||||
TDiagnostics *mDiagnostics;
|
||||
std::vector<int> mLoopSymbolIds;
|
||||
+
|
||||
+ size_t mTotalPrivateVariablesSize;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -223,6 +242,7 @@
|
||||
{
|
||||
ValidateTypeSizeLimitationsTraverser validate(symbolTable, diagnostics);
|
||||
root->traverse(&validate);
|
||||
+ validate.validateTotalPrivateVariableSize();
|
||||
return diagnostics->numErrors() == 0;
|
||||
}
|
||||
|
||||
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
index e62c8fb..996bad1 100644
|
||||
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
@@ -5271,11 +5271,12 @@
|
||||
// fairly small array.
|
||||
constexpr char kVSArrayOK[] =
|
||||
R"(varying vec4 color;
|
||||
-const int array_size = 1000;
|
||||
+const int array_size = 500;
|
||||
void main()
|
||||
{
|
||||
mat2 array[array_size];
|
||||
- if (array[0][0][0] == 2.0)
|
||||
+ mat2 array2[array_size];
|
||||
+ if (array[0][0][0] + array2[0][0][0] == 2.0)
|
||||
color = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
else
|
||||
color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
@@ -5353,6 +5354,103 @@
|
||||
EXPECT_EQ(0u, program);
|
||||
}
|
||||
|
||||
+// Reject attempts to allocate too much private memory.
|
||||
+// This is an implementation-defined limit - crbug.com/1431761.
|
||||
+TEST_P(WebGLCompatibilityTest, ValidateTotalPrivateSize)
|
||||
+{
|
||||
+ constexpr char kTooLargeGlobalMemory1[] =
|
||||
+ R"(precision mediump float;
|
||||
+
|
||||
+// 1 MB / 16 bytes per vec4 = 65536
|
||||
+vec4 array[32768];
|
||||
+vec4 array2[32769];
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ if (array[0].x + array[1].x == 0.)
|
||||
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
+ else
|
||||
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
+})";
|
||||
+
|
||||
+ constexpr char kTooLargeGlobalMemory2[] =
|
||||
+ R"(precision mediump float;
|
||||
+
|
||||
+// 1 MB / 16 bytes per vec4 = 65536
|
||||
+vec4 array[32767];
|
||||
+vec4 array2[32767];
|
||||
+vec4 x, y, z;
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ if (array[0].x + array[1].x == x.w + y.w + z.w)
|
||||
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
+ else
|
||||
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
+})";
|
||||
+
|
||||
+ constexpr char kTooLargeGlobalAndLocalMemory1[] =
|
||||
+ R"(precision mediump float;
|
||||
+
|
||||
+// 1 MB / 16 bytes per vec4 = 65536
|
||||
+vec4 array[32768];
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ vec4 array2[32769];
|
||||
+ if (array[0].x + array[1].x == 2.0)
|
||||
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
+ else
|
||||
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
+})";
|
||||
+
|
||||
+ // Note: The call stack is not taken into account for the purposes of total memory calculation.
|
||||
+ constexpr char kTooLargeGlobalAndLocalMemory2[] =
|
||||
+ R"(precision mediump float;
|
||||
+
|
||||
+// 1 MB / 16 bytes per vec4 = 65536
|
||||
+vec4 array[32768];
|
||||
+
|
||||
+float f()
|
||||
+{
|
||||
+ vec4 array2[16384];
|
||||
+ return array2[0].x;
|
||||
+}
|
||||
+
|
||||
+float g()
|
||||
+{
|
||||
+ vec4 array3[16383];
|
||||
+ return array3[0].x;
|
||||
+}
|
||||
+
|
||||
+float h()
|
||||
+{
|
||||
+ vec4 value;
|
||||
+ float value2
|
||||
+ return value.x + value2;
|
||||
+}
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ if (array[0].x + f() + g() + h() == 2.0)
|
||||
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
+ else
|
||||
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
+})";
|
||||
+
|
||||
+ GLuint program = CompileProgram(essl1_shaders::vs::Simple(), kTooLargeGlobalMemory1);
|
||||
+ EXPECT_EQ(0u, program);
|
||||
+
|
||||
+ program = CompileProgram(essl1_shaders::vs::Simple(), kTooLargeGlobalMemory2);
|
||||
+ EXPECT_EQ(0u, program);
|
||||
+
|
||||
+ program = CompileProgram(essl1_shaders::vs::Simple(), kTooLargeGlobalAndLocalMemory1);
|
||||
+ EXPECT_EQ(0u, program);
|
||||
+
|
||||
+ program = CompileProgram(essl1_shaders::vs::Simple(), kTooLargeGlobalAndLocalMemory2);
|
||||
+ EXPECT_EQ(0u, program);
|
||||
+}
|
||||
+
|
||||
// Linking should fail when corresponding vertex/fragment uniform blocks have different precision
|
||||
// qualifiers.
|
||||
TEST_P(WebGL2CompatibilityTest, UniformBlockPrecisionMismatch)
|
||||
396
patches/angle/cherry-pick-e4669a74888d.patch
Normal file
396
patches/angle/cherry-pick-e4669a74888d.patch
Normal file
@@ -0,0 +1,396 @@
|
||||
From e4669a74888d7f9bd93d79f72829d576776dfb8a Mon Sep 17 00:00:00 2001
|
||||
From: Charlie Lao <cclao@google.com>
|
||||
Date: Tue, 08 Aug 2023 10:14:47 -0700
|
||||
Subject: [PATCH] [M114-LTS] Vulkan: Fix data race with DynamicDescriptorPool
|
||||
|
||||
Right now DynamicDescriptorPool::destroyCachedDescriptorSet can be
|
||||
called from garbage clean up thread, while simultaneously accessed from
|
||||
context main thread, and data race will happen and cause bugs. This can
|
||||
only happen when the buffer is not being suballocated. In this case,
|
||||
suballocation owns the bufferBlock and bufferBlock gets destroyed when
|
||||
suballocation is destroyed from garbage collection thread. If buffer is
|
||||
suballocated, the shared group owns pool which owns bufferBlocks and
|
||||
they gets destroyed from shared group with the share group lock. This CL
|
||||
avoids this race problem by release the shared cacheKey when the buffer
|
||||
is released, while we still had the shared group lock.
|
||||
|
||||
Bug: chromium:1469542
|
||||
Change-Id: Ie6235fcfb77dee2a12b2ebde44042c3845fc0aca
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4790523
|
||||
(cherry picked from commit b48983ab8c74d2fcd9ef17c80727affb9e690c53)
|
||||
---
|
||||
|
||||
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.cpp b/src/libANGLE/renderer/vulkan/BufferVk.cpp
|
||||
index 2e07deb..93a1092 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/BufferVk.cpp
|
||||
+++ b/src/libANGLE/renderer/vulkan/BufferVk.cpp
|
||||
@@ -305,7 +305,7 @@
|
||||
RendererVk *renderer = contextVk->getRenderer();
|
||||
if (mBuffer.valid())
|
||||
{
|
||||
- mBuffer.releaseBufferAndDescriptorSetCache(contextVk);
|
||||
+ mBuffer.releaseBufferAndDescriptorSetCache(renderer);
|
||||
}
|
||||
if (mStagingBuffer.valid())
|
||||
{
|
||||
@@ -623,7 +623,7 @@
|
||||
memcpy(dstMapPtr, srcMapPtr, static_cast<size_t>(mState.getSize()));
|
||||
}
|
||||
|
||||
- src.releaseBufferAndDescriptorSetCache(contextVk);
|
||||
+ src.releaseBufferAndDescriptorSetCache(contextVk->getRenderer());
|
||||
|
||||
// Return the already mapped pointer with the offset adjustment to avoid the call to unmap().
|
||||
*mapPtr = dstMapPtr + offset;
|
||||
@@ -1029,7 +1029,7 @@
|
||||
|
||||
if (prevBuffer.valid())
|
||||
{
|
||||
- prevBuffer.releaseBufferAndDescriptorSetCache(contextVk);
|
||||
+ prevBuffer.releaseBufferAndDescriptorSetCache(contextVk->getRenderer());
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
@@ -1151,7 +1151,7 @@
|
||||
|
||||
if (mBuffer.valid())
|
||||
{
|
||||
- mBuffer.releaseBufferAndDescriptorSetCache(contextVk);
|
||||
+ mBuffer.releaseBufferAndDescriptorSetCache(renderer);
|
||||
}
|
||||
|
||||
// Allocate the buffer directly
|
||||
diff --git a/src/libANGLE/renderer/vulkan/Suballocation.h b/src/libANGLE/renderer/vulkan/Suballocation.h
|
||||
index d25481b..276f6b4 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/Suballocation.h
|
||||
+++ b/src/libANGLE/renderer/vulkan/Suballocation.h
|
||||
@@ -86,6 +86,13 @@
|
||||
{
|
||||
mDescriptorSetCacheManager.addKey(sharedCacheKey);
|
||||
}
|
||||
+ void releaseAllCachedDescriptorSetCacheKeys(RendererVk *renderer)
|
||||
+ {
|
||||
+ if (!mDescriptorSetCacheManager.empty())
|
||||
+ {
|
||||
+ mDescriptorSetCacheManager.releaseKeys(renderer);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
private:
|
||||
mutable std::mutex mVirtualBlockMutex;
|
||||
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
|
||||
index d43c172..5e6419f 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
|
||||
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
|
||||
@@ -1623,7 +1623,7 @@
|
||||
onStateChange(angle::SubjectMessage::SubjectChanged);
|
||||
}
|
||||
mRedefinedLevels.reset();
|
||||
- mDescriptorSetCacheManager.releaseKeys(contextVk);
|
||||
+ mDescriptorSetCacheManager.releaseKeys(contextVk->getRenderer());
|
||||
}
|
||||
|
||||
void TextureVk::initImageUsageFlags(ContextVk *contextVk, angle::FormatID actualFormatID)
|
||||
@@ -2857,7 +2857,7 @@
|
||||
|
||||
mBufferViews.release(contextVk);
|
||||
mBufferViews.init(renderer, offset, size);
|
||||
- mDescriptorSetCacheManager.releaseKeys(contextVk);
|
||||
+ mDescriptorSetCacheManager.releaseKeys(renderer);
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
@@ -3300,7 +3300,7 @@
|
||||
{
|
||||
RendererVk *renderer = contextVk->getRenderer();
|
||||
|
||||
- mDescriptorSetCacheManager.releaseKeys(contextVk);
|
||||
+ mDescriptorSetCacheManager.releaseKeys(renderer);
|
||||
|
||||
if (mImage == nullptr)
|
||||
{
|
||||
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
|
||||
index f750428..ae86b29 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
|
||||
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
|
||||
@@ -2589,11 +2589,19 @@
|
||||
{
|
||||
contextVk->getShareGroup()->getFramebufferCache().erase(contextVk, desc);
|
||||
}
|
||||
+void ReleaseCachedObject(RendererVk *renderer, const FramebufferDesc &desc)
|
||||
+{
|
||||
+ UNREACHABLE();
|
||||
+}
|
||||
|
||||
void ReleaseCachedObject(ContextVk *contextVk, const DescriptorSetDescAndPool &descAndPool)
|
||||
{
|
||||
+ UNREACHABLE();
|
||||
+}
|
||||
+void ReleaseCachedObject(RendererVk *renderer, const DescriptorSetDescAndPool &descAndPool)
|
||||
+{
|
||||
ASSERT(descAndPool.mPool != nullptr);
|
||||
- descAndPool.mPool->releaseCachedDescriptorSet(contextVk, descAndPool.mDesc);
|
||||
+ descAndPool.mPool->releaseCachedDescriptorSet(renderer, descAndPool.mDesc);
|
||||
}
|
||||
|
||||
void DestroyCachedObject(RendererVk *renderer, const FramebufferDesc &desc)
|
||||
@@ -6261,6 +6269,22 @@
|
||||
}
|
||||
|
||||
template <class SharedCacheKeyT>
|
||||
+void SharedCacheKeyManager<SharedCacheKeyT>::releaseKeys(RendererVk *renderer)
|
||||
+{
|
||||
+ for (SharedCacheKeyT &sharedCacheKey : mSharedCacheKeys)
|
||||
+ {
|
||||
+ if (*sharedCacheKey.get() != nullptr)
|
||||
+ {
|
||||
+ // Immediate destroy the cached object and the key itself when first releaseKeys call is
|
||||
+ // made
|
||||
+ ReleaseCachedObject(renderer, *(*sharedCacheKey.get()));
|
||||
+ *sharedCacheKey.get() = nullptr;
|
||||
+ }
|
||||
+ }
|
||||
+ mSharedCacheKeys.clear();
|
||||
+}
|
||||
+
|
||||
+template <class SharedCacheKeyT>
|
||||
void SharedCacheKeyManager<SharedCacheKeyT>::destroyKeys(RendererVk *renderer)
|
||||
{
|
||||
for (SharedCacheKeyT &sharedCacheKey : mSharedCacheKeys)
|
||||
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.h b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
|
||||
index 8c9d452..f85459a 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.h
|
||||
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
|
||||
@@ -1932,6 +1932,7 @@
|
||||
void addKey(const SharedCacheKeyT &key);
|
||||
// Iterate over the descriptor array and release the descriptor and cache.
|
||||
void releaseKeys(ContextVk *contextVk);
|
||||
+ void releaseKeys(RendererVk *rendererVk);
|
||||
// Iterate over the descriptor array and destroy the descriptor and cache.
|
||||
void destroyKeys(RendererVk *renderer);
|
||||
void clear();
|
||||
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
|
||||
index 9088e09bf..8bc027d 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
|
||||
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
|
||||
@@ -3798,7 +3798,7 @@
|
||||
return mDescriptorPools[mCurrentPoolIndex]->get().init(context, mPoolSizes, mMaxSetsPerPool);
|
||||
}
|
||||
|
||||
-void DynamicDescriptorPool::releaseCachedDescriptorSet(ContextVk *contextVk,
|
||||
+void DynamicDescriptorPool::releaseCachedDescriptorSet(RendererVk *renderer,
|
||||
const DescriptorSetDesc &desc)
|
||||
{
|
||||
VkDescriptorSet descriptorSet;
|
||||
@@ -3812,7 +3812,7 @@
|
||||
// Wrap it with helper object so that it can be GPU tracked and add it to resource list.
|
||||
DescriptorSetHelper descriptorSetHelper(poolOut->get().getResourceUse(), descriptorSet);
|
||||
poolOut->get().addGarbage(std::move(descriptorSetHelper));
|
||||
- checkAndReleaseUnusedPool(contextVk->getRenderer(), poolOut);
|
||||
+ checkAndReleaseUnusedPool(renderer, poolOut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5016,6 +5016,12 @@
|
||||
|
||||
if (mSuballocation.valid())
|
||||
{
|
||||
+ if (!mSuballocation.isSuballocated())
|
||||
+ {
|
||||
+ // Destroy cacheKeys now to avoid getting into situation that having to destroy
|
||||
+ // descriptorSet from garbage collection thread.
|
||||
+ mSuballocation.getBufferBlock()->releaseAllCachedDescriptorSetCacheKeys(renderer);
|
||||
+ }
|
||||
renderer->collectSuballocationGarbage(mUse, std::move(mSuballocation),
|
||||
std::move(mBufferForVertexArray));
|
||||
}
|
||||
@@ -5024,17 +5030,15 @@
|
||||
ASSERT(!mBufferForVertexArray.valid());
|
||||
}
|
||||
|
||||
-void BufferHelper::releaseBufferAndDescriptorSetCache(ContextVk *contextVk)
|
||||
+void BufferHelper::releaseBufferAndDescriptorSetCache(RendererVk *renderer)
|
||||
{
|
||||
- RendererVk *renderer = contextVk->getRenderer();
|
||||
-
|
||||
if (renderer->hasResourceUseFinished(getResourceUse()))
|
||||
{
|
||||
mDescriptorSetCacheManager.destroyKeys(renderer);
|
||||
}
|
||||
else
|
||||
{
|
||||
- mDescriptorSetCacheManager.releaseKeys(contextVk);
|
||||
+ mDescriptorSetCacheManager.releaseKeys(renderer);
|
||||
}
|
||||
|
||||
release(renderer);
|
||||
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
|
||||
index d2405b6..962dfe8 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
|
||||
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
|
||||
@@ -252,7 +252,7 @@
|
||||
VkDescriptorSet *descriptorSetOut,
|
||||
SharedDescriptorSetCacheKey *sharedCacheKeyOut);
|
||||
|
||||
- void releaseCachedDescriptorSet(ContextVk *contextVk, const DescriptorSetDesc &desc);
|
||||
+ void releaseCachedDescriptorSet(RendererVk *renderer, const DescriptorSetDesc &desc);
|
||||
void destroyCachedDescriptorSet(RendererVk *renderer, const DescriptorSetDesc &desc);
|
||||
|
||||
template <typename Accumulator>
|
||||
@@ -777,7 +777,7 @@
|
||||
|
||||
void destroy(RendererVk *renderer);
|
||||
void release(RendererVk *renderer);
|
||||
- void releaseBufferAndDescriptorSetCache(ContextVk *contextVk);
|
||||
+ void releaseBufferAndDescriptorSetCache(RendererVk *renderer);
|
||||
|
||||
BufferSerial getBufferSerial() const { return mSerial; }
|
||||
BufferSerial getBlockSerial() const
|
||||
diff --git a/src/tests/gl_tests/TransformFeedbackTest.cpp b/src/tests/gl_tests/TransformFeedbackTest.cpp
|
||||
index ea3eeea..226eb1f 100644
|
||||
--- a/src/tests/gl_tests/TransformFeedbackTest.cpp
|
||||
+++ b/src/tests/gl_tests/TransformFeedbackTest.cpp
|
||||
@@ -507,8 +507,8 @@
|
||||
|
||||
const std::array<uint32_t, 12> kInitialData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
const std::array<uint32_t, 12> kUpdateData = {
|
||||
- 0x12345678u, 0x9ABCDEF0u, 0x13579BDFu, 0x2468ACE0u, 0x23456781u, 0xABCDEF09u,
|
||||
- 0x3579BDF1u, 0x468ACE02u, 0x34567812u, 0xBCDEF09Au, 0x579BDF13u, 0x68ACE024u,
|
||||
+ 0x12345678u, 0x9ABCDEF0u, 0x13579BDFu, 0x2468ACE0u, 0x23456781u, 0xABCDEF09u,
|
||||
+ 0x3579BDF1u, 0x468ACE02u, 0x34567812u, 0xBCDEF09Au, 0x579BDF13u, 0x68ACE024u,
|
||||
};
|
||||
|
||||
GLBuffer buffer;
|
||||
@@ -3749,9 +3749,9 @@
|
||||
constexpr size_t kCapturedVaryingsCount = 3;
|
||||
constexpr std::array<size_t, kCapturedVaryingsCount> kCaptureSizes = {8, 9, 4};
|
||||
const std::vector<float> kExpected[kCapturedVaryingsCount] = {
|
||||
- {0.27, 0.30, 0.33, 0.36, 0.39, 0.42, 0.45, 0.48},
|
||||
- {0.63, 0.66, 0.69, 0.72, 0.75, 0.78, 0.81, 0.84, 0.87},
|
||||
- {0.25, 0.5, 0.75, 1.0},
|
||||
+ {0.27, 0.30, 0.33, 0.36, 0.39, 0.42, 0.45, 0.48},
|
||||
+ {0.63, 0.66, 0.69, 0.72, 0.75, 0.78, 0.81, 0.84, 0.87},
|
||||
+ {0.25, 0.5, 0.75, 1.0},
|
||||
};
|
||||
|
||||
ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program, kVS, kFS, tfVaryings, GL_INTERLEAVED_ATTRIBS);
|
||||
@@ -3848,9 +3848,9 @@
|
||||
constexpr size_t kCapturedVaryingsCount = 3;
|
||||
constexpr std::array<size_t, kCapturedVaryingsCount> kCaptureSizes = {1, 2, 1};
|
||||
const std::vector<float> kExpected[kCapturedVaryingsCount] = {
|
||||
- {0.25},
|
||||
- {0.5, 0.75},
|
||||
- {1.0},
|
||||
+ {0.25},
|
||||
+ {0.5, 0.75},
|
||||
+ {1.0},
|
||||
};
|
||||
|
||||
ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program, kVS, kFS, tfVaryings, GL_SEPARATE_ATTRIBS);
|
||||
@@ -4392,6 +4392,51 @@
|
||||
glEndTransformFeedback();
|
||||
}
|
||||
|
||||
+// Test bufferData call and transform feedback.
|
||||
+TEST_P(TransformFeedbackTest, BufferDataAndTransformFeedback)
|
||||
+{
|
||||
+ const char kVS[] = R"(#version 300 es
|
||||
+flat out highp int var;
|
||||
+void main() {
|
||||
+var = 1;
|
||||
+})";
|
||||
+
|
||||
+ const char kFS[] = R"(#version 300 es
|
||||
+flat in highp int var;
|
||||
+out highp int color;
|
||||
+void main() {
|
||||
+color = var;
|
||||
+})";
|
||||
+
|
||||
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
|
||||
+
|
||||
+ GLTexture texture;
|
||||
+ glBindTexture(GL_TEXTURE_2D, texture);
|
||||
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32I, 1, 1);
|
||||
+
|
||||
+ GLFramebuffer fbo;
|
||||
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
||||
+ EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
|
||||
+
|
||||
+ constexpr int kClearColor[] = {123, 0, 0, 0};
|
||||
+ glClearBufferiv(GL_COLOR, 0, kClearColor);
|
||||
+ glDrawArrays(GL_POINTS, 0, 1);
|
||||
+
|
||||
+ const char *kVarying = "var";
|
||||
+ glTransformFeedbackVaryings(program, 1, &kVarying, GL_INTERLEAVED_ATTRIBS);
|
||||
+ glLinkProgram(program);
|
||||
+ ASSERT_GL_NO_ERROR();
|
||||
+
|
||||
+ GLBuffer buffer;
|
||||
+ glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buffer);
|
||||
+ glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 0x7ffc * 10000, nullptr, GL_DYNAMIC_READ);
|
||||
+ glBeginTransformFeedback(GL_POINTS);
|
||||
+ glDrawArrays(GL_POINTS, 0, 1);
|
||||
+ glEndTransformFeedback();
|
||||
+ glFlush();
|
||||
+}
|
||||
+
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackTest);
|
||||
ANGLE_INSTANTIATE_TEST_ES3_AND(TransformFeedbackTest,
|
||||
ES3_VULKAN()
|
||||
diff --git a/src/tests/gl_tests/UniformBufferTest.cpp b/src/tests/gl_tests/UniformBufferTest.cpp
|
||||
index 4d005c0..71b8504 100644
|
||||
--- a/src/tests/gl_tests/UniformBufferTest.cpp
|
||||
+++ b/src/tests/gl_tests/UniformBufferTest.cpp
|
||||
@@ -3422,6 +3422,50 @@
|
||||
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
|
||||
}
|
||||
|
||||
+// Calling BufferData and use it in a loop to force descriptorSet creation and destroy.
|
||||
+TEST_P(UniformBufferTest, BufferDataInLoop)
|
||||
+{
|
||||
+ glClear(GL_COLOR_BUFFER_BIT);
|
||||
+
|
||||
+ // Use large buffer size to get around suballocation, so that we will gets a new buffer with
|
||||
+ // bufferData call.
|
||||
+ static constexpr size_t kBufferSize = 4 * 1024 * 1024;
|
||||
+ std::vector<float> floatData;
|
||||
+ floatData.resize(kBufferSize / (sizeof(float)), 0.0f);
|
||||
+ floatData[0] = 0.5f;
|
||||
+ floatData[1] = 0.75f;
|
||||
+ floatData[2] = 0.25f;
|
||||
+ floatData[3] = 1.0f;
|
||||
+
|
||||
+ GLTexture textures[2];
|
||||
+ GLFramebuffer fbos[2];
|
||||
+ for (int i = 0; i < 2; i++)
|
||||
+ {
|
||||
+ glBindTexture(GL_TEXTURE_2D, textures[i]);
|
||||
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256);
|
||||
+
|
||||
+ glBindFramebuffer(GL_FRAMEBUFFER, fbos[i]);
|
||||
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[i], 0);
|
||||
+ EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
|
||||
+ }
|
||||
+
|
||||
+ for (int loop = 0; loop < 10; loop++)
|
||||
+ {
|
||||
+ int i = loop & 0x1;
|
||||
+ // Switch FBO to get around deferred flush
|
||||
+ glBindFramebuffer(GL_FRAMEBUFFER, fbos[i]);
|
||||
+ glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
|
||||
+ glBufferData(GL_UNIFORM_BUFFER, kBufferSize, floatData.data(), GL_STATIC_DRAW);
|
||||
+
|
||||
+ glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
|
||||
+ glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
|
||||
+ drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
|
||||
+ glFlush();
|
||||
+ }
|
||||
+ ASSERT_GL_NO_ERROR();
|
||||
+ EXPECT_PIXEL_NEAR(0, 0, 128, 191, 64, 255, 1);
|
||||
+}
|
||||
+
|
||||
class WebGL2UniformBufferTest : public UniformBufferTest
|
||||
{
|
||||
protected:
|
||||
@@ -1,66 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Milan Burda <milan.burda@gmail.com>
|
||||
Date: Mon, 10 Oct 2022 15:11:08 +0400
|
||||
Subject: fix: rename WebSwapCGLLayer to WebSwapCGLLayerChromium
|
||||
|
||||
Class WebSwapCGLLayer is implemented in both /System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/Frameworks/libANGLE-shared.dylib (0x23c589b50)
|
||||
and src/out/testing/Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib (0x1123f8488).
|
||||
One of the two will be used. Which one is undefined.
|
||||
|
||||
diff --git a/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h b/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h
|
||||
index 7101cd271c07e604c6f5241d1a22f59f446c0033..98a89976f87e32a66bd504951698204fad938d57 100644
|
||||
--- a/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h
|
||||
+++ b/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h
|
||||
@@ -18,7 +18,7 @@ struct __IOSurface;
|
||||
typedef __IOSurface *IOSurfaceRef;
|
||||
|
||||
// WebKit's build process requires that every Objective-C class name has the prefix "Web".
|
||||
-@class WebSwapCGLLayer;
|
||||
+@class WebSwapCGLLayerChromium;
|
||||
|
||||
namespace rx
|
||||
{
|
||||
@@ -89,7 +89,7 @@ class WindowSurfaceCGL : public SurfaceGL
|
||||
gl::Framebuffer *framebuffer) override;
|
||||
|
||||
private:
|
||||
- WebSwapCGLLayer *mSwapLayer;
|
||||
+ WebSwapCGLLayerChromium *mSwapLayer;
|
||||
SharedSwapState mSwapState;
|
||||
uint64_t mCurrentSwapId;
|
||||
|
||||
diff --git a/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm b/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm
|
||||
index 27990e9c2facbbe0e30f4b275e0778a6474fb9ce..84f9fa9b6d31fb0e85a4c4270dff4d935f855c26 100644
|
||||
--- a/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm
|
||||
+++ b/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm
|
||||
@@ -24,7 +24,7 @@
|
||||
# include "libANGLE/renderer/gl/StateManagerGL.h"
|
||||
# include "libANGLE/renderer/gl/cgl/DisplayCGL.h"
|
||||
|
||||
-@interface WebSwapCGLLayer : CAOpenGLLayer {
|
||||
+@interface WebSwapCGLLayerChromium : CAOpenGLLayer {
|
||||
CGLContextObj mDisplayContext;
|
||||
|
||||
bool initialized;
|
||||
@@ -38,7 +38,7 @@ - (id)initWithSharedState:(rx::SharedSwapState *)swapState
|
||||
withFunctions:(const rx::FunctionsGL *)functions;
|
||||
@end
|
||||
|
||||
-@implementation WebSwapCGLLayer
|
||||
+@implementation WebSwapCGLLayerChromium
|
||||
- (id)initWithSharedState:(rx::SharedSwapState *)swapState
|
||||
withContext:(CGLContextObj)displayContext
|
||||
withFunctions:(const rx::FunctionsGL *)functions
|
||||
@@ -220,9 +220,9 @@ - (void)drawInCGLContext:(CGLContextObj)glContext
|
||||
mSwapState.lastRendered = &mSwapState.textures[1];
|
||||
mSwapState.beingPresented = &mSwapState.textures[2];
|
||||
|
||||
- mSwapLayer = [[WebSwapCGLLayer alloc] initWithSharedState:&mSwapState
|
||||
- withContext:mContext
|
||||
- withFunctions:mFunctions];
|
||||
+ mSwapLayer = [[WebSwapCGLLayerChromium alloc] initWithSharedState:&mSwapState
|
||||
+ withContext:mContext
|
||||
+ withFunctions:mFunctions];
|
||||
[mLayer addSublayer:mSwapLayer];
|
||||
[mSwapLayer setContentsScale:[mLayer contentsScale]];
|
||||
|
||||
@@ -20,10 +20,10 @@ index 2ca14efae5ea478f43794a81883b00dfdb1a37b0..d73055fbf39334925ef4b4804bbaca57
|
||||
|
||||
case ssl_open_record_error:
|
||||
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
|
||||
index 86e8eb33e2f99b9a6173550027cfd0812a39614e..f2aac85c096bef42a015135b02936e3d4e9a4c56 100644
|
||||
index 6b4549633585f8a65fb7b285a9be4ba83db355db..f9367d3c050f89011a09b847c29df31f321ffd09 100644
|
||||
--- a/ssl/ssl_lib.cc
|
||||
+++ b/ssl/ssl_lib.cc
|
||||
@@ -1320,7 +1320,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) {
|
||||
@@ -1335,7 +1335,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) {
|
||||
}
|
||||
|
||||
if (ret_code == 0) {
|
||||
@@ -32,7 +32,7 @@ index 86e8eb33e2f99b9a6173550027cfd0812a39614e..f2aac85c096bef42a015135b02936e3d
|
||||
return SSL_ERROR_ZERO_RETURN;
|
||||
}
|
||||
// An EOF was observed which violates the protocol, and the underlying
|
||||
@@ -2597,13 +2597,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) {
|
||||
@@ -2624,13 +2624,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) {
|
||||
return CRYPTO_get_ex_data(&ctx->ex_data, idx);
|
||||
}
|
||||
|
||||
@@ -48,10 +48,10 @@ index 86e8eb33e2f99b9a6173550027cfd0812a39614e..f2aac85c096bef42a015135b02936e3d
|
||||
void SSL_CTX_set_tmp_rsa_callback(SSL_CTX *ctx,
|
||||
RSA *(*cb)(SSL *ssl, int is_export,
|
||||
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
|
||||
index f51c11efc12209377773204808b85c9cbf5d3bff..d19fd1f9643f99fdd56753a942fccefff08728ac 100644
|
||||
index 854068f44b2bb8f738e41fab059b62ced9309fce..a080edd8b5ec48e433afa45d6246b30e9f7221f0 100644
|
||||
--- a/ssl/ssl_test.cc
|
||||
+++ b/ssl/ssl_test.cc
|
||||
@@ -8460,11 +8460,6 @@ TEST(SSLTest, ErrorSyscallAfterCloseNotify) {
|
||||
@@ -8459,11 +8459,6 @@ TEST(SSLTest, ErrorSyscallAfterCloseNotify) {
|
||||
EXPECT_EQ(ret, 0);
|
||||
EXPECT_EQ(SSL_get_error(client.get(), ret), SSL_ERROR_ZERO_RETURN);
|
||||
|
||||
@@ -63,7 +63,7 @@ index f51c11efc12209377773204808b85c9cbf5d3bff..d19fd1f9643f99fdd56753a942fcceff
|
||||
// Although the client has seen close_notify, it should continue to report
|
||||
// |SSL_ERROR_SYSCALL| when its writes fail.
|
||||
ret = SSL_write(client.get(), data, sizeof(data));
|
||||
@@ -8472,22 +8467,6 @@ TEST(SSLTest, ErrorSyscallAfterCloseNotify) {
|
||||
@@ -8471,22 +8466,6 @@ TEST(SSLTest, ErrorSyscallAfterCloseNotify) {
|
||||
EXPECT_EQ(SSL_get_error(client.get(), ret), SSL_ERROR_SYSCALL);
|
||||
EXPECT_TRUE(write_failed);
|
||||
write_failed = false;
|
||||
|
||||
@@ -101,7 +101,6 @@ make_gtk_getlibgtk_public.patch
|
||||
build_disable_print_content_analysis.patch
|
||||
custom_protocols_plzserviceworker.patch
|
||||
feat_filter_out_non-shareable_windows_in_the_current_application_in.patch
|
||||
fix_allow_guest_webcontents_to_enter_fullscreen.patch
|
||||
disable_freezing_flags_after_init_in_node.patch
|
||||
short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch
|
||||
chore_add_electron_deps_to_gitignores.patch
|
||||
@@ -124,12 +123,33 @@ chore_introduce_blocking_api_for_electron.patch
|
||||
chore_patch_out_partition_attribute_dcheck_for_webviews.patch
|
||||
expose_v8initializer_codegenerationcheckcallbackinmainthread.patch
|
||||
chore_patch_out_profile_methods_in_profile_selections_cc.patch
|
||||
fix_x11_window_restore_minimized_maximized_window.patch
|
||||
add_gin_converter_support_for_arraybufferview.patch
|
||||
chore_defer_usb_service_getdevices_request_until_usb_service_is.patch
|
||||
revert_x11_keep_windowcache_alive_for_a_time_interval.patch
|
||||
cherry-pick-ec53103cc72d.patch
|
||||
cherry-pick-94af9d13a14b.patch
|
||||
cherry-pick-ea1cd76358e0.patch
|
||||
m114_merge_fix_a_crash_caused_by_calling_trace_event.patch
|
||||
mojoipcz_copy_incoming_messages_early.patch
|
||||
base_do_not_use_va_args_twice_in_asprintf.patch
|
||||
cherry-pick-48a136e77e6d.patch
|
||||
cherry-pick-e6e23ba00379.patch
|
||||
fix_crash_on_nativetheme_change_during_context_menu_close.patch
|
||||
cherry-pick-aa23556ff213.patch
|
||||
cherry-pick-abb3ebd3d2ef.patch
|
||||
cherry-pick-83b0bdb696d8.patch
|
||||
cherry-pick-e40cb330b645.patch
|
||||
networkcontext_don_t_access_url_loader_factories_during_destruction.patch
|
||||
cherry-pick-1939f7b78eda.patch
|
||||
cherry-pick-37447eb52a74.patch
|
||||
cherry-pick-35c06406a658.patch
|
||||
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
|
||||
cherry-pick-74a2eb9c8cb2.patch
|
||||
cherry-pick-26175b0903d8.patch
|
||||
fix_use_delegated_generic_capturer_when_available.patch
|
||||
cherry-pick-b11e7d07a6f4.patch
|
||||
cherry-pick-309b604c4e88.patch
|
||||
cherry-pick-f218b4f37018.patch
|
||||
cherry-pick-d756d71a652c.patch
|
||||
parameterize_axtreeserializer_by_vector_type.patch
|
||||
avoid_allocating_recordid_objects_in_elementtiming_and_lcp.patch
|
||||
cherry-pick-80106e31c7ea.patch
|
||||
gpu_use_load_program_shader_shm_count_on_drdc_thread.patch
|
||||
crash_gpu_process_and_clear_shader_cache_when_skia_reports.patch
|
||||
cherry-pick-3df423a5b8de.patch
|
||||
cherry-pick-9384cddc7705.patch
|
||||
cherry-pick-3f45b1af5e41.patch
|
||||
cherry-pick-e13061c50998.patch
|
||||
|
||||
@@ -10,10 +10,10 @@ Allows Electron to restore WER when ELECTRON_DEFAULT_ERROR_MODE is set.
|
||||
This should be upstreamed.
|
||||
|
||||
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
|
||||
index 7bf09dd8870040e74cd1a7653665d0806910378c..ec6d1554075ee5454f79e98d0e2e539395499dbe 100644
|
||||
index a399e1ba0e6728aa8d2869864d3c328ba61e779b..b7dd5e0e48af2a88aad11139f5951d917d8d4f9d 100644
|
||||
--- a/content/gpu/gpu_main.cc
|
||||
+++ b/content/gpu/gpu_main.cc
|
||||
@@ -243,6 +243,10 @@ int GpuMain(MainFunctionParams parameters) {
|
||||
@@ -258,6 +258,10 @@ int GpuMain(MainFunctionParams parameters) {
|
||||
// to the GpuProcessHost once the GpuServiceImpl has started.
|
||||
viz::GpuServiceImpl::InstallPreInitializeLogHandler();
|
||||
|
||||
@@ -24,7 +24,7 @@ index 7bf09dd8870040e74cd1a7653665d0806910378c..ec6d1554075ee5454f79e98d0e2e5393
|
||||
// We are experiencing what appear to be memory-stomp issues in the GPU
|
||||
// process. These issues seem to be impacting the task executor and listeners
|
||||
// registered to it. Create the task executor on the heap to guard against
|
||||
@@ -327,7 +331,6 @@ int GpuMain(MainFunctionParams parameters) {
|
||||
@@ -342,7 +346,6 @@ int GpuMain(MainFunctionParams parameters) {
|
||||
const_cast<base::CommandLine*>(&command_line), gpu_preferences);
|
||||
const bool dead_on_arrival = !init_success;
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ DidCreateScriptContext is called, not all JS APIs are available in the
|
||||
context, which can cause some preload scripts to trip.
|
||||
|
||||
diff --git a/content/public/renderer/render_frame_observer.h b/content/public/renderer/render_frame_observer.h
|
||||
index 6c92a2856e447bdda11c7ed2c64b79b93a0eca26..03dedde2d83e6b09fb7e90be7dfdce246f301d8f 100644
|
||||
index fef4c8e02067c44ed804c8b53db1007fae2d2a76..b36304ee0a832c5e1e2fd3af6151b7b03fd98ec4 100644
|
||||
--- a/content/public/renderer/render_frame_observer.h
|
||||
+++ b/content/public/renderer/render_frame_observer.h
|
||||
@@ -136,6 +136,8 @@ class CONTENT_EXPORT RenderFrameObserver : public IPC::Listener,
|
||||
@@ -137,6 +137,8 @@ class CONTENT_EXPORT RenderFrameObserver : public IPC::Listener,
|
||||
virtual void DidHandleOnloadEvents() {}
|
||||
virtual void DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
int32_t world_id) {}
|
||||
@@ -23,10 +23,10 @@ index 6c92a2856e447bdda11c7ed2c64b79b93a0eca26..03dedde2d83e6b09fb7e90be7dfdce24
|
||||
int32_t world_id) {}
|
||||
virtual void DidClearWindowObject() {}
|
||||
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
|
||||
index dab59cd51f1f66ff9e712955a880f73ef9e34fd2..0fa592bd37cfef925a8af9aa8ff19177c8e91ae1 100644
|
||||
index 026a6934997f0825c3f2c0a40551ce7784a7b828..5bceb2baec943a2f58416c0ca6093014f9d68d7c 100644
|
||||
--- a/content/renderer/render_frame_impl.cc
|
||||
+++ b/content/renderer/render_frame_impl.cc
|
||||
@@ -4361,6 +4361,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
@@ -4417,6 +4417,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
observer.DidCreateScriptContext(context, world_id);
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ index dab59cd51f1f66ff9e712955a880f73ef9e34fd2..0fa592bd37cfef925a8af9aa8ff19177
|
||||
int world_id) {
|
||||
for (auto& observer : observers_)
|
||||
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
|
||||
index ff1a05ac05d2df7db3ba3579ddf0d2d329d9782c..72806c4947b7187b54c79a1cd05200183e56d4d1 100644
|
||||
index 516a8140f79cc7f8ffe4b0665184d7d3d6111da9..fce2608070d2bf31b6b02de47102aff870c8229f 100644
|
||||
--- a/content/renderer/render_frame_impl.h
|
||||
+++ b/content/renderer/render_frame_impl.h
|
||||
@@ -605,6 +605,8 @@ class CONTENT_EXPORT RenderFrameImpl
|
||||
@@ -608,6 +608,8 @@ class CONTENT_EXPORT RenderFrameImpl
|
||||
void DidObserveLayoutShift(double score, bool after_input_or_scroll) override;
|
||||
void DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
int world_id) override;
|
||||
@@ -53,10 +53,10 @@ index ff1a05ac05d2df7db3ba3579ddf0d2d329d9782c..72806c4947b7187b54c79a1cd0520018
|
||||
int world_id) override;
|
||||
void DidChangeScrollOffset() override;
|
||||
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
|
||||
index d6bb89671f9d71baa087d6f5cd2ef8d1b048b5ca..44dd685fa2d97451c3c8c06788d8d7384d554bb8 100644
|
||||
index dfd33eede0a8b0935a06eeb8f505a8d0738afa19..06827403830cb730b2521c715ec06aedbf7339c6 100644
|
||||
--- a/third_party/blink/public/web/web_local_frame_client.h
|
||||
+++ b/third_party/blink/public/web/web_local_frame_client.h
|
||||
@@ -599,6 +599,9 @@ class BLINK_EXPORT WebLocalFrameClient {
|
||||
@@ -606,6 +606,9 @@ class BLINK_EXPORT WebLocalFrameClient {
|
||||
virtual void DidCreateScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) {}
|
||||
|
||||
@@ -67,22 +67,22 @@ index d6bb89671f9d71baa087d6f5cd2ef8d1b048b5ca..44dd685fa2d97451c3c8c06788d8d738
|
||||
virtual void WillReleaseScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) {}
|
||||
diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
|
||||
index 9736b31c03a32635fb8fde581321cc10ec11b11a..c9fd2170a25a7970648b8210ebe94e225f4ee3aa 100644
|
||||
index e7d4256fa96f5bc8ad71bd13b6b33feef32b443f..0dfeda68a4dbfd6b442f8d8f928c8cb871dd6aa4 100644
|
||||
--- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
|
||||
+++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
|
||||
@@ -197,6 +197,7 @@ void LocalWindowProxy::Initialize() {
|
||||
@@ -198,6 +198,7 @@ void LocalWindowProxy::Initialize() {
|
||||
}
|
||||
|
||||
InstallConditionalFeatures();
|
||||
+ GetFrame()->Client()->DidInstallConditionalFeatures(context, world_->GetWorldId());
|
||||
|
||||
if (World().IsMainWorld()) {
|
||||
GetFrame()->Loader().DispatchDidClearWindowObjectInMainWorld();
|
||||
probe::DidCreateMainWorldContext(GetFrame());
|
||||
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
|
||||
index 553afb55280410392699855f167d51d2831ac3ca..a9a99e669689b433ce4c63b3c549676ee15255a4 100644
|
||||
index 2c526726ff1ed7008152086210b25103ce308d5b..c62ab44289bf32ac9d40d3df45f6363409f0dea9 100644
|
||||
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
|
||||
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
|
||||
@@ -317,6 +317,8 @@ class CORE_EXPORT LocalFrameClient : public FrameClient {
|
||||
@@ -315,6 +315,8 @@ class CORE_EXPORT LocalFrameClient : public FrameClient {
|
||||
|
||||
virtual void DidCreateScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) = 0;
|
||||
@@ -92,7 +92,7 @@ index 553afb55280410392699855f167d51d2831ac3ca..a9a99e669689b433ce4c63b3c549676e
|
||||
int32_t world_id) = 0;
|
||||
virtual bool AllowScriptExtensions() = 0;
|
||||
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
|
||||
index c4a593326e3de8b0c2462a54e13304d14dd7b56a..97bec4188d3aee81d6a22d487ce5364c4120d4b8 100644
|
||||
index 56ad2a8f3fabe03b2c285231d0e99dbcbaf2a9d2..130423e45f17998bf5a28c719115715941ae6620 100644
|
||||
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
|
||||
@@ -283,6 +283,13 @@ void LocalFrameClientImpl::DidCreateScriptContext(
|
||||
@@ -110,10 +110,10 @@ index c4a593326e3de8b0c2462a54e13304d14dd7b56a..97bec4188d3aee81d6a22d487ce5364c
|
||||
v8::Local<v8::Context> context,
|
||||
int32_t world_id) {
|
||||
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.h b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
|
||||
index 5d2c6425dd568b663ff68a31da510784ecf103ab..f1215d23c679aeec484e6f64c41b6df71a75e805 100644
|
||||
index cd3cc4ea62f8956b87c711362520308f062de76e..75dd52f5eb3e6a06cd6185d590ba20e17b7e4e1d 100644
|
||||
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.h
|
||||
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
|
||||
@@ -82,6 +82,8 @@ class CORE_EXPORT LocalFrameClientImpl final : public LocalFrameClient {
|
||||
@@ -83,6 +83,8 @@ class CORE_EXPORT LocalFrameClientImpl final : public LocalFrameClient {
|
||||
|
||||
void DidCreateScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) override;
|
||||
@@ -123,10 +123,10 @@ index 5d2c6425dd568b663ff68a31da510784ecf103ab..f1215d23c679aeec484e6f64c41b6df7
|
||||
int32_t world_id) override;
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
|
||||
index 84e9886e4a27b21ee6d25186a2a7e4039bb81e18..33b23b8d913f94022966dc14e6978dc0481ea231 100644
|
||||
index ca0c223d248a2ae247429059be8a6a139006e55b..20439262e08e74318035460a0f92c981fa6758e1 100644
|
||||
--- a/third_party/blink/renderer/core/loader/empty_clients.h
|
||||
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
|
||||
@@ -374,6 +374,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient {
|
||||
@@ -405,6 +405,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient {
|
||||
|
||||
void DidCreateScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) override {}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user