mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
338 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dae224619f | ||
|
|
9110380ef6 | ||
|
|
00dff390ac | ||
|
|
16d3917334 | ||
|
|
2cc843737b | ||
|
|
fe248d310d | ||
|
|
b2ea9fd381 | ||
|
|
55a293cfaf | ||
|
|
e8ba44c632 | ||
|
|
ddab5ac3fc | ||
|
|
347f18a634 | ||
|
|
a2332646fa | ||
|
|
cdf312e16f | ||
|
|
445099073a | ||
|
|
05eb145d2b | ||
|
|
c16e94ebbf | ||
|
|
48be5c3387 | ||
|
|
9fd781073f | ||
|
|
c25edb5fdd | ||
|
|
cf6df4ae36 | ||
|
|
6729edf590 | ||
|
|
2415813313 | ||
|
|
136c539836 | ||
|
|
a76f2a07a9 | ||
|
|
8b6aecc6f0 | ||
|
|
3186a246a5 | ||
|
|
15ff515437 | ||
|
|
d3a24c24af | ||
|
|
f4a51e2adc | ||
|
|
cdc897c745 | ||
|
|
8c842552b0 | ||
|
|
e918fe6030 | ||
|
|
d9e0025429 | ||
|
|
5fc4a4768e | ||
|
|
cf54123ec6 | ||
|
|
948aa12af0 | ||
|
|
5fdf37eac9 | ||
|
|
1dcf237c8d | ||
|
|
8285172412 | ||
|
|
5735cb3cb0 | ||
|
|
1adea9e34e | ||
|
|
0cb6a2ea8a | ||
|
|
48e9e34acd | ||
|
|
1433180472 | ||
|
|
48a1117cbf | ||
|
|
b213c345bf | ||
|
|
46b8dcfb03 | ||
|
|
5c48a94b48 | ||
|
|
22df8eadbf | ||
|
|
8520fa8e6f | ||
|
|
517493a1f3 | ||
|
|
c10c449258 | ||
|
|
5026e7823e | ||
|
|
c063a2d9fe | ||
|
|
c46620d4ba | ||
|
|
92f20955bd | ||
|
|
762ecfef24 | ||
|
|
ea96f4c176 | ||
|
|
d085de4787 | ||
|
|
dd7a8da6be | ||
|
|
a4ded39e61 | ||
|
|
d3ff2033ed | ||
|
|
005b193307 | ||
|
|
369269c475 | ||
|
|
339eccfc42 | ||
|
|
65c8415bac | ||
|
|
30be3150ff | ||
|
|
7d9ce1379f | ||
|
|
27d1f6a360 | ||
|
|
be46387f22 | ||
|
|
fe160165b6 | ||
|
|
be1c18096b | ||
|
|
2e3b05bcad | ||
|
|
54657fb667 | ||
|
|
c83d65eab0 | ||
|
|
3daad4fd40 | ||
|
|
b4a79672cf | ||
|
|
a71c610747 | ||
|
|
b1366421d1 | ||
|
|
f2696fb431 | ||
|
|
25c536d355 | ||
|
|
6530067b85 | ||
|
|
8626aa29d1 | ||
|
|
9e0a182821 | ||
|
|
2d8fa4d225 | ||
|
|
5a7ba8ba8c | ||
|
|
ede6b45bc1 | ||
|
|
1000ddfcab | ||
|
|
63110a7159 | ||
|
|
308ef699e6 | ||
|
|
764b2a6041 | ||
|
|
5009694dc9 | ||
|
|
c07016e1d1 | ||
|
|
7c5a346464 | ||
|
|
4f54cc0d18 | ||
|
|
520e7077d4 | ||
|
|
eb41f5b70e | ||
|
|
23afedcea8 | ||
|
|
9bc6e1a584 | ||
|
|
89b08817a9 | ||
|
|
cb6a22a7a4 | ||
|
|
74c53ad0c3 | ||
|
|
88235cb2bc | ||
|
|
f8b4dc98b9 | ||
|
|
79108c7acd | ||
|
|
b0273b96c1 | ||
|
|
f7dd18466b | ||
|
|
80175c5828 | ||
|
|
2a36199b35 | ||
|
|
f5c2bab02b | ||
|
|
acca212814 | ||
|
|
ebbaa3b352 | ||
|
|
c42ed97535 | ||
|
|
cec707ce67 | ||
|
|
0080a61c1d | ||
|
|
2f543c3fd4 | ||
|
|
f6da3b43c2 | ||
|
|
6029315e1a | ||
|
|
f6748f9795 | ||
|
|
6500bcbb32 | ||
|
|
a75617bff1 | ||
|
|
3ef74abfe4 | ||
|
|
70c534fd14 | ||
|
|
52bacd38a9 | ||
|
|
dbd18d8562 | ||
|
|
4fcc0884f8 | ||
|
|
ee19e5ee48 | ||
|
|
629d8913f6 | ||
|
|
92bff00d43 | ||
|
|
e38a0a67c6 | ||
|
|
82da4b0090 | ||
|
|
6dd33b75b2 | ||
|
|
a96f42ce86 | ||
|
|
efa70131e2 | ||
|
|
6fdf350bea | ||
|
|
8d8fcd88f5 | ||
|
|
ad98f4707f | ||
|
|
4576d9d23e | ||
|
|
64c70c420f | ||
|
|
b491a4c82f | ||
|
|
c5b517d89f | ||
|
|
9c4e3b67fb | ||
|
|
93068cfab5 | ||
|
|
c8e4cc29c0 | ||
|
|
9eaa9de3b4 | ||
|
|
664a452fb6 | ||
|
|
54b44584fa | ||
|
|
1295ba0ffc | ||
|
|
02ac33c4cd | ||
|
|
87050d75b8 | ||
|
|
00d0265782 | ||
|
|
c74b9ff312 | ||
|
|
b6a12a53e3 | ||
|
|
06b3b49214 | ||
|
|
c556ccac08 | ||
|
|
fb539f15d0 | ||
|
|
57d088517c | ||
|
|
fcad531f2e | ||
|
|
eb955af459 | ||
|
|
9dee1183f9 | ||
|
|
9891ff14a7 | ||
|
|
22abbf76fb | ||
|
|
90a3e7f927 | ||
|
|
55c57808fb | ||
|
|
ee33374675 | ||
|
|
6a1726576d | ||
|
|
64e33002f8 | ||
|
|
fb40065c1e | ||
|
|
ce6a71e936 | ||
|
|
57e3c25378 | ||
|
|
1546cb6e6c | ||
|
|
f39ba9281c | ||
|
|
8b7631228f | ||
|
|
4a2f41ee58 | ||
|
|
7379e5eb36 | ||
|
|
26f981fa3e | ||
|
|
99c0a723fd | ||
|
|
94ca57e296 | ||
|
|
1dcb8a370e | ||
|
|
e6f781f403 | ||
|
|
b8372f20a0 | ||
|
|
92222c874f | ||
|
|
c30303207a | ||
|
|
873872a32b | ||
|
|
5554de0237 | ||
|
|
ee0e15a52e | ||
|
|
6d4995ec17 | ||
|
|
8d86d84ff5 | ||
|
|
fd8eb3de1b | ||
|
|
4d89174b41 | ||
|
|
49e62f1261 | ||
|
|
e6802bf935 | ||
|
|
ad776d6113 | ||
|
|
f533c44912 | ||
|
|
63b35403ef | ||
|
|
399032252f | ||
|
|
dd7aeda6fb | ||
|
|
3b2db5f168 | ||
|
|
c1075debf3 | ||
|
|
88ff3a6a9a | ||
|
|
8007d01874 | ||
|
|
c2da4ec2bc | ||
|
|
aa9da78edb | ||
|
|
352ac21413 | ||
|
|
96131af5d3 | ||
|
|
f9c6f9af83 | ||
|
|
00d65eb9ac | ||
|
|
a9983c1d06 | ||
|
|
c2c1b22a31 | ||
|
|
501ac15b1d | ||
|
|
be43996d35 | ||
|
|
32194f0f71 | ||
|
|
e43a25724c | ||
|
|
5513e66982 | ||
|
|
10c4931477 | ||
|
|
aad1c0d493 | ||
|
|
34f1bc0e82 | ||
|
|
fb990ba1eb | ||
|
|
517b174c3c | ||
|
|
c83de755c1 | ||
|
|
aab5ea5f9d | ||
|
|
7093cd75cb | ||
|
|
2a28ca226f | ||
|
|
4820dee980 | ||
|
|
cb7c16cb2d | ||
|
|
11de995d38 | ||
|
|
ea889b423d | ||
|
|
655b614ecd | ||
|
|
cbeae20438 | ||
|
|
8699124397 | ||
|
|
ec13a0b0e6 | ||
|
|
e92d92d7eb | ||
|
|
dd16d68e96 | ||
|
|
90b5ba3bed | ||
|
|
29749f3dc6 | ||
|
|
f797159fbe | ||
|
|
b62bbfda4e | ||
|
|
db8644ee7a | ||
|
|
04aafcc5ef | ||
|
|
ff128a32d9 | ||
|
|
a9a90fa1b6 | ||
|
|
7cdd132d18 | ||
|
|
93d7968d64 | ||
|
|
cd09a54365 | ||
|
|
eaa5d372fb | ||
|
|
6669abf38d | ||
|
|
740dcc5c16 | ||
|
|
fcce2b16d5 | ||
|
|
94111c9d5c | ||
|
|
8b9d0092cb | ||
|
|
0c1f762119 | ||
|
|
a11a234eac | ||
|
|
fc9a197f6c | ||
|
|
61117a11a1 | ||
|
|
7132f36ddd | ||
|
|
d1bd9afbbf | ||
|
|
1e983e2a6e | ||
|
|
c5db7a9013 | ||
|
|
a48968c1ce | ||
|
|
d313ddbd3d | ||
|
|
6ad47322fa | ||
|
|
93b1d2d932 | ||
|
|
17615654e8 | ||
|
|
c6267d9fb0 | ||
|
|
52890e9efd | ||
|
|
7668507c9d | ||
|
|
439e83de6c | ||
|
|
08ff1c2cbf | ||
|
|
81c143318b | ||
|
|
c8f3324610 | ||
|
|
60650abf09 | ||
|
|
e11953b0e6 | ||
|
|
41646d1168 | ||
|
|
42936b07fe | ||
|
|
985f1b5c04 | ||
|
|
ac49e6af4a | ||
|
|
69f1c1b083 | ||
|
|
f17e6ae318 | ||
|
|
4f739d7837 | ||
|
|
590858a38d | ||
|
|
edb7413bae | ||
|
|
e223b4db94 | ||
|
|
08e9aea940 | ||
|
|
66c458a353 | ||
|
|
6a9cada98c | ||
|
|
d783e944d3 | ||
|
|
641260bc32 | ||
|
|
bc508c6113 | ||
|
|
ec6cd0053e | ||
|
|
8e1160fde4 | ||
|
|
320bea4c28 | ||
|
|
59ab79417d | ||
|
|
423172775e | ||
|
|
4703dc0a1d | ||
|
|
481b774fd7 | ||
|
|
acbd643e2a | ||
|
|
97929eab5f | ||
|
|
1c29734c91 | ||
|
|
8179349625 | ||
|
|
a17e48061a | ||
|
|
40e76dca07 | ||
|
|
6e43b0bcbf | ||
|
|
4519bc3cd1 | ||
|
|
2b897c8ad8 | ||
|
|
0cabff0a21 | ||
|
|
888ac65c72 | ||
|
|
c0e72bd335 | ||
|
|
ced2e8779f | ||
|
|
9693fb9a37 | ||
|
|
9a63d96e83 | ||
|
|
7b5deb2f3a | ||
|
|
8651c5d02d | ||
|
|
639f03977f | ||
|
|
9cc1b55663 | ||
|
|
3ee6326b93 | ||
|
|
39c3ff5292 | ||
|
|
4aa9455613 | ||
|
|
c5ad7ed0cd | ||
|
|
c3abbdefdd | ||
|
|
bd85b1d028 | ||
|
|
fa464286ee | ||
|
|
d63980edeb | ||
|
|
1c95d30c0f | ||
|
|
fc7ea75009 | ||
|
|
ceebae170e | ||
|
|
cce27a0961 | ||
|
|
10424c0149 | ||
|
|
a545cd3790 | ||
|
|
461db8f1ab | ||
|
|
d2989de5d9 | ||
|
|
385d0f590f | ||
|
|
64ba8feb93 | ||
|
|
a6ab702ae4 | ||
|
|
62711e23ad | ||
|
|
3cb06457c4 | ||
|
|
e3fe80e0e8 | ||
|
|
b24cfe17bc | ||
|
|
eca1098b55 |
@@ -45,7 +45,7 @@ executors:
|
||||
type: enum
|
||||
enum: ["medium", "xlarge", "2xlarge+"]
|
||||
docker:
|
||||
- image: electron.azurecr.io/build:d818f06a9b1540c7fd38f75ad5a2c493dd6843b6
|
||||
- image: ghcr.io/electron/build:27db4a3e3512bfd2e47f58cea69922da0835f1d9
|
||||
resource_class: << parameters.size >>
|
||||
|
||||
macos:
|
||||
|
||||
59
.devcontainer/README.md
Normal file
59
.devcontainer/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Electron Dev on Codespaces
|
||||
|
||||
Welcome to the Codespaces Electron Developer Environment.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Upon creation of your codespace you should have [build tools](https://github.com/electron/build-tools) installed and an initialized gclient checkout of Electron. In order to build electron you'll need to run the following commands.
|
||||
|
||||
```bash
|
||||
e sync -vv
|
||||
e build
|
||||
```
|
||||
|
||||
The initial sync will take approximately ~30 minutes and the build will take ~8 minutes. Incremental syncs and incremental builds are substantially quicker.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
Codespaces doesn't lean very well into gclient based checkouts, the directory structure is slightly strange. There are two locations for the `electron` checkout that both map to the same files under the hood.
|
||||
|
||||
```graphql
|
||||
# Primary gclient checkout container
|
||||
/workspaces/gclient/*
|
||||
└─ src/* - # Chromium checkout
|
||||
└─ electron - # Electron checkout
|
||||
# Symlinked Electron checkout (identical to the above)
|
||||
/workspaces/electron
|
||||
```
|
||||
|
||||
## Goma
|
||||
|
||||
If you are a maintainer [with Goma access](../docs/development/goma.md) it should be automatically configured and authenticated when you spin up a new codespaces instance. You can validate this by checking `e d goma_auth info` or by checking that your build-tools configuration has a goma mode of `cluster`.
|
||||
|
||||
## Running Electron
|
||||
|
||||
You can run Electron in a few ways. If you just want to see if it launches:
|
||||
|
||||
```bash
|
||||
# Enter an interactive JS prompt headlessly
|
||||
xvfb-run e start -i
|
||||
```
|
||||
|
||||
But if you want to actually see Electron you will need to use the built-in VNC capability. If you click "Ports" in codespaces and then open the `VNC web client` forwarded port you should see a web based VNC portal in your browser. When you are asked for a password use `builduser`.
|
||||
|
||||
Once in the VNC UI you can open `Applications -> System -> XTerm` which will open a VNC based terminal app and then you can run `e start` like normal and Electron will open in your VNC session.
|
||||
|
||||
## Running Tests
|
||||
|
||||
You run tests via build-tools and `xvfb`.
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
xvfb-run e test
|
||||
|
||||
# Run the main process tests
|
||||
xvfb-run e test --runners=main
|
||||
|
||||
# Run the old remote tests
|
||||
xvfb-run e test --runners=remote
|
||||
```
|
||||
43
.devcontainer/devcontainer.json
Normal file
43
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "buildtools",
|
||||
"onCreateCommand": ".devcontainer/on-create-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"
|
||||
],
|
||||
"settings": {
|
||||
"[gn]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"editor.tabSize": 2,
|
||||
"bashBeautify.tabSize": 2
|
||||
},
|
||||
"forwardPorts": [8088, 6080, 5901],
|
||||
"portsAttributes": {
|
||||
"8088": {
|
||||
"label": "Goma Control Panel",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"6080": {
|
||||
"label": "VNC web client (noVNC)",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"5901": {
|
||||
"label": "VNC TCP port",
|
||||
"onAutoForward": "silent"
|
||||
}
|
||||
},
|
||||
"hostRequirements": {
|
||||
"storage": "32gb",
|
||||
"cpus": 8
|
||||
},
|
||||
"remoteUser": "builduser"
|
||||
}
|
||||
19
.devcontainer/docker-compose.yml
Normal file
19
.devcontainer/docker-compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
buildtools:
|
||||
image: ghcr.io/electron/devcontainer:27db4a3e3512bfd2e47f58cea69922da0835f1d9
|
||||
|
||||
volumes:
|
||||
- ..:/workspaces/gclient/src/electron:cached
|
||||
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
command: /bin/sh -c "while sleep 1000; do :; done"
|
||||
|
||||
user: builduser
|
||||
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
74
.devcontainer/on-create-command.sh
Executable file
74
.devcontainer/on-create-command.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
buildtools=$HOME/.electron_build_tools
|
||||
gclient_root=/workspaces/gclient
|
||||
buildtools_configs=/workspaces/buildtools-configs
|
||||
|
||||
export PATH="$PATH:$buildtools/src"
|
||||
|
||||
# Create the persisted buildtools config folder
|
||||
mkdir -p $buildtools_configs
|
||||
rm -f $buildtools/configs
|
||||
ln -s $buildtools_configs $buildtools/configs
|
||||
|
||||
# Write the gclient config if it does not already exist
|
||||
if [ ! -f $gclient_root/.gclient ]; then
|
||||
echo "solutions = [
|
||||
{ \"name\" : \"src/electron\",
|
||||
\"url\" : \"https://github.com/electron/electron\",
|
||||
\"deps_file\" : \"DEPS\",
|
||||
\"managed\" : False,
|
||||
\"custom_deps\" : {
|
||||
},
|
||||
\"custom_vars\": {},
|
||||
},
|
||||
]
|
||||
" >$gclient_root/.gclient
|
||||
fi
|
||||
|
||||
# Write the default buildtools config file if it does
|
||||
# not already exist
|
||||
if [ ! -f $buildtools/configs/evm.testing.json ]; then
|
||||
write_config() {
|
||||
echo "
|
||||
{
|
||||
\"root\": \"/workspaces/gclient\",
|
||||
\"goma\": \"$1\",
|
||||
\"gen\": {
|
||||
\"args\": [
|
||||
\"import(\\\"//electron/build/args/testing.gn\\\")\",
|
||||
\"import(\\\"/home/builduser/.electron_build_tools/third_party/goma.gn\\\")\"
|
||||
],
|
||||
\"out\": \"Testing\"
|
||||
},
|
||||
\"env\": {
|
||||
\"CHROMIUM_BUILDTOOLS_PATH\": \"/workspaces/gclient/src/buildtools\",
|
||||
\"GIT_CACHE_PATH\": \"/workspaces/gclient/.git-cache\"
|
||||
},
|
||||
\"remotes\": {
|
||||
\"electron\": {
|
||||
\"origin\": \"https://github.com/electron/electron.git\"
|
||||
}
|
||||
}
|
||||
}
|
||||
" >$buildtools/configs/evm.testing.json
|
||||
}
|
||||
|
||||
# Start out as cache only
|
||||
write_config cache-only
|
||||
|
||||
e use testing
|
||||
|
||||
# Attempt to auth to the goma service via codespaces tokens
|
||||
# if it works we can use the goma cluster
|
||||
export NOTGOMA_CODESPACES_TOKEN=$GITHUB_TOKEN
|
||||
if e d goma_auth login; then
|
||||
write_config cluster
|
||||
fi
|
||||
else
|
||||
# Even if the config file existed we still need to re-auth with the goma
|
||||
# cluster
|
||||
NOTGOMA_CODESPACES_TOKEN=$GITHUB_TOKEN e d goma_auth login || true
|
||||
fi
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -16,3 +16,4 @@ DEPS @electron/wg-upgrades
|
||||
/lib/browser/guest-view-manager.ts @electron/wg-security
|
||||
/lib/browser/guest-window-proxy.ts @electron/wg-security
|
||||
/lib/browser/rpc-server.ts @electron/wg-security
|
||||
/lib/renderer/security-warnings.ts @electron/wg-security
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -37,4 +37,4 @@ body:
|
||||
label: Additional Information
|
||||
description: Add any other context about the problem here.
|
||||
validations:
|
||||
required: true
|
||||
required: false
|
||||
|
||||
29
BUILD.gn
29
BUILD.gn
@@ -187,6 +187,12 @@ action("electron_js2c") {
|
||||
rebase_path(sources, root_build_dir)
|
||||
}
|
||||
|
||||
action("generate_config_gypi") {
|
||||
outputs = [ "$root_gen_dir/config.gypi" ]
|
||||
script = "script/generate-config-gypi.py"
|
||||
args = rebase_path(outputs) + [ target_cpu ]
|
||||
}
|
||||
|
||||
target_gen_default_app_js = "$target_gen_dir/js/default_app"
|
||||
|
||||
typescript_build("default_app_js") {
|
||||
@@ -307,6 +313,23 @@ action("electron_fuses") {
|
||||
args = rebase_path(outputs)
|
||||
}
|
||||
|
||||
action("electron_generate_node_defines") {
|
||||
script = "build/generate_node_defines.py"
|
||||
|
||||
inputs = [
|
||||
"//third_party/electron_node/src/tracing/trace_event_common.h",
|
||||
"//third_party/electron_node/src/tracing/trace_event.h",
|
||||
"//third_party/electron_node/src/util.h",
|
||||
]
|
||||
|
||||
outputs = [
|
||||
"$target_gen_dir/push_and_undef_node_defines.h",
|
||||
"$target_gen_dir/pop_node_defines.h",
|
||||
]
|
||||
|
||||
args = [ rebase_path(target_gen_dir) ] + rebase_path(inputs)
|
||||
}
|
||||
|
||||
source_set("electron_lib") {
|
||||
configs += [ "//v8:external_startup_data" ]
|
||||
configs += [ "//third_party/electron_node:node_internals" ]
|
||||
@@ -318,6 +341,7 @@ source_set("electron_lib") {
|
||||
|
||||
deps = [
|
||||
":electron_fuses",
|
||||
":electron_generate_node_defines",
|
||||
":electron_js2c",
|
||||
":electron_version_header",
|
||||
":resources",
|
||||
@@ -329,6 +353,7 @@ source_set("electron_lib") {
|
||||
"//base/allocator:buildflags",
|
||||
"//chrome/app:command_ids",
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//components/autofill/core/common:features",
|
||||
"//components/certificate_transparency",
|
||||
"//components/language/core/browser",
|
||||
"//components/net_log",
|
||||
@@ -395,7 +420,6 @@ source_set("electron_lib") {
|
||||
]
|
||||
|
||||
include_dirs = [
|
||||
"chromium_src",
|
||||
".",
|
||||
"$target_gen_dir",
|
||||
|
||||
@@ -529,8 +553,9 @@ source_set("electron_lib") {
|
||||
"GLIB_DISABLE_DEPRECATION_WARNINGS",
|
||||
]
|
||||
|
||||
sources += filenames.lib_sources_nss
|
||||
sources += [
|
||||
"shell/browser/certificate_manager_model.cc",
|
||||
"shell/browser/certificate_manager_model.h",
|
||||
"shell/browser/ui/gtk/app_indicator_icon.cc",
|
||||
"shell/browser/ui/gtk/app_indicator_icon.h",
|
||||
"shell/browser/ui/gtk/app_indicator_icon_menu.cc",
|
||||
|
||||
4
DEPS
4
DEPS
@@ -15,9 +15,9 @@ gclient_gn_args = [
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'94.0.4606.81',
|
||||
'96.0.4664.45',
|
||||
'node_version':
|
||||
'v16.5.0',
|
||||
'v16.9.1',
|
||||
'nan_version':
|
||||
# The following commit hash of NAN is v2.14.2 with *only* changes to the
|
||||
# test suite. This should be updated to a specific tag when one becomes
|
||||
|
||||
@@ -1 +1 @@
|
||||
15.2.0
|
||||
16.0.0
|
||||
@@ -60,7 +60,6 @@ npm start
|
||||
- [electronjs.org/community#boilerplates](https://electronjs.org/community#boilerplates) - Sample starter apps created by the community
|
||||
- [electron/simple-samples](https://github.com/electron/simple-samples) - Small applications with ideas for taking them further
|
||||
- [electron/electron-api-demos](https://github.com/electron/electron-api-demos) - An Electron app that teaches you how to use Electron
|
||||
- [hokein/electron-sample-apps](https://github.com/hokein/electron-sample-apps) - Small demo apps for the various Electron APIs
|
||||
|
||||
## Programmatic usage
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ build_script:
|
||||
}
|
||||
}
|
||||
}
|
||||
- if "%RUN_GCLIENT_SYNC%"=="true" ( gclient sync --with_branch_heads --with_tags --ignore_locks)
|
||||
- if "%RUN_GCLIENT_SYNC%"=="true" ( gclient sync )
|
||||
- ps: >-
|
||||
if ($env:SAVE_GCLIENT_SRC -eq 'true') {
|
||||
# archive current source for future use
|
||||
|
||||
@@ -2,7 +2,7 @@ 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 = 98
|
||||
node_module_version = 99
|
||||
|
||||
v8_promise_internal_field_count = 1
|
||||
v8_typed_array_max_size_in_heap = 0
|
||||
|
||||
34
build/generate_node_defines.py
Executable file
34
build/generate_node_defines.py
Executable file
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
DEFINE_EXTRACT_REGEX = re.compile('^ *# *define (\w*)', re.MULTILINE)
|
||||
|
||||
def main(outDir, headers):
|
||||
defines = []
|
||||
for filename in headers:
|
||||
with open(filename, 'r') as f:
|
||||
content = f.read()
|
||||
defines += read_defines(content)
|
||||
|
||||
push_and_undef = ''
|
||||
for define in defines:
|
||||
push_and_undef += '#pragma push_macro("%s")\n' % define
|
||||
push_and_undef += '#undef %s\n' % define
|
||||
with open(os.path.join(outDir, 'push_and_undef_node_defines.h'), 'w') as o:
|
||||
o.write(push_and_undef)
|
||||
|
||||
pop = ''
|
||||
for define in defines:
|
||||
pop += '#pragma pop_macro("%s")\n' % define
|
||||
with open(os.path.join(outDir, 'pop_node_defines.h'), 'w') as o:
|
||||
o.write(pop)
|
||||
|
||||
def read_defines(content):
|
||||
defines = []
|
||||
for match in DEFINE_EXTRACT_REGEX.finditer(content):
|
||||
defines.append(match.group(1))
|
||||
return defines
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1], sys.argv[2:])
|
||||
@@ -5,8 +5,8 @@ template("npm_action") {
|
||||
|
||||
action("npm_pre_flight_" + target_name) {
|
||||
inputs = [
|
||||
"package.json",
|
||||
"yarn.lock",
|
||||
"//electron/package.json",
|
||||
"//electron/yarn.lock",
|
||||
]
|
||||
script = "//electron/build/npm-run.py"
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ template("webpack_build") {
|
||||
rebase_path("$target_gen_dir/buildflags/buildflags.h"),
|
||||
"--env.mode=" + mode,
|
||||
]
|
||||
deps += [ "buildflags" ]
|
||||
deps += [ "//electron/buildflags" ]
|
||||
|
||||
outputs = [ invoker.out_file ]
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ static_library("chrome") {
|
||||
sources = [
|
||||
"//chrome/browser/accessibility/accessibility_ui.cc",
|
||||
"//chrome/browser/accessibility/accessibility_ui.h",
|
||||
"//chrome/browser/app_mode/app_mode_utils.cc",
|
||||
"//chrome/browser/app_mode/app_mode_utils.h",
|
||||
"//chrome/browser/browser_features.cc",
|
||||
"//chrome/browser/browser_features.h",
|
||||
"//chrome/browser/browser_process.cc",
|
||||
"//chrome/browser/browser_process.h",
|
||||
"//chrome/browser/devtools/devtools_contents_resizing_strategy.cc",
|
||||
@@ -25,6 +29,7 @@ static_library("chrome") {
|
||||
"//chrome/browser/devtools/devtools_eye_dropper.h",
|
||||
"//chrome/browser/devtools/devtools_file_system_indexer.cc",
|
||||
"//chrome/browser/devtools/devtools_file_system_indexer.h",
|
||||
"//chrome/browser/devtools/devtools_settings.h",
|
||||
"//chrome/browser/extensions/global_shortcut_listener.cc",
|
||||
"//chrome/browser/extensions/global_shortcut_listener.h",
|
||||
"//chrome/browser/icon_loader.cc",
|
||||
@@ -47,6 +52,23 @@ static_library("chrome") {
|
||||
"//chrome/browser/predictors/proxy_lookup_client_impl.h",
|
||||
"//chrome/browser/predictors/resolve_host_client_impl.cc",
|
||||
"//chrome/browser/predictors/resolve_host_client_impl.h",
|
||||
"//chrome/browser/process_singleton.h",
|
||||
"//chrome/browser/ui/browser_dialogs.cc",
|
||||
"//chrome/browser/ui/browser_dialogs.h",
|
||||
"//chrome/browser/ui/exclusive_access/exclusive_access_bubble_type.cc",
|
||||
"//chrome/browser/ui/exclusive_access/exclusive_access_bubble_type.h",
|
||||
"//chrome/browser/ui/exclusive_access/exclusive_access_controller_base.cc",
|
||||
"//chrome/browser/ui/exclusive_access/exclusive_access_controller_base.h",
|
||||
"//chrome/browser/ui/exclusive_access/exclusive_access_manager.cc",
|
||||
"//chrome/browser/ui/exclusive_access/exclusive_access_manager.h",
|
||||
"//chrome/browser/ui/exclusive_access/fullscreen_controller.cc",
|
||||
"//chrome/browser/ui/exclusive_access/fullscreen_controller.h",
|
||||
"//chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.cc",
|
||||
"//chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.h",
|
||||
"//chrome/browser/ui/exclusive_access/keyboard_lock_controller.cc",
|
||||
"//chrome/browser/ui/exclusive_access/keyboard_lock_controller.h",
|
||||
"//chrome/browser/ui/exclusive_access/mouse_lock_controller.cc",
|
||||
"//chrome/browser/ui/exclusive_access/mouse_lock_controller.h",
|
||||
"//chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc",
|
||||
"//chrome/browser/ui/views/autofill/autofill_popup_view_utils.h",
|
||||
"//chrome/browser/ui/views/eye_dropper/eye_dropper.cc",
|
||||
@@ -57,6 +79,10 @@ static_library("chrome") {
|
||||
"//extensions/browser/app_window/size_constraints.h",
|
||||
]
|
||||
|
||||
if (is_posix) {
|
||||
sources += [ "//chrome/browser/process_singleton_posix.cc" ]
|
||||
}
|
||||
|
||||
if (is_mac) {
|
||||
sources += [
|
||||
"//chrome/browser/extensions/global_shortcut_listener_mac.h",
|
||||
@@ -65,6 +91,7 @@ static_library("chrome") {
|
||||
"//chrome/browser/media/webrtc/system_media_capture_permissions_mac.h",
|
||||
"//chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm",
|
||||
"//chrome/browser/media/webrtc/window_icon_util_mac.mm",
|
||||
"//chrome/browser/process_singleton_mac.mm",
|
||||
"//chrome/browser/ui/views/eye_dropper/eye_dropper_view_mac.h",
|
||||
"//chrome/browser/ui/views/eye_dropper/eye_dropper_view_mac.mm",
|
||||
]
|
||||
@@ -76,6 +103,7 @@ static_library("chrome") {
|
||||
"//chrome/browser/extensions/global_shortcut_listener_win.h",
|
||||
"//chrome/browser/icon_loader_win.cc",
|
||||
"//chrome/browser/media/webrtc/window_icon_util_win.cc",
|
||||
"//chrome/browser/process_singleton_win.cc",
|
||||
"//chrome/browser/ui/frame/window_frame_util.h",
|
||||
"//chrome/browser/ui/view_ids.h",
|
||||
"//chrome/browser/win/chrome_process_finder.cc",
|
||||
@@ -87,7 +115,7 @@ static_library("chrome") {
|
||||
}
|
||||
|
||||
if (is_linux) {
|
||||
sources += [ "//chrome/browser/media/webrtc/window_icon_util_linux.cc" ]
|
||||
sources += [ "//chrome/browser/media/webrtc/window_icon_util_ozone.cc" ]
|
||||
}
|
||||
|
||||
if (use_aura) {
|
||||
@@ -104,13 +132,13 @@ static_library("chrome") {
|
||||
"//components/keyed_service/content",
|
||||
"//components/paint_preview/buildflags",
|
||||
"//components/proxy_config",
|
||||
"//components/services/language_detection/public/mojom",
|
||||
"//content/public/browser",
|
||||
"//services/strings",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//chrome/browser:resource_prefetch_predictor_proto",
|
||||
"//chrome/services/speech:buildflags",
|
||||
"//components/optimization_guide/proto:optimization_guide_proto",
|
||||
]
|
||||
|
||||
@@ -120,16 +148,6 @@ static_library("chrome") {
|
||||
|
||||
if (is_linux) {
|
||||
sources += [ "//chrome/browser/icon_loader_auralinux.cc" ]
|
||||
if (use_x11 || use_ozone) {
|
||||
sources +=
|
||||
[ "//chrome/browser/extensions/global_shortcut_listener_linux.cc" ]
|
||||
}
|
||||
if (use_x11) {
|
||||
sources += [
|
||||
"//chrome/browser/extensions/global_shortcut_listener_x11.cc",
|
||||
"//chrome/browser/extensions/global_shortcut_listener_x11.h",
|
||||
]
|
||||
}
|
||||
if (use_ozone) {
|
||||
deps += [ "//ui/ozone" ]
|
||||
sources += [
|
||||
@@ -159,6 +177,7 @@ static_library("chrome") {
|
||||
|
||||
if (enable_desktop_capturer) {
|
||||
sources += [
|
||||
"//chrome/browser/media/webrtc/desktop_media_list.cc",
|
||||
"//chrome/browser/media/webrtc/desktop_media_list.h",
|
||||
"//chrome/browser/media/webrtc/desktop_media_list_base.cc",
|
||||
"//chrome/browser/media/webrtc/desktop_media_list_base.h",
|
||||
@@ -196,6 +215,13 @@ static_library("chrome") {
|
||||
"//chrome/browser/printing/printing_service.h",
|
||||
]
|
||||
|
||||
if (enable_oop_printing) {
|
||||
sources += [
|
||||
"//chrome/browser/printing/print_backend_service_manager.cc",
|
||||
"//chrome/browser/printing/print_backend_service_manager.h",
|
||||
]
|
||||
}
|
||||
|
||||
public_deps += [
|
||||
"//chrome/services/printing:lib",
|
||||
"//components/printing/browser",
|
||||
@@ -203,6 +229,7 @@ static_library("chrome") {
|
||||
"//components/services/print_compositor",
|
||||
"//components/services/print_compositor/public/cpp",
|
||||
"//components/services/print_compositor/public/mojom",
|
||||
"//printing/backend",
|
||||
]
|
||||
|
||||
deps += [
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PROCESS_SINGLETON_H_
|
||||
#define CHROME_BROWSER_PROCESS_SINGLETON_H_
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win/message_window.h"
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
namespace base {
|
||||
class CommandLine;
|
||||
}
|
||||
|
||||
// ProcessSingleton ----------------------------------------------------------
|
||||
//
|
||||
// This class allows different browser processes to communicate with
|
||||
// each other. It is named according to the user data directory, so
|
||||
// we can be sure that no more than one copy of the application can be
|
||||
// running at once with a given data directory.
|
||||
//
|
||||
// Implementation notes:
|
||||
// - the Windows implementation uses an invisible global message window;
|
||||
// - the Linux implementation uses a Unix domain socket in the user data dir.
|
||||
|
||||
class ProcessSingleton {
|
||||
public:
|
||||
enum NotifyResult {
|
||||
PROCESS_NONE,
|
||||
PROCESS_NOTIFIED,
|
||||
PROFILE_IN_USE,
|
||||
LOCK_ERROR,
|
||||
};
|
||||
|
||||
// Implement this callback to handle notifications from other processes. The
|
||||
// callback will receive the command line and directory with which the other
|
||||
// Chrome process was launched. Return true if the command line will be
|
||||
// handled within the current browser instance or false if the remote process
|
||||
// should handle it (i.e., because the current process is shutting down).
|
||||
using NotificationCallback = base::RepeatingCallback<bool(
|
||||
const base::CommandLine::StringVector& command_line,
|
||||
const base::FilePath& current_directory)>;
|
||||
|
||||
ProcessSingleton(const base::FilePath& user_data_dir,
|
||||
const NotificationCallback& notification_callback);
|
||||
~ProcessSingleton();
|
||||
|
||||
// Notify another process, if available. Otherwise sets ourselves as the
|
||||
// singleton instance. Returns PROCESS_NONE if we became the singleton
|
||||
// instance. Callers are guaranteed to either have notified an existing
|
||||
// process or have grabbed the singleton (unless the profile is locked by an
|
||||
// unreachable process).
|
||||
// TODO(brettw): Make the implementation of this method non-platform-specific
|
||||
// by making Linux re-use the Windows implementation.
|
||||
NotifyResult NotifyOtherProcessOrCreate();
|
||||
void StartListeningOnSocket();
|
||||
void OnBrowserReady();
|
||||
|
||||
// Sets ourself up as the singleton instance. Returns true on success. If
|
||||
// false is returned, we are not the singleton instance and the caller must
|
||||
// exit.
|
||||
// NOTE: Most callers should generally prefer NotifyOtherProcessOrCreate() to
|
||||
// this method, only callers for whom failure is preferred to notifying
|
||||
// another process should call this directly.
|
||||
bool Create();
|
||||
|
||||
// Clear any lock state during shutdown.
|
||||
void Cleanup();
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
static void DisablePromptForTesting();
|
||||
#endif
|
||||
#if defined(OS_WIN)
|
||||
// Called to query whether to kill a hung browser process that has visible
|
||||
// windows. Return true to allow killing the hung process.
|
||||
using ShouldKillRemoteProcessCallback = base::RepeatingCallback<bool()>;
|
||||
void OverrideShouldKillRemoteProcessCallbackForTesting(
|
||||
const ShouldKillRemoteProcessCallback& display_dialog_callback);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Notify another process, if available.
|
||||
// Returns true if another process was found and notified, false if we should
|
||||
// continue with the current process.
|
||||
// On Windows, Create() has to be called before this.
|
||||
NotifyResult NotifyOtherProcess();
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
// Exposed for testing. We use a timeout on Linux, and in tests we want
|
||||
// this timeout to be short.
|
||||
NotifyResult NotifyOtherProcessWithTimeout(
|
||||
const base::CommandLine& command_line,
|
||||
int retry_attempts,
|
||||
const base::TimeDelta& timeout,
|
||||
bool kill_unresponsive);
|
||||
NotifyResult NotifyOtherProcessWithTimeoutOrCreate(
|
||||
const base::CommandLine& command_line,
|
||||
int retry_attempts,
|
||||
const base::TimeDelta& timeout);
|
||||
void OverrideCurrentPidForTesting(base::ProcessId pid);
|
||||
void OverrideKillCallbackForTesting(
|
||||
const base::RepeatingCallback<void(int)>& callback);
|
||||
#endif
|
||||
|
||||
private:
|
||||
NotificationCallback notification_callback_; // Handler for notifications.
|
||||
|
||||
#if defined(OS_WIN)
|
||||
HWND remote_window_ = nullptr; // The HWND_MESSAGE of another browser.
|
||||
base::win::MessageWindow window_; // The message-only window.
|
||||
bool is_virtualized_ =
|
||||
false; // Stuck inside Microsoft Softricity VM environment.
|
||||
HANDLE lock_file_ = INVALID_HANDLE_VALUE;
|
||||
base::FilePath user_data_dir_;
|
||||
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
|
||||
#elif defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
// Start listening to the socket.
|
||||
void StartListening(int sock);
|
||||
|
||||
// Return true if the given pid is one of our child processes.
|
||||
// Assumes that the current pid is the root of all pids of the current
|
||||
// instance.
|
||||
bool IsSameChromeInstance(pid_t pid);
|
||||
|
||||
// Extract the process's pid from a symbol link path and if it is on
|
||||
// the same host, kill the process, unlink the lock file and return true.
|
||||
// If the process is part of the same chrome instance, unlink the lock file
|
||||
// and return true without killing it.
|
||||
// If the process is on a different host, return false.
|
||||
bool KillProcessByLockPath();
|
||||
|
||||
// Default function to kill a process, overridable by tests.
|
||||
void KillProcess(int pid);
|
||||
|
||||
// Allow overriding for tests.
|
||||
base::ProcessId current_pid_;
|
||||
|
||||
// Function to call when the other process is hung and needs to be killed.
|
||||
// Allows overriding for tests.
|
||||
base::RepeatingCallback<void(int)> kill_callback_;
|
||||
|
||||
// Path in file system to the socket.
|
||||
base::FilePath socket_path_;
|
||||
|
||||
// Path in file system to the lock.
|
||||
base::FilePath lock_path_;
|
||||
|
||||
// Path in file system to the cookie file.
|
||||
base::FilePath cookie_path_;
|
||||
|
||||
// Temporary directory to hold the socket.
|
||||
base::ScopedTempDir socket_dir_;
|
||||
|
||||
// Helper class for linux specific messages. LinuxWatcher is ref counted
|
||||
// because it posts messages between threads.
|
||||
class LinuxWatcher;
|
||||
scoped_refptr<LinuxWatcher> watcher_;
|
||||
int sock_ = -1;
|
||||
bool listen_on_ready_ = false;
|
||||
#endif
|
||||
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessSingleton);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_PROCESS_SINGLETON_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,315 +0,0 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/process_singleton.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include "base/base_paths.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/process/process_info.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/win/registry.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "chrome/browser/win/chrome_process_finder.h"
|
||||
#include "content/public/common/result_codes.h"
|
||||
#include "net/base/escape.h"
|
||||
#include "ui/gfx/win/hwnd_util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const char kLockfile[] = "lockfile";
|
||||
|
||||
// A helper class that acquires the given |mutex| while the AutoLockMutex is in
|
||||
// scope.
|
||||
class AutoLockMutex {
|
||||
public:
|
||||
explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
|
||||
DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
|
||||
DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
|
||||
}
|
||||
|
||||
~AutoLockMutex() {
|
||||
BOOL released = ::ReleaseMutex(mutex_);
|
||||
DPCHECK(released);
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE mutex_;
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
|
||||
};
|
||||
|
||||
// A helper class that releases the given |mutex| while the AutoUnlockMutex is
|
||||
// in scope and immediately re-acquires it when going out of scope.
|
||||
class AutoUnlockMutex {
|
||||
public:
|
||||
explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
|
||||
BOOL released = ::ReleaseMutex(mutex_);
|
||||
DPCHECK(released);
|
||||
}
|
||||
|
||||
~AutoUnlockMutex() {
|
||||
DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
|
||||
DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE mutex_;
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
|
||||
};
|
||||
|
||||
// Checks the visibility of the enumerated window and signals once a visible
|
||||
// window has been found.
|
||||
BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||
bool* result = reinterpret_cast<bool*>(param);
|
||||
*result = ::IsWindowVisible(window) != 0;
|
||||
// Stops enumeration if a visible window has been found.
|
||||
return !*result;
|
||||
}
|
||||
|
||||
bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
base::CommandLine::StringVector* parsed_command_line,
|
||||
base::FilePath* current_directory) {
|
||||
// We should have enough room for the shortest command (min_message_size)
|
||||
// and also be a multiple of wchar_t bytes. The shortest command
|
||||
// possible is L"START\0\0" (empty current directory and command line).
|
||||
static const int min_message_size = 7;
|
||||
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
|
||||
cds->cbData % sizeof(wchar_t) != 0) {
|
||||
LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We split the string into 4 parts on NULLs.
|
||||
DCHECK(cds->lpData);
|
||||
const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
|
||||
cds->cbData / sizeof(wchar_t));
|
||||
const std::wstring::size_type first_null = msg.find_first_of(L'\0');
|
||||
if (first_null == 0 || first_null == std::wstring::npos) {
|
||||
// no NULL byte, don't know what to do
|
||||
LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length()
|
||||
<< ", first null = " << first_null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decode the command, which is everything until the first NULL.
|
||||
if (msg.substr(0, first_null) == L"START") {
|
||||
// Another instance is starting parse the command line & do what it would
|
||||
// have done.
|
||||
VLOG(1) << "Handling STARTUP request from another process";
|
||||
const std::wstring::size_type second_null =
|
||||
msg.find_first_of(L'\0', first_null + 1);
|
||||
if (second_null == std::wstring::npos || first_null == msg.length() - 1 ||
|
||||
second_null == msg.length()) {
|
||||
LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
|
||||
"parts separated by NULLs";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current directory.
|
||||
*current_directory =
|
||||
base::FilePath(msg.substr(first_null + 1, second_null - first_null));
|
||||
|
||||
const std::wstring::size_type third_null =
|
||||
msg.find_first_of(L'\0', second_null + 1);
|
||||
if (third_null == std::wstring::npos || third_null == msg.length()) {
|
||||
LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
|
||||
"parts separated by NULLs";
|
||||
}
|
||||
|
||||
// Get command line.
|
||||
const std::wstring cmd_line =
|
||||
msg.substr(second_null + 1, third_null - second_null);
|
||||
*parsed_command_line = base::CommandLine::FromString(cmd_line).argv();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProcessLaunchNotification(
|
||||
const ProcessSingleton::NotificationCallback& notification_callback,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
LRESULT* result) {
|
||||
if (message != WM_COPYDATA)
|
||||
return false;
|
||||
|
||||
// Handle the WM_COPYDATA message from another process.
|
||||
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
|
||||
|
||||
base::CommandLine::StringVector parsed_command_line;
|
||||
base::FilePath current_directory;
|
||||
if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
||||
*result = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
*result = notification_callback.Run(parsed_command_line, current_directory)
|
||||
? TRUE
|
||||
: FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerminateAppWithError() {
|
||||
// TODO: This is called when the secondary process can't ping the primary
|
||||
// process. Need to find out what to do here.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
const NotificationCallback& notification_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
user_data_dir_(user_data_dir),
|
||||
should_kill_remote_process_callback_(
|
||||
base::BindRepeating(&TerminateAppWithError)) {
|
||||
// The user_data_dir may have not been created yet.
|
||||
base::CreateDirectoryAndGetError(user_data_dir, nullptr);
|
||||
}
|
||||
|
||||
ProcessSingleton::~ProcessSingleton() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (lock_file_ != INVALID_HANDLE_VALUE)
|
||||
::CloseHandle(lock_file_);
|
||||
}
|
||||
|
||||
// Code roughly based on Mozilla.
|
||||
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
if (is_virtualized_)
|
||||
return PROCESS_NOTIFIED; // We already spawned the process in this case.
|
||||
if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
|
||||
return LOCK_ERROR;
|
||||
} else if (!remote_window_) {
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
|
||||
case chrome::NOTIFY_SUCCESS:
|
||||
return PROCESS_NOTIFIED;
|
||||
case chrome::NOTIFY_FAILED:
|
||||
remote_window_ = NULL;
|
||||
return PROCESS_NONE;
|
||||
case chrome::NOTIFY_WINDOW_HUNG:
|
||||
// Fall through and potentially terminate the hung browser.
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD process_id = 0;
|
||||
DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
|
||||
if (!thread_id || !process_id) {
|
||||
remote_window_ = NULL;
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
base::Process process = base::Process::Open(process_id);
|
||||
|
||||
// The window is hung. Scan for every window to find a visible one.
|
||||
bool visible_window = false;
|
||||
::EnumThreadWindows(thread_id, &BrowserWindowEnumeration,
|
||||
reinterpret_cast<LPARAM>(&visible_window));
|
||||
|
||||
// If there is a visible browser window, ask the user before killing it.
|
||||
if (visible_window && !should_kill_remote_process_callback_.Run()) {
|
||||
// The user denied. Quit silently.
|
||||
return PROCESS_NOTIFIED;
|
||||
}
|
||||
|
||||
// Time to take action. Kill the browser process.
|
||||
process.Terminate(content::RESULT_CODE_HUNG, true);
|
||||
remote_window_ = NULL;
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
|
||||
ProcessSingleton::NotifyResult result = PROCESS_NONE;
|
||||
if (!Create()) {
|
||||
result = NotifyOtherProcess();
|
||||
if (result == PROCESS_NONE)
|
||||
result = PROFILE_IN_USE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProcessSingleton::StartListeningOnSocket() {}
|
||||
void ProcessSingleton::OnBrowserReady() {}
|
||||
|
||||
// Look for a Chrome instance that uses the same profile directory. If there
|
||||
// isn't one, create a message window with its title set to the profile
|
||||
// directory path.
|
||||
bool ProcessSingleton::Create() {
|
||||
static const wchar_t kMutexName[] = L"Local\\AtomProcessSingletonStartup!";
|
||||
|
||||
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
|
||||
if (!remote_window_) {
|
||||
// Make sure we will be the one and only process creating the window.
|
||||
// We use a named Mutex since we are protecting against multi-process
|
||||
// access. As documented, it's clearer to NOT request ownership on creation
|
||||
// since it isn't guaranteed we will get it. It is better to create it
|
||||
// without ownership and explicitly get the ownership afterward.
|
||||
base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
|
||||
if (!only_me.IsValid()) {
|
||||
DPLOG(FATAL) << "CreateMutex failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoLockMutex auto_lock_only_me(only_me.Get());
|
||||
|
||||
// We now own the mutex so we are the only process that can create the
|
||||
// window at this time, but we must still check if someone created it
|
||||
// between the time where we looked for it above and the time the mutex
|
||||
// was given to us.
|
||||
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
|
||||
if (!remote_window_) {
|
||||
// We have to make sure there is no Chrome instance running on another
|
||||
// machine that uses the same profile.
|
||||
base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
|
||||
lock_file_ =
|
||||
::CreateFile(lock_file_path.value().c_str(), GENERIC_WRITE,
|
||||
FILE_SHARE_READ, NULL, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
||||
DWORD error = ::GetLastError();
|
||||
LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
|
||||
error == ERROR_ALREADY_EXISTS)
|
||||
<< "Lock file exists but is writable.";
|
||||
LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
|
||||
<< "Lock file can not be created! Error code: " << error;
|
||||
|
||||
if (lock_file_ != INVALID_HANDLE_VALUE) {
|
||||
// Set the window's title to the path of our user data directory so
|
||||
// other Chrome instances can decide if they should forward to us.
|
||||
bool result =
|
||||
window_.CreateNamed(base::BindRepeating(&ProcessLaunchNotification,
|
||||
notification_callback_),
|
||||
user_data_dir_.value());
|
||||
|
||||
// NB: Ensure that if the primary app gets started as elevated
|
||||
// admin inadvertently, secondary windows running not as elevated
|
||||
// will still be able to send messages
|
||||
::ChangeWindowMessageFilterEx(window_.hwnd(), WM_COPYDATA, MSGFLT_ALLOW,
|
||||
NULL);
|
||||
CHECK(result && window_.hwnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return window_.hwnd() != NULL;
|
||||
}
|
||||
|
||||
void ProcessSingleton::Cleanup() {}
|
||||
|
||||
void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
|
||||
const ShouldKillRemoteProcessCallback& display_dialog_callback) {
|
||||
should_kill_remote_process_callback_ = display_dialog_callback;
|
||||
}
|
||||
@@ -59,10 +59,9 @@ an issue:
|
||||
* [Testing and Debugging](tutorial/application-debugging.md)
|
||||
* [Debugging the Main Process](tutorial/debugging-main-process.md)
|
||||
* [Debugging with Visual Studio Code](tutorial/debugging-vscode.md)
|
||||
* [Using Selenium and WebDriver](tutorial/using-selenium-and-webdriver.md)
|
||||
* [Testing on Headless CI Systems (Travis, Jenkins)](tutorial/testing-on-headless-ci.md)
|
||||
* [DevTools Extension](tutorial/devtools-extension.md)
|
||||
* [Automated Testing with a Custom Driver](tutorial/automated-testing-with-a-custom-driver.md)
|
||||
* [Automated Testing](tutorial/automated-testing.md)
|
||||
* [REPL](tutorial/repl.md)
|
||||
* [Distribution](tutorial/application-distribution.md)
|
||||
* [Supported Platforms](tutorial/support.md#supported-platforms)
|
||||
|
||||
@@ -36,10 +36,10 @@ Returns:
|
||||
* `launchInfo` Record<string, any> | [NotificationResponse](structures/notification-response.md) _macOS_
|
||||
|
||||
Emitted once, when Electron has finished initializing. On macOS, `launchInfo`
|
||||
holds the `userInfo` of the `NSUserNotification` or information from
|
||||
[`UNNotificationResponse`](structures/notification-response.md) that was used to open the
|
||||
application, if it was launched from Notification Center. You can also call
|
||||
`app.isReady()` to check if this event has already fired and `app.whenReady()`
|
||||
holds the `userInfo` of the [`NSUserNotification`](https://developer.apple.com/documentation/foundation/nsusernotification)
|
||||
or information from [`UNNotificationResponse`](https://developer.apple.com/documentation/usernotifications/unnotificationresponse)
|
||||
that was used to open the application, if it was launched from Notification Center.
|
||||
You can also call `app.isReady()` to check if this event has already fired and `app.whenReady()`
|
||||
to get a Promise that is fulfilled when Electron is initialized.
|
||||
|
||||
### Event: 'window-all-closed'
|
||||
@@ -483,6 +483,7 @@ Returns:
|
||||
* `event` Event
|
||||
* `argv` String[] - An array of the second instance's command line arguments
|
||||
* `workingDirectory` String - The second instance's working directory
|
||||
* `additionalData` unknown - A JSON object of additional data passed from the second instance
|
||||
|
||||
This event will be emitted inside the primary instance of your application
|
||||
when a second instance has been executed and calls `app.requestSingleInstanceLock()`.
|
||||
@@ -941,6 +942,8 @@ app.setJumpList([
|
||||
|
||||
### `app.requestSingleInstanceLock()`
|
||||
|
||||
* `additionalData` unknown (optional) - A JSON object containing additional data to send to the first instance.
|
||||
|
||||
Returns `Boolean`
|
||||
|
||||
The return value of this method indicates whether or not this instance of your
|
||||
@@ -966,12 +969,16 @@ starts:
|
||||
const { app } = require('electron')
|
||||
let myWindow = null
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
const additionalData = { myKey: 'myValue' }
|
||||
const gotTheLock = app.requestSingleInstanceLock(additionalData)
|
||||
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory, additionalData) => {
|
||||
// Print out data received from the second instance.
|
||||
console.log(additionalData)
|
||||
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (myWindow) {
|
||||
if (myWindow.isMinimized()) myWindow.restore()
|
||||
|
||||
@@ -17,10 +17,11 @@ win.loadURL('https://github.com')
|
||||
win.loadFile('index.html')
|
||||
```
|
||||
|
||||
## Frameless window
|
||||
## Window customization
|
||||
|
||||
To create a window without chrome, or a transparent window in arbitrary shape,
|
||||
you can use the [Frameless Window](frameless-window.md) API.
|
||||
The `BrowserWindow` class exposes various ways to modify the look and behavior of
|
||||
your app's windows. For more details, see the [Window Customization](../tutorial/window-customization.md)
|
||||
tutorial.
|
||||
|
||||
## Showing the window gracefully
|
||||
|
||||
@@ -184,7 +185,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
`true`.
|
||||
* `paintWhenInitiallyHidden` Boolean (optional) - Whether the renderer should be active when `show` is `false` and it has just been created. In order for `document.visibilityState` to work correctly on first load with `show: false` you should set this to `false`. Setting this to `false` will cause the `ready-to-show` event to not fire. Default is `true`.
|
||||
* `frame` Boolean (optional) - Specify `false` to create a
|
||||
[Frameless Window](frameless-window.md). Default is `true`.
|
||||
[frameless window](../tutorial/window-customization.md#create-frameless-windows). Default is `true`.
|
||||
* `parent` BrowserWindow (optional) - Specify parent window. Default is `null`.
|
||||
* `modal` Boolean (optional) - Whether this is a modal window. This only works when the
|
||||
window is a child window. Default is `false`.
|
||||
@@ -206,7 +207,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
transparent) and 1.0 (fully opaque). This is only implemented on Windows and macOS.
|
||||
* `darkTheme` Boolean (optional) - Forces using dark theme for the window, only works on
|
||||
some GTK+3 desktop environments. Default is `false`.
|
||||
* `transparent` Boolean (optional) - Makes the window [transparent](frameless-window.md#transparent-window).
|
||||
* `transparent` Boolean (optional) - Makes the window [transparent](../tutorial/window-customization.md#create-transparent-windows).
|
||||
Default is `false`. On Windows, does not work unless the window is frameless.
|
||||
* `type` String (optional) - The type of window, default is normal window. See more about
|
||||
this below.
|
||||
@@ -1696,7 +1697,7 @@ current window into a top-level window.
|
||||
|
||||
#### `win.getParentWindow()`
|
||||
|
||||
Returns `BrowserWindow` - The parent window.
|
||||
Returns `BrowserWindow | null` - The parent window or `null` if there is no parent.
|
||||
|
||||
#### `win.getChildWindows()`
|
||||
|
||||
|
||||
@@ -131,15 +131,15 @@ Returns `Object`:
|
||||
|
||||
Returns an Object containing `title` and `url` keys representing the bookmark in
|
||||
the clipboard. The `title` and `url` values will be empty strings when the
|
||||
bookmark is unavailable.
|
||||
bookmark is unavailable. The `title` value will always be empty on Windows.
|
||||
|
||||
### `clipboard.writeBookmark(title, url[, type])` _macOS_ _Windows_
|
||||
|
||||
* `title` String
|
||||
* `title` String - Unused on Windows
|
||||
* `url` String
|
||||
* `type` String (optional) - Can be `selection` or `clipboard`; default is 'clipboard'. `selection` is only available on Linux.
|
||||
|
||||
Writes the `title` and `url` into the clipboard as a bookmark.
|
||||
Writes the `title` (macOS only) and `url` into the clipboard as a bookmark.
|
||||
|
||||
**Note:** Most apps on Windows don't support pasting bookmarks into them so
|
||||
you can use `clipboard.write` to write both a bookmark and fallback text to the
|
||||
@@ -197,7 +197,7 @@ Returns `Boolean` - Whether the clipboard supports the specified `format`.
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const hasFormat = clipboard.has('<p>selection</p>')
|
||||
const hasFormat = clipboard.has('public/utf8-plain-text')
|
||||
console.log(hasFormat)
|
||||
// 'true' or 'false'
|
||||
```
|
||||
|
||||
@@ -99,7 +99,7 @@ the response.
|
||||
* `expirationDate` Double (optional) - The expiration date of the cookie as the number of
|
||||
seconds since the UNIX epoch. If omitted then the cookie becomes a session
|
||||
cookie and will not be retained between sessions.
|
||||
* `sameSite` String (optional) - The [Same Site](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies) policy to apply to this cookie. Can be `unspecified`, `no_restriction`, `lax` or `strict`. Default is `no_restriction`.
|
||||
* `sameSite` String (optional) - The [Same Site](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies) policy to apply to this cookie. Can be `unspecified`, `no_restriction`, `lax` or `strict`. Default is `lax`.
|
||||
|
||||
Returns `Promise<void>` - A promise which resolves when the cookie has been set
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ following projects:
|
||||
* [socorro](https://github.com/mozilla/socorro)
|
||||
* [mini-breakpad-server](https://github.com/electron/mini-breakpad-server)
|
||||
|
||||
> **Note:** Electron uses Crashpad, not Breakpad, to collect and upload
|
||||
> crashes, but for the time being, the [upload protocol is the same](https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/doc/overview_design.md#Upload-to-collection-server).
|
||||
|
||||
Or use a 3rd party hosted solution:
|
||||
|
||||
* [Backtrace](https://backtrace.io/electron/)
|
||||
@@ -26,49 +29,12 @@ Or use a 3rd party hosted solution:
|
||||
* [BugSplat](https://www.bugsplat.com/docs/platforms/electron)
|
||||
|
||||
Crash reports are stored temporarily before being uploaded in a directory
|
||||
underneath the app's user data directory (called 'Crashpad' on Windows and Mac,
|
||||
or 'Crash Reports' on Linux). You can override this directory by calling
|
||||
`app.setPath('crashDumps', '/path/to/crashes')` before starting the crash
|
||||
reporter.
|
||||
underneath the app's user data directory, called 'Crashpad'. You can override
|
||||
this directory by calling `app.setPath('crashDumps', '/path/to/crashes')`
|
||||
before starting the crash reporter.
|
||||
|
||||
On Windows and macOS, Electron uses
|
||||
[crashpad](https://chromium.googlesource.com/crashpad/crashpad/+/master/README.md)
|
||||
to monitor and report crashes. On Linux, Electron uses
|
||||
[breakpad](https://chromium.googlesource.com/breakpad/breakpad/+/master/). This
|
||||
is an implementation detail driven by Chromium, and it may change in future. In
|
||||
particular, crashpad is newer and will likely eventually replace breakpad on
|
||||
all platforms.
|
||||
|
||||
### Note about Node child processes on Linux
|
||||
|
||||
If you are using the Node.js `child_process` module and want to report crashes
|
||||
from those processes on Linux, there is an extra step you will need to take to
|
||||
properly initialize the crash reporter in the child process. This is not
|
||||
necessary on Mac or Windows, as those platforms use Crashpad, which
|
||||
automatically monitors child processes.
|
||||
|
||||
Since `require('electron')` is not available in Node child processes, the
|
||||
following APIs are available on the `process` object in Node child processes.
|
||||
Note that, on Linux, each Node child process has its own separate instance of
|
||||
the breakpad crash reporter. This is dissimilar to renderer child processes,
|
||||
which have a "stub" breakpad reporter which returns information to the main
|
||||
process for reporting.
|
||||
|
||||
#### `process.crashReporter.start(options)`
|
||||
|
||||
See [`crashReporter.start()`](#crashreporterstartoptions).
|
||||
|
||||
#### `process.crashReporter.getParameters()`
|
||||
|
||||
See [`crashReporter.getParameters()`](#crashreportergetparameters).
|
||||
|
||||
#### `process.crashReporter.addExtraParameter(key, value)`
|
||||
|
||||
See [`crashReporter.addExtraParameter(key, value)`](#crashreporteraddextraparameterkey-value).
|
||||
|
||||
#### `process.crashReporter.removeExtraParameter(key)`
|
||||
|
||||
See [`crashReporter.removeExtraParameter(key)`](#crashreporterremoveextraparameterkey).
|
||||
Electron uses [crashpad](https://chromium.googlesource.com/crashpad/crashpad/+/refs/heads/main/README.md)
|
||||
to monitor and report crashes.
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -186,12 +152,6 @@ names must be no longer than 39 bytes, and values must be no longer than 20320
|
||||
bytes. Keys with names longer than the maximum will be silently ignored. Key
|
||||
values longer than the maximum length will be truncated.
|
||||
|
||||
**Note:** On linux values that are longer than 127 bytes will be chunked into
|
||||
multiple keys, each 127 bytes in length. E.g. `addExtraParameter('foo', 'a'.repeat(130))`
|
||||
will result in two chunked keys `foo__1` and `foo__2`, the first will contain
|
||||
the first 127 bytes and the second will contain the remaining 3 bytes. On
|
||||
your crash reporting backend you should stitch together keys in this format.
|
||||
|
||||
### `crashReporter.removeExtraParameter(key)`
|
||||
|
||||
* `key` String - Parameter key, must be no longer than 39 bytes.
|
||||
@@ -203,6 +163,32 @@ will not include this parameter.
|
||||
|
||||
Returns `Record<String, String>` - The current 'extra' parameters of the crash reporter.
|
||||
|
||||
## In Node child processes
|
||||
|
||||
Since `require('electron')` is not available in Node child processes, the
|
||||
following APIs are available on the `process` object in Node child processes.
|
||||
|
||||
#### `process.crashReporter.start(options)`
|
||||
|
||||
See [`crashReporter.start()`](#crashreporterstartoptions).
|
||||
|
||||
Note that if the crash reporter is started in the main process, it will
|
||||
automatically monitor child processes, so it should not be started in the child
|
||||
process. Only use this method if the main process does not initialize the crash
|
||||
reporter.
|
||||
|
||||
#### `process.crashReporter.getParameters()`
|
||||
|
||||
See [`crashReporter.getParameters()`](#crashreportergetparameters).
|
||||
|
||||
#### `process.crashReporter.addExtraParameter(key, value)`
|
||||
|
||||
See [`crashReporter.addExtraParameter(key, value)`](#crashreporteraddextraparameterkey-value).
|
||||
|
||||
#### `process.crashReporter.removeExtraParameter(key)`
|
||||
|
||||
See [`crashReporter.removeExtraParameter(key)`](#crashreporterremoveextraparameterkey).
|
||||
|
||||
## Crash Report Payload
|
||||
|
||||
The crash reporter will send the following data to the `submitURL` as
|
||||
|
||||
@@ -100,6 +100,8 @@ The following methods of `chrome.tabs` are supported:
|
||||
|
||||
- `chrome.tabs.sendMessage`
|
||||
- `chrome.tabs.executeScript`
|
||||
- `chrome.tabs.update` (partial support)
|
||||
- supported properties: `url`, `muted`.
|
||||
|
||||
> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active
|
||||
> tab". Since Electron has no such concept, passing `-1` as a tab ID is not
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
# Frameless Window
|
||||
|
||||
> Open a window without toolbars, borders, or other graphical "chrome".
|
||||
|
||||
A frameless window is a window that has no
|
||||
[chrome](https://developer.mozilla.org/en-US/docs/Glossary/Chrome), the parts of
|
||||
the window, like toolbars, that are not a part of the web page. These are
|
||||
options on the [`BrowserWindow`](browser-window.md) class.
|
||||
|
||||
## Create a frameless window
|
||||
|
||||
To create a frameless window, you need to set `frame` to `false` in
|
||||
[BrowserWindow](browser-window.md)'s `options`:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ width: 800, height: 600, frame: false })
|
||||
win.show()
|
||||
```
|
||||
|
||||
### Alternatives
|
||||
|
||||
There's an alternative way to specify a chromeless window on macOS and Windows.
|
||||
Instead of setting `frame` to `false` which disables both the titlebar and window controls,
|
||||
you may want to have the title bar hidden and your content extend to the full window size,
|
||||
yet still preserve the window controls ("traffic lights" on macOS) for standard window actions.
|
||||
You can do so by specifying the `titleBarStyle` option:
|
||||
|
||||
#### `hidden`
|
||||
|
||||
Results in a hidden title bar and a full size content window. On macOS, the title bar still has the standard window controls (“traffic lights”) in the top left.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ titleBarStyle: 'hidden' })
|
||||
win.show()
|
||||
```
|
||||
|
||||
### Alternatives on macOS
|
||||
|
||||
#### `hiddenInset`
|
||||
|
||||
Results in a hidden title bar with an alternative look where the traffic light buttons are slightly more inset from the window edge.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ titleBarStyle: 'hiddenInset' })
|
||||
win.show()
|
||||
```
|
||||
|
||||
#### `customButtonsOnHover`
|
||||
|
||||
Uses custom drawn close, and miniaturize buttons that display
|
||||
when hovering in the top left of the window. The fullscreen button
|
||||
is not available due to restrictions of frameless windows as they
|
||||
interface with Apple's macOS window masks. These custom buttons prevent
|
||||
issues with mouse events that occur with the standard window toolbar buttons.
|
||||
This option is only applicable for frameless windows.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ titleBarStyle: 'customButtonsOnHover', frame: false })
|
||||
win.show()
|
||||
```
|
||||
|
||||
## Windows Control Overlay
|
||||
|
||||
When using a frameless window in conjuction with `win.setWindowButtonVisibility(true)` on macOS, using one of the `titleBarStyle`s as described above so
|
||||
that the traffic lights are visible, or using `titleBarStyle: hidden` on Windows, you can access the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and
|
||||
[CSS Environment Variables][overlay-css-env-vars] by setting the `titleBarOverlay` option to true. Specifying `true` will result in an overlay with default system colors.
|
||||
|
||||
On Windows, you can also specify the color of the overlay and its symbols by setting `titleBarOverlay` to an object with the options `color` and `symbolColor`. If an option is not specified, the color will default to its system color for the window control buttons:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({
|
||||
titleBarStyle: 'hidden',
|
||||
titleBarOverlay: true
|
||||
})
|
||||
win.show()
|
||||
```
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({
|
||||
titleBarStyle: 'hidden',
|
||||
titleBarOverlay: {
|
||||
color: '#2f3241',
|
||||
symbolColor: '#74b1be'
|
||||
}
|
||||
})
|
||||
win.show()
|
||||
```
|
||||
|
||||
## Transparent window
|
||||
|
||||
By setting the `transparent` option to `true`, you can also make the frameless
|
||||
window transparent:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ transparent: true, frame: false })
|
||||
win.show()
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
* You can not click through the transparent area. We are going to introduce an
|
||||
API to set window shape to solve this, see
|
||||
[our issue](https://github.com/electron/electron/issues/1335) for details.
|
||||
* Transparent windows are not resizable. Setting `resizable` to `true` may make
|
||||
a transparent window stop working on some platforms.
|
||||
* The `blur` filter only applies to the web page, so there is no way to apply
|
||||
blur effect to the content below the window (i.e. other applications open on
|
||||
the user's system).
|
||||
* The window will not be transparent when DevTools is opened.
|
||||
* On Windows operating systems,
|
||||
* transparent windows will not work when DWM is
|
||||
disabled.
|
||||
* transparent windows can not be maximized using the Windows system menu or by double clicking the title bar. The reasoning behind this can be seen on [this pull request](https://github.com/electron/electron/pull/28207).
|
||||
* On Linux, users have to put `--enable-transparent-visuals --disable-gpu` in
|
||||
the command line to disable GPU and allow ARGB to make transparent window,
|
||||
this is caused by an upstream bug that [alpha channel doesn't work on some
|
||||
NVidia drivers](https://bugs.chromium.org/p/chromium/issues/detail?id=369209) on
|
||||
Linux.
|
||||
* On Mac, the native window shadow will not be shown on a transparent window.
|
||||
|
||||
## Click-through window
|
||||
|
||||
To create a click-through window, i.e. making the window ignore all mouse
|
||||
events, you can call the [win.setIgnoreMouseEvents(ignore)][ignore-mouse-events]
|
||||
API:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow()
|
||||
win.setIgnoreMouseEvents(true)
|
||||
```
|
||||
|
||||
### Forwarding
|
||||
|
||||
Ignoring mouse messages makes the web page oblivious to mouse movement, meaning
|
||||
that mouse movement events will not be emitted. On Windows operating systems an
|
||||
optional parameter can be used to forward mouse move messages to the web page,
|
||||
allowing events such as `mouseleave` to be emitted:
|
||||
|
||||
```javascript
|
||||
const { ipcRenderer } = require('electron')
|
||||
const el = document.getElementById('clickThroughElement')
|
||||
el.addEventListener('mouseenter', () => {
|
||||
ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
})
|
||||
el.addEventListener('mouseleave', () => {
|
||||
ipcRenderer.send('set-ignore-mouse-events', false)
|
||||
})
|
||||
|
||||
// Main process
|
||||
const { ipcMain } = require('electron')
|
||||
ipcMain.on('set-ignore-mouse-events', (event, ...args) => {
|
||||
BrowserWindow.fromWebContents(event.sender).setIgnoreMouseEvents(...args)
|
||||
})
|
||||
```
|
||||
|
||||
This makes the web page click-through when over `el`, and returns to normal
|
||||
outside it.
|
||||
|
||||
## Draggable region
|
||||
|
||||
By default, the frameless window is non-draggable. Apps need to specify
|
||||
`-webkit-app-region: drag` in CSS to tell Electron which regions are draggable
|
||||
(like the OS's standard titlebar), and apps can also use
|
||||
`-webkit-app-region: no-drag` to exclude the non-draggable area from the
|
||||
draggable region. Note that only rectangular shapes are currently supported.
|
||||
|
||||
Note: `-webkit-app-region: drag` is known to have problems while the developer tools are open. See this [GitHub issue](https://github.com/electron/electron/issues/3647) for more information including a workaround.
|
||||
|
||||
To make the whole window draggable, you can add `-webkit-app-region: drag` as
|
||||
`body`'s style:
|
||||
|
||||
```html
|
||||
<body style="-webkit-app-region: drag">
|
||||
</body>
|
||||
```
|
||||
|
||||
And note that if you have made the whole window draggable, you must also mark
|
||||
buttons as non-draggable, otherwise it would be impossible for users to click on
|
||||
them:
|
||||
|
||||
```css
|
||||
button {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
```
|
||||
|
||||
If you're only setting a custom titlebar as draggable, you also need to make all
|
||||
buttons in titlebar non-draggable.
|
||||
|
||||
## Text selection
|
||||
|
||||
In a frameless window the dragging behavior may conflict with selecting text.
|
||||
For example, when you drag the titlebar you may accidentally select the text on
|
||||
the titlebar. To prevent this, you need to disable text selection within a
|
||||
draggable area like this:
|
||||
|
||||
```css
|
||||
.titlebar {
|
||||
-webkit-user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
```
|
||||
|
||||
## Context menu
|
||||
|
||||
On some platforms, the draggable area will be treated as a non-client frame, so
|
||||
when you right click on it a system menu will pop up. To make the context menu
|
||||
behave correctly on all platforms you should never use a custom context menu on
|
||||
draggable areas.
|
||||
|
||||
[ignore-mouse-events]: browser-window.md#winsetignoremouseeventsignore-options
|
||||
[overlay-javascript-apis]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#javascript-apis
|
||||
[overlay-css-env-vars]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#css-environment-variables
|
||||
@@ -297,6 +297,35 @@ app.whenReady().then(() => {
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
|
||||
if (permission === 'serial') {
|
||||
// Add logic here to determine if permission should be given to allow serial selection
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// Optionally, retrieve previously persisted devices from a persistent store
|
||||
const grantedDevices = fetchGrantedDevices()
|
||||
|
||||
win.webContents.session.setDevicePermissionHandler((details) => {
|
||||
if (new URL(details.origin).hostname === 'some-host' && details.deviceType === 'serial') {
|
||||
if (details.device.vendorId === 123 && details.device.productId === 345) {
|
||||
// Always allow this type of device (this allows skipping the call to `navigator.serial.requestPort` first)
|
||||
return true
|
||||
}
|
||||
|
||||
// Search through the list of devices that have previously been granted permission
|
||||
return grantedDevices.some((grantedDevice) => {
|
||||
return grantedDevice.vendorId === details.device.vendorId &&
|
||||
grantedDevice.productId === details.device.productId &&
|
||||
grantedDevice.serialNumber && grantedDevice.serialNumber === details.device.serialNumber
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
win.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
|
||||
event.preventDefault()
|
||||
const selectedPort = portList.find((device) => {
|
||||
@@ -533,7 +562,8 @@ the original network configuration.
|
||||
* `hostname` String
|
||||
* `certificate` [Certificate](structures/certificate.md)
|
||||
* `validatedCertificate` [Certificate](structures/certificate.md)
|
||||
* `verificationResult` String - Verification result from chromium.
|
||||
* `isIssuedByKnownRoot` Boolean - `true` if Chromium recognises the root CA as a standard root. If it isn't then it's probably the case that this certificate was generated by a MITM proxy whose root has been installed locally (for example, by a corporate proxy). This should not be trusted if the `verificationResult` is not `OK`.
|
||||
* `verificationResult` String - `OK` if the certificate is trusted, otherwise an error like `CERT_REVOKED`.
|
||||
* `errorCode` Integer - Error code.
|
||||
* `callback` Function
|
||||
* `verificationResult` Integer - Value can be one of certificate error codes
|
||||
@@ -588,6 +618,7 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||
* `permissionGranted` Boolean - Allow or deny the permission.
|
||||
* `details` Object - Some properties are only available on certain permission types.
|
||||
* `externalURL` String (optional) - The url of the `openExternal` request.
|
||||
* `securityOrigin` String (optional) - The security origin of the `media` request.
|
||||
* `mediaTypes` String[] (optional) - The types of media access being requested, elements can be `video`
|
||||
or `audio`
|
||||
* `requestingUrl` String - The last URL the requesting frame loaded
|
||||
@@ -646,9 +677,9 @@ session.fromPartition('some-partition').setPermissionCheckHandler((webContents,
|
||||
|
||||
* `handler` Function\<Boolean> | null
|
||||
* `details` Object
|
||||
* `deviceType` String - The type of device that permission is being requested on, can be `hid`.
|
||||
* `deviceType` String - The type of device that permission is being requested on, can be `hid` or `serial`.
|
||||
* `origin` String - The origin URL of the device permission check.
|
||||
* `device` [HIDDevice](structures/hid-device.md) - the device that permission is being requested for.
|
||||
* `device` [HIDDevice](structures/hid-device.md) | [SerialPort](structures/serial-port.md)- the device that permission is being requested for.
|
||||
* `frame` [WebFrameMain](web-frame-main.md) - WebFrameMain checking the device permission.
|
||||
|
||||
Sets the handler which can be used to respond to device permission checks for the `session`.
|
||||
@@ -673,6 +704,8 @@ app.whenReady().then(() => {
|
||||
if (permission === 'hid') {
|
||||
// Add logic here to determine if permission should be given to allow HID selection
|
||||
return true
|
||||
} else if (permission === 'serial') {
|
||||
// Add logic here to determine if permission should be given to allow serial port selection
|
||||
}
|
||||
return false
|
||||
})
|
||||
@@ -693,6 +726,11 @@ app.whenReady().then(() => {
|
||||
grantedDevice.productId === details.device.productId &&
|
||||
grantedDevice.serialNumber && grantedDevice.serialNumber === details.device.serialNumber
|
||||
})
|
||||
} else if (details.deviceType === 'serial') {
|
||||
if (details.device.vendorId === 123 && details.device.productId === 345) {
|
||||
// Always allow this type of device (this allows skipping the call to `navigator.hid.requestDevice` first)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -1494,8 +1494,8 @@ win.loadURL('http://github.com')
|
||||
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
// Use default printing options
|
||||
const pdfPath = path.join(os.homedir(), 'Desktop', 'temp.pdf')
|
||||
win.webContents.printToPDF({}).then(data => {
|
||||
const pdfPath = path.join(os.homedir(), 'Desktop', 'temp.pdf')
|
||||
fs.writeFile(pdfPath, data, (error) => {
|
||||
if (error) throw error
|
||||
console.log(`Wrote PDF successfully to ${pdfPath}`)
|
||||
|
||||
@@ -12,6 +12,63 @@ 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 (17.0)
|
||||
|
||||
### Removed: `desktopCapturer.getSources` in the renderer
|
||||
|
||||
The `desktopCapturer.getSources` API is now only available in the main process.
|
||||
This has been changed in order to improve the default security of Electron
|
||||
apps.
|
||||
|
||||
If you need this functionality, it can be replaced as follows:
|
||||
|
||||
```js
|
||||
// Main process
|
||||
const { ipcMain, desktopCapturer } = require('electron')
|
||||
|
||||
ipcMain.handle(
|
||||
'DESKTOP_CAPTURER_GET_SOURCES',
|
||||
(event, opts) => desktopCapturer.getSources(opts)
|
||||
)
|
||||
```
|
||||
|
||||
```js
|
||||
// Renderer process
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
const desktopCapturer = {
|
||||
getSources: (opts) => ipcRenderer.invoke('DESKTOP_CAPTURER_GET_SOURCES', opts)
|
||||
}
|
||||
```
|
||||
|
||||
However, you should consider further restricting the information returned to
|
||||
the renderer; for instance, displaying a source selector to the user and only
|
||||
returning the selected source.
|
||||
|
||||
## Planned Breaking API Changes (16.0)
|
||||
|
||||
### Behavior Changed: `crashReporter` implementation switched to Crashpad on Linux
|
||||
|
||||
The underlying implementation of the `crashReporter` API on Linux has changed
|
||||
from Breakpad to Crashpad, bringing it in line with Windows and Mac. As a
|
||||
result of this, child processes are now automatically monitored, and calling
|
||||
`process.crashReporter.start` in Node child processes is no longer needed (and
|
||||
is not advisable, as it will start a second instance of the Crashpad reporter).
|
||||
|
||||
There are also some subtle changes to how annotations will be reported on
|
||||
Linux, including that long values will no longer be split between annotations
|
||||
appended with `__1`, `__2` and so on, and instead will be truncated at the
|
||||
(new, longer) annotation value limit.
|
||||
|
||||
### Deprecated: `desktopCapturer.getSources` in the renderer
|
||||
|
||||
Usage of the `desktopCapturer.getSources` API in the renderer has been
|
||||
deprecated and will be removed. This change improves the default security of
|
||||
Electron apps.
|
||||
|
||||
See [here](#removed-desktopcapturergetsources-in-the-renderer) for details on
|
||||
how to replace this API in your app.
|
||||
|
||||
## Planned Breaking API Changes (15.0)
|
||||
|
||||
### Default Changed: `nativeWindowOpen` defaults to `true`
|
||||
|
||||
171
docs/development/creating-api.md
Normal file
171
docs/development/creating-api.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Creating a New Electron Browser Module
|
||||
|
||||
Welcome to the Electron API guide! If you are unfamiliar with creating a new Electron API module within the [`browser`](https://github.com/electron/electron/tree/main/shell/browser) directory, this guide serves as a checklist for some of the necessary steps that you will need to implement.
|
||||
|
||||
This is not a comprehensive end-all guide to creating an Electron Browser API, rather an outline documenting some of the more unintuitive steps.
|
||||
|
||||
## Add your files to Electron's project configuration
|
||||
|
||||
Electron uses [GN](https://gn.googlesource.com/gn) as a meta build system to generate files for its compiler, [Ninja](https://ninja-build.org/). This means that in order to tell Electron to compile your code, we have to add your API's code and header file names into [`filenames.gni`](https://github.com/electron/electron/blob/main/filenames.gni).
|
||||
|
||||
You will need to append your API file names alphabetically into the appropriate files like so:
|
||||
|
||||
```cpp title='filenames.gni'
|
||||
lib_sources = [
|
||||
"path/to/api/api_name.cc",
|
||||
"path/to/api/api_name.h",
|
||||
]
|
||||
|
||||
lib_sources_mac = [
|
||||
"path/to/api/api_name_mac.h",
|
||||
"path/to/api/api_name_mac.mm",
|
||||
]
|
||||
|
||||
lib_sources_win = [
|
||||
"path/to/api/api_name_win.cc",
|
||||
"path/to/api/api_name_win.h",
|
||||
]
|
||||
|
||||
lib_sources_linux = [
|
||||
"path/to/api/api_name_linux.cc",
|
||||
"path/to/api/api_name_linux.h",
|
||||
]
|
||||
```
|
||||
|
||||
Note that the Windows, macOS and Linux array additions are optional and should only be added if your API has specific platform implementations.
|
||||
|
||||
## Create API documentation
|
||||
|
||||
Type definitions are generated by Electron using [`@electron/docs-parser`](https://github.com/electron/docs-parser) and [`@electron/typescript-definitions`](https://github.com/electron/typescript-definitions). This step is necessary to ensure consistency across Electron's API documentation. This means that for your API type definition to appear in the `electron.d.ts` file, we must create a `.md` file. Examples can be found in [this folder](https://github.com/electron/electron/tree/main/docs/api).
|
||||
|
||||
## Set up `ObjectTemplateBuilder` and `Wrappable`
|
||||
|
||||
Electron constructs its modules using [`object_template_builder`](https://www.electronjs.org/blog/from-native-to-js#mateobjecttemplatebuilder).
|
||||
|
||||
[`wrappable`](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/gin/wrappable.h) is a base class for C++ objects that have corresponding v8 wrapper objects.
|
||||
|
||||
Here is a basic example of code that you may need to add, in order to incorporate `object_template_builder` and `wrappable` into your API. For further reference, you can find more implementations [here](https://github.com/electron/electron/tree/main/shell/browser/api).
|
||||
|
||||
In your `api_name.h` file:
|
||||
|
||||
```cpp title='api_name.h'
|
||||
|
||||
#ifndef SHELL_BROWSER_API_ELECTRON_API_{API_NAME}_H_
|
||||
#define SHELL_BROWSER_API_ELECTRON_API_{API_NAME}_H_
|
||||
|
||||
#include "gin/handle.h"
|
||||
#include "gin/wrappable.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace api {
|
||||
|
||||
class ApiName : public gin::Wrappable<ApiName> {
|
||||
public:
|
||||
static gin::Handle<ApiName> Create(v8::Isolate* isolate);
|
||||
|
||||
// gin::Wrappable
|
||||
static gin::WrapperInfo kWrapperInfo;
|
||||
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) override;
|
||||
const char* GetTypeName() override;
|
||||
} // namespace api
|
||||
} // namespace electron
|
||||
```
|
||||
|
||||
In your `api_name.cc` file:
|
||||
|
||||
```cpp title='api_name.cc'
|
||||
#include "shell/browser/api/electron_api_safe_storage.h"
|
||||
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/common/gin_converters/base_converter.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace api {
|
||||
|
||||
gin::WrapperInfo ApiName::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
gin::ObjectTemplateBuilder ApiName::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
return gin::ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("methodName", &ApiName::methodName);
|
||||
}
|
||||
|
||||
const char* ApiName::GetTypeName() {
|
||||
return "ApiName";
|
||||
}
|
||||
|
||||
// static
|
||||
gin::Handle<ApiName> ApiName::Create(v8::Isolate* isolate) {
|
||||
return gin::CreateHandle(isolate, new ApiName());
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace electron
|
||||
|
||||
namespace {
|
||||
|
||||
void Initialize(v8::Local<v8::Object> exports,
|
||||
v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context,
|
||||
void* priv) {
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
gin_helper::Dictionary dict(isolate, exports);
|
||||
dict.Set("apiName", electron::api::ApiName::Create(isolate));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
```
|
||||
|
||||
## Link your Electron API with Node
|
||||
|
||||
In the [`typings/internal-ambient.d.ts`](https://github.com/electron/electron/blob/main/typings/internal-ambient.d.ts) file, we need to append a new property onto the `Process` interface like so:
|
||||
|
||||
```ts title='typings/internal-ambient.d.ts'
|
||||
interface Process {
|
||||
_linkedBinding(name: 'electron_browser_{api_name}', Electron.ApiName);
|
||||
}
|
||||
```
|
||||
|
||||
At the very bottom of your `api_name.cc` file:
|
||||
|
||||
```cpp title='api_name.cc'
|
||||
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_browser_{api_name},Initialize)
|
||||
```
|
||||
|
||||
In your [`shell/common/node_bindings.cc`](https://github.com/electron/electron/blob/main/shell/common/node_bindings.cc) file, add your node binding name to Electron's built-in modules.
|
||||
|
||||
```cpp title='shell/common/node_bindings.cc'
|
||||
#define ELECTRON_BUILTIN_MODULES(V) \
|
||||
V(electron_browser_{api_name})
|
||||
```
|
||||
|
||||
> Note: More technical details on how Node links with Electron can be found on [our blog](https://www.electronjs.org/blog/electron-internals-using-node-as-a-library#link-node-with-electron).
|
||||
|
||||
## Expose your API to TypeScript
|
||||
|
||||
### Export your API as a module
|
||||
|
||||
We will need to create a new TypeScript file in the path that follows:
|
||||
|
||||
`"lib/browser/api/{electron_browser_{api_name}}.ts"`
|
||||
|
||||
An example of the contents of this file can be found [here](https://github.com/electron/electron/blob/main/lib/browser/api/native-image.ts).
|
||||
|
||||
### Expose your module to TypeScript
|
||||
|
||||
Add your module to the module list found at `"lib/browser/api/module-list.ts"` like so:
|
||||
|
||||
```typescript title='lib/browser/api/module-list.ts'
|
||||
export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'apiName', loader: () => require('./api-name') },
|
||||
];
|
||||
```
|
||||
@@ -59,8 +59,9 @@
|
||||
|
||||
<p>
|
||||
For more details, see the
|
||||
<a href="https://electronjs.org/docs/api/frameless-window/">
|
||||
Frameless Window
|
||||
<a href="https://electronjs.org/docs/tutorial/window-customization/">
|
||||
Window Customization
|
||||
|
||||
</a>
|
||||
documentation.
|
||||
</p>
|
||||
|
||||
BIN
docs/images/windows-progress-bar.png
Normal file
BIN
docs/images/windows-progress-bar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -1,48 +1,7 @@
|
||||
# Accessibility
|
||||
|
||||
Making accessible applications is important and we're happy to provide
|
||||
functionality to [Devtron][devtron] and [Spectron][spectron] that gives
|
||||
developers the opportunity to make their apps better for everyone.
|
||||
|
||||
---
|
||||
|
||||
Accessibility concerns in Electron applications are similar to those of
|
||||
websites because they're both ultimately HTML. With Electron apps, however,
|
||||
you can't use the online resources for accessibility audits because your app
|
||||
doesn't have a URL to point the auditor to.
|
||||
|
||||
These features bring those auditing tools to your Electron app. You can
|
||||
choose to add audits to your tests with Spectron or use them within DevTools
|
||||
with Devtron. Read on for a summary of the tools.
|
||||
|
||||
## Spectron
|
||||
|
||||
In the testing framework Spectron, you can now audit each window and `<webview>`
|
||||
tag in your application. For example:
|
||||
|
||||
```javascript
|
||||
app.client.auditAccessibility().then(function (audit) {
|
||||
if (audit.failed) {
|
||||
console.error(audit.message)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
You can read more about this feature in [Spectron's documentation][spectron-a11y].
|
||||
|
||||
## Devtron
|
||||
|
||||
In Devtron, there is an accessibility tab which will allow you to audit a
|
||||
page in your app, sort and filter the results.
|
||||
|
||||
![devtron screenshot][devtron-screenshot]
|
||||
|
||||
Both of these tools are using the [Accessibility Developer Tools][a11y-devtools]
|
||||
library built by Google for Chrome. You can learn more about the accessibility
|
||||
audit rules this library uses on that [repository's wiki][a11y-devtools-wiki].
|
||||
|
||||
If you know of other great accessibility tools for Electron, add them to the
|
||||
accessibility documentation with a pull request.
|
||||
websites because they're both ultimately HTML.
|
||||
|
||||
## Manually enabling accessibility features
|
||||
|
||||
@@ -84,10 +43,6 @@ CFStringRef kAXManualAccessibility = CFSTR("AXManualAccessibility");
|
||||
}
|
||||
```
|
||||
|
||||
[devtron]: https://electronjs.org/devtron
|
||||
[devtron-screenshot]: https://cloud.githubusercontent.com/assets/1305617/17156618/9f9bcd72-533f-11e6-880d-389115f40a2a.png
|
||||
[spectron]: https://electronjs.org/spectron
|
||||
[spectron-a11y]: https://github.com/electron/spectron#accessibility-testing
|
||||
[a11y-docs]: https://www.chromium.org/developers/design-documents/accessibility#TOC-How-Chrome-detects-the-presence-of-Assistive-Technology
|
||||
[a11y-devtools]: https://github.com/GoogleChrome/accessibility-developer-tools
|
||||
[a11y-devtools-wiki]: https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
# Automated Testing with a Custom Driver
|
||||
|
||||
To write automated tests for your Electron app, you will need a way to "drive" your application. [Spectron](https://electronjs.org/spectron) is a commonly-used solution which lets you emulate user actions via [WebDriver](https://webdriver.io/). However, it's also possible to write your own custom driver using node's builtin IPC-over-STDIO. The benefit of a custom driver is that it tends to require less overhead than Spectron, and lets you expose custom methods to your test suite.
|
||||
|
||||
To create a custom driver, we'll use Node.js' [child_process](https://nodejs.org/api/child_process.html) API. The test suite will spawn the Electron process, then establish a simple messaging protocol:
|
||||
|
||||
```js
|
||||
const childProcess = require('child_process')
|
||||
const electronPath = require('electron')
|
||||
|
||||
// spawn the process
|
||||
const env = { /* ... */ }
|
||||
const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
|
||||
const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
|
||||
|
||||
// listen for IPC messages from the app
|
||||
appProcess.on('message', (msg) => {
|
||||
// ...
|
||||
})
|
||||
|
||||
// send an IPC message to the app
|
||||
appProcess.send({ my: 'message' })
|
||||
```
|
||||
|
||||
From within the Electron app, you can listen for messages and send replies using the Node.js [process](https://nodejs.org/api/process.html) API:
|
||||
|
||||
```js
|
||||
// listen for IPC messages from the test suite
|
||||
process.on('message', (msg) => {
|
||||
// ...
|
||||
})
|
||||
|
||||
// send an IPC message to the test suite
|
||||
process.send({ my: 'message' })
|
||||
```
|
||||
|
||||
We can now communicate from the test suite to the Electron app using the `appProcess` object.
|
||||
|
||||
For convenience, you may want to wrap `appProcess` in a driver object that provides more high-level functions. Here is an example of how you can do this:
|
||||
|
||||
```js
|
||||
class TestDriver {
|
||||
constructor ({ path, args, env }) {
|
||||
this.rpcCalls = []
|
||||
|
||||
// start child process
|
||||
env.APP_TEST_DRIVER = 1 // let the app know it should listen for messages
|
||||
this.process = childProcess.spawn(path, args, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })
|
||||
|
||||
// handle rpc responses
|
||||
this.process.on('message', (message) => {
|
||||
// pop the handler
|
||||
const rpcCall = this.rpcCalls[message.msgId]
|
||||
if (!rpcCall) return
|
||||
this.rpcCalls[message.msgId] = null
|
||||
// reject/resolve
|
||||
if (message.reject) rpcCall.reject(message.reject)
|
||||
else rpcCall.resolve(message.resolve)
|
||||
})
|
||||
|
||||
// wait for ready
|
||||
this.isReady = this.rpc('isReady').catch((err) => {
|
||||
console.error('Application failed to start', err)
|
||||
this.stop()
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
|
||||
// simple RPC call
|
||||
// to use: driver.rpc('method', 1, 2, 3).then(...)
|
||||
async rpc (cmd, ...args) {
|
||||
// send rpc request
|
||||
const msgId = this.rpcCalls.length
|
||||
this.process.send({ msgId, cmd, args })
|
||||
return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))
|
||||
}
|
||||
|
||||
stop () {
|
||||
this.process.kill()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the app, you'd need to write a simple handler for the RPC calls:
|
||||
|
||||
```js
|
||||
if (process.env.APP_TEST_DRIVER) {
|
||||
process.on('message', onMessage)
|
||||
}
|
||||
|
||||
async function onMessage ({ msgId, cmd, args }) {
|
||||
let method = METHODS[cmd]
|
||||
if (!method) method = () => new Error('Invalid method: ' + cmd)
|
||||
try {
|
||||
const resolve = await method(...args)
|
||||
process.send({ msgId, resolve })
|
||||
} catch (err) {
|
||||
const reject = {
|
||||
message: err.message,
|
||||
stack: err.stack,
|
||||
name: err.name
|
||||
}
|
||||
process.send({ msgId, reject })
|
||||
}
|
||||
}
|
||||
|
||||
const METHODS = {
|
||||
isReady () {
|
||||
// do any setup needed
|
||||
return true
|
||||
}
|
||||
// define your RPC-able methods here
|
||||
}
|
||||
```
|
||||
|
||||
Then, in your test suite, you can use your test-driver as follows:
|
||||
|
||||
```js
|
||||
const test = require('ava')
|
||||
const electronPath = require('electron')
|
||||
|
||||
const app = new TestDriver({
|
||||
path: electronPath,
|
||||
args: ['./app'],
|
||||
env: {
|
||||
NODE_ENV: 'test'
|
||||
}
|
||||
})
|
||||
test.before(async t => {
|
||||
await app.isReady
|
||||
})
|
||||
test.after.always('cleanup', async t => {
|
||||
await app.stop()
|
||||
})
|
||||
```
|
||||
265
docs/tutorial/automated-testing.md
Normal file
265
docs/tutorial/automated-testing.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Automated Testing
|
||||
|
||||
Test automation is an efficient way of validating that your application code works as intended.
|
||||
While Electron doesn't actively maintain its own testing solution, this guide will go over a couple
|
||||
ways you can run end-to-end automated tests on your Electron app.
|
||||
|
||||
## Using the WebDriver interface
|
||||
|
||||
From [ChromeDriver - WebDriver for Chrome][chrome-driver]:
|
||||
|
||||
> WebDriver is an open source tool for automated testing of web apps across many
|
||||
> browsers. It provides capabilities for navigating to web pages, user input,
|
||||
> JavaScript execution, and more. ChromeDriver is a standalone server which
|
||||
> implements WebDriver's wire protocol for Chromium. It is being developed by
|
||||
> members of the Chromium and WebDriver teams.
|
||||
|
||||
There are a few ways that you can set up testing using WebDriver.
|
||||
|
||||
### With WebdriverIO
|
||||
|
||||
[WebdriverIO](https://webdriver.io/) (WDIO) is a test automation framework that provides a
|
||||
Node.js package for testing with WebDriver. Its ecosystem also includes various plugins
|
||||
(e.g. reporter and services) that can help you put together your test setup.
|
||||
|
||||
#### Install the testrunner
|
||||
|
||||
First you need to run the WebdriverIO starter toolkit in your project root directory:
|
||||
|
||||
```sh npm2yarn
|
||||
npx wdio . --yes
|
||||
```
|
||||
|
||||
This installs all necessary packages for you and generates a `wdio.conf.js` configuration file.
|
||||
|
||||
#### Connect WDIO to your Electron app
|
||||
|
||||
Update the capabilities in your configuration file to point to your Electron app binary:
|
||||
|
||||
```javascript title='wdio.conf.js'
|
||||
export.config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'chrome',
|
||||
'goog:chromeOptions': {
|
||||
binary: '/path/to/your/electron/binary', // Path to your Electron binary.
|
||||
args: [/* cli arguments */] // Optional, perhaps 'app=' + /path/to/your/app/
|
||||
}
|
||||
}]
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Run your tests
|
||||
|
||||
To run your tests:
|
||||
|
||||
```sh
|
||||
$ npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
[chrome-driver]: https://sites.google.com/chromium.org/driver/
|
||||
|
||||
### With Selenium
|
||||
|
||||
[Selenium](https://www.selenium.dev/) is a web automation framework that
|
||||
exposes bindings to WebDriver APIs in many languages. Their Node.js bindings
|
||||
are available under the `selenium-webdriver` package on NPM.
|
||||
|
||||
#### Run a ChromeDriver server
|
||||
|
||||
In order to use Selenium with Electron, you need to download the `electron-chromedriver`
|
||||
binary, and run it:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev electron-chromedriver
|
||||
./node_modules/.bin/chromedriver
|
||||
Starting ChromeDriver (v2.10.291558) on port 9515
|
||||
Only local connections are allowed.
|
||||
```
|
||||
|
||||
Remember the port number `9515`, which will be used later.
|
||||
|
||||
#### Connect Selenium to ChromeDriver
|
||||
|
||||
Next, install Selenium into your project:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev selenium-webdriver
|
||||
```
|
||||
|
||||
Usage of `selenium-webdriver` with Electron is the same as with
|
||||
normal websites, except that you have to manually specify how to connect
|
||||
ChromeDriver and where to find the binary of your Electron app:
|
||||
|
||||
```js title='test.js'
|
||||
const webdriver = require('selenium-webdriver')
|
||||
const driver = new webdriver.Builder()
|
||||
// The "9515" is the port opened by ChromeDriver.
|
||||
.usingServer('http://localhost:9515')
|
||||
.withCapabilities({
|
||||
'goog:chromeOptions': {
|
||||
// Here is the path to your Electron binary.
|
||||
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
|
||||
}
|
||||
})
|
||||
.forBrowser('chrome') // note: use .forBrowser('electron') for selenium-webdriver <= 3.6.0
|
||||
.build()
|
||||
driver.get('http://www.google.com')
|
||||
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver')
|
||||
driver.findElement(webdriver.By.name('btnG')).click()
|
||||
driver.wait(() => {
|
||||
return driver.getTitle().then((title) => {
|
||||
return title === 'webdriver - Google Search'
|
||||
})
|
||||
}, 1000)
|
||||
driver.quit()
|
||||
```
|
||||
|
||||
## Using a custom test driver
|
||||
|
||||
It's also possible to write your own custom driver using Node.js' built-in IPC-over-STDIO.
|
||||
Custom test drivers require you to write additional app code, but have lower overhead and let you
|
||||
expose custom methods to your test suite.
|
||||
|
||||
To create a custom driver, we'll use Node.js' [`child_process`](https://nodejs.org/api/child_process.html) API.
|
||||
The test suite will spawn the Electron process, then establish a simple messaging protocol:
|
||||
|
||||
```js title='testDriver.js'
|
||||
const childProcess = require('child_process')
|
||||
const electronPath = require('electron')
|
||||
|
||||
// spawn the process
|
||||
const env = { /* ... */ }
|
||||
const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
|
||||
const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
|
||||
|
||||
// listen for IPC messages from the app
|
||||
appProcess.on('message', (msg) => {
|
||||
// ...
|
||||
})
|
||||
|
||||
// send an IPC message to the app
|
||||
appProcess.send({ my: 'message' })
|
||||
```
|
||||
|
||||
From within the Electron app, you can listen for messages and send replies using the Node.js
|
||||
[`process`](https://nodejs.org/api/process.html) API:
|
||||
|
||||
```js title='main.js'
|
||||
// listen for messages from the test suite
|
||||
process.on('message', (msg) => {
|
||||
// ...
|
||||
})
|
||||
|
||||
// send a message to the test suite
|
||||
process.send({ my: 'message' })
|
||||
```
|
||||
|
||||
We can now communicate from the test suite to the Electron app using the `appProcess` object.
|
||||
|
||||
For convenience, you may want to wrap `appProcess` in a driver object that provides more
|
||||
high-level functions. Here is an example of how you can do this. Let's start by creating
|
||||
a `TestDriver` class:
|
||||
|
||||
```js title='testDriver.js'
|
||||
class TestDriver {
|
||||
constructor ({ path, args, env }) {
|
||||
this.rpcCalls = []
|
||||
|
||||
// start child process
|
||||
env.APP_TEST_DRIVER = 1 // let the app know it should listen for messages
|
||||
this.process = childProcess.spawn(path, args, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })
|
||||
|
||||
// handle rpc responses
|
||||
this.process.on('message', (message) => {
|
||||
// pop the handler
|
||||
const rpcCall = this.rpcCalls[message.msgId]
|
||||
if (!rpcCall) return
|
||||
this.rpcCalls[message.msgId] = null
|
||||
// reject/resolve
|
||||
if (message.reject) rpcCall.reject(message.reject)
|
||||
else rpcCall.resolve(message.resolve)
|
||||
})
|
||||
|
||||
// wait for ready
|
||||
this.isReady = this.rpc('isReady').catch((err) => {
|
||||
console.error('Application failed to start', err)
|
||||
this.stop()
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
|
||||
// simple RPC call
|
||||
// to use: driver.rpc('method', 1, 2, 3).then(...)
|
||||
async rpc (cmd, ...args) {
|
||||
// send rpc request
|
||||
const msgId = this.rpcCalls.length
|
||||
this.process.send({ msgId, cmd, args })
|
||||
return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))
|
||||
}
|
||||
|
||||
stop () {
|
||||
this.process.kill()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { TestDriver };
|
||||
```
|
||||
|
||||
In your app code, can then write a simple handler to receive RPC calls:
|
||||
|
||||
```js title='main.js'
|
||||
const METHODS = {
|
||||
isReady () {
|
||||
// do any setup needed
|
||||
return true
|
||||
}
|
||||
// define your RPC-able methods here
|
||||
}
|
||||
|
||||
const onMessage = async ({ msgId, cmd, args }) => {
|
||||
let method = METHODS[cmd]
|
||||
if (!method) method = () => new Error('Invalid method: ' + cmd)
|
||||
try {
|
||||
const resolve = await method(...args)
|
||||
process.send({ msgId, resolve })
|
||||
} catch (err) {
|
||||
const reject = {
|
||||
message: err.message,
|
||||
stack: err.stack,
|
||||
name: err.name
|
||||
}
|
||||
process.send({ msgId, reject })
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.APP_TEST_DRIVER) {
|
||||
process.on('message', onMessage)
|
||||
}
|
||||
```
|
||||
|
||||
Then, in your test suite, you can use your `TestDriver` class with the test automation
|
||||
framework of your choosing. The following example uses
|
||||
[`ava`](https://www.npmjs.com/package/ava), but other popular choices like Jest
|
||||
or Mocha would work as well:
|
||||
|
||||
```js title='test.js'
|
||||
const test = require('ava')
|
||||
const electronPath = require('electron')
|
||||
const { TestDriver } = require('./testDriver')
|
||||
|
||||
const app = new TestDriver({
|
||||
path: electronPath,
|
||||
args: ['./app'],
|
||||
env: {
|
||||
NODE_ENV: 'test'
|
||||
}
|
||||
})
|
||||
test.before(async t => {
|
||||
await app.isReady
|
||||
})
|
||||
test.after.always('cleanup', async t => {
|
||||
await app.stop()
|
||||
})
|
||||
```
|
||||
@@ -138,7 +138,7 @@ Finally, the `main.js` file represents the main process and contains the actual
|
||||
const { app, BrowserWindow, ipcMain, nativeTheme } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
|
||||
@@ -84,13 +84,22 @@ There are several additional APIs for working with the Web Serial API:
|
||||
and [`serial-port-removed`](../api/session.md#event-serial-port-removed) events
|
||||
on the Session can be used to handle devices being plugged in or unplugged during the
|
||||
`navigator.serial.requestPort` process.
|
||||
* [`ses.setDevicePermissionHandler(handler)`](../api/session.md#sessetdevicepermissionhandlerhandler)
|
||||
can be used to provide default permissioning to devices without first calling
|
||||
for permission to devices via `navigator.serial.requestPort`. Additionally,
|
||||
the default behavior of Electron is to store granted device permision through
|
||||
the lifetime of the corresponding WebContents. If longer term storage is
|
||||
needed, a developer can store granted device permissions (eg when handling
|
||||
the `select-serial-port` event) and then read from that storage with
|
||||
`setDevicePermissionHandler`.
|
||||
* [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler)
|
||||
can be used to disable serial access for specific origins.
|
||||
|
||||
### Example
|
||||
|
||||
This example demonstrates an Electron application that automatically selects
|
||||
the first available Arduino Uno serial device (if connected) through
|
||||
serial devices through [`ses.setDevicePermissionHandler(handler)`](../api/session.md#sessetdevicepermissionhandlerhandler)
|
||||
as well as demonstrating selecting the first available Arduino Uno serial device (if connected) through
|
||||
[`select-serial-port` event on the Session](../api/session.md#event-select-serial-port)
|
||||
when the `Test Web Serial` button is clicked.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Special notes:
|
||||
* All dates are our goals but there may be reasons for adjusting the stable deadline, such as security bugs.
|
||||
* Take a look at the [5.0.0 Timeline blog post](https://electronjs.org/blog/electron-5-0-timeline) for info about publicizing our release dates.
|
||||
* Since Electron 6.0, we've been targeting every other Chromium version and releasing our stable on the same day as Chrome stable. You can reference Chromium's release schedule [here](https://chromiumdash.appspot.com/schedule). See [Electron's new release cadence blog post](https://www.electronjs.org/blog/12-week-cadence) for more details on our release schedule.
|
||||
* Electron 15.0 only will include a special Alpha release. Starting in Electron 16.0, we will release on an 8-week cadence. See [Electron's new 8-week cadence blog post](https://www.electronjs.org/blog/8-week-cadence) for more details.
|
||||
* Starting in Electron 16.0, we will release on an 8-week cadence. See [Electron's new 8-week cadence blog post](https://www.electronjs.org/blog/8-week-cadence) for more details.
|
||||
|
||||
| Electron | Alpha | Beta | Stable | Chrome | Node |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- |
|
||||
@@ -25,4 +25,5 @@ Special notes:
|
||||
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | M91 | v14.16 |
|
||||
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | M93 | v14.17 |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | M94 | v16.5 |
|
||||
| 16.0.0 | -- | 2021-Sep-23 | 2021-Nov-16 | M96 | TBD |
|
||||
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | M96 | v16.9 |
|
||||
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | M98 | TBD |
|
||||
|
||||
@@ -18,7 +18,7 @@ npm install --save-dev electron@latest
|
||||
|
||||
There are several major changes from our 1.x strategy outlined below. Each change is intended to satisfy the needs and priorities of developers/maintainers and app developers.
|
||||
|
||||
1. Strict use of the [SemVer](#semver) spec
|
||||
1. Strict use of the the [SemVer](#semver) spec
|
||||
2. Introduction of semver-compliant `-beta` tags
|
||||
3. Introduction of [conventional commit messages](https://conventionalcommits.org/)
|
||||
4. Well-defined stabilization branches
|
||||
|
||||
@@ -12,7 +12,7 @@ Fuses are the solution to this problem, at a high level they are "magic bits" in
|
||||
|
||||
### The easy way
|
||||
|
||||
We've made a handy module `@electron/fuses` to make flipping these fuses easy. Check out the README of that module for more details on usage and potential error cases.
|
||||
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
|
||||
require('@electron/fuses').flipFuses(
|
||||
|
||||
@@ -39,7 +39,7 @@ inAppPurchase.on('transactions-updated', (event, transactions) => {
|
||||
}
|
||||
|
||||
// Check each transaction.
|
||||
transactions.forEach(function (transaction) {
|
||||
transactions.forEach((transaction) => {
|
||||
const payment = transaction.payment
|
||||
|
||||
switch (transaction.transactionState) {
|
||||
|
||||
@@ -82,7 +82,7 @@ listen for the `keyup` and `keydown` [DOM events][dom-events] inside the
|
||||
renderer process using the [addEventListener() API][addEventListener-api].
|
||||
|
||||
```javascript fiddle='docs/fiddles/features/keyboard-shortcuts/web-apis|focus=renderer.js'
|
||||
function handleKeyPress(event) {
|
||||
const handleKeyPress = (event) => {
|
||||
// You can put code here to handle the keypress.
|
||||
document.getElementById("last-keypress").innerText = event.key;
|
||||
console.log(`You pressed ${event.key}`);
|
||||
|
||||
@@ -25,7 +25,7 @@ Starting with a working application from the
|
||||
```javascript fiddle='docs/fiddles/features/macos-dock-menu'
|
||||
const { app, BrowserWindow, Menu } = require('electron')
|
||||
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
|
||||
@@ -134,7 +134,7 @@ app.whenReady().then(async () => {
|
||||
<script>
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
function doWork(input) {
|
||||
const doWork = (input) => {
|
||||
// Something cpu-intensive.
|
||||
return input * 2
|
||||
}
|
||||
@@ -185,7 +185,7 @@ stream of data.
|
||||
```js
|
||||
// renderer.js ///////////////////////////////////////////////////////////////
|
||||
|
||||
function makeStreamingRequest (element, callback) {
|
||||
const makeStreamingRequest = (element, callback) => {
|
||||
// MessageChannels are lightweight--it's cheap to create a new one for each
|
||||
// request.
|
||||
const { port1, port2 } = new MessageChannel()
|
||||
|
||||
@@ -54,7 +54,7 @@ const { Notification } = require('electron')
|
||||
const NOTIFICATION_TITLE = 'Basic Notification'
|
||||
const NOTIFICATION_BODY = 'Notification from the Main process'
|
||||
|
||||
function showNotification () {
|
||||
const showNotification = () => {
|
||||
new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY }).show()
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ the dirty area is passed to the `paint` event to be more efficient.
|
||||
losses with no benefits.
|
||||
* When nothing is happening on a webpage, no frames are generated.
|
||||
* An offscreen window is always created as a
|
||||
[Frameless Window](../api/frameless-window.md).
|
||||
[Frameless Window](../tutorial/window-customization.md)..
|
||||
|
||||
### Rendering Modes
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ Starting with an HTML file `index.html`, this example will demonstrate how the `
|
||||
In order to mutate the DOM, create a `renderer.js` file that adds event listeners to the `'online'` and `'offline'` `window` events. The event handler sets the content of the `<strong id='status'>` element depending on the result of `navigator.onLine`.
|
||||
|
||||
```js title='renderer.js'
|
||||
function updateOnlineStatus () {
|
||||
const updateOnlineStatus = () => {
|
||||
document.getElementById('status').innerHTML = navigator.onLine ? 'online' : 'offline'
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ Finally, create a `main.js` file for main process that creates the window.
|
||||
```js title='main.js'
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
const onlineStatusWindow = new BrowserWindow({
|
||||
width: 400,
|
||||
height: 100
|
||||
|
||||
@@ -84,7 +84,7 @@ uses `app` APIs to create a more native application window experience.
|
||||
|
||||
```js title='main.js'
|
||||
// quitting the app when no windows are open on non-macOS platforms
|
||||
app.on('window-all-closed', function () {
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
```
|
||||
|
||||
@@ -7,7 +7,7 @@ without the need of switching to the window itself.
|
||||
|
||||
On Windows, you can use a taskbar button to display a progress bar.
|
||||
|
||||
![Windows Progress Bar][https://cloud.githubusercontent.com/assets/639601/5081682/16691fda-6f0e-11e4-9676-49b6418f1264.png]
|
||||

|
||||
|
||||
On macOS, the progress bar will be displayed as a part of the dock icon.
|
||||
|
||||
@@ -48,7 +48,7 @@ const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let progressInterval
|
||||
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
|
||||
@@ -166,7 +166,7 @@ Then, add a `createWindow()` function that loads `index.html` into a new `Browse
|
||||
instance.
|
||||
|
||||
```js
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
@@ -216,7 +216,7 @@ To implement this, listen for the `app` module's [`'window-all-closed'`][window-
|
||||
event, and call [`app.quit()`][app-quit] if the user is not on macOS (`darwin`).
|
||||
|
||||
```js
|
||||
app.on('window-all-closed', function () {
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
```
|
||||
@@ -244,7 +244,7 @@ from within your existing `whenReady()` callback.
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', function () {
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
@@ -295,7 +295,7 @@ to the `webPreferences.preload` option in your existing `BrowserWindow` construc
|
||||
const path = require('path')
|
||||
|
||||
// modify your existing createWindow() function
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
@@ -360,7 +360,7 @@ The full code is available below:
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
@@ -383,7 +383,7 @@ function createWindow () {
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', function () {
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
@@ -393,7 +393,7 @@ app.whenReady().then(() => {
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', function () {
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ const { app, BrowserWindow } = require('electron')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
|
||||
@@ -24,7 +24,7 @@ To set the represented file of window, you can use the
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const os = require('os');
|
||||
|
||||
function createWindow () {
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
|
||||
@@ -216,7 +216,7 @@ access to a `window.readConfig()` method, but no Node.js features.
|
||||
```js
|
||||
const { readFileSync } = require('fs')
|
||||
|
||||
window.readConfig = function () {
|
||||
window.readConfig = () => {
|
||||
const data = readFileSync('./config.json')
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -70,10 +70,10 @@ until the maintainers feel the maintenance burden is too high to continue doing
|
||||
|
||||
### Currently supported versions
|
||||
|
||||
* 16.x.y
|
||||
* 15.x.y
|
||||
* 14.x.y
|
||||
* 13.x.y
|
||||
* 12
|
||||
* 13
|
||||
|
||||
### End-of-life
|
||||
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
# Selenium and WebDriver
|
||||
|
||||
From [ChromeDriver - WebDriver for Chrome][chrome-driver]:
|
||||
|
||||
> WebDriver is an open source tool for automated testing of web apps across many
|
||||
> browsers. It provides capabilities for navigating to web pages, user input,
|
||||
> JavaScript execution, and more. ChromeDriver is a standalone server which
|
||||
> implements WebDriver's wire protocol for Chromium. It is being developed by
|
||||
> members of the Chromium and WebDriver teams.
|
||||
|
||||
## Setting up Spectron
|
||||
|
||||
[Spectron][spectron] is the officially supported ChromeDriver testing framework
|
||||
for Electron. It is built on top of [WebdriverIO](https://webdriver.io/) and
|
||||
has helpers to access Electron APIs in your tests and bundles ChromeDriver.
|
||||
|
||||
```sh
|
||||
$ npm install --save-dev spectron
|
||||
```
|
||||
|
||||
```javascript
|
||||
// A simple test to verify a visible window is opened with a title
|
||||
const Application = require('spectron').Application
|
||||
const assert = require('assert')
|
||||
|
||||
const myApp = new Application({
|
||||
path: '/Applications/MyApp.app/Contents/MacOS/MyApp'
|
||||
})
|
||||
|
||||
const verifyWindowIsVisibleWithTitle = async (app) => {
|
||||
await app.start()
|
||||
try {
|
||||
// Check if the window is visible
|
||||
const isVisible = await app.browserWindow.isVisible()
|
||||
// Verify the window is visible
|
||||
assert.strictEqual(isVisible, true)
|
||||
// Get the window's title
|
||||
const title = await app.client.getTitle()
|
||||
// Verify the window's title
|
||||
assert.strictEqual(title, 'My App')
|
||||
} catch (error) {
|
||||
// Log any failures
|
||||
console.error('Test failed', error.message)
|
||||
}
|
||||
// Stop the application
|
||||
await app.stop()
|
||||
}
|
||||
|
||||
verifyWindowIsVisibleWithTitle(myApp)
|
||||
```
|
||||
|
||||
## Setting up with WebDriverJs
|
||||
|
||||
[WebDriverJs](https://www.selenium.dev/selenium/docs/api/javascript/index.html) provides
|
||||
a Node package for testing with web driver, we will use it as an example.
|
||||
|
||||
### 1. Start ChromeDriver
|
||||
|
||||
First you need to download the `chromedriver` binary, and run it:
|
||||
|
||||
```sh
|
||||
$ npm install electron-chromedriver
|
||||
$ ./node_modules/.bin/chromedriver
|
||||
Starting ChromeDriver (v2.10.291558) on port 9515
|
||||
Only local connections are allowed.
|
||||
```
|
||||
|
||||
Remember the port number `9515`, which will be used later
|
||||
|
||||
### 2. Install WebDriverJS
|
||||
|
||||
```sh
|
||||
$ npm install selenium-webdriver
|
||||
```
|
||||
|
||||
### 3. Connect to ChromeDriver
|
||||
|
||||
The usage of `selenium-webdriver` with Electron is the same with
|
||||
upstream, except that you have to manually specify how to connect
|
||||
chrome driver and where to find Electron's binary:
|
||||
|
||||
```javascript
|
||||
const webdriver = require('selenium-webdriver')
|
||||
|
||||
const driver = new webdriver.Builder()
|
||||
// The "9515" is the port opened by chrome driver.
|
||||
.usingServer('http://localhost:9515')
|
||||
.withCapabilities({
|
||||
'goog:chromeOptions': {
|
||||
// Here is the path to your Electron binary.
|
||||
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
|
||||
}
|
||||
})
|
||||
.forBrowser('chrome') // note: use .forBrowser('electron') for selenium-webdriver <= 3.6.0
|
||||
.build()
|
||||
|
||||
driver.get('http://www.google.com')
|
||||
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver')
|
||||
driver.findElement(webdriver.By.name('btnG')).click()
|
||||
driver.wait(() => {
|
||||
return driver.getTitle().then((title) => {
|
||||
return title === 'webdriver - Google Search'
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
driver.quit()
|
||||
```
|
||||
|
||||
## Setting up with WebdriverIO
|
||||
|
||||
[WebdriverIO](https://webdriver.io/) provides a Node package for testing with web
|
||||
driver.
|
||||
|
||||
### 1. Start ChromeDriver
|
||||
|
||||
First you need to download the `chromedriver` binary, and run it:
|
||||
|
||||
```sh
|
||||
$ npm install electron-chromedriver
|
||||
$ ./node_modules/.bin/chromedriver --url-base=wd/hub --port=9515
|
||||
Starting ChromeDriver (v2.10.291558) on port 9515
|
||||
Only local connections are allowed.
|
||||
```
|
||||
|
||||
Remember the port number `9515`, which will be used later
|
||||
|
||||
### 2. Install WebdriverIO
|
||||
|
||||
```sh
|
||||
$ npm install webdriverio
|
||||
```
|
||||
|
||||
### 3. Connect to chrome driver
|
||||
|
||||
```javascript
|
||||
const webdriverio = require('webdriverio')
|
||||
const options = {
|
||||
host: 'localhost', // Use localhost as chrome driver server
|
||||
port: 9515, // "9515" is the port opened by chrome driver.
|
||||
desiredCapabilities: {
|
||||
browserName: 'chrome',
|
||||
'goog:chromeOptions': {
|
||||
binary: '/Path-to-Your-App/electron', // Path to your Electron binary.
|
||||
args: [/* cli arguments */] // Optional, perhaps 'app=' + /path/to/your/app/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const client = webdriverio.remote(options)
|
||||
|
||||
client
|
||||
.init()
|
||||
.url('http://google.com')
|
||||
.setValue('#q', 'webdriverio')
|
||||
.click('#btnG')
|
||||
.getTitle().then((title) => {
|
||||
console.log('Title was: ' + title)
|
||||
})
|
||||
.end()
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
To test your application without rebuilding Electron,
|
||||
[place](application-distribution.md)
|
||||
your app source into Electron's resource directory.
|
||||
|
||||
Alternatively, pass an argument to run with your Electron binary that points to
|
||||
your app's folder. This eliminates the need to copy-paste your app into
|
||||
Electron's resource directory.
|
||||
|
||||
[chrome-driver]: https://sites.google.com/a/chromium.org/chromedriver/
|
||||
[spectron]: https://electronjs.org/spectron
|
||||
271
docs/tutorial/window-customization.md
Normal file
271
docs/tutorial/window-customization.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# Window Customization
|
||||
|
||||
The `BrowserWindow` module is the foundation of your Electron application, and it exposes
|
||||
many APIs that can change the look and behavior of your browser windows. In this
|
||||
tutorial, we will be going over the various use-cases for window customization on
|
||||
macOS, Windows, and Linux.
|
||||
|
||||
## Create frameless windows
|
||||
|
||||
A frameless window is a window that has no [chrome]. Not to be confused with the Google
|
||||
Chrome browser, window _chrome_ refers to the parts of the window (e.g. toolbars, controls)
|
||||
that are not a part of the web page.
|
||||
|
||||
To create a frameless window, you need to set `frame` to `false` in the `BrowserWindow`
|
||||
constructor.
|
||||
|
||||
```javascript title='main.js'
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ frame: false })
|
||||
```
|
||||
|
||||
## Apply custom title bar styles _macOS_ _Windows_
|
||||
|
||||
Title bar styles allow you to hide most of a BrowserWindow's chrome while keeping the
|
||||
system's native window controls intact and can be configured with the `titleBarStyle`
|
||||
option in the `BrowserWindow` constructor.
|
||||
|
||||
Applying the `hidden` title bar style results in a hidden title bar and a full-size
|
||||
content window.
|
||||
|
||||
```javascript title='main.js'
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ titleBarStyle: 'hidden' })
|
||||
```
|
||||
|
||||
### Control the traffic lights _macOS_
|
||||
|
||||
On macOS, applying the `hidden` title bar style will still expose the standard window
|
||||
controls (“traffic lights”) in the top left.
|
||||
|
||||
#### Customize the look of your traffic lights _macOS_
|
||||
|
||||
The `customButtonsOnHover` title bar style will hide the traffic lights until you hover
|
||||
over them. This is useful if you want to create custom traffic lights in your HTML but still
|
||||
use the native UI to control the window.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ titleBarStyle: 'customButtonsOnHover' })
|
||||
```
|
||||
|
||||
#### Customize the traffic light position _macOS_
|
||||
|
||||
To modify the position of the traffic light window controls, there are two configuration
|
||||
options available.
|
||||
|
||||
Applying `hiddenInset` title bar style will shift the vertical inset of the traffic lights
|
||||
by a fixed amount.
|
||||
|
||||
```javascript title='main.js'
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ titleBarStyle: 'hiddenInset' })
|
||||
```
|
||||
|
||||
If you need more granular control over the positioning of the traffic lights, you can pass
|
||||
a set of coordinates to the `trafficLightPosition` option in the `BrowserWindow`
|
||||
constructor.
|
||||
|
||||
```javascript title='main.js'
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({
|
||||
titleBarStyle: 'hidden',
|
||||
trafficLightPosition: { x: 10, y: 10 }
|
||||
})
|
||||
```
|
||||
|
||||
#### Show and hide the traffic lights programmatically _macOS_
|
||||
|
||||
You can also show and hide the traffic lights programmatically from the main process.
|
||||
The `win.setWindowButtonVisibility` forces traffic lights to be show or hidden depending
|
||||
on the value of its boolean parameter.
|
||||
|
||||
```javascript title='main.js'
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow()
|
||||
// hides the traffic lights
|
||||
win.setWindowButtonVisibility(false)
|
||||
```
|
||||
|
||||
> Note: Given the number of APIs available, there are many ways of achieving this. For instance,
|
||||
> combining `frame: false` with `win.setWindowButtonVisibility(true)` will yield the same
|
||||
> layout outcome as setting `titleBarStyle: 'hidden'`.
|
||||
|
||||
## Window Controls Overlay _macOS_ _Windows_
|
||||
|
||||
The [Window Controls Overlay API] is a web standard that gives web apps the ability to
|
||||
customize their title bar region when installed on desktop. Electron exposes this API
|
||||
through the `BrowserWindow` constructor option `titleBarOverlay`.
|
||||
|
||||
This option only works whenever a custom `titlebarStyle` is applied on macOS or Windows.
|
||||
When `titleBarOverlay` is enabled, the window controls become exposed in their default
|
||||
position, and DOM elements cannot use the area underneath this region.
|
||||
|
||||
The `titleBarOverlay` option accepts two different value formats.
|
||||
|
||||
Specifying `true` on either platform will result in an overlay region with default
|
||||
system colors:
|
||||
|
||||
```javascript title='main.js'
|
||||
// on macOS or Windows
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({
|
||||
titleBarStyle: 'hidden',
|
||||
titleBarOverlay: true
|
||||
})
|
||||
```
|
||||
|
||||
On Windows, you can also specify the color of the overlay and its symbols by setting
|
||||
`titleBarOverlay` to an object with the `color` and `symbolColor` properties. If an option
|
||||
is not specified, the color will default to its system color for the window control buttons:
|
||||
|
||||
```javascript title='main.js'
|
||||
// on Windows
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({
|
||||
titleBarStyle: 'hidden',
|
||||
titleBarOverlay: {
|
||||
color: '#2f3241',
|
||||
symbolColor: '#74b1be'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
> Note: Once your title bar overlay is enabled from the main process, you can access the overlay's
|
||||
> 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].
|
||||
|
||||
## Create transparent windows
|
||||
|
||||
By setting the `transparent` option to `true`, you can make a fully transparent window.
|
||||
|
||||
```javascript title='main.js'
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow({ transparent: true })
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
* You cannot click through the transparent area. See
|
||||
[#1335](https://github.com/electron/electron/issues/1335) for details.
|
||||
* Transparent windows are not resizable. Setting `resizable` to `true` may make
|
||||
a transparent window stop working on some platforms.
|
||||
* The CSS [`blur()`] filter only applies to the window's web contents, so there is no way to apply
|
||||
blur effect to the content below the window (i.e. other applications open on
|
||||
the user's system).
|
||||
* The window will not be transparent when DevTools is opened.
|
||||
* On _Windows_:
|
||||
* Transparent windows will not work when DWM is disabled.
|
||||
* Transparent windows can not be maximized using the Windows system menu or by double
|
||||
clicking the title bar. The reasoning behind this can be seen on
|
||||
PR [#28207](https://github.com/electron/electron/pull/28207).
|
||||
* On _macOS_:
|
||||
* The native window shadow will not be shown on a transparent window.
|
||||
|
||||
## Create click-through windows
|
||||
|
||||
To create a click-through window, i.e. making the window ignore all mouse
|
||||
events, you can call the [win.setIgnoreMouseEvents(ignore)][ignore-mouse-events]
|
||||
API:
|
||||
|
||||
```javascript title='main.js'
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow()
|
||||
win.setIgnoreMouseEvents(true)
|
||||
```
|
||||
|
||||
### Forward mouse events _macOS_ _Windows_
|
||||
|
||||
Ignoring mouse messages makes the web contents oblivious to mouse movement,
|
||||
meaning that mouse movement events will not be emitted. On Windows and macOS, an
|
||||
optional parameter can be used to forward mouse move messages to the web page,
|
||||
allowing events such as `mouseleave` to be emitted:
|
||||
|
||||
```javascript title='main.js'
|
||||
const { BrowserWindow, ipcMain } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
const win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('set-ignore-mouse-events', (event, ...args) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
win.setIgnoreMouseEvents(...args)
|
||||
})
|
||||
```
|
||||
|
||||
```javascript title='preload.js'
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const el = document.getElementById('clickThroughElement')
|
||||
el.addEventListener('mouseenter', () => {
|
||||
ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
})
|
||||
el.addEventListener('mouseleave', () => {
|
||||
ipcRenderer.send('set-ignore-mouse-events', false)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
This makes the web page click-through when over the `#clickThroughElement` element,
|
||||
and returns to normal outside it.
|
||||
|
||||
## Set custom draggable region
|
||||
|
||||
By default, the frameless window is non-draggable. Apps need to specify
|
||||
`-webkit-app-region: drag` in CSS to tell Electron which regions are draggable
|
||||
(like the OS's standard titlebar), and apps can also use
|
||||
`-webkit-app-region: no-drag` to exclude the non-draggable area from the
|
||||
draggable region. Note that only rectangular shapes are currently supported.
|
||||
|
||||
To make the whole window draggable, you can add `-webkit-app-region: drag` as
|
||||
`body`'s style:
|
||||
|
||||
```css title='styles.css'
|
||||
body {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
```
|
||||
|
||||
And note that if you have made the whole window draggable, you must also mark
|
||||
buttons as non-draggable, otherwise it would be impossible for users to click on
|
||||
them:
|
||||
|
||||
```css title='styles.css'
|
||||
button {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
```
|
||||
|
||||
If you're only setting a custom titlebar as draggable, you also need to make all
|
||||
buttons in titlebar non-draggable.
|
||||
|
||||
### Tip: disable text selection
|
||||
|
||||
When creating a draggable region, the dragging behavior may conflict with text selection.
|
||||
For example, when you drag the titlebar, you may accidentally select its text contents.
|
||||
To prevent this, you need to disable text selection within a draggable area like this:
|
||||
|
||||
```css
|
||||
.titlebar {
|
||||
-webkit-user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
```
|
||||
|
||||
### Tip: disable context menus
|
||||
|
||||
On some platforms, the draggable area will be treated as a non-client frame, so
|
||||
when you right click on it, a system menu will pop up. To make the context menu
|
||||
behave correctly on all platforms, you should never use a custom context menu on
|
||||
draggable areas.
|
||||
|
||||
[`blur()`]: https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/blur()
|
||||
[`BrowserWindow`]: ../api/browser-window.md
|
||||
[chrome]: https://developer.mozilla.org/en-US/docs/Glossary/Chrome
|
||||
[ignore-mouse-events]: ../api/browser-window.md#winsetignoremouseeventsignore-options
|
||||
[overlay-css-env-vars]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#css-environment-variables
|
||||
[overlay-javascript-apis]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#javascript-apis
|
||||
[Window Controls Overlay API]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md
|
||||
@@ -19,6 +19,7 @@
|
||||
<includes>
|
||||
<include name="IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE" file="${target_gen_dir}/shell_devtools_discovery_page.html" use_base_dir="false" type="BINDATA" />
|
||||
<include name="IDR_PDF_MANIFEST" file="../chrome/browser/resources/pdf/manifest.json" type="BINDATA" />
|
||||
<include name="IDR_CRYPTOTOKEN_MANIFEST" file="../chrome/browser/resources/cryptotoken/manifest.json" type="BINDATA" />
|
||||
</includes>
|
||||
</release>
|
||||
</grit>
|
||||
|
||||
@@ -23,7 +23,6 @@ auto_filenames = {
|
||||
"docs/api/environment-variables.md",
|
||||
"docs/api/extensions.md",
|
||||
"docs/api/file-object.md",
|
||||
"docs/api/frameless-window.md",
|
||||
"docs/api/global-shortcut.md",
|
||||
"docs/api/in-app-purchase.md",
|
||||
"docs/api/incoming-message.md",
|
||||
@@ -136,16 +135,14 @@ auto_filenames = {
|
||||
|
||||
sandbox_bundle_deps = [
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-events.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/native-image.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
@@ -170,7 +167,6 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
isolated_bundle_deps = [
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/isolated_renderer/init.ts",
|
||||
"lib/renderer/web-view/web-view-attributes.ts",
|
||||
@@ -207,7 +203,6 @@ auto_filenames = {
|
||||
"lib/browser/api/menu.ts",
|
||||
"lib/browser/api/message-channel.ts",
|
||||
"lib/browser/api/module-list.ts",
|
||||
"lib/browser/api/native-image.ts",
|
||||
"lib/browser/api/native-theme.ts",
|
||||
"lib/browser/api/net-log.ts",
|
||||
"lib/browser/api/net.ts",
|
||||
@@ -242,13 +237,13 @@ auto_filenames = {
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/parse-features-string.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-events.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
@@ -265,13 +260,12 @@ auto_filenames = {
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-events.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
@@ -280,7 +274,6 @@ auto_filenames = {
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/native-image.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
@@ -306,12 +299,12 @@ auto_filenames = {
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
@@ -319,7 +312,6 @@ auto_filenames = {
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/native-image.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
|
||||
@@ -44,8 +44,8 @@ filenames = {
|
||||
]
|
||||
|
||||
lib_sources_linux_x11 = [
|
||||
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc",
|
||||
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h",
|
||||
"shell/browser/ui/views/global_menu_bar_registrar_x11.cc",
|
||||
"shell/browser/ui/views/global_menu_bar_registrar_x11.h",
|
||||
"shell/browser/ui/views/global_menu_bar_x11.cc",
|
||||
"shell/browser/ui/views/global_menu_bar_x11.h",
|
||||
"shell/browser/ui/x/event_disabler.cc",
|
||||
@@ -56,13 +56,9 @@ filenames = {
|
||||
"shell/browser/ui/x/x_window_utils.h",
|
||||
]
|
||||
|
||||
lib_sources_posix = [
|
||||
"chromium_src/chrome/browser/process_singleton_posix.cc",
|
||||
"shell/browser/electron_browser_main_parts_posix.cc",
|
||||
]
|
||||
lib_sources_posix = [ "shell/browser/electron_browser_main_parts_posix.cc" ]
|
||||
|
||||
lib_sources_win = [
|
||||
"chromium_src/chrome/browser/process_singleton_win.cc",
|
||||
"shell/browser/api/electron_api_power_monitor_win.cc",
|
||||
"shell/browser/api/electron_api_system_preferences_win.cc",
|
||||
"shell/browser/browser_win.cc",
|
||||
@@ -237,7 +233,6 @@ filenames = {
|
||||
]
|
||||
|
||||
lib_sources = [
|
||||
"chromium_src/chrome/browser/process_singleton.h",
|
||||
"shell/app/command_line_args.cc",
|
||||
"shell/app/command_line_args.h",
|
||||
"shell/app/electron_content_client.cc",
|
||||
@@ -509,8 +504,6 @@ filenames = {
|
||||
"shell/browser/web_contents_preferences.h",
|
||||
"shell/browser/web_contents_zoom_controller.cc",
|
||||
"shell/browser/web_contents_zoom_controller.h",
|
||||
"shell/browser/web_dialog_helper.cc",
|
||||
"shell/browser/web_dialog_helper.h",
|
||||
"shell/browser/web_view_guest_delegate.cc",
|
||||
"shell/browser/web_view_guest_delegate.h",
|
||||
"shell/browser/web_view_manager.cc",
|
||||
@@ -682,14 +675,11 @@ filenames = {
|
||||
"shell/utility/electron_content_utility_client.h",
|
||||
]
|
||||
|
||||
lib_sources_nss = [
|
||||
"chromium_src/chrome/browser/certificate_manager_model.cc",
|
||||
"chromium_src/chrome/browser/certificate_manager_model.h",
|
||||
]
|
||||
|
||||
lib_sources_extensions = [
|
||||
"shell/browser/extensions/api/i18n/i18n_api.cc",
|
||||
"shell/browser/extensions/api/i18n/i18n_api.h",
|
||||
"shell/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc",
|
||||
"shell/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.h",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.cc",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.h",
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.cc",
|
||||
|
||||
@@ -143,7 +143,10 @@ export const roleList: Record<RoleId, Role> = {
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: w => w.webContents.toggleDevTools()
|
||||
webContentsMethod: wc => {
|
||||
const bw = wc.getOwnerBrowserWindow();
|
||||
if (bw) bw.webContents.toggleDevTools();
|
||||
}
|
||||
},
|
||||
togglefullscreen: {
|
||||
label: 'Toggle Full Screen',
|
||||
|
||||
@@ -16,7 +16,6 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'Menu', loader: () => require('./menu') },
|
||||
{ name: 'MenuItem', loader: () => require('./menu-item') },
|
||||
{ name: 'MessageChannelMain', loader: () => require('./message-channel') },
|
||||
{ name: 'nativeImage', loader: () => require('./native-image') },
|
||||
{ name: 'nativeTheme', loader: () => require('./native-theme') },
|
||||
{ name: 'net', loader: () => require('./net') },
|
||||
{ name: 'netLog', loader: () => require('./net-log') },
|
||||
|
||||
@@ -593,6 +593,9 @@ WebContents.prototype._init = function () {
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
} else {
|
||||
addReplyToEvent(event);
|
||||
if (this.listenerCount('ipc-message-sync') === 0 && ipcMain.listenerCount(channel) === 0) {
|
||||
console.warn(`WebContents #${this.id} called ipcRenderer.sendSync() with '${channel}' channel without listeners.`);
|
||||
}
|
||||
this.emit('ipc-message-sync', event, channel, ...args);
|
||||
ipcMain.emit(channel, event, ...args);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ ipcMainInternal.handle(IPC_MESSAGES.INSPECTOR_CONTEXT_MENU, function (event, ite
|
||||
|
||||
const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve);
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
const window = event.sender.getOwnerBrowserWindow()!;
|
||||
|
||||
menu.popup({ window, callback: () => resolve() });
|
||||
});
|
||||
@@ -94,7 +94,7 @@ ipcMainUtils.handleSync(IPC_MESSAGES.INSPECTOR_CONFIRM, async function (event, m
|
||||
buttons: ['OK', 'Cancel'],
|
||||
cancelId: 1
|
||||
};
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
const window = event.sender.getOwnerBrowserWindow()!;
|
||||
const { response } = await dialog.showMessageBox(window, options);
|
||||
return response === 0;
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-util
|
||||
import { parseWebViewWebPreferences } from '@electron/internal/common/parse-features-string';
|
||||
import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods';
|
||||
import { webViewEvents } from '@electron/internal/common/web-view-events';
|
||||
import { serialize } from '@electron/internal/common/type-utils';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
interface GuestInstance {
|
||||
@@ -15,6 +14,7 @@ interface GuestInstance {
|
||||
}
|
||||
|
||||
const webViewManager = process._linkedBinding('electron_browser_web_view_manager');
|
||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
||||
|
||||
const supportedWebViewEvents = Object.keys(webViewEvents);
|
||||
|
||||
@@ -28,8 +28,54 @@ function sanitizeOptionsForGuest (options: Record<string, any>) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function makeWebPreferences (embedder: Electron.WebContents, params: Record<string, any>) {
|
||||
// parse the 'webpreferences' attribute string, if set
|
||||
// this uses the same parsing rules as window.open uses for its features
|
||||
const parsedWebPreferences =
|
||||
typeof params.webpreferences === 'string'
|
||||
? parseWebViewWebPreferences(params.webpreferences)
|
||||
: null;
|
||||
|
||||
const webPreferences: Electron.WebPreferences = {
|
||||
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
|
||||
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
|
||||
plugins: params.plugins,
|
||||
zoomFactor: embedder.zoomFactor,
|
||||
disablePopups: !params.allowpopups,
|
||||
webSecurity: !params.disablewebsecurity,
|
||||
enableBlinkFeatures: params.blinkfeatures,
|
||||
disableBlinkFeatures: params.disableblinkfeatures,
|
||||
...parsedWebPreferences
|
||||
};
|
||||
|
||||
if (params.preload) {
|
||||
webPreferences.preloadURL = params.preload;
|
||||
}
|
||||
|
||||
// Security options that guest will always inherit from embedder
|
||||
const inheritedWebPreferences = new Map([
|
||||
['contextIsolation', true],
|
||||
['javascript', false],
|
||||
['nativeWindowOpen', true],
|
||||
['nodeIntegration', false],
|
||||
['sandbox', true],
|
||||
['nodeIntegrationInSubFrames', false],
|
||||
['enableWebSQL', false]
|
||||
]);
|
||||
|
||||
// Inherit certain option values from embedder
|
||||
const lastWebPreferences = embedder.getLastWebPreferences()!;
|
||||
for (const [name, value] of inheritedWebPreferences) {
|
||||
if (lastWebPreferences[name as keyof Electron.WebPreferences] === value) {
|
||||
(webPreferences as any)[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return webPreferences;
|
||||
}
|
||||
|
||||
// Create a new guest instance.
|
||||
const createGuest = function (embedder: Electron.WebContents, params: Record<string, any>) {
|
||||
const createGuest = function (embedder: Electron.WebContents, embedderFrameId: number, elementInstanceId: number, params: Record<string, any>) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const guest = (webContents as typeof ElectronInternal.WebContents).create({
|
||||
type: 'webview',
|
||||
@@ -81,27 +127,33 @@ const createGuest = function (embedder: Electron.WebContents, params: Record<str
|
||||
}
|
||||
};
|
||||
|
||||
// Dispatch events to embedder.
|
||||
const fn = function (event: string) {
|
||||
guest.on(event as any, function (_, ...args: any[]) {
|
||||
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, event, ...args);
|
||||
const makeProps = (eventKey: string, args: any[]) => {
|
||||
const props: Record<string, any> = {};
|
||||
webViewEvents[eventKey].forEach((prop, index) => {
|
||||
props[prop] = args[index];
|
||||
});
|
||||
return props;
|
||||
};
|
||||
|
||||
// Dispatch events to embedder.
|
||||
for (const event of supportedWebViewEvents) {
|
||||
if (event !== 'new-window') {
|
||||
fn(event);
|
||||
}
|
||||
guest.on(event as any, function (_, ...args: any[]) {
|
||||
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, event, makeProps(event, args));
|
||||
});
|
||||
}
|
||||
|
||||
guest.on('new-window', function (event, url, frameName, disposition, options, additionalFeatures, referrer) {
|
||||
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'new-window', url,
|
||||
frameName, disposition, sanitizeOptionsForGuest(options),
|
||||
additionalFeatures, referrer);
|
||||
guest.on('new-window', function (event, url, frameName, disposition, options) {
|
||||
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'new-window', {
|
||||
url,
|
||||
frameName,
|
||||
disposition,
|
||||
options: sanitizeOptionsForGuest(options)
|
||||
});
|
||||
});
|
||||
|
||||
// Dispatch guest's IPC messages to embedder.
|
||||
guest.on('ipc-message-host' as any, function (event: Electron.IpcMainEvent, channel: string, args: any[]) {
|
||||
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_IPC_MESSAGE, {
|
||||
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'ipc-message', {
|
||||
frameId: [event.processId, event.frameId],
|
||||
channel,
|
||||
args
|
||||
@@ -117,20 +169,22 @@ const createGuest = function (embedder: Electron.WebContents, params: Record<str
|
||||
}
|
||||
});
|
||||
|
||||
return guestInstanceId;
|
||||
if (attachGuest(embedder, embedderFrameId, elementInstanceId, guestInstanceId, params)) {
|
||||
return guestInstanceId;
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Attach the guest to an element of embedder.
|
||||
const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
||||
embedderFrameId: number, elementInstanceId: number, guestInstanceId: number, params: Record<string, any>) {
|
||||
const embedder = event.sender;
|
||||
const attachGuest = function (embedder: Electron.WebContents, embedderFrameId: number, elementInstanceId: number, guestInstanceId: number, params: Record<string, any>) {
|
||||
// Destroy the old guest when attaching.
|
||||
const key = `${embedder.id}-${elementInstanceId}`;
|
||||
const oldGuestInstanceId = embedderElementsMap.get(key);
|
||||
if (oldGuestInstanceId != null) {
|
||||
// Reattachment to the same guest is just a no-op.
|
||||
if (oldGuestInstanceId === guestInstanceId) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const oldGuestInstance = guestInstances.get(oldGuestInstanceId);
|
||||
@@ -142,11 +196,13 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
||||
const guestInstance = guestInstances.get(guestInstanceId);
|
||||
// If this isn't a valid guest instance then do nothing.
|
||||
if (!guestInstance) {
|
||||
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`);
|
||||
console.error(new Error(`Guest attach failed: Invalid guestInstanceId ${guestInstanceId}`));
|
||||
return false;
|
||||
}
|
||||
const { guest } = guestInstance;
|
||||
if (guest.hostWebContents !== embedder) {
|
||||
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`);
|
||||
console.error(new Error(`Guest attach failed: Access denied to guestInstanceId ${guestInstanceId}`));
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this guest is already attached to an element then remove it
|
||||
@@ -161,54 +217,14 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
||||
}
|
||||
}
|
||||
|
||||
// parse the 'webpreferences' attribute string, if set
|
||||
// this uses the same parsing rules as window.open uses for its features
|
||||
const parsedWebPreferences =
|
||||
typeof params.webpreferences === 'string'
|
||||
? parseWebViewWebPreferences(params.webpreferences)
|
||||
: null;
|
||||
|
||||
const webPreferences: Electron.WebPreferences = {
|
||||
guestInstanceId,
|
||||
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
|
||||
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
|
||||
plugins: params.plugins,
|
||||
zoomFactor: embedder.zoomFactor,
|
||||
disablePopups: !params.allowpopups,
|
||||
webSecurity: !params.disablewebsecurity,
|
||||
enableBlinkFeatures: params.blinkfeatures,
|
||||
disableBlinkFeatures: params.disableblinkfeatures,
|
||||
...parsedWebPreferences
|
||||
};
|
||||
|
||||
if (params.preload) {
|
||||
webPreferences.preloadURL = params.preload;
|
||||
}
|
||||
|
||||
// Security options that guest will always inherit from embedder
|
||||
const inheritedWebPreferences = new Map([
|
||||
['contextIsolation', true],
|
||||
['javascript', false],
|
||||
['nativeWindowOpen', true],
|
||||
['nodeIntegration', false],
|
||||
['sandbox', true],
|
||||
['nodeIntegrationInSubFrames', false],
|
||||
['enableWebSQL', false]
|
||||
]);
|
||||
|
||||
// Inherit certain option values from embedder
|
||||
const lastWebPreferences = embedder.getLastWebPreferences();
|
||||
for (const [name, value] of inheritedWebPreferences) {
|
||||
if (lastWebPreferences[name as keyof Electron.WebPreferences] === value) {
|
||||
(webPreferences as any)[name] = value;
|
||||
}
|
||||
}
|
||||
const webPreferences = makeWebPreferences(embedder, params);
|
||||
|
||||
const event = eventBinding.createWithSender(embedder);
|
||||
embedder.emit('will-attach-webview', event, webPreferences, params);
|
||||
if (event.defaultPrevented) {
|
||||
if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId;
|
||||
guest.destroy();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
guest.attachParams = params;
|
||||
@@ -222,6 +238,7 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
||||
|
||||
webViewManager.addGuest(guestInstanceId, embedder, guest, webPreferences);
|
||||
guest.attachToIframe(embedder, embedderFrameId);
|
||||
return true;
|
||||
};
|
||||
|
||||
// Remove an guest-embedder relationship.
|
||||
@@ -306,16 +323,8 @@ const handleMessageSync = function (channel: string, handler: (event: ElectronIn
|
||||
ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler));
|
||||
};
|
||||
|
||||
handleMessage(IPC_MESSAGES.GUEST_VIEW_MANAGER_CREATE_GUEST, function (event, params) {
|
||||
return createGuest(event.sender, params);
|
||||
});
|
||||
|
||||
handleMessage(IPC_MESSAGES.GUEST_VIEW_MANAGER_ATTACH_GUEST, function (event, embedderFrameId: number, elementInstanceId: number, guestInstanceId: number, params) {
|
||||
try {
|
||||
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params);
|
||||
} catch (error) {
|
||||
console.error(`Guest attach failed: ${error}`);
|
||||
}
|
||||
handleMessage(IPC_MESSAGES.GUEST_VIEW_MANAGER_CREATE_AND_ATTACH_GUEST, function (event, embedderFrameId: number, elementInstanceId: number, params) {
|
||||
return createGuest(event.sender, embedderFrameId, elementInstanceId, params);
|
||||
});
|
||||
|
||||
handleMessageSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_DETACH_GUEST, function (event, guestInstanceId: number) {
|
||||
@@ -363,12 +372,6 @@ handleMessageSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_PROPERTY_SET, function (event,
|
||||
(guest as any)[property] = val;
|
||||
});
|
||||
|
||||
handleMessage(IPC_MESSAGES.GUEST_VIEW_MANAGER_CAPTURE_PAGE, async function (event, guestInstanceId: number, args: any[]) {
|
||||
const guest = getGuestForWebContents(guestInstanceId, event.sender);
|
||||
|
||||
return serialize(await guest.capturePage(...args));
|
||||
});
|
||||
|
||||
// Returns WebContents from its guest id hosted in given webContents.
|
||||
const getGuestForWebContents = function (guestInstanceId: number, contents: Electron.WebContents) {
|
||||
const guestInstance = guestInstances.get(guestInstanceId);
|
||||
|
||||
@@ -65,8 +65,13 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
|
||||
// https://html.spec.whatwg.org/multipage/window-object.html#apis-for-creating-and-navigating-browsing-contexts-by-name
|
||||
const existingWindow = getGuestWindowByFrameName(frameName);
|
||||
if (existingWindow) {
|
||||
existingWindow.loadURL(url);
|
||||
return existingWindow;
|
||||
if (existingWindow.isDestroyed() || existingWindow.webContents.isDestroyed()) {
|
||||
// FIXME(t57ser): The webContents is destroyed for some reason, unregister the frame name
|
||||
unregisterFrameName(frameName);
|
||||
} else {
|
||||
existingWindow.loadURL(url);
|
||||
return existingWindow;
|
||||
}
|
||||
}
|
||||
|
||||
const window = new BrowserWindow({
|
||||
@@ -144,7 +149,7 @@ function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs,
|
||||
postData?: PostData,
|
||||
}): boolean {
|
||||
const { url, frameName } = windowOpenArgs;
|
||||
const isWebViewWithPopupsDisabled = embedder.getType() === 'webview' && embedder.getLastWebPreferences().disablePopups;
|
||||
const isWebViewWithPopupsDisabled = embedder.getType() === 'webview' && embedder.getLastWebPreferences()!.disablePopups;
|
||||
const postBody = postData ? {
|
||||
data: postData,
|
||||
...parseContentTypeFormat(postData)
|
||||
@@ -229,7 +234,7 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||
// have unvetted prefs, use parsedWebPreferences.
|
||||
secureOverrideWebPreferences?: BrowserWindowConstructorOptions['webPreferences'],
|
||||
}) {
|
||||
const parentWebPreferences = embedder.getLastWebPreferences();
|
||||
const parentWebPreferences = embedder.getLastWebPreferences()!;
|
||||
const securityWebPreferencesFromParent = (Object.keys(securityWebPreferences).reduce((map, key) => {
|
||||
if (securityWebPreferences[key] === parentWebPreferences[key as keyof Electron.WebPreferences]) {
|
||||
(map as any)[key] = parentWebPreferences[key as keyof Electron.WebPreferences];
|
||||
|
||||
@@ -28,7 +28,7 @@ const getGuestWindow = function (guestContents: WebContents) {
|
||||
};
|
||||
|
||||
const isChildWindow = function (sender: WebContents, target: WebContents) {
|
||||
return target.getLastWebPreferences().openerId === sender.id;
|
||||
return target.getLastWebPreferences()!.openerId === sender.id;
|
||||
};
|
||||
|
||||
const isRelatedWindow = function (sender: WebContents, target: WebContents) {
|
||||
@@ -43,7 +43,7 @@ const isScriptableWindow = function (sender: WebContents, target: WebContents) {
|
||||
};
|
||||
|
||||
const isNodeIntegrationEnabled = function (sender: WebContents) {
|
||||
return sender.getLastWebPreferences().nodeIntegration === true;
|
||||
return sender.getLastWebPreferences()!.nodeIntegration === true;
|
||||
};
|
||||
|
||||
// Checks whether |sender| can access the |target|:
|
||||
@@ -65,7 +65,7 @@ ipcMainInternal.on(
|
||||
features: string
|
||||
) => {
|
||||
// This should only be allowed for senders that have nativeWindowOpen: false
|
||||
const lastWebPreferences = event.sender.getLastWebPreferences();
|
||||
const lastWebPreferences = event.sender.getLastWebPreferences()!;
|
||||
if (lastWebPreferences.nativeWindowOpen || lastWebPreferences.sandbox) {
|
||||
event.returnValue = null;
|
||||
throw new Error(
|
||||
|
||||
@@ -4,7 +4,6 @@ import { clipboard } from 'electron/common';
|
||||
import * as fs from 'fs';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
||||
import * as typeUtils from '@electron/internal/common/type-utils';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
import type * as desktopCapturerModule from '@electron/internal/browser/desktop-capturer';
|
||||
@@ -60,7 +59,7 @@ ipcMainUtils.handleSync(IPC_MESSAGES.BROWSER_CLIPBOARD_SYNC, function (event, me
|
||||
throw new Error(`Invalid method: ${method}`);
|
||||
}
|
||||
|
||||
return typeUtils.serialize((clipboard as any)[method](...typeUtils.deserialize(args)));
|
||||
return (clipboard as any)[method](...args);
|
||||
});
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
@@ -75,7 +74,7 @@ if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return typeUtils.serialize(await desktopCapturer.getSourcesImpl(event.sender, options));
|
||||
return await desktopCapturer.getSourcesImpl(event.sender, options);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
import type * as ipcRendererUtilsModule from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
import type * as typeUtilsModule from '@electron/internal/common/type-utils';
|
||||
|
||||
const clipboard = process._linkedBinding('electron_common_clipboard');
|
||||
|
||||
if (process.type === 'renderer') {
|
||||
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') as typeof ipcRendererUtilsModule;
|
||||
const typeUtils = require('@electron/internal/common/type-utils') as typeof typeUtilsModule;
|
||||
|
||||
const makeRemoteMethod = function (method: keyof Electron.Clipboard) {
|
||||
return (...args: any[]) => {
|
||||
args = typeUtils.serialize(args);
|
||||
const result = ipcRendererUtils.invokeSync(IPC_MESSAGES.BROWSER_CLIPBOARD_SYNC, method, ...args);
|
||||
return typeUtils.deserialize(result);
|
||||
};
|
||||
const makeRemoteMethod = function (method: keyof Electron.Clipboard): any {
|
||||
return (...args: any[]) => ipcRendererUtils.invokeSync(IPC_MESSAGES.BROWSER_CLIPBOARD_SYNC, method, ...args);
|
||||
};
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Common modules, please sort alphabetically
|
||||
export const commonModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'clipboard', loader: () => require('./clipboard') },
|
||||
{ name: 'nativeImage', loader: () => require('./native-image') },
|
||||
{ name: 'shell', loader: () => require('./shell') },
|
||||
// The internal modules, invisible unless you know their names.
|
||||
{ name: 'deprecate', loader: () => require('./deprecate'), private: true }
|
||||
|
||||
@@ -10,14 +10,11 @@ export const enum IPC_MESSAGES {
|
||||
|
||||
GUEST_VIEW_INTERNAL_DESTROY_GUEST = 'GUEST_VIEW_INTERNAL_DESTROY_GUEST',
|
||||
GUEST_VIEW_INTERNAL_DISPATCH_EVENT = 'GUEST_VIEW_INTERNAL_DISPATCH_EVENT',
|
||||
GUEST_VIEW_INTERNAL_IPC_MESSAGE = 'GUEST_VIEW_INTERNAL_IPC_MESSAGE',
|
||||
|
||||
GUEST_VIEW_MANAGER_CREATE_GUEST = 'GUEST_VIEW_MANAGER_CREATE_GUEST',
|
||||
GUEST_VIEW_MANAGER_ATTACH_GUEST = 'GUEST_VIEW_MANAGER_ATTACH_GUEST',
|
||||
GUEST_VIEW_MANAGER_CREATE_AND_ATTACH_GUEST = 'GUEST_VIEW_MANAGER_CREATE_AND_ATTACH_GUEST',
|
||||
GUEST_VIEW_MANAGER_DETACH_GUEST = 'GUEST_VIEW_MANAGER_DETACH_GUEST',
|
||||
GUEST_VIEW_MANAGER_FOCUS_CHANGE = 'GUEST_VIEW_MANAGER_FOCUS_CHANGE',
|
||||
GUEST_VIEW_MANAGER_CALL = 'GUEST_VIEW_MANAGER_CALL',
|
||||
GUEST_VIEW_MANAGER_CAPTURE_PAGE = 'GUEST_VIEW_MANAGER_CAPTURE_PAGE',
|
||||
GUEST_VIEW_MANAGER_PROPERTY_GET = 'GUEST_VIEW_MANAGER_PROPERTY_GET',
|
||||
GUEST_VIEW_MANAGER_PROPERTY_SET = 'GUEST_VIEW_MANAGER_PROPERTY_SET',
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
export const enum IPC_MESSAGES {
|
||||
BROWSER_REQUIRE = 'REMOTE_BROWSER_REQUIRE',
|
||||
BROWSER_GET_BUILTIN = 'REMOTE_BROWSER_GET_BUILTIN',
|
||||
BROWSER_GET_GLOBAL = 'REMOTE_BROWSER_GET_GLOBAL',
|
||||
BROWSER_GET_CURRENT_WINDOW = 'REMOTE_BROWSER_GET_CURRENT_WINDOW',
|
||||
BROWSER_GET_CURRENT_WEB_CONTENTS = 'REMOTE_BROWSER_GET_CURRENT_WEB_CONTENTS',
|
||||
BROWSER_CONSTRUCTOR = 'REMOTE_BROWSER_CONSTRUCTOR',
|
||||
BROWSER_FUNCTION_CALL = 'REMOTE_BROWSER_FUNCTION_CALL',
|
||||
BROWSER_MEMBER_CONSTRUCTOR = 'REMOTE_BROWSER_MEMBER_CONSTRUCTOR',
|
||||
BROWSER_MEMBER_CALL = 'REMOTE_BROWSER_MEMBER_CALL',
|
||||
BROWSER_MEMBER_GET = 'REMOTE_BROWSER_MEMBER_GET',
|
||||
BROWSER_MEMBER_SET = 'REMOTE_BROWSER_MEMBER_SET',
|
||||
BROWSER_DEREFERENCE = 'REMOTE_BROWSER_DEREFERENCE',
|
||||
BROWSER_CONTEXT_RELEASE = 'REMOTE_BROWSER_CONTEXT_RELEASE',
|
||||
BROWSER_WRONG_CONTEXT_ERROR = 'REMOTE_BROWSER_WRONG_CONTEXT_ERROR',
|
||||
|
||||
RENDERER_CALLBACK = 'REMOTE_RENDERER_CALLBACK',
|
||||
RENDERER_RELEASE_CALLBACK = 'REMOTE_RENDERER_RELEASE_CALLBACK',
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
function getCreateNativeImage () {
|
||||
return process._linkedBinding('electron_common_native_image').nativeImage.createEmpty;
|
||||
}
|
||||
|
||||
export function isPromise (val: any) {
|
||||
return (
|
||||
val &&
|
||||
val.then &&
|
||||
val.then instanceof Function &&
|
||||
val.constructor &&
|
||||
val.constructor.reject &&
|
||||
val.constructor.reject instanceof Function &&
|
||||
val.constructor.resolve &&
|
||||
val.constructor.resolve instanceof Function
|
||||
);
|
||||
}
|
||||
|
||||
const serializableTypes = [
|
||||
Boolean,
|
||||
Number,
|
||||
String,
|
||||
Date,
|
||||
Error,
|
||||
RegExp,
|
||||
ArrayBuffer
|
||||
];
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#Supported_types
|
||||
export function isSerializableObject (value: any) {
|
||||
return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type);
|
||||
}
|
||||
|
||||
const objectMap = function (source: Object, mapper: (value: any) => any) {
|
||||
const sourceEntries = Object.entries(source);
|
||||
const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]);
|
||||
return Object.fromEntries(targetEntries);
|
||||
};
|
||||
|
||||
function serializeNativeImage (image: Electron.NativeImage) {
|
||||
const representations = [];
|
||||
const scaleFactors = image.getScaleFactors();
|
||||
|
||||
// Use Buffer when there's only one representation for better perf.
|
||||
// This avoids compressing to/from PNG where it's not necessary to
|
||||
// ensure uniqueness of dataURLs (since there's only one).
|
||||
if (scaleFactors.length === 1) {
|
||||
const scaleFactor = scaleFactors[0];
|
||||
const size = image.getSize(scaleFactor);
|
||||
const buffer = image.toBitmap({ scaleFactor });
|
||||
representations.push({ scaleFactor, size, buffer });
|
||||
} else {
|
||||
// Construct from dataURLs to ensure that they are not lost in creation.
|
||||
for (const scaleFactor of scaleFactors) {
|
||||
const size = image.getSize(scaleFactor);
|
||||
const dataURL = image.toDataURL({ scaleFactor });
|
||||
representations.push({ scaleFactor, size, dataURL });
|
||||
}
|
||||
}
|
||||
return { __ELECTRON_SERIALIZED_NativeImage__: true, representations };
|
||||
}
|
||||
|
||||
function deserializeNativeImage (value: any, createNativeImage: typeof Electron.nativeImage['createEmpty']) {
|
||||
const image = createNativeImage();
|
||||
|
||||
// Use Buffer when there's only one representation for better perf.
|
||||
// This avoids compressing to/from PNG where it's not necessary to
|
||||
// ensure uniqueness of dataURLs (since there's only one).
|
||||
if (value.representations.length === 1) {
|
||||
const { buffer, size, scaleFactor } = value.representations[0];
|
||||
const { width, height } = size;
|
||||
image.addRepresentation({ buffer, scaleFactor, width, height });
|
||||
} else {
|
||||
// Construct from dataURLs to ensure that they are not lost in creation.
|
||||
for (const rep of value.representations) {
|
||||
const { dataURL, size, scaleFactor } = rep;
|
||||
const { width, height } = size;
|
||||
image.addRepresentation({ dataURL, scaleFactor, width, height });
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
export function serialize (value: any): any {
|
||||
if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
||||
return serializeNativeImage(value);
|
||||
} if (Array.isArray(value)) {
|
||||
return value.map(serialize);
|
||||
} else if (isSerializableObject(value)) {
|
||||
return value;
|
||||
} else if (value instanceof Object) {
|
||||
return objectMap(value, serialize);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export function deserialize (value: any, createNativeImage: typeof Electron.nativeImage['createEmpty'] = getCreateNativeImage()): any {
|
||||
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
|
||||
return deserializeNativeImage(value, createNativeImage);
|
||||
} else if (Array.isArray(value)) {
|
||||
return value.map(value => deserialize(value, createNativeImage));
|
||||
} else if (isSerializableObject(value)) {
|
||||
return value;
|
||||
} else if (value instanceof Object) {
|
||||
return objectMap(value, value => deserialize(value, createNativeImage));
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ export const webViewEvents: Record<string, readonly string[]> = {
|
||||
'devtools-opened': [],
|
||||
'devtools-closed': [],
|
||||
'devtools-focused': [],
|
||||
'new-window': ['url', 'frameName', 'disposition', 'options'],
|
||||
'will-navigate': ['url'],
|
||||
'did-start-navigation': ['url', 'isInPlace', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
|
||||
'did-redirect-navigation': ['url', 'isInPlace', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
|
||||
|
||||
@@ -59,6 +59,7 @@ export const properties = new Set([
|
||||
]);
|
||||
|
||||
export const asyncMethods = new Set([
|
||||
'capturePage',
|
||||
'loadURL',
|
||||
'executeJavaScript',
|
||||
'insertCSS',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import { deserialize } from '@electron/internal/common/type-utils';
|
||||
import deprecate from '@electron/internal/common/api/deprecate';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
const { hasSwitch } = process._linkedBinding('electron_common_command_line');
|
||||
@@ -14,6 +14,11 @@ function getCurrentStack () {
|
||||
return (target as any).stack;
|
||||
}
|
||||
|
||||
let warned = process.noDeprecation;
|
||||
export async function getSources (options: Electron.SourcesOptions) {
|
||||
return deserialize(await ipcRendererInternal.invoke(IPC_MESSAGES.DESKTOP_CAPTURER_GET_SOURCES, options, getCurrentStack()));
|
||||
if (!warned) {
|
||||
deprecate.log('The use of \'desktopCapturer.getSources\' in the renderer process is deprecated and will be removed. See https://www.electronjs.org/docs/breaking-changes#removed-desktopcapturergetsources-in-the-renderer for more details.');
|
||||
warned = true;
|
||||
}
|
||||
return await ipcRendererInternal.invoke(IPC_MESSAGES.DESKTOP_CAPTURER_GET_SOURCES, options, getCurrentStack());
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'contextBridge', loader: () => require('./context-bridge') },
|
||||
{ name: 'crashReporter', loader: () => require('./crash-reporter') },
|
||||
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
||||
{ name: 'nativeImage', loader: () => require('./native-image') },
|
||||
{ name: 'webFrame', loader: () => require('./web-frame') }
|
||||
];
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
const { nativeImage } = process._linkedBinding('electron_common_native_image');
|
||||
|
||||
export default nativeImage;
|
||||
@@ -78,7 +78,7 @@ const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
|
||||
const usesNativeWindowOpen = mainFrame.getWebPreference('nativeWindowOpen');
|
||||
const preloadScript = mainFrame.getWebPreference('preload');
|
||||
const preloadScripts = mainFrame.getWebPreference('preloadScripts');
|
||||
const guestInstanceId = mainFrame.getWebPreference('guestInstanceId');
|
||||
const isWebView = mainFrame.getWebPreference('isWebView');
|
||||
const openerId = mainFrame.getWebPreference('openerId');
|
||||
const appPath = hasSwitch('app-path') ? getSwitchValue('app-path') : null;
|
||||
|
||||
@@ -102,14 +102,14 @@ switch (window.location.protocol) {
|
||||
default: {
|
||||
// Override default web functions.
|
||||
const { windowSetup } = require('@electron/internal/renderer/window-setup') as typeof windowSetupModule;
|
||||
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen);
|
||||
windowSetup(isWebView, openerId, isHiddenPage, usesNativeWindowOpen);
|
||||
}
|
||||
}
|
||||
|
||||
// Load webview tag implementation.
|
||||
if (process.isMainFrame) {
|
||||
const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') as typeof webViewInitModule;
|
||||
webViewInit(contextIsolation, webviewTag, guestInstanceId);
|
||||
webViewInit(contextIsolation, webviewTag, isWebView);
|
||||
}
|
||||
|
||||
if (nodeIntegration) {
|
||||
|
||||
@@ -77,15 +77,8 @@ const isLocalhost = function () {
|
||||
*
|
||||
* @returns {boolean} Is a CSP with `unsafe-eval` set?
|
||||
*/
|
||||
const isUnsafeEvalEnabled: () => Promise<boolean> = function () {
|
||||
return webFrame.executeJavaScript(`(${(() => {
|
||||
try {
|
||||
eval(window.trustedTypes.emptyScript); // eslint-disable-line no-eval
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).toString()})()`, false);
|
||||
const isUnsafeEvalEnabled = () => {
|
||||
return webFrame._isEvalAllowed();
|
||||
};
|
||||
|
||||
const moreInformation = `\nFor more information and help, consult
|
||||
@@ -174,16 +167,14 @@ const warnAboutDisabledWebSecurity = function (webPreferences?: Electron.WebPref
|
||||
* Logs a warning message about unset or insecure CSP
|
||||
*/
|
||||
const warnAboutInsecureCSP = function () {
|
||||
isUnsafeEvalEnabled().then((enabled) => {
|
||||
if (!enabled) return;
|
||||
if (!isUnsafeEvalEnabled()) return;
|
||||
|
||||
const warning = `This renderer process has either no Content Security
|
||||
Policy set or a policy with "unsafe-eval" enabled. This exposes users of
|
||||
this app to unnecessary security risks.\n${moreInformation}`;
|
||||
const warning = `This renderer process has either no Content Security
|
||||
Policy set or a policy with "unsafe-eval" enabled. This exposes users of
|
||||
this app to unnecessary security risks.\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
|
||||
'font-weight: bold;', warning);
|
||||
}).catch(() => {});
|
||||
console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
|
||||
'font-weight: bold;', warning);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
import { webViewEvents } from '@electron/internal/common/web-view-events';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
const { mainFrame: webFrame } = process._linkedBinding('electron_renderer_web_frame');
|
||||
@@ -14,45 +13,27 @@ const DEPRECATED_EVENTS: Record<string, string> = {
|
||||
'page-title-updated': 'page-title-set'
|
||||
} as const;
|
||||
|
||||
const dispatchEvent = function (delegate: GuestViewDelegate, eventName: string, eventKey: string, ...args: Array<any>) {
|
||||
if (DEPRECATED_EVENTS[eventName] != null) {
|
||||
dispatchEvent(delegate, DEPRECATED_EVENTS[eventName], eventKey, ...args);
|
||||
}
|
||||
|
||||
const props: Record<string, any> = {};
|
||||
webViewEvents[eventKey].forEach((prop, index) => {
|
||||
props[prop] = args[index];
|
||||
});
|
||||
|
||||
delegate.dispatchEvent(eventName, props);
|
||||
};
|
||||
|
||||
export function registerEvents (viewInstanceId: number, delegate: GuestViewDelegate) {
|
||||
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DESTROY_GUEST}-${viewInstanceId}`, function () {
|
||||
delegate.reset();
|
||||
delegate.dispatchEvent('destroyed', {});
|
||||
});
|
||||
|
||||
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT}-${viewInstanceId}`, function (event, eventName, ...args) {
|
||||
dispatchEvent(delegate, eventName, eventName, ...args);
|
||||
});
|
||||
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT}-${viewInstanceId}`, function (event, eventName, props) {
|
||||
if (DEPRECATED_EVENTS[eventName] != null) {
|
||||
delegate.dispatchEvent(DEPRECATED_EVENTS[eventName], props);
|
||||
}
|
||||
|
||||
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_IPC_MESSAGE}-${viewInstanceId}`, function (event, data) {
|
||||
delegate.dispatchEvent('ipc-message', data);
|
||||
delegate.dispatchEvent(eventName, props);
|
||||
});
|
||||
}
|
||||
|
||||
export function deregisterEvents (viewInstanceId: number) {
|
||||
ipcRendererInternal.removeAllListeners(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DESTROY_GUEST}-${viewInstanceId}`);
|
||||
ipcRendererInternal.removeAllListeners(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT}-${viewInstanceId}`);
|
||||
ipcRendererInternal.removeAllListeners(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_IPC_MESSAGE}-${viewInstanceId}`);
|
||||
}
|
||||
|
||||
export function createGuest (params: Record<string, any>): Promise<number> {
|
||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CREATE_GUEST, params);
|
||||
}
|
||||
|
||||
export function attachGuest (iframe: HTMLIFrameElement, elementInstanceId: number, guestInstanceId: number, params: Record<string, any>) {
|
||||
export function createGuest (iframe: HTMLIFrameElement, elementInstanceId: number, params: Record<string, any>): Promise<number> {
|
||||
if (!(iframe instanceof HTMLIFrameElement)) {
|
||||
throw new Error('Invalid embedder frame');
|
||||
}
|
||||
@@ -62,17 +43,13 @@ export function attachGuest (iframe: HTMLIFrameElement, elementInstanceId: numbe
|
||||
throw new Error('Invalid embedder frame');
|
||||
}
|
||||
|
||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_ATTACH_GUEST, embedderFrameId, elementInstanceId, guestInstanceId, params);
|
||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CREATE_AND_ATTACH_GUEST, embedderFrameId, elementInstanceId, params);
|
||||
}
|
||||
|
||||
export function detachGuest (guestInstanceId: number) {
|
||||
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_DETACH_GUEST, guestInstanceId);
|
||||
}
|
||||
|
||||
export function capturePage (guestInstanceId: number, args: any[]) {
|
||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CAPTURE_PAGE, guestInstanceId, args);
|
||||
}
|
||||
|
||||
export function invoke (guestInstanceId: number, method: string, args: any[]) {
|
||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CALL, guestInstanceId, method, args);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-vie
|
||||
import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods';
|
||||
import type { WebViewAttribute, PartitionAttribute } from '@electron/internal/renderer/web-view/web-view-attributes';
|
||||
import { setupWebViewAttributes } from '@electron/internal/renderer/web-view/web-view-attributes';
|
||||
import { deserialize } from '@electron/internal/common/type-utils';
|
||||
|
||||
// ID generator.
|
||||
let nextId = 0;
|
||||
@@ -16,7 +15,6 @@ export interface WebViewImplHooks {
|
||||
readonly guestViewInternal: typeof guestViewInternalModule;
|
||||
readonly allowGuestViewElementDefinition: NodeJS.InternalWebFrame['allowGuestViewElementDefinition'];
|
||||
readonly setIsWebView: (iframe: HTMLIFrameElement) => void;
|
||||
readonly createNativeImage?: typeof Electron.nativeImage['createEmpty'];
|
||||
}
|
||||
|
||||
// Represents the internal state of the WebView node.
|
||||
@@ -115,9 +113,11 @@ export class WebViewImpl {
|
||||
}
|
||||
|
||||
createGuest () {
|
||||
this.hooks.guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => {
|
||||
this.attachGuestInstance(guestInstanceId);
|
||||
});
|
||||
this.internalInstanceId = getNextId();
|
||||
this.hooks.guestViewInternal.createGuest(this.internalElement, this.internalInstanceId, this.buildParams())
|
||||
.then(guestInstanceId => {
|
||||
this.attachGuestInstance(guestInstanceId);
|
||||
});
|
||||
}
|
||||
|
||||
dispatchEvent (eventName: string, props: Record<string, any> = {}) {
|
||||
@@ -192,20 +192,19 @@ export class WebViewImpl {
|
||||
}
|
||||
|
||||
attachGuestInstance (guestInstanceId: number) {
|
||||
if (!this.elementAttached) {
|
||||
// The element could be detached before we got response from browser.
|
||||
if (guestInstanceId === -1) {
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
this.internalInstanceId = getNextId();
|
||||
|
||||
if (!this.elementAttached) {
|
||||
// The element could be detached before we got response from browser.
|
||||
// Destroy the backing webContents to avoid any zombie nodes in the frame tree.
|
||||
this.hooks.guestViewInternal.detachGuest(guestInstanceId);
|
||||
return;
|
||||
}
|
||||
|
||||
this.guestInstanceId = guestInstanceId;
|
||||
|
||||
this.hooks.guestViewInternal.attachGuest(
|
||||
this.internalElement,
|
||||
this.internalInstanceId,
|
||||
this.guestInstanceId,
|
||||
this.buildParams()
|
||||
);
|
||||
|
||||
// TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not
|
||||
// even documented.
|
||||
this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this));
|
||||
@@ -234,10 +233,6 @@ export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElem
|
||||
};
|
||||
}
|
||||
|
||||
WebViewElement.prototype.capturePage = async function (...args) {
|
||||
return deserialize(await hooks.guestViewInternal.capturePage(this.getWebContentsId(), args), hooks.createNativeImage);
|
||||
};
|
||||
|
||||
const createPropertyGetter = function (property: string) {
|
||||
return function (this: ElectronInternal.WebViewElement) {
|
||||
return hooks.guestViewInternal.propertyGet(this.getWebContentsId(), property);
|
||||
|
||||
@@ -20,9 +20,9 @@ function handleFocusBlur () {
|
||||
});
|
||||
}
|
||||
|
||||
export function webViewInit (contextIsolation: boolean, webviewTag: boolean, guestInstanceId: number) {
|
||||
export function webViewInit (contextIsolation: boolean, webviewTag: boolean, isWebView: boolean) {
|
||||
// Don't allow recursive `<webview>`.
|
||||
if (webviewTag && !guestInstanceId) {
|
||||
if (webviewTag && !isWebView) {
|
||||
const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal') as typeof guestViewInternalModule;
|
||||
if (contextIsolation) {
|
||||
v8Util.setHiddenValue(window, 'guestViewInternal', guestViewInternal);
|
||||
@@ -36,7 +36,7 @@ export function webViewInit (contextIsolation: boolean, webviewTag: boolean, gue
|
||||
}
|
||||
}
|
||||
|
||||
if (guestInstanceId) {
|
||||
if (isWebView) {
|
||||
// Report focus/blur events of webview to browser.
|
||||
handleFocusBlur();
|
||||
}
|
||||
|
||||
@@ -243,8 +243,8 @@ class BrowserWindowProxy {
|
||||
}
|
||||
|
||||
export const windowSetup = (
|
||||
guestInstanceId: number, openerId: number, isHiddenPage: boolean, usesNativeWindowOpen: boolean) => {
|
||||
if (!process.sandboxed && !guestInstanceId) {
|
||||
isWebView: boolean, openerId: number, isHiddenPage: boolean, usesNativeWindowOpen: boolean) => {
|
||||
if (!process.sandboxed && !isWebView) {
|
||||
// Override default window.close.
|
||||
window.close = function () {
|
||||
ipcRendererInternal.send(IPC_MESSAGES.BROWSER_WINDOW_CLOSE);
|
||||
@@ -318,7 +318,7 @@ export const windowSetup = (
|
||||
});
|
||||
}
|
||||
|
||||
if (guestInstanceId) {
|
||||
if (isWebView) {
|
||||
// Webview `document.visibilityState` tracks window visibility (and ignores
|
||||
// the actual <webview> element visibility) for backwards compatibility.
|
||||
// See discussion in #9178.
|
||||
|
||||
@@ -13,7 +13,7 @@ export const moduleList: ElectronInternal.ModuleEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'nativeImage',
|
||||
loader: () => require('@electron/internal/renderer/api/native-image')
|
||||
loader: () => require('@electron/internal/common/api/native-image')
|
||||
},
|
||||
{
|
||||
name: 'webFrame',
|
||||
|
||||
@@ -131,7 +131,7 @@ const contextIsolation = mainFrame.getWebPreference('contextIsolation');
|
||||
const webviewTag = mainFrame.getWebPreference('webviewTag');
|
||||
const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
|
||||
const usesNativeWindowOpen = true;
|
||||
const guestInstanceId = mainFrame.getWebPreference('guestInstanceId');
|
||||
const isWebView = mainFrame.getWebPreference('isWebView');
|
||||
const openerId = mainFrame.getWebPreference('openerId');
|
||||
|
||||
switch (window.location.protocol) {
|
||||
@@ -149,14 +149,14 @@ switch (window.location.protocol) {
|
||||
default: {
|
||||
// Override default web functions.
|
||||
const { windowSetup } = require('@electron/internal/renderer/window-setup') as typeof windowSetupModule;
|
||||
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen);
|
||||
windowSetup(isWebView, openerId, isHiddenPage, usesNativeWindowOpen);
|
||||
}
|
||||
}
|
||||
|
||||
// Load webview tag implementation.
|
||||
if (process.isMainFrame) {
|
||||
const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') as typeof webViewInitModule;
|
||||
webViewInit(contextIsolation, webviewTag, guestInstanceId);
|
||||
webViewInit(contextIsolation, webviewTag, isWebView);
|
||||
}
|
||||
|
||||
// Wrap the script into a function executed in global scope. It won't have
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "15.2.0",
|
||||
"version": "16.0.0",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
@@ -56,8 +56,8 @@
|
||||
"minimist": "^1.2.5",
|
||||
"null-loader": "^4.0.0",
|
||||
"pre-flight": "^1.1.0",
|
||||
"remark-cli": "^4.0.0",
|
||||
"remark-preset-lint-markdown-style-guide": "^2.1.1",
|
||||
"remark-cli": "^10.0.0",
|
||||
"remark-preset-lint-markdown-style-guide": "^4.0.0",
|
||||
"semver": "^5.6.0",
|
||||
"shx": "^0.3.2",
|
||||
"standard-markdown": "^6.0.0",
|
||||
@@ -78,7 +78,7 @@
|
||||
"generate-version-json": "node script/generate-version-json.js",
|
||||
"lint": "node ./script/lint.js && npm run lint:clang-format && npm run lint:docs",
|
||||
"lint:js": "node ./script/lint.js --js",
|
||||
"lint:clang-format": "python script/run-clang-format.py -r -c chromium_src/ shell/ || (echo \"\\nCode not formatted correctly.\" && exit 1)",
|
||||
"lint:clang-format": "python script/run-clang-format.py -r -c shell/ || (echo \"\\nCode not formatted correctly.\" && exit 1)",
|
||||
"lint:clang-tidy": "ts-node ./script/run-clang-tidy.ts",
|
||||
"lint:cpp": "node ./script/lint.js --cc",
|
||||
"lint:objc": "node ./script/lint.js --objc",
|
||||
|
||||
@@ -2,3 +2,4 @@ expose_ripemd160.patch
|
||||
expose_aes-cfb.patch
|
||||
expose_des-ede3.patch
|
||||
fix_sync_evp_get_cipherbynid_and_evp_get_cipherbyname.patch
|
||||
enable_x509_v_flag_trusted_first_flag.patch
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Juan Cruz Viotti <jv@jviotti.com>
|
||||
Date: Thu, 30 Sep 2021 13:39:23 -0400
|
||||
Subject: Enable X509_V_FLAG_TRUSTED_FIRST flag
|
||||
|
||||
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
|
||||
|
||||
diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
|
||||
index 5a881d64c30076404cc800fff9e943bb0b30d2ac..29d5341efc8eb7ae6f90bdde5a8032e99f75c98e 100644
|
||||
--- a/crypto/x509/x509_vpm.c
|
||||
+++ b/crypto/x509/x509_vpm.c
|
||||
@@ -528,7 +528,7 @@ static const X509_VERIFY_PARAM default_table[] = {
|
||||
(char *)"default", /* X509 default parameters */
|
||||
0, /* Check time */
|
||||
0, /* internal flags */
|
||||
- 0, /* flags */
|
||||
+ X509_V_FLAG_TRUSTED_FIRST, /* flags */
|
||||
0, /* purpose */
|
||||
0, /* trust */
|
||||
100, /* depth */
|
||||
@@ -12,26 +12,27 @@ https://boringssl-review.googlesource.com/c/boringssl/+/33984 for a
|
||||
similar patch that was merged upstream.
|
||||
|
||||
diff --git a/crypto/cipher_extra/cipher_extra.c b/crypto/cipher_extra/cipher_extra.c
|
||||
index 786a5d5fb13d7ceafc9b7d58c0aaccb88552506d..5ede89f9f0761d1da1baa899e9a02b77ffcffe93 100644
|
||||
index 62850ab6a216d401d023f81007fb59a33b4585f3..0c30b0329d32b94b22f342f95035e927797d0aaf 100644
|
||||
--- a/crypto/cipher_extra/cipher_extra.c
|
||||
+++ b/crypto/cipher_extra/cipher_extra.c
|
||||
@@ -105,10 +105,14 @@ const EVP_CIPHER *EVP_get_cipherbyname(const char *name) {
|
||||
return EVP_des_ede3_cbc();
|
||||
} else if (OPENSSL_strcasecmp(name, "aes-128-cbc") == 0) {
|
||||
return EVP_aes_128_cbc();
|
||||
+ } else if (OPENSSL_strcasecmp(name, "aes-128-cfb") == 0) {
|
||||
+ return EVP_aes_128_cfb128();
|
||||
} else if (OPENSSL_strcasecmp(name, "aes-192-cbc") == 0) {
|
||||
return EVP_aes_192_cbc();
|
||||
} else if (OPENSSL_strcasecmp(name, "aes-256-cbc") == 0) {
|
||||
return EVP_aes_256_cbc();
|
||||
+ } else if (OPENSSL_strcasecmp(name, "aes-256-cfb") == 0) {
|
||||
+ return EVP_aes_256_cfb128();
|
||||
} else if (OPENSSL_strcasecmp(name, "aes-128-ctr") == 0) {
|
||||
return EVP_aes_128_ctr();
|
||||
} else if (OPENSSL_strcasecmp(name, "aes-192-ctr") == 0) {
|
||||
@@ -73,6 +73,7 @@ static const struct {
|
||||
const EVP_CIPHER *(*func)(void);
|
||||
} kCiphers[] = {
|
||||
{NID_aes_128_cbc, "aes-128-cbc", EVP_aes_128_cbc},
|
||||
+ {NID_aes_128_cfb128, "aes-128-cfb", EVP_aes_128_cfb128},
|
||||
{NID_aes_128_ctr, "aes-128-ctr", EVP_aes_128_ctr},
|
||||
{NID_aes_128_ecb, "aes-128-ecb", EVP_aes_128_ecb},
|
||||
{NID_aes_128_gcm, "aes-128-gcm", EVP_aes_128_gcm},
|
||||
@@ -83,6 +84,7 @@ static const struct {
|
||||
{NID_aes_192_gcm, "aes-192-gcm", EVP_aes_192_gcm},
|
||||
{NID_aes_192_ofb128, "aes-192-ofb", EVP_aes_192_ofb},
|
||||
{NID_aes_256_cbc, "aes-256-cbc", EVP_aes_256_cbc},
|
||||
+ {NID_aes_256_cfb128, "aes-256-cfb", EVP_aes_256_cfb128},
|
||||
{NID_aes_256_ctr, "aes-256-ctr", EVP_aes_256_ctr},
|
||||
{NID_aes_256_ecb, "aes-256-ecb", EVP_aes_256_ecb},
|
||||
{NID_aes_256_gcm, "aes-256-gcm", EVP_aes_256_gcm},
|
||||
diff --git a/decrepit/evp/evp_do_all.c b/decrepit/evp/evp_do_all.c
|
||||
index 5a41a7b7dc9afee65d9004c497da735073715bd3..c6c901eaff474eaa3f06128ea825b8203d064a52 100644
|
||||
index 852b76bea69988e0b3ac76a17b603128f239dde0..d443f4dc2daea0b7aa86ae75d31d995fae667ba9 100644
|
||||
--- a/decrepit/evp/evp_do_all.c
|
||||
+++ b/decrepit/evp/evp_do_all.c
|
||||
@@ -20,8 +20,10 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
@@ -57,10 +58,10 @@ index 5a41a7b7dc9afee65d9004c497da735073715bd3..c6c901eaff474eaa3f06128ea825b820
|
||||
callback(EVP_aes_192_ctr(), "aes-192-ctr", NULL, arg);
|
||||
callback(EVP_aes_256_ctr(), "aes-256-ctr", NULL, arg);
|
||||
diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h
|
||||
index badd496293fb9748adacff10478ea702d1155c5f..8565934bac9a810281b04946cb9f38d7623320f7 100644
|
||||
index 09d72ec2c343f23409d35eca804a02e3290c86f1..1bea1a88b265312d700729ad2828f0b5d9f2d84f 100644
|
||||
--- a/include/openssl/cipher.h
|
||||
+++ b/include/openssl/cipher.h
|
||||
@@ -430,6 +430,7 @@ OPENSSL_EXPORT const EVP_CIPHER *EVP_des_ede3_ecb(void);
|
||||
@@ -436,6 +436,7 @@ OPENSSL_EXPORT const EVP_CIPHER *EVP_des_ede3_ecb(void);
|
||||
|
||||
// EVP_aes_128_cfb128 is only available in decrepit.
|
||||
OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_128_cfb128(void);
|
||||
|
||||
@@ -6,20 +6,19 @@ Subject: expose des-ede3
|
||||
This should be upstreamed.
|
||||
|
||||
diff --git a/crypto/cipher_extra/cipher_extra.c b/crypto/cipher_extra/cipher_extra.c
|
||||
index 5ede89f9f0761d1da1baa899e9a02b77ffcffe93..8205e121c152fe4e2d8df34a1ac2fe0498381f31 100644
|
||||
index 0c30b0329d32b94b22f342f95035e927797d0aaf..d97f67fb03756169446edf6b41d3a33fe3ae8205 100644
|
||||
--- a/crypto/cipher_extra/cipher_extra.c
|
||||
+++ b/crypto/cipher_extra/cipher_extra.c
|
||||
@@ -97,6 +97,8 @@ const EVP_CIPHER *EVP_get_cipherbyname(const char *name) {
|
||||
return EVP_rc4();
|
||||
} else if (OPENSSL_strcasecmp(name, "des-cbc") == 0) {
|
||||
return EVP_des_cbc();
|
||||
+ } else if (OPENSSL_strcasecmp(name, "des-ede3") == 0) {
|
||||
+ return EVP_des_ede3();
|
||||
} else if (OPENSSL_strcasecmp(name, "des-ede3-cbc") == 0 ||
|
||||
// This is not a name used by OpenSSL, but tcpdump registers it
|
||||
// with |EVP_add_cipher_alias|. Our |EVP_add_cipher_alias| is a
|
||||
@@ -93,6 +93,7 @@ static const struct {
|
||||
{NID_des_ecb, "des-ecb", EVP_des_ecb},
|
||||
{NID_des_ede_cbc, "des-ede-cbc", EVP_des_ede_cbc},
|
||||
{NID_des_ede_ecb, "des-ede", EVP_des_ede},
|
||||
+ {NID_des_ede3_ecb, "des-ede3", EVP_des_ede3},
|
||||
{NID_des_ede3_cbc, "des-ede3-cbc", EVP_des_ede3_cbc},
|
||||
{NID_rc2_cbc, "rc2-cbc", EVP_rc2_cbc},
|
||||
{NID_rc4, "rc4", EVP_rc4},
|
||||
diff --git a/decrepit/evp/evp_do_all.c b/decrepit/evp/evp_do_all.c
|
||||
index c6c901eaff474eaa3f06128ea825b8203d064a52..f577cd23bf72b94b2651fe5eeb757106f5adaea2 100644
|
||||
index d443f4dc2daea0b7aa86ae75d31d995fae667ba9..5e71420b765019edea82a33884ace539cd91bda5 100644
|
||||
--- a/decrepit/evp/evp_do_all.c
|
||||
+++ b/decrepit/evp/evp_do_all.c
|
||||
@@ -39,6 +39,7 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
|
||||
@@ -10,12 +10,12 @@ this patch is required to provide ripemd160 support in the nodejs crypto
|
||||
module.
|
||||
|
||||
diff --git a/crypto/digest_extra/digest_extra.c b/crypto/digest_extra/digest_extra.c
|
||||
index a93601c170dc407f7d6d628fb7e2d2f0788467f3..00911ee69bb99a34b938ea6a62cd10bc930ec95c 100644
|
||||
index 8cbb28e3afde3dbae3887b22e8b607fa7303e89f..32caba196eb9f0823f774dac9e91314035b3ff7f 100644
|
||||
--- a/crypto/digest_extra/digest_extra.c
|
||||
+++ b/crypto/digest_extra/digest_extra.c
|
||||
@@ -84,6 +84,7 @@ static const struct nid_to_digest nid_to_digest_mapping[] = {
|
||||
{NID_sha384, EVP_sha384, SN_sha384, LN_sha384},
|
||||
@@ -85,6 +85,7 @@ static const struct nid_to_digest nid_to_digest_mapping[] = {
|
||||
{NID_sha512, EVP_sha512, SN_sha512, LN_sha512},
|
||||
{NID_sha512_256, EVP_sha512_256, SN_sha512_256, LN_sha512_256},
|
||||
{NID_md5_sha1, EVP_md5_sha1, SN_md5_sha1, LN_md5_sha1},
|
||||
+ {NID_ripemd160, EVP_ripemd160, SN_ripemd160, LN_ripemd160},
|
||||
// As a remnant of signing |EVP_MD|s, OpenSSL returned the corresponding
|
||||
@@ -62,21 +62,21 @@ index f006ebbc53eea78ce0337a076a05285f22da7a18..7b9309f39a2e5dc6e61bb89e5d32b176
|
||||
+
|
||||
#undef CHECK
|
||||
diff --git a/decrepit/evp/evp_do_all.c b/decrepit/evp/evp_do_all.c
|
||||
index d8023e08f70a060aed083212fefd1de3ecc142e4..5a41a7b7dc9afee65d9004c497da735073715bd3 100644
|
||||
index a3fb077b9b9e66d1bc524fd7987622e73aa4776a..852b76bea69988e0b3ac76a17b603128f239dde0 100644
|
||||
--- a/decrepit/evp/evp_do_all.c
|
||||
+++ b/decrepit/evp/evp_do_all.c
|
||||
@@ -78,6 +78,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
|
||||
callback(EVP_sha256(), "SHA256", NULL, arg);
|
||||
@@ -79,6 +79,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
|
||||
callback(EVP_sha384(), "SHA384", NULL, arg);
|
||||
callback(EVP_sha512(), "SHA512", NULL, arg);
|
||||
+ callback(EVP_ripemd160(), "RIPEMD160", NULL, arg);
|
||||
callback(EVP_sha512_256(), "SHA512-256", NULL, arg);
|
||||
+ callback(EVP_ripemd160(), "ripemd160", NULL, arg);
|
||||
|
||||
callback(EVP_md4(), "md4", NULL, arg);
|
||||
callback(EVP_md5(), "md5", NULL, arg);
|
||||
@@ -86,6 +87,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
|
||||
callback(EVP_sha256(), "sha256", NULL, arg);
|
||||
@@ -88,6 +89,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
|
||||
callback(EVP_sha384(), "sha384", NULL, arg);
|
||||
callback(EVP_sha512(), "sha512", NULL, arg);
|
||||
callback(EVP_sha512_256(), "sha512-256", NULL, arg);
|
||||
+ callback(EVP_ripemd160(), "ripemd160", NULL, arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,71 +12,14 @@ and should also hold in BoringSSL.
|
||||
This will be upstreamed.
|
||||
|
||||
diff --git a/crypto/cipher_extra/cipher_extra.c b/crypto/cipher_extra/cipher_extra.c
|
||||
index 8205e121c152fe4e2d8df34a1ac2fe0498381f31..0870ffe6bff3f647907d7df66d7bf74916be1836 100644
|
||||
index d97f67fb03756169446edf6b41d3a33fe3ae8205..cfdb69e3c556fea11aa7c2d28d4b7da524df15c3 100644
|
||||
--- a/crypto/cipher_extra/cipher_extra.c
|
||||
+++ b/crypto/cipher_extra/cipher_extra.c
|
||||
@@ -69,20 +69,58 @@
|
||||
@@ -97,6 +97,7 @@ static const struct {
|
||||
{NID_des_ede3_cbc, "des-ede3-cbc", EVP_des_ede3_cbc},
|
||||
{NID_rc2_cbc, "rc2-cbc", EVP_rc2_cbc},
|
||||
{NID_rc4, "rc4", EVP_rc4},
|
||||
+ {NID_rc2_40_cbc, "rc2-40-cbc", EVP_rc2_40_cbc}
|
||||
};
|
||||
|
||||
const EVP_CIPHER *EVP_get_cipherbynid(int nid) {
|
||||
switch (nid) {
|
||||
- case NID_rc2_cbc:
|
||||
- return EVP_rc2_cbc();
|
||||
- case NID_rc2_40_cbc:
|
||||
- return EVP_rc2_40_cbc();
|
||||
+ case NID_rc4:
|
||||
+ return EVP_rc4();
|
||||
+ case NID_des_cbc:
|
||||
+ return EVP_des_cbc();
|
||||
+ case NID_des_ede3_ecb:
|
||||
+ return EVP_des_ede3();
|
||||
case NID_des_ede3_cbc:
|
||||
return EVP_des_ede3_cbc();
|
||||
- case NID_des_ede_cbc:
|
||||
- return EVP_des_cbc();
|
||||
case NID_aes_128_cbc:
|
||||
return EVP_aes_128_cbc();
|
||||
+ case NID_aes_128_cfb128:
|
||||
+ return EVP_aes_128_cfb128();
|
||||
case NID_aes_192_cbc:
|
||||
return EVP_aes_192_cbc();
|
||||
case NID_aes_256_cbc:
|
||||
return EVP_aes_256_cbc();
|
||||
+ case NID_aes_256_cfb128:
|
||||
+ return EVP_aes_256_cfb128();
|
||||
+ case NID_aes_128_ctr:
|
||||
+ return EVP_aes_128_ctr();
|
||||
+ case NID_aes_192_ctr:
|
||||
+ return EVP_aes_192_ctr();
|
||||
+ case NID_aes_256_ctr:
|
||||
+ return EVP_aes_256_ctr();
|
||||
+ case NID_aes_128_ecb:
|
||||
+ return EVP_aes_128_ecb();
|
||||
+ case NID_aes_192_ecb:
|
||||
+ return EVP_aes_192_ecb();
|
||||
+ case NID_aes_256_ecb:
|
||||
+ return EVP_aes_256_ecb();
|
||||
+ case NID_aes_128_gcm:
|
||||
+ return EVP_aes_128_gcm();
|
||||
+ case NID_aes_192_gcm:
|
||||
+ return EVP_aes_192_gcm();
|
||||
+ case NID_aes_256_gcm:
|
||||
+ return EVP_aes_256_gcm();
|
||||
+ case NID_aes_128_ofb128:
|
||||
+ return EVP_aes_128_ofb();
|
||||
+ case NID_aes_192_ofb128:
|
||||
+ return EVP_aes_192_ofb();
|
||||
+ case NID_aes_256_ofb128:
|
||||
+ return EVP_aes_256_ofb();
|
||||
+ case NID_des_ecb:
|
||||
+ return EVP_des_ecb();
|
||||
+ case NID_des_ede_ecb:
|
||||
+ return EVP_des_ede();
|
||||
+ case NID_des_ede_cbc:
|
||||
+ return EVP_des_ede_cbc();
|
||||
+ case NID_rc2_cbc:
|
||||
+ return EVP_rc2_cbc();
|
||||
+ case NID_rc2_40_cbc:
|
||||
+ return EVP_rc2_40_cbc();
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,6 @@ webview_fullscreen.patch
|
||||
disable_unload_metrics.patch
|
||||
fix_add_check_for_sandbox_then_result.patch
|
||||
extend_apply_webpreferences.patch
|
||||
fix_expose_decrementcapturercount_in_web_contents_impl.patch
|
||||
add_setter_for_browsermainloop_result_code.patch
|
||||
make_include_of_stack_trace_h_unconditional.patch
|
||||
build_libc_as_static_library.patch
|
||||
@@ -102,7 +101,13 @@ refactor_restore_base_adaptcallbackforrepeating.patch
|
||||
hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch
|
||||
don_t_run_pcscan_notifythreadcreated_if_pcscan_is_disabled.patch
|
||||
logging_win32_only_create_a_console_if_logging_to_stderr.patch
|
||||
feat_expose_raw_response_headers_from_urlloader.patch
|
||||
fix_media_key_usage_with_globalshortcuts.patch
|
||||
cherry-pick-ec42dfd3545f.patch
|
||||
cherry-pick-39090918efac.patch
|
||||
feat_expose_raw_response_headers_from_urlloader.patch
|
||||
chore_do_not_use_chrome_windows_in_cryptotoken_webrequestsender.patch
|
||||
process_singleton.patch
|
||||
fix_expose_decrementcapturercount_in_web_contents_impl.patch
|
||||
add_ui_scopedcliboardwriter_writeunsaferawdata.patch
|
||||
feat_add_data_parameter_to_processsingleton.patch
|
||||
mas_gate_private_enterprise_APIs
|
||||
load_v8_snapshot_in_browser_process.patch
|
||||
fix_patch_out_permissions_checks_in_exclusive_access.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 d5bb3412094125492ecf9b5d2c34ce5df16a7c6f..cce3fb48264b0585fa4672d6249c11510e4e5d9d 100644
|
||||
index 51b004f5e459eb265627c01e9e38acd655a65481..7140d92416709c467ae9b65860ac32947913989d 100644
|
||||
--- a/content/gpu/gpu_main.cc
|
||||
+++ b/content/gpu/gpu_main.cc
|
||||
@@ -237,6 +237,10 @@ int GpuMain(const MainFunctionParams& parameters) {
|
||||
@@ -239,6 +239,10 @@ int GpuMain(const MainFunctionParams& parameters) {
|
||||
// to the GpuProcessHost once the GpuServiceImpl has started.
|
||||
viz::GpuServiceImpl::InstallPreInitializeLogHandler();
|
||||
|
||||
@@ -24,7 +24,7 @@ index d5bb3412094125492ecf9b5d2c34ce5df16a7c6f..cce3fb48264b0585fa4672d6249c1151
|
||||
// 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
|
||||
@@ -374,7 +378,6 @@ int GpuMain(const MainFunctionParams& parameters) {
|
||||
@@ -376,7 +380,6 @@ int GpuMain(const MainFunctionParams& parameters) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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 e24c169444c699f295de2c1f1f42683eeca73436..4675cdccb1876a318a9a0253cdf552bb65516310 100644
|
||||
index 94fbf201b82ac000ceac21685f09f68497be1350..70766b01824e63c45e5c035848e5814876956c77 100644
|
||||
--- a/content/public/renderer/render_frame_observer.h
|
||||
+++ b/content/public/renderer/render_frame_observer.h
|
||||
@@ -127,6 +127,8 @@ class CONTENT_EXPORT RenderFrameObserver : public IPC::Listener,
|
||||
@@ -128,6 +128,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 e24c169444c699f295de2c1f1f42683eeca73436..4675cdccb1876a318a9a0253cdf552bb
|
||||
int32_t world_id) {}
|
||||
virtual void DidClearWindowObject() {}
|
||||
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
|
||||
index d88901876c14b375bfe2e65698d741e10882fd48..c3bed8fa670cb079bc182addd93f9507e5448dca 100644
|
||||
index 78ec20c40e5dbf7f706b57d9b810b4149510defc..900b8125f3fc6dcec4a1637fae6fcb2af40af5ab 100644
|
||||
--- a/content/renderer/render_frame_impl.cc
|
||||
+++ b/content/renderer/render_frame_impl.cc
|
||||
@@ -4449,6 +4449,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
@@ -4442,6 +4442,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
observer.DidCreateScriptContext(context, world_id);
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ index d88901876c14b375bfe2e65698d741e10882fd48..c3bed8fa670cb079bc182addd93f9507
|
||||
int world_id) {
|
||||
for (auto& observer : observers_)
|
||||
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
|
||||
index 1714c5b0a43fa21e370136afd8572b98df805f0f..8d71e6b8dd6d0b229f040b6b97bbda2845bd0db3 100644
|
||||
index 5f89641216bc467ea52619b1732c13174a16c723..061fc0c4a265335faccbf81594504eafabae77f8 100644
|
||||
--- a/content/renderer/render_frame_impl.h
|
||||
+++ b/content/renderer/render_frame_impl.h
|
||||
@@ -589,6 +589,8 @@ class CONTENT_EXPORT RenderFrameImpl
|
||||
@@ -601,6 +601,8 @@ class CONTENT_EXPORT RenderFrameImpl
|
||||
blink::WebLocalFrameClient::LazyLoadBehavior lazy_load_behavior) override;
|
||||
void DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
int world_id) override;
|
||||
@@ -53,10 +53,10 @@ index 1714c5b0a43fa21e370136afd8572b98df805f0f..8d71e6b8dd6d0b229f040b6b97bbda28
|
||||
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 99ddb8f449481647a499a5bb3fd7f5d5272ae414..00d7f0bbb8dc3afdc2303668481cf17fbdfbdcba 100644
|
||||
index 994841c02b0472e5239d9b73a07b2592a39df8be..ad19a3cddf200f6600a04c1136fd21218d496ba8 100644
|
||||
--- a/third_party/blink/public/web/web_local_frame_client.h
|
||||
+++ b/third_party/blink/public/web/web_local_frame_client.h
|
||||
@@ -587,6 +587,9 @@ class BLINK_EXPORT WebLocalFrameClient {
|
||||
@@ -596,6 +596,9 @@ class BLINK_EXPORT WebLocalFrameClient {
|
||||
virtual void DidCreateScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) {}
|
||||
|
||||
@@ -67,10 +67,10 @@ index 99ddb8f449481647a499a5bb3fd7f5d5272ae414..00d7f0bbb8dc3afdc2303668481cf17f
|
||||
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 fbef5207c921a5443f26083a860becc48eb497c1..5a25ce658f3115325e2213063ac502c855d878ca 100644
|
||||
index b0d5db60fbe57275dda835113b0fb21acb9a422f..b6c9c389943088004a419677a2a17be0c6ab6398 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
|
||||
@@ -200,6 +200,7 @@ void LocalWindowProxy::Initialize() {
|
||||
@@ -201,6 +201,7 @@ void LocalWindowProxy::Initialize() {
|
||||
}
|
||||
|
||||
InstallConditionalFeatures();
|
||||
@@ -79,10 +79,10 @@ index fbef5207c921a5443f26083a860becc48eb497c1..5a25ce658f3115325e2213063ac502c8
|
||||
if (World().IsMainWorld()) {
|
||||
GetFrame()->Loader().DispatchDidClearWindowObjectInMainWorld();
|
||||
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 fd9d33e9ee3214673757d2857f339fff70e139f2..ed2ccb72e394f177ec17d823fbf22d725ba43853 100644
|
||||
index 19a1ef792848026cb6a125124acd9e798e077e35..bb8220819750a6499568b64b49ca7710035d6092 100644
|
||||
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
|
||||
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
|
||||
@@ -299,6 +299,8 @@ class CORE_EXPORT LocalFrameClient : public FrameClient {
|
||||
@@ -310,6 +310,8 @@ class CORE_EXPORT LocalFrameClient : public FrameClient {
|
||||
|
||||
virtual void DidCreateScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) = 0;
|
||||
@@ -92,7 +92,7 @@ index fd9d33e9ee3214673757d2857f339fff70e139f2..ed2ccb72e394f177ec17d823fbf22d72
|
||||
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 96aea6ae2dc5322699741701569e8a1ccd4b9728..92a957fc4971ed3d5b343e8db4db9d603f11b1e9 100644
|
||||
index 4b1bfb735082513071181f30e8e8af1b699847b1..21f6a54f0255df0d88894860da83b1459c3a378f 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
|
||||
@@ -273,6 +273,13 @@ void LocalFrameClientImpl::DidCreateScriptContext(
|
||||
@@ -110,10 +110,10 @@ index 96aea6ae2dc5322699741701569e8a1ccd4b9728..92a957fc4971ed3d5b343e8db4db9d60
|
||||
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 b66e501d692b75e37f50fa3be018df092fc35176..3135bdfa960e9e914ded8ee87f227d5897c9ed33 100644
|
||||
index 45d493d7a9846c7f4ad567cec6d5fac4aadf9be3..23759d417d81d5bb6c920c94bacd66c303804319 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
|
||||
@@ -77,6 +77,8 @@ class CORE_EXPORT LocalFrameClientImpl final : public LocalFrameClient {
|
||||
@@ -78,6 +78,8 @@ class CORE_EXPORT LocalFrameClientImpl final : public LocalFrameClient {
|
||||
|
||||
void DidCreateScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) override;
|
||||
@@ -123,10 +123,10 @@ index b66e501d692b75e37f50fa3be018df092fc35176..3135bdfa960e9e914ded8ee87f227d58
|
||||
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 e405538cf1f520c67bcb72e164c66f4a4859d588..054df6b7718cef2c891eb4d00c95792dde5abd4b 100644
|
||||
index 0a4a0b8a4dc871dc86887774a6c81282ea98cda6..ab6ada68a78aa22e723080925d20cf14e8fe822a 100644
|
||||
--- a/third_party/blink/renderer/core/loader/empty_clients.h
|
||||
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
|
||||
@@ -355,6 +355,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient {
|
||||
@@ -360,6 +360,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient {
|
||||
|
||||
void DidCreateScriptContext(v8::Local<v8::Context>,
|
||||
int32_t world_id) override {}
|
||||
|
||||
@@ -10,10 +10,10 @@ valid use cases for setting custom exit codes of the main loop. This
|
||||
exposes a simple setter that embedders can call.
|
||||
|
||||
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
|
||||
index 5d41104a616c53d93ff8eb68ce2f87b1c2be7025..21223accc340242d2e0e301e92d7dafd01023e23 100644
|
||||
index 2c13914c1fbf8b81b6b0dc5d059d48525ac9f059..1c6179a00f404cc8d19e47b2e1bb635a6893e751 100644
|
||||
--- a/content/browser/browser_main_loop.h
|
||||
+++ b/content/browser/browser_main_loop.h
|
||||
@@ -161,6 +161,10 @@ class CONTENT_EXPORT BrowserMainLoop {
|
||||
@@ -165,6 +165,10 @@ class CONTENT_EXPORT BrowserMainLoop {
|
||||
|
||||
int GetResultCode() const { return result_code_; }
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user