mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
192 Commits
v18.0.0-al
...
v17.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73c87bcfc6 | ||
|
|
39ff8744ea | ||
|
|
2bbc560e22 | ||
|
|
590711fdd8 | ||
|
|
14baae0436 | ||
|
|
76c6e64d18 | ||
|
|
a09db1f6fe | ||
|
|
66ac88bc82 | ||
|
|
6654e37197 | ||
|
|
ff50d30a42 | ||
|
|
609f2d5e64 | ||
|
|
9716ae5a35 | ||
|
|
7d79120dac | ||
|
|
ee521475e3 | ||
|
|
3c6be55e13 | ||
|
|
0b031afcef | ||
|
|
3a61e4ce16 | ||
|
|
f4fc41b877 | ||
|
|
bd1e0176f5 | ||
|
|
16b72c0315 | ||
|
|
e9f673f322 | ||
|
|
e78bfc119c | ||
|
|
1b4eeda7eb | ||
|
|
90c299f3ad | ||
|
|
9ace5b3491 | ||
|
|
db0ad4f58d | ||
|
|
a071825ca7 | ||
|
|
1401284b44 | ||
|
|
611446ea11 | ||
|
|
04b0655d81 | ||
|
|
242504a451 | ||
|
|
7699f5c5f5 | ||
|
|
43f97845a8 | ||
|
|
908b8e40cd | ||
|
|
f61070be77 | ||
|
|
d0a98dc134 | ||
|
|
9aa0ff8f5f | ||
|
|
9d5f7c1673 | ||
|
|
a4f81c2256 | ||
|
|
68292f0861 | ||
|
|
f9830999c1 | ||
|
|
a6d7483784 | ||
|
|
8603223566 | ||
|
|
aca1e8ef20 | ||
|
|
22ec56a6a0 | ||
|
|
ea1f402417 | ||
|
|
e316c829fb | ||
|
|
ddee14db78 | ||
|
|
a60420b50a | ||
|
|
53d6660b82 | ||
|
|
2da8b5515d | ||
|
|
1d802ee6cb | ||
|
|
f20f1b176f | ||
|
|
f79ee8ac3f | ||
|
|
b570eb07d3 | ||
|
|
d82053d785 | ||
|
|
bcb4aac11b | ||
|
|
fdaee16db7 | ||
|
|
0d67bdf4ac | ||
|
|
753bde866a | ||
|
|
d35270ebb1 | ||
|
|
8ee96a13e7 | ||
|
|
5691f1c67e | ||
|
|
a582a4376f | ||
|
|
2ea5521940 | ||
|
|
d02dd1b894 | ||
|
|
f0815caf98 | ||
|
|
795a79e06e | ||
|
|
1d57a87c64 | ||
|
|
931db4a747 | ||
|
|
bbe5808090 | ||
|
|
08efa9a9b9 | ||
|
|
2c282517de | ||
|
|
1a346eaba5 | ||
|
|
74cc0023f8 | ||
|
|
594e905218 | ||
|
|
680e2041b5 | ||
|
|
fb7cf29086 | ||
|
|
d0e8f9b306 | ||
|
|
58218f2735 | ||
|
|
0891d2f7f7 | ||
|
|
122cc8d9e5 | ||
|
|
029b445c53 | ||
|
|
9ab1ecdd5f | ||
|
|
9825a202b0 | ||
|
|
2cc6113a04 | ||
|
|
6d794ca879 | ||
|
|
23d3ad9d1a | ||
|
|
3001b69f58 | ||
|
|
a729c8c048 | ||
|
|
4ce0b50f4b | ||
|
|
ffdf4f3dd9 | ||
|
|
e4b5c94192 | ||
|
|
32988f18eb | ||
|
|
124cfcaf88 | ||
|
|
e01c215a0a | ||
|
|
a23c93a6a0 | ||
|
|
a63456bb82 | ||
|
|
34769dd981 | ||
|
|
a9b71c121f | ||
|
|
2abe74e9f0 | ||
|
|
d2aeb53317 | ||
|
|
b69c4dfa3b | ||
|
|
57ddb3bd6d | ||
|
|
a4fd5ed5fb | ||
|
|
a2268557c4 | ||
|
|
97f88f102c | ||
|
|
0c3c6cc8fa | ||
|
|
94da1f3106 | ||
|
|
c8e08fd2f0 | ||
|
|
3f521cad0c | ||
|
|
c63ef8529e | ||
|
|
5dfa0f10f0 | ||
|
|
758141f111 | ||
|
|
ca2cadb571 | ||
|
|
250382cec5 | ||
|
|
0b41e61658 | ||
|
|
c41628d3b5 | ||
|
|
60e3ce32a6 | ||
|
|
3cbfc6ac30 | ||
|
|
d9b3ad4a69 | ||
|
|
871c9e930d | ||
|
|
490b76d9a9 | ||
|
|
cc50d1b3f8 | ||
|
|
6d0f801150 | ||
|
|
09b26234d3 | ||
|
|
ea54e169a2 | ||
|
|
5a0b9d08c1 | ||
|
|
74ca6a0fdf | ||
|
|
a80c38ea09 | ||
|
|
5ce0e897cb | ||
|
|
e1bda99547 | ||
|
|
e4247804c4 | ||
|
|
5048ffff8e | ||
|
|
478ec9c5a6 | ||
|
|
7ea67dea27 | ||
|
|
856e68145e | ||
|
|
3e73892db6 | ||
|
|
7ed6be22af | ||
|
|
c6dac2019d | ||
|
|
0e26e9a922 | ||
|
|
e87666ef6b | ||
|
|
a94efc28b9 | ||
|
|
d232ca2a81 | ||
|
|
58c7b02d2e | ||
|
|
b3268a50a8 | ||
|
|
2449ef1eb1 | ||
|
|
583421f3bb | ||
|
|
cc54eace57 | ||
|
|
cef58e9cd8 | ||
|
|
f120ea0810 | ||
|
|
a747752241 | ||
|
|
2233e8173b | ||
|
|
ae84eb3329 | ||
|
|
8aa412b79e | ||
|
|
c1d15916b9 | ||
|
|
c2522c1ea2 | ||
|
|
3b61e14fe9 | ||
|
|
1d70429c99 | ||
|
|
6ea5f92d14 | ||
|
|
e4f64afa3a | ||
|
|
9ef50c0bb8 | ||
|
|
efd6f85d1a | ||
|
|
aed4df546a | ||
|
|
3c6802b9e1 | ||
|
|
3060a28e2b | ||
|
|
be6d503de4 | ||
|
|
b273fcc78d | ||
|
|
ea8275042b | ||
|
|
0d5c7e1ae8 | ||
|
|
17411dbde7 | ||
|
|
8368b887f7 | ||
|
|
a3106030f8 | ||
|
|
230a38cdac | ||
|
|
d7a59f4f96 | ||
|
|
4b726b194c | ||
|
|
50dc23f449 | ||
|
|
91f4945bd8 | ||
|
|
40648dcb76 | ||
|
|
34a818bccf | ||
|
|
7a5e286eed | ||
|
|
744b16adf9 | ||
|
|
6f1f8b7847 | ||
|
|
b6f8a1686f | ||
|
|
53b76bdd6c | ||
|
|
984c675ee1 | ||
|
|
df46cdd685 | ||
|
|
1693b4974e | ||
|
|
04f400392d | ||
|
|
5ecdd7ba10 | ||
|
|
8eca8c3acc | ||
|
|
7e155e50ab |
@@ -45,7 +45,7 @@ executors:
|
||||
type: enum
|
||||
enum: ["medium", "xlarge", "2xlarge+"]
|
||||
docker:
|
||||
- image: ghcr.io/electron/build:e6bebd08a51a0d78ec23e5b3fd7e7c0846412328
|
||||
- image: ghcr.io/electron/build:27db4a3e3512bfd2e47f58cea69922da0835f1d9
|
||||
resource_class: << parameters.size >>
|
||||
|
||||
macos:
|
||||
@@ -322,6 +322,10 @@ step-setup-goma-for-build: &step-setup-goma-for-build
|
||||
node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })"
|
||||
export GOMA_FALLBACK_ON_AUTH_FAILURE=true
|
||||
third_party/goma/goma_ctl.py ensure_start
|
||||
if [ ! -z "$RAW_GOMA_AUTH" ] && [ "`third_party/goma/goma_auth.py info`" != "Login as Fermi Planck" ]; then
|
||||
echo "WARNING!!!!!! Goma authentication is incorrect; please update Goma auth token."
|
||||
exit 1
|
||||
fi
|
||||
echo 'export GN_GOMA_FILE='`node -e "console.log(require('./src/utils/goma.js').gnFilePath)"` >> $BASH_ENV
|
||||
echo 'export LOCAL_GOMA_DIR='`node -e "console.log(require('./src/utils/goma.js').dir)"` >> $BASH_ENV
|
||||
echo 'export GOMA_FALLBACK_ON_AUTH_FAILURE=true' >> $BASH_ENV
|
||||
@@ -890,12 +894,12 @@ step-touch-sync-done: &step-touch-sync-done
|
||||
step-maybe-restore-src-cache: &step-maybe-restore-src-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- v12-src-cache-{{ checksum "src/electron/.depshash" }}
|
||||
- v8-src-cache-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache
|
||||
step-maybe-restore-src-cache-marker: &step-maybe-restore-src-cache-marker
|
||||
restore_cache:
|
||||
keys:
|
||||
- v5-src-cache-marker-{{ checksum "src/electron/.depshash" }}
|
||||
- v1-src-cache-marker-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache marker
|
||||
|
||||
# Restore exact or closest git cache based on the hash of DEPS and .circle-sync-done
|
||||
@@ -904,10 +908,10 @@ step-maybe-restore-src-cache-marker: &step-maybe-restore-src-cache-marker
|
||||
step-maybe-restore-git-cache: &step-maybe-restore-git-cache
|
||||
restore_cache:
|
||||
paths:
|
||||
- gclient-cache
|
||||
- ~/.gclient-cache
|
||||
keys:
|
||||
- v5-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
- v5-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }}
|
||||
- v2-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
- v2-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }}
|
||||
name: Conditionally restoring git cache
|
||||
|
||||
step-restore-out-cache: &step-restore-out-cache
|
||||
@@ -924,15 +928,15 @@ step-set-git-cache-path: &step-set-git-cache-path
|
||||
command: |
|
||||
# CircleCI does not support interpolation when setting environment variables.
|
||||
# https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-shell-command
|
||||
echo 'export GIT_CACHE_PATH="$PWD/gclient-cache"' >> $BASH_ENV
|
||||
echo 'export GIT_CACHE_PATH="$HOME/.gclient-cache"' >> $BASH_ENV
|
||||
|
||||
# Persist the git cache based on the hash of DEPS and .circle-sync-done
|
||||
# If the src cache was restored above then this will persist an empty cache
|
||||
step-save-git-cache: &step-save-git-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- gclient-cache
|
||||
key: v5-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
- ~/.gclient-cache
|
||||
key: v2-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
name: Persisting git cache
|
||||
|
||||
step-save-out-cache: &step-save-out-cache
|
||||
@@ -977,7 +981,7 @@ step-save-src-cache: &step-save-src-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- /var/portal
|
||||
key: v12-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
key: v8-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
name: Persisting src cache
|
||||
step-make-src-cache-marker: &step-make-src-cache-marker
|
||||
run:
|
||||
@@ -987,7 +991,7 @@ step-save-src-cache-marker: &step-save-src-cache-marker
|
||||
save_cache:
|
||||
paths:
|
||||
- .src-cache-marker
|
||||
key: v5-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
key: v1-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
|
||||
step-maybe-early-exit-no-doc-change: &step-maybe-early-exit-no-doc-change
|
||||
run:
|
||||
@@ -1352,6 +1356,10 @@ commands:
|
||||
- *step-gclient-sync
|
||||
- store_artifacts:
|
||||
path: patches
|
||||
- when:
|
||||
condition: << parameters.save-git-cache >>
|
||||
steps:
|
||||
- *step-save-git-cache
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- run:
|
||||
name: Wipe Electron
|
||||
@@ -1360,11 +1368,6 @@ commands:
|
||||
- *step-run-electron-only-hooks
|
||||
- *step-generate-deps-hash-cleanly
|
||||
- *step-mark-sync-done
|
||||
# Save git cache AFTER sync marked done because other jobs expect that to be the case
|
||||
- when:
|
||||
condition: << parameters.save-git-cache >>
|
||||
steps:
|
||||
- *step-save-git-cache
|
||||
- *step-minimize-workspace-size-from-checkout
|
||||
- *step-delete-git-directories
|
||||
- when:
|
||||
|
||||
1
BUILD.gn
1
BUILD.gn
@@ -556,7 +556,6 @@ source_set("electron_lib") {
|
||||
defines += [
|
||||
# Disable warnings for g_settings_list_schemas.
|
||||
"GLIB_DISABLE_DEPRECATION_WARNINGS",
|
||||
"USE_X11=1",
|
||||
]
|
||||
|
||||
sources += [
|
||||
|
||||
4
DEPS
4
DEPS
@@ -15,9 +15,9 @@ gclient_gn_args = [
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'99.0.4767.0',
|
||||
'98.0.4758.141',
|
||||
'node_version':
|
||||
'v16.13.2',
|
||||
'v16.13.0',
|
||||
'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 @@
|
||||
18.0.0-alpha.4
|
||||
17.4.1
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://circleci.com/gh/electron/electron/tree/main)
|
||||
[](https://ci.appveyor.com/project/electron-bot/electron-ljo26/branch/main)
|
||||
[](https://discord.com/invite/APGC3k5yaH)
|
||||
[](https://discord.com/invite/electron)
|
||||
|
||||
:memo: Available Translations: 🇨🇳 🇧🇷 🇪🇸 🇯🇵 🇷🇺 🇫🇷 🇺🇸 🇩🇪.
|
||||
View these docs in other languages at [electron/i18n](https://github.com/electron/i18n/tree/master/content/).
|
||||
@@ -80,7 +80,7 @@ const child = proc.spawn(electron)
|
||||
|
||||
### Mirrors
|
||||
|
||||
- [China](https://npmmirror.com/mirrors/electron)
|
||||
- [China](https://npm.taobao.org/mirrors/electron)
|
||||
|
||||
## Documentation Translations
|
||||
|
||||
|
||||
40
appveyor.yml
40
appveyor.yml
@@ -66,6 +66,31 @@ build_script:
|
||||
- mkdir src
|
||||
- update_depot_tools.bat
|
||||
- ps: Move-Item $env:APPVEYOR_BUILD_FOLDER -Destination src\electron
|
||||
- ps: >-
|
||||
if (Test-Path 'env:RAW_GOMA_AUTH') {
|
||||
$env:GOMA_OAUTH2_CONFIG_FILE = "$pwd\.goma_oauth2_config"
|
||||
$env:RAW_GOMA_AUTH | Set-Content $env:GOMA_OAUTH2_CONFIG_FILE
|
||||
}
|
||||
- git clone https://github.com/electron/build-tools.git
|
||||
- cd build-tools
|
||||
- npm install
|
||||
- mkdir third_party
|
||||
- ps: >-
|
||||
node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })"
|
||||
- ps: $env:GN_GOMA_FILE = node -e "console.log(require('./src/utils/goma.js').gnFilePath)"
|
||||
- ps: $env:LOCAL_GOMA_DIR = node -e "console.log(require('./src/utils/goma.js').dir)"
|
||||
- cd ..
|
||||
- ps: .\src\electron\script\start-goma.ps1 -gomaDir $env:LOCAL_GOMA_DIR
|
||||
- ps: >-
|
||||
if (Test-Path 'env:RAW_GOMA_AUTH') {
|
||||
$goma_login = python $env:LOCAL_GOMA_DIR\goma_auth.py info
|
||||
if ($goma_login -eq 'Login as Fermi Planck') {
|
||||
Write-warning "Goma authentication is correct";
|
||||
} else {
|
||||
Write-warning "WARNING!!!!!! Goma authentication is incorrect; please update Goma auth token.";
|
||||
$host.SetShouldExit(1)
|
||||
}
|
||||
}
|
||||
- ps: $env:CHROMIUM_BUILDTOOLS_PATH="$pwd\src\buildtools"
|
||||
- ps: >-
|
||||
if ($env:GN_CONFIG -ne 'release') {
|
||||
@@ -129,21 +154,6 @@ build_script:
|
||||
Write-warning "Failed to add third_party\angle\.git; continuing anyway"
|
||||
}
|
||||
}
|
||||
- ps: >-
|
||||
if (Test-Path 'env:RAW_GOMA_AUTH') {
|
||||
$env:GOMA_OAUTH2_CONFIG_FILE = "$pwd\.goma_oauth2_config"
|
||||
$env:RAW_GOMA_AUTH | Set-Content $env:GOMA_OAUTH2_CONFIG_FILE
|
||||
}
|
||||
- git clone https://github.com/electron/build-tools.git
|
||||
- cd build-tools
|
||||
- npm install
|
||||
- mkdir third_party
|
||||
- ps: >-
|
||||
node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })"
|
||||
- ps: $env:GN_GOMA_FILE = node -e "console.log(require('./src/utils/goma.js').gnFilePath)"
|
||||
- ps: $env:LOCAL_GOMA_DIR = node -e "console.log(require('./src/utils/goma.js').dir)"
|
||||
- cd ..
|
||||
- ps: .\src\electron\script\start-goma.ps1 -gomaDir $env:LOCAL_GOMA_DIR
|
||||
- cd src
|
||||
- set BUILD_CONFIG_PATH=//electron/build/args/%GN_CONFIG%.gn
|
||||
- gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") import(\"%GN_GOMA_FILE%\") %GN_EXTRA_ARGS% "
|
||||
|
||||
@@ -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 = 103
|
||||
node_module_version = 101
|
||||
|
||||
v8_promise_internal_field_count = 1
|
||||
v8_typed_array_max_size_in_heap = 0
|
||||
@@ -14,9 +14,6 @@ v8_enable_snapshot_native_code_counters = false
|
||||
# TODO(codebytere): remove when Node.js handles https://chromium-review.googlesource.com/c/v8/v8/+/3211575
|
||||
v8_scriptormodule_legacy_lifetime = true
|
||||
|
||||
# we use this api
|
||||
v8_enable_javascript_promise_hooks = true
|
||||
|
||||
enable_cdm_host_verification = false
|
||||
proprietary_codecs = true
|
||||
ffmpeg_branding = "Chrome"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from __future__ import with_statement
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import contextlib
|
||||
import sys
|
||||
import os
|
||||
import optparse
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
sys.path.append("%s/../../build" % os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
@@ -33,36 +36,56 @@ def calculate_hash(root):
|
||||
return CalculateHash('.', None)
|
||||
|
||||
def windows_installed_software():
|
||||
import win32com.client
|
||||
strComputer = "."
|
||||
objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator")
|
||||
objSWbemServices = objWMIService.ConnectServer(strComputer, "root\cimv2")
|
||||
colItems = objSWbemServices.ExecQuery("Select * from Win32_Product")
|
||||
items = []
|
||||
powershell_command = [
|
||||
"Get-CimInstance",
|
||||
"-Namespace",
|
||||
"root\cimv2",
|
||||
"-Class",
|
||||
"Win32_product",
|
||||
"|",
|
||||
"Select",
|
||||
"vendor,",
|
||||
"description,",
|
||||
"@{l='install_location';e='InstallLocation'},",
|
||||
"@{l='install_date';e='InstallDate'},",
|
||||
"@{l='install_date_2';e='InstallDate2'},",
|
||||
"caption,",
|
||||
"version,",
|
||||
"name,",
|
||||
"@{l='sku_number';e='SKUNumber'}",
|
||||
"|",
|
||||
"ConvertTo-Json",
|
||||
]
|
||||
|
||||
for objItem in colItems:
|
||||
item = {}
|
||||
if objItem.Caption:
|
||||
item['caption'] = objItem.Caption
|
||||
if objItem.Caption:
|
||||
item['description'] = objItem.Description
|
||||
if objItem.InstallDate:
|
||||
item['install_date'] = objItem.InstallDate
|
||||
if objItem.InstallDate2:
|
||||
item['install_date_2'] = objItem.InstallDate2
|
||||
if objItem.InstallLocation:
|
||||
item['install_location'] = objItem.InstallLocation
|
||||
if objItem.Name:
|
||||
item['name'] = objItem.Name
|
||||
if objItem.SKUNumber:
|
||||
item['sku_number'] = objItem.SKUNumber
|
||||
if objItem.Vendor:
|
||||
item['vendor'] = objItem.Vendor
|
||||
if objItem.Version:
|
||||
item['version'] = objItem.Version
|
||||
items.append(item)
|
||||
proc = subprocess.Popen(
|
||||
["powershell.exe", "-Command", "-"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
|
||||
return items
|
||||
stdout, _ = proc.communicate(" ".join(powershell_command).encode("utf-8"))
|
||||
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError("Failed to get list of installed software")
|
||||
|
||||
# On AppVeyor there's other output related to PSReadline,
|
||||
# so grab only the JSON output and ignore everything else
|
||||
json_match = re.match(
|
||||
r".*(\[.*{.*}.*\]).*", stdout.decode("utf-8"), re.DOTALL
|
||||
)
|
||||
|
||||
if not json_match:
|
||||
raise RuntimeError(
|
||||
"Couldn't find JSON output for list of installed software"
|
||||
)
|
||||
|
||||
# Filter out missing keys
|
||||
return list(
|
||||
map(
|
||||
lambda info: {k: info[k] for k in info if info[k]},
|
||||
json.loads(json_match.group(1)),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def windows_profile():
|
||||
@@ -89,7 +112,7 @@ def windows_profile():
|
||||
|
||||
def main(options):
|
||||
if sys.platform == 'win32':
|
||||
with open(options.output_json, 'wb') as f:
|
||||
with open(options.output_json, 'w') as f:
|
||||
json.dump(windows_profile(), f)
|
||||
else:
|
||||
raise OSError("Unsupported OS")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { shell } from 'electron/common';
|
||||
import { app, dialog, BrowserWindow, ipcMain } from 'electron/main';
|
||||
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as electron from 'electron/main';
|
||||
import * as electron from 'electron';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
@@ -92,7 +92,7 @@ function loadApplicationPackage (packagePath: string) {
|
||||
try {
|
||||
packageJson = require(packageJsonPath);
|
||||
} catch (e) {
|
||||
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${(e as Error).message}`);
|
||||
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ function loadApplicationPackage (packagePath: string) {
|
||||
const filePath = Module._resolveFilename(packagePath, module, true);
|
||||
app.setAppPath(appPath || path.dirname(filePath));
|
||||
} catch (e) {
|
||||
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${(e as Error).message}`);
|
||||
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ function loadApplicationPackage (packagePath: string) {
|
||||
Module._load(packagePath, module, true);
|
||||
} catch (e) {
|
||||
console.error('App threw an error during load');
|
||||
console.error((e as Error).stack || e);
|
||||
console.error(e.stack || e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ipcRenderer, contextBridge } from 'electron/renderer';
|
||||
import { ipcRenderer, contextBridge } from 'electron';
|
||||
|
||||
const policy = window.trustedTypes.createPolicy('electron-default-app', {
|
||||
// we trust the SVG contents
|
||||
|
||||
@@ -106,6 +106,7 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||
* [`File` Object](api/file-object.md)
|
||||
* [`<webview>` Tag](api/webview-tag.md)
|
||||
* [`window.open` Function](api/window-open.md)
|
||||
* [`BrowserWindowProxy` Object](api/browser-window-proxy.md)
|
||||
|
||||
### Modules for the Main Process:
|
||||
|
||||
|
||||
@@ -484,7 +484,6 @@ Returns:
|
||||
* `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
|
||||
* `ackCallback` unknown - A function that can be used to send data back to 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()`.
|
||||
@@ -496,35 +495,12 @@ non-minimized.
|
||||
|
||||
**Note:** If the second instance is started by a different user than the first, the `argv` array will not include the arguments.
|
||||
|
||||
**Note:** `ackCallback` allows the user to send data back to the
|
||||
second instance during the `app.requestSingleInstanceLock()` flow.
|
||||
This callback can be used for cases where the second instance
|
||||
needs to obtain additional information from the first instance
|
||||
before quitting.
|
||||
|
||||
Currently, the limit on the message size is kMaxMessageLength,
|
||||
or around 32kB. To be safe, keep the amount of data passed to 31kB at most.
|
||||
|
||||
In order to call the callback, `event.preventDefault()` must be called, first.
|
||||
If the callback is not called in either case, `null` will be sent back.
|
||||
If `event.preventDefault()` is not called, but `ackCallback` is called
|
||||
by the user in the event, then the behaviour is undefined.
|
||||
|
||||
This event is guaranteed to be emitted after the `ready` event of `app`
|
||||
gets emitted.
|
||||
|
||||
**Note:** Extra command line arguments might be added by Chromium,
|
||||
such as `--original-process-start-time`.
|
||||
|
||||
### Event: 'first-instance-ack'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `additionalData` unknown - A JSON object of additional data passed from the first instance, in response to the first instance's `second-instance` event.
|
||||
|
||||
This event will be emitted within the second instance during the call to `app.requestSingleInstanceLock()`, when the first instance calls the `ackCallback` provided by the `second-instance` event handler.
|
||||
|
||||
## Methods
|
||||
|
||||
The `app` object has the following methods:
|
||||
@@ -983,13 +959,6 @@ starts:
|
||||
const { app } = require('electron')
|
||||
let myWindow = null
|
||||
|
||||
app.on('first-instance-ack', (event, additionalData) => {
|
||||
// Print out the ack received from the first instance.
|
||||
// Note this event handler must come before the requestSingleInstanceLock call.
|
||||
// Expected output: '{"myAckKey":"myAckValue"}'
|
||||
console.log(JSON.stringify(additionalData))
|
||||
})
|
||||
|
||||
const additionalData = { myKey: 'myValue' }
|
||||
const gotTheLock = app.requestSingleInstanceLock(additionalData)
|
||||
|
||||
@@ -997,19 +966,14 @@ if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory, additionalData) => {
|
||||
// We must call preventDefault if we're sending back data.
|
||||
event.preventDefault()
|
||||
// Print out data received from the second instance.
|
||||
// Expected output: '{"myKey":"myValue"}'
|
||||
console.log(JSON.stringify(additionalData))
|
||||
console.log(additionalData)
|
||||
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (myWindow) {
|
||||
if (myWindow.isMinimized()) myWindow.restore()
|
||||
myWindow.focus()
|
||||
}
|
||||
const ackData = { myAckKey: 'myAckValue' }
|
||||
ackCallback(ackData)
|
||||
})
|
||||
|
||||
// Create myWindow, load the rest of the app, etc...
|
||||
|
||||
54
docs/api/browser-window-proxy.md
Normal file
54
docs/api/browser-window-proxy.md
Normal file
@@ -0,0 +1,54 @@
|
||||
## Class: BrowserWindowProxy
|
||||
|
||||
> Manipulate the child browser window
|
||||
|
||||
Process: [Renderer](../glossary.md#renderer-process)<br />
|
||||
_This class is not exported from the `'electron'` module. It is only available as a return value of other methods in the Electron API._
|
||||
|
||||
The `BrowserWindowProxy` object is returned from `window.open` and provides
|
||||
limited functionality with the child window.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
The `BrowserWindowProxy` object has the following instance methods:
|
||||
|
||||
#### `win.blur()`
|
||||
|
||||
Removes focus from the child window.
|
||||
|
||||
#### `win.close()`
|
||||
|
||||
Forcefully closes the child window without calling its unload event.
|
||||
|
||||
#### `win.eval(code)`
|
||||
|
||||
* `code` string
|
||||
|
||||
Evaluates the code in the child window.
|
||||
|
||||
#### `win.focus()`
|
||||
|
||||
Focuses the child window (brings the window to front).
|
||||
|
||||
#### `win.print()`
|
||||
|
||||
Invokes the print dialog on the child window.
|
||||
|
||||
#### `win.postMessage(message, targetOrigin)`
|
||||
|
||||
* `message` any
|
||||
* `targetOrigin` string
|
||||
|
||||
Sends a message to the child window with the specified origin or `*` for no
|
||||
origin preference.
|
||||
|
||||
In addition to these methods, the child window implements `window.opener` object
|
||||
with no properties and a single method.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
The `BrowserWindowProxy` object has the following instance properties:
|
||||
|
||||
#### `win.closed`
|
||||
|
||||
A `boolean` that is set to true after the child window gets closed.
|
||||
@@ -341,6 +341,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
[Chrome Content Scripts][chrome-content-scripts]. You can access this
|
||||
context in the dev tools by selecting the 'Electron Isolated Context'
|
||||
entry in the combo box at the top of the Console tab.
|
||||
* `nativeWindowOpen` boolean (optional) - Whether to use native
|
||||
`window.open()`. Defaults to `true`. Child windows will always have node
|
||||
integration disabled unless `nodeIntegrationInSubFrames` is true.
|
||||
* `webviewTag` boolean (optional) - Whether to enable the [`<webview>` tag](webview-tag.md).
|
||||
Defaults to `false`. **Note:** The
|
||||
`preload` script configured for the `<webview>` will have node integration
|
||||
@@ -388,7 +391,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
contain the layout of the document—without requiring scrolling. Enabling
|
||||
this will cause the `preferred-size-changed` event to be emitted on the
|
||||
`WebContents` when the preferred size changes. Default is `false`.
|
||||
* `titleBarOverlay` Object | Boolean (optional) - When using a frameless window in conjunction with `win.setWindowButtonVisibility(true)` on macOS or using a `titleBarStyle` so that the standard window controls ("traffic lights" on macOS) are visible, this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars]. Specifying `true` will result in an overlay with default system colors. Default is `false`.
|
||||
* `titleBarOverlay` Object | Boolean (optional) - When using a frameless window in conjuction with `win.setWindowButtonVisibility(true)` on macOS or using a `titleBarStyle` so that the standard window controls ("traffic lights" on macOS) are visible, this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars]. Specifying `true` will result in an overlay with default system colors. Default is `false`.
|
||||
* `color` String (optional) _Windows_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color.
|
||||
* `symbolColor` String (optional) _Windows_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color.
|
||||
* `height` Integer (optional) _macOS_ _Windows_ - The height of the title bar and Window Controls Overlay in pixels. Default is system height.
|
||||
@@ -454,7 +457,7 @@ window.onbeforeunload = (e) => {
|
||||
// a non-void value will silently cancel the close.
|
||||
// It is recommended to use the dialog API to let the user confirm closing the
|
||||
// application.
|
||||
e.returnValue = false // equivalent to `return false` but not recommended
|
||||
e.returnValue = false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1808,6 +1811,16 @@ with `addBrowserView` or `setBrowserView`.
|
||||
**Note:** The BrowserView API is currently experimental and may change or be
|
||||
removed in future Electron releases.
|
||||
|
||||
#### `win.setTitleBarOverlay(options)` _Windows_
|
||||
|
||||
* `options` Object
|
||||
* `color` String (optional) _Windows_ - The CSS color of the Window Controls Overlay when enabled.
|
||||
* `symbolColor` String (optional) _Windows_ - The CSS color of the symbols on the Window Controls Overlay when enabled.
|
||||
* `height` Integer (optional) _Windows_ - The height of the title bar and Window Controls Overlay in pixels.
|
||||
|
||||
On a Window with Window Controls Overlay already enabled, this method updates
|
||||
the style of the title bar overlay.
|
||||
|
||||
[runtime-enabled-features]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/runtime_enabled_features.json5?l=70
|
||||
[page-visibility-api]: https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
|
||||
[quick-look]: https://en.wikipedia.org/wiki/Quick_Look
|
||||
|
||||
@@ -27,7 +27,6 @@ Or use a 3rd party hosted solution:
|
||||
* [Backtrace](https://backtrace.io/electron/)
|
||||
* [Sentry](https://docs.sentry.io/clients/electron)
|
||||
* [BugSplat](https://www.bugsplat.com/docs/platforms/electron)
|
||||
* [Bugsnag](https://docs.bugsnag.com/platforms/electron/)
|
||||
|
||||
Crash reports are stored temporarily before being uploaded in a directory
|
||||
underneath the app's user data directory, called 'Crashpad'. You can override
|
||||
|
||||
@@ -28,7 +28,7 @@ When `informational` is passed, the dock icon will bounce for one second.
|
||||
However, the request remains active until either the application becomes active
|
||||
or the request is canceled.
|
||||
|
||||
**Note:** This method can only be used while the app is not focused; when the app is focused it will return -1.
|
||||
**Nota Bene:** This method can only be used while the app is not focused; when the app is focused it will return -1.
|
||||
|
||||
#### `dock.cancelBounce(id)` _macOS_
|
||||
|
||||
|
||||
@@ -80,25 +80,3 @@ An `Integer` indicating the HTTP protocol major version number.
|
||||
An `Integer` indicating the HTTP protocol minor version number.
|
||||
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
|
||||
#### `response.rawHeaders`
|
||||
|
||||
A `string[]` containing the raw HTTP response headers exactly as they were
|
||||
received. The keys and values are in the same list. It is not a list of
|
||||
tuples. So, the even-numbered offsets are key values, and the odd-numbered
|
||||
offsets are the associated values. Header names are not lowercased, and
|
||||
duplicates are not merged.
|
||||
|
||||
```javascript
|
||||
// Prints something like:
|
||||
//
|
||||
// [ 'user-agent',
|
||||
// 'this is invalid because there can be only one',
|
||||
// 'User-Agent',
|
||||
// 'curl/7.22.0',
|
||||
// 'Host',
|
||||
// '127.0.0.1:8000',
|
||||
// 'ACCEPT',
|
||||
// '*/*' ]
|
||||
console.log(request.rawHeaders)
|
||||
```
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
title: "ipcMain"
|
||||
description: "Communicate asynchronously from the main process to renderer processes."
|
||||
slug: ipc-main
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
# ipcMain
|
||||
|
||||
> Communicate asynchronously from the main process to renderer processes.
|
||||
@@ -9,7 +16,9 @@ process, it handles asynchronous and synchronous messages sent from a renderer
|
||||
process (web page). Messages sent from a renderer will be emitted to this
|
||||
module.
|
||||
|
||||
## Sending Messages
|
||||
For usage examples, check out the [IPC tutorial].
|
||||
|
||||
## Sending messages
|
||||
|
||||
It is also possible to send messages from the main process to the renderer
|
||||
process, see [webContents.send][web-contents-send] for more information.
|
||||
@@ -21,36 +30,6 @@ process, see [webContents.send][web-contents-send] for more information.
|
||||
coming from frames that aren't the main frame (e.g. iframes) whereas
|
||||
`event.sender.send(...)` will always send to the main frame.
|
||||
|
||||
An example of sending and handling messages between the render and main
|
||||
processes:
|
||||
|
||||
```javascript
|
||||
// In main process.
|
||||
const { ipcMain } = require('electron')
|
||||
ipcMain.on('asynchronous-message', (event, arg) => {
|
||||
console.log(arg) // prints "ping"
|
||||
event.reply('asynchronous-reply', 'pong')
|
||||
})
|
||||
|
||||
ipcMain.on('synchronous-message', (event, arg) => {
|
||||
console.log(arg) // prints "ping"
|
||||
event.returnValue = 'pong'
|
||||
})
|
||||
```
|
||||
|
||||
```javascript
|
||||
// In renderer process (web page).
|
||||
// NB. Electron APIs are only accessible from preload, unless contextIsolation is disabled.
|
||||
// See https://www.electronjs.org/docs/tutorial/process-model#preload-scripts for more details.
|
||||
const { ipcRenderer } = require('electron')
|
||||
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"
|
||||
|
||||
ipcRenderer.on('asynchronous-reply', (event, arg) => {
|
||||
console.log(arg) // prints "pong"
|
||||
})
|
||||
ipcRenderer.send('asynchronous-message', 'ping')
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
The `ipcMain` module has the following method to listen for events:
|
||||
@@ -59,7 +38,7 @@ The `ipcMain` module has the following method to listen for events:
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function
|
||||
* `event` IpcMainEvent
|
||||
* `event` [IpcMainEvent][ipc-main-event]
|
||||
* `...args` any[]
|
||||
|
||||
Listens to `channel`, when a new message arrives `listener` would be called with
|
||||
@@ -69,7 +48,7 @@ Listens to `channel`, when a new message arrives `listener` would be called with
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function
|
||||
* `event` IpcMainEvent
|
||||
* `event` [IpcMainEvent][ipc-main-event]
|
||||
* `...args` any[]
|
||||
|
||||
Adds a one time `listener` function for the event. This `listener` is invoked
|
||||
@@ -93,8 +72,8 @@ Removes listeners of the specified `channel`.
|
||||
### `ipcMain.handle(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function<Promise\<void> | any>
|
||||
* `event` IpcMainInvokeEvent
|
||||
* `listener` Function<Promise\<void> | any>
|
||||
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
|
||||
* `...args` any[]
|
||||
|
||||
Adds a handler for an `invoke`able IPC. This handler will be called whenever a
|
||||
@@ -104,14 +83,14 @@ If `listener` returns a Promise, the eventual result of the promise will be
|
||||
returned as a reply to the remote caller. Otherwise, the return value of the
|
||||
listener will be used as the value of the reply.
|
||||
|
||||
```js
|
||||
// Main process
|
||||
```js title='Main Process'
|
||||
ipcMain.handle('my-invokable-ipc', async (event, ...args) => {
|
||||
const result = await somePromise(...args)
|
||||
return result
|
||||
})
|
||||
```
|
||||
|
||||
// Renderer process
|
||||
```js title='Renderer Process'
|
||||
async () => {
|
||||
const result = await ipcRenderer.invoke('my-invokable-ipc', arg1, arg2)
|
||||
// ...
|
||||
@@ -130,7 +109,7 @@ provided to the renderer process. Please refer to
|
||||
### `ipcMain.handleOnce(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function<Promise\<void> | any>
|
||||
* `listener` Function<Promise\<void> | any>
|
||||
* `event` IpcMainInvokeEvent
|
||||
* `...args` any[]
|
||||
|
||||
@@ -146,13 +125,16 @@ Removes any handler for `channel`, if present.
|
||||
## IpcMainEvent object
|
||||
|
||||
The documentation for the `event` object passed to the `callback` can be found
|
||||
in the [`ipc-main-event`](structures/ipc-main-event.md) structure docs.
|
||||
in the [`ipc-main-event`][ipc-main-event] structure docs.
|
||||
|
||||
## IpcMainInvokeEvent object
|
||||
|
||||
The documentation for the `event` object passed to `handle` callbacks can be
|
||||
found in the [`ipc-main-invoke-event`](structures/ipc-main-invoke-event.md)
|
||||
found in the [`ipc-main-invoke-event`][ipc-main-invoke-event]
|
||||
structure docs.
|
||||
|
||||
[IPC tutorial]: ../tutorial/ipc.md
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[web-contents-send]: web-contents.md#contentssendchannel-args
|
||||
[web-contents-send]: ../api/web-contents.md#contentssendchannel-args
|
||||
[ipc-main-event]:../api/structures/ipc-main-event.md
|
||||
[ipc-main-invoke-event]:../api/structures/ipc-main-invoke-event.md
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
title: "ipcRenderer"
|
||||
description: "Communicate asynchronously from a renderer process to the main process."
|
||||
slug: ipc-renderer
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
# ipcRenderer
|
||||
|
||||
> Communicate asynchronously from a renderer process to the main process.
|
||||
@@ -9,7 +16,7 @@ methods so you can send synchronous and asynchronous messages from the render
|
||||
process (web page) to the main process. You can also receive replies from the
|
||||
main process.
|
||||
|
||||
See [ipcMain](ipc-main.md) for code examples.
|
||||
See [IPC tutorial](../tutorial/ipc.md) for code examples.
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -70,7 +77,7 @@ throw an exception.
|
||||
> them. Attempting to send such objects over IPC will result in an error.
|
||||
|
||||
The main process handles it by listening for `channel` with the
|
||||
[`ipcMain`](ipc-main.md) module.
|
||||
[`ipcMain`](./ipc-main.md) module.
|
||||
|
||||
If you need to transfer a [`MessagePort`][] to the main process, use [`ipcRenderer.postMessage`](#ipcrendererpostmessagechannel-message-transfer).
|
||||
|
||||
@@ -98,7 +105,7 @@ throw an exception.
|
||||
> them. Attempting to send such objects over IPC will result in an error.
|
||||
|
||||
The main process should listen for `channel` with
|
||||
[`ipcMain.handle()`](ipc-main.md#ipcmainhandlechannel-listener).
|
||||
[`ipcMain.handle()`](./ipc-main.md#ipcmainhandlechannel-listener).
|
||||
|
||||
For example:
|
||||
|
||||
@@ -124,11 +131,11 @@ If you do not need a response to the message, consider using [`ipcRenderer.send`
|
||||
* `channel` string
|
||||
* `...args` any[]
|
||||
|
||||
Returns `any` - The value sent back by the [`ipcMain`](ipc-main.md) handler.
|
||||
Returns `any` - The value sent back by the [`ipcMain`](./ipc-main.md) handler.
|
||||
|
||||
Send a message to the main process via `channel` and expect a result
|
||||
synchronously. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`window.postMessage`][], so prototype chains will not be
|
||||
Algorithm][SCA], just like [`window.postMessage`], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
@@ -140,13 +147,13 @@ throw an exception.
|
||||
> Electron's IPC to the main process, as the main process would have no way to decode
|
||||
> them. Attempting to send such objects over IPC will result in an error.
|
||||
|
||||
The main process handles it by listening for `channel` with [`ipcMain`](ipc-main.md) module,
|
||||
The main process handles it by listening for `channel` with [`ipcMain`](./ipc-main.md) module,
|
||||
and replies by setting `event.returnValue`.
|
||||
|
||||
> :warning: **WARNING**: Sending a synchronous message will block the whole
|
||||
> renderer process until the reply is received, so use this method only as a
|
||||
> last resort. It's much better to use the asynchronous version,
|
||||
> [`invoke()`](ipc-renderer.md#ipcrendererinvokechannel-args).
|
||||
> [`invoke()`](./ipc-renderer.md#ipcrendererinvokechannel-args).
|
||||
|
||||
### `ipcRenderer.postMessage(channel, message, [transfer])`
|
||||
|
||||
@@ -158,7 +165,7 @@ Send a message to the main process, optionally transferring ownership of zero
|
||||
or more [`MessagePort`][] objects.
|
||||
|
||||
The transferred `MessagePort` objects will be available in the main process as
|
||||
[`MessagePortMain`](message-port-main.md) objects by accessing the `ports`
|
||||
[`MessagePortMain`](./message-port-main.md) objects by accessing the `ports`
|
||||
property of the emitted event.
|
||||
|
||||
For example:
|
||||
@@ -197,7 +204,7 @@ the host page instead of the main process.
|
||||
## Event object
|
||||
|
||||
The documentation for the `event` object passed to the `callback` can be found
|
||||
in the [`ipc-renderer-event`](structures/ipc-renderer-event.md) structure docs.
|
||||
in the [`ipc-renderer-event`](./structures/ipc-renderer-event.md) structure docs.
|
||||
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
|
||||
@@ -14,7 +14,7 @@ See [`Menu`](menu.md) for examples.
|
||||
* `menuItem` MenuItem
|
||||
* `browserWindow` [BrowserWindow](browser-window.md) | undefined - This will not be defined if no window is open.
|
||||
* `event` [KeyboardEvent](structures/keyboard-event.md)
|
||||
* `role` string (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `toggleSpellChecker`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, 'showSubstitutions', 'toggleSmartQuotes', 'toggleSmartDashes', 'toggleTextReplacement', `startSpeaking`, `stopSpeaking`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `shareMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu` - Define the action of the menu item, when specified the
|
||||
* `role` string (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `toggleSpellChecker`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, `startSpeaking`, `stopSpeaking`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `shareMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu` - Define the action of the menu item, when specified the
|
||||
`click` property will be ignored. See [roles](#roles).
|
||||
* `type` string (optional) - Can be `normal`, `separator`, `submenu`, `checkbox` or
|
||||
`radio`.
|
||||
@@ -100,10 +100,6 @@ The following additional roles are available on _macOS_:
|
||||
* `hide` - Map to the `hide` action.
|
||||
* `hideOthers` - Map to the `hideOtherApplications` action.
|
||||
* `unhide` - Map to the `unhideAllApplications` action.
|
||||
* `showSubstitutions` - Map to the `orderFrontSubstitutionsPanel` action.
|
||||
* `toggleSmartQuotes` - Map to the `toggleAutomaticQuoteSubstitution` action.
|
||||
* `toggleSmartDashes` - Map to the `toggleAutomaticDashSubstitution` action.
|
||||
* `toggleTextReplacement` - Map to the `toggleAutomaticTextReplacement` action.
|
||||
* `startSpeaking` - Map to the `startSpeaking` action.
|
||||
* `stopSpeaking` - Map to the `stopSpeaking` action.
|
||||
* `front` - Map to the `arrangeInFront` action.
|
||||
@@ -124,7 +120,7 @@ When specifying a `role` on macOS, `label` and `accelerator` are the only
|
||||
options that will affect the menu item. All other options will be ignored.
|
||||
Lowercase `role`, e.g. `toggledevtools`, is still supported.
|
||||
|
||||
**Note:** The `enabled` and `visibility` properties are not available for top-level menu items in the tray on macOS.
|
||||
**Nota Bene:** The `enabled` and `visibility` properties are not available for top-level menu items in the tray on macOS.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
|
||||
@@ -67,3 +67,8 @@ or is being instructed to show a high-contrast UI.
|
||||
|
||||
A `boolean` for if the OS / Chromium currently has an inverted color scheme
|
||||
or is being instructed to use an inverted color scheme.
|
||||
|
||||
### `nativeTheme.inForcedColorsMode` _Windows_ _Readonly_
|
||||
|
||||
A `boolean` indicating whether Chromium is in forced colors mode, controlled by system accessibility settings.
|
||||
Currently, Windows high contrast is the only system setting that triggers forced colors mode.
|
||||
|
||||
@@ -178,7 +178,6 @@ Returns an object with V8 heap statistics. Note that all statistics are reported
|
||||
Returns `Object`:
|
||||
|
||||
* `allocated` Integer - Size of all allocated objects in Kilobytes.
|
||||
* `marked` Integer - Size of all marked objects in Kilobytes.
|
||||
* `total` Integer - Total allocated space in Kilobytes.
|
||||
|
||||
Returns an object with Blink memory information.
|
||||
|
||||
@@ -868,6 +868,20 @@ this session just before normal `preload` scripts run.
|
||||
Returns `string[]` an array of paths to preload scripts that have been
|
||||
registered.
|
||||
|
||||
#### `ses.setCodeCachePath(path)`
|
||||
|
||||
* `path` String - Absolute path to store the v8 generated JS code cache from the renderer.
|
||||
|
||||
Sets the directory to store the generated JS [code cache](https://v8.dev/blog/code-caching-for-devs) for this session. The directory is not required to be created by the user before this call, the runtime will create if it does not exist otherwise will use the existing directory. If directory cannot be created, then code cache will not be used and all operations related to code cache will fail silently inside the runtime. By default, the directory will be `Code Cache` under the
|
||||
respective user data folder.
|
||||
|
||||
#### `ses.clearCodeCaches(options)`
|
||||
|
||||
* `options` Object
|
||||
* `urls` String[] (optional) - An array of url corresponding to the resource whose generated code cache needs to be removed. If the list is empty then all entries in the cache directory will be removed.
|
||||
|
||||
Returns `Promise<void>` - resolves when the code cache clear operation is complete.
|
||||
|
||||
#### `ses.setSpellCheckerEnabled(enable)`
|
||||
|
||||
* `enable` boolean
|
||||
|
||||
@@ -7,3 +7,17 @@
|
||||
the `enctype` attribute of the submitted HTML form.
|
||||
* `boundary` string (optional) - The boundary used to separate multiple parts of
|
||||
the message. Only valid when `contentType` is `multipart/form-data`.
|
||||
|
||||
Note that keys starting with `--` are not currently supported. For example, this will errantly submit as `multipart/form-data` when `nativeWindowOpen` is set to `false` in webPreferences:
|
||||
|
||||
```html
|
||||
<form
|
||||
target="_blank"
|
||||
method="POST"
|
||||
enctype="application/x-www-form-urlencoded"
|
||||
action="https://postman-echo.com/post"
|
||||
>
|
||||
<input type="text" name="--theKey">
|
||||
<input type="submit">
|
||||
</form>
|
||||
```
|
||||
|
||||
@@ -290,7 +290,7 @@ Returns:
|
||||
* `frameProcessId` Integer
|
||||
* `frameRoutingId` Integer
|
||||
|
||||
Emitted when a server side redirect occurs during navigation. For example a 302
|
||||
Emitted as a server side redirect occurs during navigation. For example a 302
|
||||
redirect.
|
||||
|
||||
This event will be emitted after `did-start-navigation` and always before the
|
||||
@@ -508,23 +508,6 @@ Returns:
|
||||
|
||||
Emitted when the user is requesting to change the zoom level using the mouse wheel.
|
||||
|
||||
#### Event: 'blur'
|
||||
|
||||
Emitted when the `WebContents` loses focus.
|
||||
|
||||
#### Event: 'focus'
|
||||
|
||||
Emitted when the `WebContents` gains focus.
|
||||
|
||||
Note that on macOS, having focus means the `WebContents` is the first responder
|
||||
of window, so switching focus between windows would not trigger the `focus` and
|
||||
`blur` events of `WebContents`, as the first responder of each window is not
|
||||
changed.
|
||||
|
||||
The `focus` and `blur` events of `WebContents` should only be used to detect
|
||||
focus change between different `WebContents` and `BrowserView` in the same
|
||||
window.
|
||||
|
||||
#### Event: 'devtools-opened'
|
||||
|
||||
Emitted when DevTools is opened.
|
||||
|
||||
@@ -110,9 +110,11 @@ webFrame.setSpellCheckProvider('en-US', {
|
||||
})
|
||||
```
|
||||
|
||||
### `webFrame.insertCSS(css)`
|
||||
#### `webFrame.insertCSS(css[, options])`
|
||||
|
||||
* `css` string - CSS source code.
|
||||
* `css` string
|
||||
* `options` Object (optional)
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
|
||||
Returns `string` - A key for the inserted CSS that can later be used to remove
|
||||
the CSS via `webFrame.removeInsertedCSS(key)`.
|
||||
|
||||
@@ -98,7 +98,6 @@ Some examples of valid `urls`:
|
||||
* `resourceType` string - Can be `mainFrame`, `subFrame`, `stylesheet`, `script`, `image`, `font`, `object`, `xhr`, `ping`, `cspReport`, `media`, `webSocket` or `other`.
|
||||
* `referrer` string
|
||||
* `timestamp` Double
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md) (optional)
|
||||
* `requestHeaders` Record<string, string>
|
||||
* `callback` Function
|
||||
* `beforeSendResponse` Object
|
||||
|
||||
@@ -12,6 +12,10 @@ useful for app sub-windows that act as preference panels, or similar, as the
|
||||
parent can render to the sub-window directly, as if it were a `div` in the
|
||||
parent. This is the same behavior as in the browser.
|
||||
|
||||
When `nativeWindowOpen` is set to false, `window.open` instead results in the
|
||||
creation of a [`BrowserWindowProxy`](browser-window-proxy.md), a light wrapper
|
||||
around `BrowserWindow`.
|
||||
|
||||
Electron pairs this native Chrome `Window` with a BrowserWindow under the hood.
|
||||
You can take advantage of all the customization available when creating a
|
||||
BrowserWindow in the main process by using `webContents.setWindowOpenHandler()`
|
||||
@@ -30,7 +34,7 @@ because it is invoked in the main process.
|
||||
* `frameName` string (optional)
|
||||
* `features` string (optional)
|
||||
|
||||
Returns [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) | null
|
||||
Returns [`BrowserWindowProxy`](browser-window-proxy.md) | [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window)
|
||||
|
||||
`features` is a comma-separated key-value list, following the standard format of
|
||||
the browser. Electron will parse `BrowserWindowConstructorOptions` out of this
|
||||
@@ -104,3 +108,33 @@ mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
const childWindow = window.open('', 'modal')
|
||||
childWindow.document.write('<h1>Hello</h1>')
|
||||
```
|
||||
|
||||
### `BrowserWindowProxy` example
|
||||
|
||||
```javascript
|
||||
|
||||
// main.js
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: { nativeWindowOpen: false }
|
||||
})
|
||||
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith('https://github.com/')) {
|
||||
return { action: 'allow' }
|
||||
}
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
mainWindow.webContents.on('did-create-window', (childWindow) => {
|
||||
// For example...
|
||||
childWindow.webContents.on('will-navigate', (e) => {
|
||||
e.preventDefault()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
```javascript
|
||||
// renderer.js
|
||||
const windowProxy = window.open('https://github.com/', null, 'minimizable=false')
|
||||
windowProxy.postMessage('hi', '*')
|
||||
```
|
||||
|
||||
@@ -12,18 +12,6 @@ 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 (18.0)
|
||||
|
||||
### Removed: `nativeWindowOpen`
|
||||
|
||||
Prior to Electron 15, `window.open` was by default shimmed to use
|
||||
`BrowserWindowProxy`. This meant that `window.open('about:blank')` did not work
|
||||
to open synchronously scriptable child windows, among other incompatibilities.
|
||||
Since Electron 15, `nativeWindowOpen` has been enabled by default.
|
||||
|
||||
See the documentation for [window.open in Electron](api/window-open.md)
|
||||
for more details.
|
||||
|
||||
## Planned Breaking API Changes (17.0)
|
||||
|
||||
### Removed: `desktopCapturer.getSources` in the renderer
|
||||
|
||||
@@ -131,7 +131,7 @@ $ gn gen out/Release --args="import(\"//electron/build/args/release.gn\")"
|
||||
Also you shouldn't have to run `gn gen` again—if you want to change the build arguments, you can run `gn args out/Testing` to bring up an editor. To see the list of available build configuration options, run `gn args out/Testing --list`.
|
||||
|
||||
**To build, run `ninja` with the `electron` target:**
|
||||
Note: This will also take a while and probably heat up your lap.
|
||||
Nota Bene: This will also take a while and probably heat up your lap.
|
||||
|
||||
For the testing configuration:
|
||||
|
||||
|
||||
@@ -7,21 +7,7 @@ Follow the guidelines below for building **Electron itself** on Linux, for the p
|
||||
## Prerequisites
|
||||
|
||||
* At least 25GB disk space and 8GB RAM.
|
||||
* Python 2.7.x. Some distributions like CentOS 6.x still use Python 2.6.x
|
||||
so you may need to check your Python version with `python -V`.
|
||||
|
||||
Please also ensure that your system and Python version support at least TLS 1.2.
|
||||
For a quick test, run the following script:
|
||||
|
||||
```sh
|
||||
$ npx @electron/check-python-tls
|
||||
```
|
||||
|
||||
If the script returns that your configuration is using an outdated security
|
||||
protocol, use your system's package manager to update Python to the latest
|
||||
version in the 2.7.x branch. Alternatively, visit https://www.python.org/downloads/
|
||||
for detailed instructions.
|
||||
|
||||
* Python >= 3.7.
|
||||
* Node.js. There are various ways to install Node. You can download
|
||||
source code from [nodejs.org](https://nodejs.org) and compile it.
|
||||
Doing so permits installing Node on your own home directory as a standard user.
|
||||
@@ -29,17 +15,7 @@ Follow the guidelines below for building **Electron itself** on Linux, for the p
|
||||
* [clang](https://clang.llvm.org/get_started.html) 3.4 or later.
|
||||
* Development headers of GTK 3 and libnotify.
|
||||
|
||||
On Ubuntu >= 20.04, install the following libraries:
|
||||
|
||||
```sh
|
||||
$ sudo apt-get install build-essential clang libdbus-1-dev libgtk-3-dev \
|
||||
libnotify-dev libasound2-dev libcap-dev \
|
||||
libcups2-dev libxtst-dev \
|
||||
libxss1 libnss3-dev gcc-multilib g++-multilib curl \
|
||||
gperf bison python3-dbusmock openjdk-8-jre
|
||||
```
|
||||
|
||||
On Ubuntu < 20.04, install the following libraries:
|
||||
On Ubuntu, install the following libraries:
|
||||
|
||||
```sh
|
||||
$ sudo apt-get install build-essential clang libdbus-1-dev libgtk-3-dev \
|
||||
|
||||
@@ -6,45 +6,12 @@ Follow the guidelines below for building **Electron itself** on macOS, for the p
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* macOS >= 10.11.6
|
||||
* [Xcode](https://developer.apple.com/technologies/tools/) >= 9.0.0
|
||||
* macOS >= 11.6.0
|
||||
* [Xcode](https://developer.apple.com/technologies/tools/). The exact version
|
||||
needed depends on what branch you are building, but the latest version of
|
||||
Xcode is generally a good bet for building `main`.
|
||||
* [node.js](https://nodejs.org) (external)
|
||||
* Python 2.7 with support for TLS 1.2
|
||||
|
||||
## Python
|
||||
|
||||
Please also ensure that your system and Python version support at least TLS 1.2.
|
||||
This depends on both your version of macOS and Python. For a quick test, run:
|
||||
|
||||
```sh
|
||||
$ npx @electron/check-python-tls
|
||||
```
|
||||
|
||||
If the script returns that your configuration is using an outdated security
|
||||
protocol, you can either update macOS to High Sierra or install a new version
|
||||
of Python 2.7.x. To upgrade Python, use [Homebrew](https://brew.sh/):
|
||||
|
||||
```sh
|
||||
$ brew install python@2 && brew link python@2 --force
|
||||
```
|
||||
|
||||
If you are using Python as provided by Homebrew, you also need to install
|
||||
the following Python modules:
|
||||
|
||||
* [pyobjc](https://pypi.org/project/pyobjc/#description)
|
||||
|
||||
You can use `pip` to install it:
|
||||
|
||||
```sh
|
||||
$ pip install pyobjc
|
||||
```
|
||||
|
||||
## macOS SDK
|
||||
|
||||
If you're developing Electron and don't plan to redistribute your
|
||||
custom Electron build, you may skip this section.
|
||||
|
||||
Official Electron builds are built with [Xcode 12.2](https://download.developer.apple.com/Developer_Tools/Xcode_12.2/Xcode_12.2.xip), and the macOS 11.0 SDK. Building with a newer SDK works too, but the releases currently use the 11.0 SDK.
|
||||
* Python >= 3.7
|
||||
|
||||
## Building Electron
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ For C++ and Python, we follow Chromium's [Coding
|
||||
Style](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/styleguide/styleguide.md).
|
||||
There is also a script `script/cpplint.py` to check whether all files conform.
|
||||
|
||||
The Python version we are using now is Python 2.7.
|
||||
The Python version we are using now is Python 3.9.
|
||||
|
||||
The C++ code uses a lot of Chromium's abstractions and types, so it's
|
||||
recommended to get acquainted with them. A good place to start is
|
||||
|
||||
@@ -35,28 +35,6 @@ base::debug::StackTrace().Print();
|
||||
|
||||
This will allow you to observe call chains and identify potential issue areas.
|
||||
|
||||
## Breakpoint Debugging
|
||||
|
||||
> Note that this will increase the size of the build significantly, taking up around 50G of disk space
|
||||
|
||||
Write the following file to `electron/.git/info/exclude/debug.gn`
|
||||
|
||||
```gn
|
||||
import("//electron/build/args/testing.gn")
|
||||
is_debug = true
|
||||
symbol_level = 2
|
||||
forbid_non_component_debug_builds = false
|
||||
```
|
||||
|
||||
Then execute:
|
||||
|
||||
```sh
|
||||
$ gn gen out/Debug --args="import(\"//electron/.git/info/exclude/debug.gn\") $GN_EXTRA_ARGS"
|
||||
$ ninja -C out/Debug electron
|
||||
```
|
||||
|
||||
Now you can use `LLDB` for breakpoint debugging.
|
||||
|
||||
## Platform-Specific Debugging
|
||||
<!-- TODO(@codebytere): add debugging file for Linux-->
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Asynchronous messages</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="async-msg">Ping</button>
|
||||
<span id="async-reply"></span>
|
||||
</div>
|
||||
<p>Using <code>ipc</code> to send messages between processes asynchronously is the preferred method since it will return when finished without blocking other operations in the same process.</p>
|
||||
|
||||
<p>This example sends a "ping" from this process (renderer) to the main process. The main process then replies with "pong".</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,29 +0,0 @@
|
||||
const { app, BrowserWindow, ipcMain } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
title: 'Asynchronous messages',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
ipcMain.on('asynchronous-message', (event, arg) => {
|
||||
event.sender.send('asynchronous-reply', 'pong')
|
||||
})
|
||||
@@ -1,12 +0,0 @@
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
const asyncMsgBtn = document.getElementById('async-msg')
|
||||
|
||||
asyncMsgBtn.addEventListener('click', () => {
|
||||
ipcRenderer.send('asynchronous-message', 'ping')
|
||||
})
|
||||
|
||||
ipcRenderer.on('asynchronous-reply', (event, arg) => {
|
||||
const message = `Asynchronous message reply: ${arg}`
|
||||
document.getElementById('async-reply').innerHTML = message
|
||||
})
|
||||
@@ -1,27 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Synchronous messages</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="sync-msg">Ping</button>
|
||||
<span id="sync-reply"></span>
|
||||
</div>
|
||||
<p>You can use the <code>ipc</code> module to send synchronous messages between processes as well, but note that the synchronous nature of this method means that it <b>will block</b> other operations while completing its task.</p>
|
||||
|
||||
<p>This example sends a synchronous message, "ping", from this process (renderer) to the main process. The main process then replies with "pong".</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// You can also require other files to run in this process
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,29 +0,0 @@
|
||||
const { app, BrowserWindow, ipcMain } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
function createWindow () {
|
||||
const windowOptions = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
title: 'Synchronous Messages',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(windowOptions)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
ipcMain.on('synchronous-message', (event, arg) => {
|
||||
event.returnValue = 'pong'
|
||||
})
|
||||
@@ -1,9 +0,0 @@
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
const syncMsgBtn = document.getElementById('sync-msg')
|
||||
|
||||
syncMsgBtn.addEventListener('click', () => {
|
||||
const reply = ipcRenderer.sendSync('synchronous-message', 'ping')
|
||||
const message = `Synchronous message reply: ${reply}`
|
||||
document.getElementById('sync-reply').innerHTML = message
|
||||
})
|
||||
14
docs/fiddles/ipc/pattern-1/index.html
Normal file
14
docs/fiddles/ipc/pattern-1/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
Title: <input id="title"/>
|
||||
<button id="btn" type="button">Set</button>
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
30
docs/fiddles/ipc/pattern-1/main.js
Normal file
30
docs/fiddles/ipc/pattern-1/main.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const {app, BrowserWindow, ipcMain} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('set-title', (event, title) => {
|
||||
const webContents = event.sender
|
||||
const win = BrowserWindow.fromWebContents(webContents)
|
||||
win.setTitle(title)
|
||||
})
|
||||
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', function () {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
5
docs/fiddles/ipc/pattern-1/preload.js
Normal file
5
docs/fiddles/ipc/pattern-1/preload.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
setTitle: (title) => ipcRenderer.send('set-title', title)
|
||||
})
|
||||
6
docs/fiddles/ipc/pattern-1/renderer.js
Normal file
6
docs/fiddles/ipc/pattern-1/renderer.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const setButton = document.getElementById('btn')
|
||||
const titleInput = document.getElementById('title')
|
||||
setButton.addEventListener('click', () => {
|
||||
const title = titleInput.value
|
||||
window.electronAPI.setTitle(title)
|
||||
});
|
||||
14
docs/fiddles/ipc/pattern-2/index.html
Normal file
14
docs/fiddles/ipc/pattern-2/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Dialog</title>
|
||||
</head>
|
||||
<body>
|
||||
<button type="button" id="btn">Open a File</button>
|
||||
File path: <strong id="filePath"></strong>
|
||||
<script src='./renderer.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
32
docs/fiddles/ipc/pattern-2/main.js
Normal file
32
docs/fiddles/ipc/pattern-2/main.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const {app, BrowserWindow, ipcMain,dialog} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
async function handleFileOpen() {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog()
|
||||
if (canceled) {
|
||||
return
|
||||
} else {
|
||||
return filePaths[0]
|
||||
}
|
||||
}
|
||||
|
||||
function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
ipcMain.handle('dialog:openFile', handleFileOpen)
|
||||
createWindow()
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', function () {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
5
docs/fiddles/ipc/pattern-2/preload.js
Normal file
5
docs/fiddles/ipc/pattern-2/preload.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI',{
|
||||
openFile: () => ipcRenderer.invoke('dialog:openFile')
|
||||
})
|
||||
7
docs/fiddles/ipc/pattern-2/renderer.js
Normal file
7
docs/fiddles/ipc/pattern-2/renderer.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const btn = document.getElementById('btn')
|
||||
const filePathElement = document.getElementById('filePath')
|
||||
|
||||
btn.addEventListener('click', async () => {
|
||||
const filePath = await window.electronAPI.openFile()
|
||||
filePathElement.innerText = filePath
|
||||
})
|
||||
13
docs/fiddles/ipc/pattern-3/index.html
Normal file
13
docs/fiddles/ipc/pattern-3/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Menu Counter</title>
|
||||
</head>
|
||||
<body>
|
||||
Current value: <strong id="counter">0</strong>
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
48
docs/fiddles/ipc/pattern-3/main.js
Normal file
48
docs/fiddles/ipc/pattern-3/main.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const {app, BrowserWindow, Menu} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
const menu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: app.name,
|
||||
submenu: [
|
||||
{
|
||||
click: () => mainWindow.webContents.send('update-counter', 1),
|
||||
label: 'Increment',
|
||||
},
|
||||
{
|
||||
click: () => mainWindow.webContents.send('update-counter', -1),
|
||||
label: 'Decrement',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
])
|
||||
|
||||
Menu.setApplicationMenu(menu)
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
mainWindow.webContents.openDevTools()
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
ipcMain.on('counter-value', (_event, value) => {
|
||||
console.log(value) // will print value to Node console
|
||||
})
|
||||
createWindow()
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', function () {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
5
docs/fiddles/ipc/pattern-3/preload.js
Normal file
5
docs/fiddles/ipc/pattern-3/preload.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
handleCounter: (callback) => ipcRenderer.on('update-counter', callback)
|
||||
})
|
||||
8
docs/fiddles/ipc/pattern-3/renderer.js
Normal file
8
docs/fiddles/ipc/pattern-3/renderer.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const counter = document.getElementById('counter')
|
||||
|
||||
window.electronAPI.handleCounter((event, value) => {
|
||||
const oldValue = Number(counter.innerText)
|
||||
const newValue = oldValue + value
|
||||
counter.innerText = newValue
|
||||
event.sender.send('counter-value', newValue)
|
||||
})
|
||||
@@ -1,4 +1,11 @@
|
||||
# In-App Purchases (macOS)
|
||||
---
|
||||
title: In-App Purchases
|
||||
description: Add in-app purchases to your Mac App Store (MAS) application
|
||||
slug: in-app-purchases
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# In-App Purchases
|
||||
|
||||
## Preparing
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ url = ELECTRON_MIRROR + ELECTRON_CUSTOM_DIR + '/' + ELECTRON_CUSTOM_FILENAME
|
||||
For instance, to use the China CDN mirror:
|
||||
|
||||
```shell
|
||||
ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
|
||||
ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/"
|
||||
```
|
||||
|
||||
By default, `ELECTRON_CUSTOM_DIR` is set to `v$VERSION`. To change the format,
|
||||
@@ -83,12 +83,12 @@ resolves to `version-5.0.0`, `{{ version }}` resolves to `5.0.0`, and
|
||||
use the China non-CDN mirror:
|
||||
|
||||
```shell
|
||||
ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
|
||||
ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
|
||||
ELECTRON_CUSTOM_DIR="{{ version }}"
|
||||
```
|
||||
|
||||
The above configuration will download from URLs such as
|
||||
`https://npmmirror.com/mirrors/electron/8.0.0/electron-v8.0.0-linux-x64.zip`.
|
||||
`https://npm.taobao.org/mirrors/electron/8.0.0/electron-v8.0.0-linux-x64.zip`.
|
||||
|
||||
If your mirror serves artifacts with different checksums to the official
|
||||
Electron release you may have to set `electron_use_remote_checksums=1` to
|
||||
|
||||
571
docs/tutorial/ipc.md
Normal file
571
docs/tutorial/ipc.md
Normal file
@@ -0,0 +1,571 @@
|
||||
---
|
||||
title: Inter-Process Communication
|
||||
description: Use the ipcMain and ipcRenderer modules to communicate between Electron processes
|
||||
slug: ipc
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
# Inter-Process Communication
|
||||
|
||||
Inter-process communication (IPC) is a key part of building feature-rich desktop applications
|
||||
in Electron. Because the main and renderer processes have different responsibilities in
|
||||
Electron's process model, IPC is the only way to perform many common tasks, such as calling a
|
||||
native API from your UI or triggering changes in your web contents from native menus.
|
||||
|
||||
## IPC channels
|
||||
|
||||
In Electron, processes communicate by passing messages through developer-defined "channels"
|
||||
with the [`ipcMain`] and [`ipcRenderer`] modules. These channels are
|
||||
**arbitrary** (you can name them anything you want) and **bidirectional** (you can use the
|
||||
same channel name for both modules).
|
||||
|
||||
In this guide, we'll be going over some fundamental IPC patterns with concrete examples that
|
||||
you can use as a reference for your app code.
|
||||
|
||||
## Understanding context-isolated processes
|
||||
|
||||
Before proceeding to implementation details, you should be familiar with the idea of using a
|
||||
[preload script] to import Node.js and Electron modules in a context-isolated renderer process.
|
||||
|
||||
* For a full overview of Electron's process model, you can read the [process model docs].
|
||||
* For a primer into exposing APIs from your preload script using the `contextBridge` module, check
|
||||
out the [context isolation tutorial].
|
||||
|
||||
## Pattern 1: Renderer to main (one-way)
|
||||
|
||||
To fire a one-way IPC message from a renderer process to the main process, you can use the
|
||||
[`ipcRenderer.send`] API to send a message that is then received by the [`ipcMain.on`] API.
|
||||
|
||||
You usually use this pattern to call a main process API from your web contents. We'll demonstrate
|
||||
this pattern by creating a simple app that can programmatically change its window title.
|
||||
|
||||
For this demo, you'll need to add code to your main process, your renderer process, and a preload
|
||||
script. The full code is below, but we'll be explaining each file individually in the following
|
||||
sections.
|
||||
|
||||
```fiddle docs/fiddles/ipc/pattern-1
|
||||
```
|
||||
|
||||
### 1. Listen for events with `ipcMain.on`
|
||||
|
||||
In the main process, set an IPC listener on the `set-title` channel with the `ipcMain.on` API:
|
||||
|
||||
```javascript {6-10,22} title='main.js (Main Process)'
|
||||
const {app, BrowserWindow, ipcMain} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
//...
|
||||
|
||||
function handleSetTitle (event, title) {
|
||||
const webContents = event.sender
|
||||
const win = BrowserWindow.fromWebContents(webContents)
|
||||
win.setTitle(title)
|
||||
}
|
||||
|
||||
function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
ipcMain.on('set-title', handleSetTitle)
|
||||
createWindow()
|
||||
}
|
||||
//...
|
||||
```
|
||||
|
||||
The above `handleSetTitle` callback has two parameters: an [IpcMainEvent] structure and a
|
||||
`title` string. Whenever a message comes through the `set-title` channel, this function will
|
||||
find the BrowserWindow instance attached to the message sender and use the `win.setTitle`
|
||||
API on it.
|
||||
|
||||
:::info
|
||||
Make sure you're loading the `index.html` and `preload.js` entry points for the following steps!
|
||||
:::
|
||||
|
||||
### 2. Expose `ipcRenderer.send` via preload
|
||||
|
||||
To send messages to the listener created above, you can use the `ipcRenderer.send` API.
|
||||
By default, the renderer process has no Node.js or Electron module access. As an app developer,
|
||||
you need to choose which APIs to expose from your preload script using the `contextBridge` API.
|
||||
|
||||
In your preload script, add the following code, which will expose a global `window.electronAPI`
|
||||
variable to your renderer process.
|
||||
|
||||
```javascript title='preload.js (Preload Script)'
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
setTitle: (title) => ipcRenderer.send('set-title', title)
|
||||
})
|
||||
```
|
||||
|
||||
At this point, you'll be able to use the `window.electronAPI.setTitle()` function in the renderer
|
||||
process.
|
||||
|
||||
:::caution Security warning
|
||||
We don't directly expose the whole `ipcRenderer.send` API for [security reasons]. Make sure to
|
||||
limit the renderer's access to Electron APIs as much as possible.
|
||||
:::
|
||||
|
||||
### 3. Build the renderer process UI
|
||||
|
||||
In our BrowserWindow's loaded HTML file, add a basic user interface consisting of a text input
|
||||
and a button:
|
||||
|
||||
```html {11-12} title='index.html'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
Title: <input id="title"/>
|
||||
<button id="btn" type="button">Set</button>
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
To make these elements interactive, we'll be adding a few lines of code in the imported
|
||||
`renderer.js` file that leverages the `window.electronAPI` functionality exposed from the preload
|
||||
script:
|
||||
|
||||
```javascript title='renderer.js (Renderer Process)'
|
||||
const setButton = document.getElementById('btn')
|
||||
const titleInput = document.getElementById('title')
|
||||
setButton.addEventListener('click', () => {
|
||||
const title = titleInput.value
|
||||
window.electronAPI.setTitle(title)
|
||||
});
|
||||
```
|
||||
|
||||
At this point, your demo should be fully functional. Try using the input field and see what happens
|
||||
to your BrowserWindow title!
|
||||
|
||||
## Pattern 2: Renderer to main (two-way)
|
||||
|
||||
A common application for two-way IPC is calling a main process module from your renderer process
|
||||
code and waiting for a result. This can be done by using [`ipcRenderer.invoke`] paired with
|
||||
[`ipcMain.handle`].
|
||||
|
||||
In the following example, we'll be opening a native file dialog from the renderer process and
|
||||
returning the selected file's path.
|
||||
|
||||
For this demo, you'll need to add code to your main process, your renderer process, and a preload
|
||||
script. The full code is below, but we'll be explaining each file individually in the following
|
||||
sections.
|
||||
|
||||
```fiddle docs/fiddles/ipc/pattern-2
|
||||
```
|
||||
|
||||
### 1. Listen for events with `ipcMain.handle`
|
||||
|
||||
In the main process, we'll be creating a `handleFileOpen()` function that calls
|
||||
`dialog.showOpenDialog` and returns the value of the file path selected by the user. This function
|
||||
is used as a callback whenever an `ipcRender.invoke` message is sent through the `dialog:openFile`
|
||||
channel from the renderer process. The return value is then returned as a Promise to the original
|
||||
`invoke` call.
|
||||
|
||||
:::caution A word on error handling
|
||||
Errors thrown through `handle` in the main process are not transparent as they
|
||||
are serialized and only the `message` property from the original error is
|
||||
provided to the renderer process. Please refer to
|
||||
[#24427](https://github.com/electron/electron/issues/24427) for details.
|
||||
:::
|
||||
|
||||
```javascript {6-13,25} title='main.js (Main Process)'
|
||||
const { BrowserWindow, dialog, ipcMain } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
//...
|
||||
|
||||
async function handleFileOpen() {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog()
|
||||
if (canceled) {
|
||||
return
|
||||
} else {
|
||||
return filePaths[0]
|
||||
}
|
||||
}
|
||||
|
||||
function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady(() => {
|
||||
ipcMain.handle('dialog:openFile', handleFileOpen)
|
||||
createWindow()
|
||||
})
|
||||
//...
|
||||
```
|
||||
|
||||
:::tip on channel names
|
||||
The `dialog:` prefix on the IPC channel name has no effect on the code. It only serves
|
||||
as a namespace that helps with code readability.
|
||||
:::
|
||||
|
||||
:::info
|
||||
Make sure you're loading the `index.html` and `preload.js` entry points for the following steps!
|
||||
:::
|
||||
|
||||
### 2. Expose `ipcRenderer.invoke` via preload
|
||||
|
||||
In the preload script, we expose a one-line `openFile` function that calls and returns the value of
|
||||
`ipcRenderer.invoke('dialog:openFile')`. We'll be using this API in the next step to call the
|
||||
native dialog from our renderer's user interface.
|
||||
|
||||
```javascript title='preload.js (Preload Script)'
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
openFile: () => ipcRenderer.invoke('dialog:openFile')
|
||||
})
|
||||
```
|
||||
|
||||
:::caution Security warning
|
||||
We don't directly expose the whole `ipcRenderer.invoke` API for [security reasons]. Make sure to
|
||||
limit the renderer's access to Electron APIs as much as possible.
|
||||
:::
|
||||
|
||||
### 3. Build the renderer process UI
|
||||
|
||||
Finally, let's build the HTML file that we load into our BrowserWindow.
|
||||
|
||||
```html {10-11} title='index.html'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Dialog</title>
|
||||
</head>
|
||||
<body>
|
||||
<button type="button" id="btn">Open a File</button>
|
||||
File path: <strong id="filePath"></strong>
|
||||
<script src='./renderer.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
The UI consists of a single `#btn` button element that will be used to trigger our preload API, and
|
||||
a `#filePath` element that will be used to display the path of the selected file. Making these
|
||||
pieces work will take a few lines of code in the renderer process script:
|
||||
|
||||
```javascript title='renderer.js (Renderer Process)'
|
||||
const btn = document.getElementById('btn')
|
||||
const filePathElement = document.getElementById('filePath')
|
||||
|
||||
btn.addEventListener('click', async () => {
|
||||
const filePath = await window.electronAPI.openFile()
|
||||
filePathElement.innerText = filePath
|
||||
})
|
||||
```
|
||||
|
||||
In the above snippet, we listen for clicks on the `#btn` button, and call our
|
||||
`window.electronAPI.openFile()` API to activate the native Open File dialog. We then display the
|
||||
selected file path in the `#filePath` element.
|
||||
|
||||
### Note: legacy approaches
|
||||
|
||||
The `ipcRenderer.invoke` API was added in Electron 7 as a developer-friendly way to tackle two-way
|
||||
IPC from the renderer process. However, there exist a couple alternative approaches to this IPC
|
||||
pattern.
|
||||
|
||||
:::warning Avoid legacy approaches if possible
|
||||
We recommend using `ipcRenderer.invoke` whenever possible. The following two-way renderer-to-main
|
||||
patterns are documented for historical purposes.
|
||||
:::
|
||||
|
||||
:::info
|
||||
For the following examples, we're calling `ipcRenderer` directly from the preload script to keep
|
||||
the code samples small.
|
||||
:::
|
||||
|
||||
#### Using `ipcRenderer.send`
|
||||
|
||||
The `ipcRenderer.send` API that we used for single-way communication can also be leveraged to
|
||||
perform two-way communication. This was the recommended way for asynchronous two-way communication
|
||||
via IPC prior to Electron 7.
|
||||
|
||||
```javascript title='preload.js (Preload Script)'
|
||||
// You can also put expose this code to the renderer
|
||||
// process with the `contextBridge` API
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
ipcRenderer.on('asynchronous-reply', (_event, arg) => {
|
||||
console.log(arg) // prints "pong" in the DevTools console
|
||||
})
|
||||
ipcRenderer.send('asynchronous-message', 'ping')
|
||||
```
|
||||
|
||||
```javascript title='main.js (Main Process)'
|
||||
ipcMain.on('asynchronous-message', (event, arg) => {
|
||||
console.log(arg) // prints "ping" in the Node console
|
||||
// works like `send`, but returning a message back
|
||||
// to the renderer that sent the original message
|
||||
event.reply('asynchronous-reply', 'pong')
|
||||
})
|
||||
```
|
||||
|
||||
There are a couple downsides to this approach:
|
||||
|
||||
* You need to set up a second `ipcRenderer.on` listener to handle the response in the renderer
|
||||
process. With `invoke`, you get the response value returned as a Promise to the original API call.
|
||||
* There's no obvious way to pair the `asynchronous-reply` message to the original
|
||||
`asynchronous-message` one. If you have very frequent messages going back and forth through these
|
||||
channels, you would need to add additional app code to track each call and response individually.
|
||||
|
||||
#### Using `ipcRenderer.sendSync`
|
||||
|
||||
The `ipcRenderer.sendSync` API sends a message to the main process and waits _synchronously_ for a
|
||||
response.
|
||||
|
||||
```javascript title='main.js (Main Process)'
|
||||
const { ipcMain } = require('electron')
|
||||
ipcMain.on('synchronous-message', (event, arg) => {
|
||||
console.log(arg) // prints "ping" in the Node console
|
||||
event.returnValue = 'pong'
|
||||
})
|
||||
```
|
||||
|
||||
```javascript title='preload.js (Preload Script)'
|
||||
// You can also put expose this code to the renderer
|
||||
// process with the `contextBridge` API
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
const result = ipcRenderer.sendSync('synchronous-message', 'ping')
|
||||
console.log(result) // prints "pong" in the DevTools console
|
||||
```
|
||||
|
||||
The structure of this code is very similar to the `invoke` model, but we recommend
|
||||
**avoiding this API** for performance reasons. Its synchronous nature means that it'll block the
|
||||
renderer process until a reply is received.
|
||||
|
||||
## Pattern 3: Main to renderer
|
||||
|
||||
When sending a message from the main process to a renderer process, you need to specify which
|
||||
renderer is receiving the message. Messages need to be sent to a renderer process
|
||||
via its [`WebContents`] instance. This WebContents instance contains a [`send`][webcontents-send] method
|
||||
that can be used in the same way as `ipcRenderer.send`.
|
||||
|
||||
To demonstrate this pattern, we'll be building a number counter controlled by the native operating
|
||||
system menu.
|
||||
|
||||
For this demo, you'll need to add code to your main process, your renderer process, and a preload
|
||||
script. The full code is below, but we'll be explaining each file individually in the following
|
||||
sections.
|
||||
|
||||
```fiddle docs/fiddles/ipc/pattern-3
|
||||
```
|
||||
|
||||
### 1. Send messages with the `webContents` module
|
||||
|
||||
For this demo, we'll need to first build a custom menu in the main process using Electron's `Menu`
|
||||
module that uses the `webContents.send` API to send an IPC message from the main process to the
|
||||
target renderer.
|
||||
|
||||
```javascript {11-26} title='main.js (Main Process)'
|
||||
const {app, BrowserWindow, Menu, ipcMain} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
const menu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: app.name,
|
||||
submenu: [
|
||||
{
|
||||
click: () => mainWindow.webContents.send('update-counter', 1),
|
||||
label: 'Increment',
|
||||
},
|
||||
{
|
||||
click: () => mainWindow.webContents.send('update-counter', -1),
|
||||
label: 'Decrement',
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
Menu.setApplicationMenu(menu)
|
||||
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
//...
|
||||
|
||||
```
|
||||
|
||||
For the purposes of the tutorial, it's important to note that the `click` handler
|
||||
sends a message (either `1` or `-1`) to the renderer process through the `counter` channel.
|
||||
|
||||
```javascript
|
||||
click: () => mainWindow.webContents.send('update-counter', -1)
|
||||
```
|
||||
|
||||
:::info
|
||||
Make sure you're loading the `index.html` and `preload.js` entry points for the following steps!
|
||||
:::
|
||||
|
||||
### 2. Expose `ipcRenderer.on` via preload
|
||||
|
||||
Like in the previous renderer-to-main example, we use the `contextBridge` and `ipcRenderer`
|
||||
modules in the preload script to expose IPC functionality to the renderer process:
|
||||
|
||||
```javascript title='preload.js (Preload Script)'
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback)
|
||||
})
|
||||
```
|
||||
|
||||
After loading the preload script, your renderer process should have access to the
|
||||
`window.electronAPI.onUpdateCounter()` listener function.
|
||||
|
||||
:::caution Security warning
|
||||
We don't directly expose the whole `ipcRenderer.on` API for [security reasons]. Make sure to
|
||||
limit the renderer's access to Electron APIs as much as possible.
|
||||
:::
|
||||
|
||||
:::info
|
||||
In the case of this minimal example, you can call `ipcRenderer.on` directly in the preload script
|
||||
rather than exposing it over the context bridge.
|
||||
|
||||
```javascript title='preload.js (Preload Script)'
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const counter = document.getElementById('counter')
|
||||
ipcRenderer.on('update-counter', (_event, value) => {
|
||||
const oldValue = Number(counter.innerText)
|
||||
const newValue = oldValue + value
|
||||
counter.innerText = newValue
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
However, this approach has limited flexibility compared to exposing your preload APIs
|
||||
over the context bridge, since your listener can't directly interact with your renderer code.
|
||||
:::
|
||||
|
||||
### 3. Build the renderer process UI
|
||||
|
||||
To tie it all together, we'll create an interface in the loaded HTML file that contains a
|
||||
`#counter` element that we'll use to display the values:
|
||||
|
||||
```html {10} title='index.html'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Menu Counter</title>
|
||||
</head>
|
||||
<body>
|
||||
Current value: <strong id="counter">0</strong>
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Finally, to make the values update in the HTML document, we'll add a few lines of DOM manipulation
|
||||
so that the value of the `#counter` element is updated whenever we fire an `update-counter` event.
|
||||
|
||||
```javascript title='renderer.js (Renderer Process)'
|
||||
const counter = document.getElementById('counter')
|
||||
|
||||
window.electronAPI.onUpdateCounter((_event, value) => {
|
||||
const oldValue = Number(counter.innerText)
|
||||
const newValue = oldValue + value
|
||||
counter.innerText = newValue
|
||||
})
|
||||
```
|
||||
|
||||
In the above code, we're passing in a callback to the `window.electronAPI.onUpdateCounter` function
|
||||
exposed from our preload script. The second `value` parameter corresponds to the `1` or `-1` we
|
||||
were passing in from the `webContents.send` call from the native menu.
|
||||
|
||||
### Optional: returning a reply
|
||||
|
||||
There's no equivalent for `ipcRenderer.invoke` for main-to-renderer IPC. Instead, you can
|
||||
send a reply back to the main process from within the `ipcRenderer.on` callback.
|
||||
|
||||
We can demonstrate this with slight modifications to the code from the previous example. In the
|
||||
renderer process, use the `event` parameter to send a reply back to the main process through the
|
||||
`counter-value` channel.
|
||||
|
||||
```javascript title='renderer.js (Renderer Process)'
|
||||
const counter = document.getElementById('counter')
|
||||
|
||||
window.electronAPI.onUpdateCounter((event, value) => {
|
||||
const oldValue = Number(counter.innerText)
|
||||
const newValue = oldValue + value
|
||||
counter.innerText = newValue
|
||||
event.sender.send('counter-value', newValue)
|
||||
})
|
||||
```
|
||||
|
||||
In the main process, listen for `counter-value` events and handle them appropriately.
|
||||
|
||||
```javascript title='main.js (Main Process)'
|
||||
//...
|
||||
ipcMain.on('counter-value', (_event, value) => {
|
||||
console.log(value) // will print value to Node console
|
||||
})
|
||||
//...
|
||||
```
|
||||
|
||||
## Pattern 4: Renderer to renderer
|
||||
|
||||
There's no direct way to send messages between renderer processes in Electron using the `ipcMain`
|
||||
and `ipcRenderer` modules. To achieve this, you have two options:
|
||||
|
||||
* Use the main process as a message broker between renderers. This would involve sending a message
|
||||
from one renderer to the main process, which would forward the message to the other renderer.
|
||||
* Pass a [MessagePort] from the main process to both renderers. This will allow direct communication
|
||||
between renderers after the initial setup.
|
||||
|
||||
## Object serialization
|
||||
|
||||
Electron's IPC implementation uses the HTML standard
|
||||
[Structured Clone Algorithm][sca] to serialize objects passed between processes, meaning that
|
||||
only certain types of objects can be passed through IPC channels.
|
||||
|
||||
In particular, DOM objects (e.g. `Element`, `Location` and `DOMMatrix`), Node.js objects
|
||||
backed by C++ classes (e.g. `process.env`, some members of `Stream`), and Electron objects
|
||||
backed by C++ classes (e.g. `WebContents`, `BrowserWindow` and `WebFrame`) are not serializable
|
||||
with Structured Clone.
|
||||
|
||||
[context isolation tutorial]: context-isolation.md
|
||||
[security reasons]: ./context-isolation.md#security-considerations
|
||||
[`ipcMain`]: ../api/ipc-main.md
|
||||
[`ipcMain.handle`]: ../api/ipc-main.md#ipcmainhandlechannel-listener
|
||||
[`ipcMain.on`]: ../api/ipc-main.md
|
||||
[IpcMainEvent]: ../api/structures/ipc-main-event.md
|
||||
[`ipcRenderer`]: ../api/ipc-renderer.md
|
||||
[`ipcRenderer.invoke`]: ../api/ipc-renderer.md#ipcrendererinvokechannel-args
|
||||
[`ipcRenderer.send`]: ../api/ipc-renderer.md
|
||||
[MessagePort]: ./message-ports.md
|
||||
[preload script]: process-model.md#preload-scripts
|
||||
[process model docs]: process-model.md
|
||||
[sca]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
[`WebContents`]: ../api/web-contents.md
|
||||
[webcontents-send]: ../api/web-contents.md#contentssendchannel-args
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: Launching Your Electron App From a URL In Another App
|
||||
description: This guide will take you through the process of setting your electron app as the default handler for a specific protocol.
|
||||
title: Deep Links
|
||||
description: Set your Electron app as the default handler for a specific protocol.
|
||||
slug: launch-app-from-url-in-another-app
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Launching Your Electron App From A URL In Another App
|
||||
# Deep Links
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# Desktop Launcher Actions (Linux)
|
||||
---
|
||||
title: Desktop Launcher Actions
|
||||
description: Add actions to the system launcher on Linux environments.
|
||||
slug: linux-desktop-actions
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Desktop Launcher Actions
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -42,4 +49,4 @@ parameters. You can find them in your application in the global variable
|
||||
|
||||
[unity-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher
|
||||
[audacious-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles?action=AttachFile&do=get&target=shortcuts.png
|
||||
[spec]: https://specifications.freedesktop.org/desktop-entry-spec/1.1/ar01s11.html
|
||||
[spec]: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# Dock (macOS)
|
||||
---
|
||||
title: Dock
|
||||
description: Configure your application's Dock presence on macOS.
|
||||
slug: macos-dock
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Dock
|
||||
|
||||
Electron has APIs to configure the app's icon in the macOS Dock. A macOS-only
|
||||
API exists to create a custom dock menu, but Electron also uses the app dock
|
||||
@@ -16,12 +23,6 @@ To set your custom dock menu, you need to use the
|
||||
[`app.dock.setMenu`](../api/dock.md#docksetmenumenu-macos) API,
|
||||
which is only available on macOS.
|
||||
|
||||
## Example
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), update the `main.js` file with the
|
||||
following lines:
|
||||
|
||||
```javascript fiddle='docs/fiddles/features/macos-dock-menu'
|
||||
const { app, BrowserWindow, Menu } = require('electron')
|
||||
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# Taskbar Progress Bar (Windows & macOS)
|
||||
---
|
||||
title: Progress Bars
|
||||
description: Provide progress information to users outside of a BrowserWindow.
|
||||
slug: progress-bar
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Progress Bars
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -463,46 +463,46 @@ The fastest way to distribute your newly created app is using
|
||||
1. Add Electron Forge as a development dependency of your app, and use its `import` command to set up
|
||||
Forge's scaffolding:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @electron-forge/cli
|
||||
npx electron-forge import
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @electron-forge/cli
|
||||
npx electron-forge import
|
||||
|
||||
✔ Checking your system
|
||||
✔ Initializing Git Repository
|
||||
✔ Writing modified package.json file
|
||||
✔ Installing dependencies
|
||||
✔ Writing modified package.json file
|
||||
✔ Fixing .gitignore
|
||||
✔ Checking your system
|
||||
✔ Initializing Git Repository
|
||||
✔ Writing modified package.json file
|
||||
✔ Installing dependencies
|
||||
✔ Writing modified package.json file
|
||||
✔ Fixing .gitignore
|
||||
|
||||
We have ATTEMPTED to convert your app to be in a format that electron-forge understands.
|
||||
We have ATTEMPTED to convert your app to be in a format that electron-forge understands.
|
||||
|
||||
Thanks for using "electron-forge"!!!
|
||||
```
|
||||
Thanks for using "electron-forge"!!!
|
||||
```
|
||||
|
||||
1. Create a distributable using Forge's `make` command:
|
||||
2. Create a distributable using Forge's `make` command:
|
||||
|
||||
```sh npm2yarn
|
||||
npm run make
|
||||
```sh npm2yarn
|
||||
npm run make
|
||||
|
||||
> my-electron-app@1.0.0 make /my-electron-app
|
||||
> electron-forge make
|
||||
> my-electron-app@1.0.0 make /my-electron-app
|
||||
> electron-forge make
|
||||
|
||||
✔ Checking your system
|
||||
✔ Resolving Forge Config
|
||||
We need to package your application before we can make it
|
||||
✔ Preparing to Package Application for arch: x64
|
||||
✔ Preparing native dependencies
|
||||
✔ Packaging Application
|
||||
Making for the following targets: zip
|
||||
✔ Making for target: zip - On platform: darwin - For arch: x64
|
||||
```
|
||||
✔ Checking your system
|
||||
✔ Resolving Forge Config
|
||||
We need to package your application before we can make it
|
||||
✔ Preparing to Package Application for arch: x64
|
||||
✔ Preparing native dependencies
|
||||
✔ Packaging Application
|
||||
Making for the following targets: zip
|
||||
✔ Making for target: zip - On platform: darwin - For arch: x64
|
||||
```
|
||||
|
||||
Electron Forge creates the `out` folder where your package will be located:
|
||||
Electron Forge creates the `out` folder where your package will be located:
|
||||
|
||||
```plain
|
||||
// Example for macOS
|
||||
out/
|
||||
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
|
||||
├── ...
|
||||
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app
|
||||
```
|
||||
```plain
|
||||
// Example for macOS
|
||||
out/
|
||||
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
|
||||
├── ...
|
||||
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app
|
||||
```
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# Recent Documents (Windows & macOS)
|
||||
---
|
||||
title: Recent Documents
|
||||
description: Provide a list of recent documents via Windows JumpList or macOS Dock
|
||||
slug: recent-documents
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Recent Documents
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# Representing Files in a BrowserWindow (macOS)
|
||||
---
|
||||
title: Representing Files in a BrowserWindow
|
||||
description: Set a represented file in the macOS title bar.
|
||||
slug: represented-file
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Representing Files in a BrowserWindow
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ for answers to questions,
|
||||
or to join in discussion with other developers who use Electron,
|
||||
you can interact with the community in these locations:
|
||||
|
||||
* [Electron's Discord server](https://discord.com/invite/APGC3k5yaH) has channels for:
|
||||
* [`Electron's Discord`](https://discord.com/invite/electron) has channels for:
|
||||
* Getting help
|
||||
* Ecosystem apps like [Electron Forge](https://github.com/electron-userland/electron-forge) and [Electron Fiddle](https://github.com/electron/fiddle)
|
||||
* Sharing ideas with other Electron app developers
|
||||
@@ -70,10 +70,10 @@ until the maintainers feel the maintenance burden is too high to continue doing
|
||||
|
||||
### Currently supported versions
|
||||
|
||||
* 18.x.y
|
||||
* 17.x.y
|
||||
* 16.x.y
|
||||
* 15.x.y
|
||||
* 14.x.y
|
||||
|
||||
### End-of-life
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Being based on Chromium, Electron requires a display driver to function.
|
||||
If Chromium can't find a display driver, Electron will fail to launch -
|
||||
and therefore not executing any of your tests, regardless of how you are running
|
||||
them. Testing Electron-based apps on Travis, CircleCI, Jenkins or similar Systems
|
||||
them. Testing Electron-based apps on Travis, Circle, Jenkins or similar Systems
|
||||
requires therefore a little bit of configuration. In essence, we need to use
|
||||
a virtual display driver.
|
||||
|
||||
@@ -49,9 +49,10 @@ install:
|
||||
|
||||
For Jenkins, a [Xvfb plugin is available](https://wiki.jenkins-ci.org/display/JENKINS/Xvfb+Plugin).
|
||||
|
||||
### CircleCI
|
||||
### Circle CI
|
||||
|
||||
CircleCI is awesome and has Xvfb and `$DISPLAY` already set up, so no further configuration is required.
|
||||
Circle CI is awesome and has Xvfb and `$DISPLAY`
|
||||
[already set up, so no further configuration is required](https://circleci.com/docs/environment#browsers).
|
||||
|
||||
### AppVeyor
|
||||
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# Taskbar Customization (Windows)
|
||||
---
|
||||
title: Taskbar Customization
|
||||
description: Customize the look and feel of your app's Windows taskbar presence.
|
||||
slug: windows-taskbar
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Taskbar Customization
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ auto_filenames = {
|
||||
"docs/api/app.md",
|
||||
"docs/api/auto-updater.md",
|
||||
"docs/api/browser-view.md",
|
||||
"docs/api/browser-window-proxy.md",
|
||||
"docs/api/browser-window.md",
|
||||
"docs/api/client-request.md",
|
||||
"docs/api/clipboard.md",
|
||||
@@ -228,6 +229,7 @@ auto_filenames = {
|
||||
"lib/browser/devtools.ts",
|
||||
"lib/browser/guest-view-manager.ts",
|
||||
"lib/browser/guest-window-manager.ts",
|
||||
"lib/browser/guest-window-proxy.ts",
|
||||
"lib/browser/init.ts",
|
||||
"lib/browser/ipc-main-impl.ts",
|
||||
"lib/browser/ipc-main-internal-utils.ts",
|
||||
|
||||
@@ -53,8 +53,6 @@ filenames = {
|
||||
"shell/browser/ui/views/global_menu_bar_x11.h",
|
||||
"shell/browser/ui/x/event_disabler.cc",
|
||||
"shell/browser/ui/x/event_disabler.h",
|
||||
"shell/browser/ui/x/window_state_watcher.cc",
|
||||
"shell/browser/ui/x/window_state_watcher.h",
|
||||
"shell/browser/ui/x/x_window_utils.cc",
|
||||
"shell/browser/ui/x/x_window_utils.h",
|
||||
]
|
||||
@@ -350,6 +348,8 @@ filenames = {
|
||||
"shell/browser/child_web_contents_tracker.h",
|
||||
"shell/browser/cookie_change_notifier.cc",
|
||||
"shell/browser/cookie_change_notifier.h",
|
||||
"shell/browser/electron_api_ipc_handler_impl.cc",
|
||||
"shell/browser/electron_api_ipc_handler_impl.h",
|
||||
"shell/browser/electron_autofill_driver.cc",
|
||||
"shell/browser/electron_autofill_driver.h",
|
||||
"shell/browser/electron_autofill_driver_factory.cc",
|
||||
@@ -358,8 +358,6 @@ filenames = {
|
||||
"shell/browser/electron_browser_client.h",
|
||||
"shell/browser/electron_browser_context.cc",
|
||||
"shell/browser/electron_browser_context.h",
|
||||
"shell/browser/electron_browser_handler_impl.cc",
|
||||
"shell/browser/electron_browser_handler_impl.h",
|
||||
"shell/browser/electron_browser_main_parts.cc",
|
||||
"shell/browser/electron_browser_main_parts.h",
|
||||
"shell/browser/electron_download_manager_delegate.cc",
|
||||
@@ -376,6 +374,8 @@ filenames = {
|
||||
"shell/browser/electron_quota_permission_context.h",
|
||||
"shell/browser/electron_speech_recognition_manager_delegate.cc",
|
||||
"shell/browser/electron_speech_recognition_manager_delegate.h",
|
||||
"shell/browser/electron_web_contents_utility_handler_impl.cc",
|
||||
"shell/browser/electron_web_contents_utility_handler_impl.h",
|
||||
"shell/browser/electron_web_ui_controller_factory.cc",
|
||||
"shell/browser/electron_web_ui_controller_factory.h",
|
||||
"shell/browser/event_emitter_mixin.cc",
|
||||
|
||||
@@ -30,11 +30,13 @@ const getOrCreateArchive = (archivePath: string) => {
|
||||
return cachedArchives.get(archivePath);
|
||||
}
|
||||
|
||||
const newArchive = asar.createArchive(archivePath);
|
||||
if (!newArchive) return null;
|
||||
|
||||
cachedArchives.set(archivePath, newArchive);
|
||||
return newArchive;
|
||||
try {
|
||||
const newArchive = new asar.Archive(archivePath);
|
||||
cachedArchives.set(archivePath, newArchive);
|
||||
return newArchive;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const asarRe = /\.asar/i;
|
||||
|
||||
@@ -35,7 +35,7 @@ const spawnUpdate = function (args: string[], detached: boolean, callback: Funct
|
||||
spawnedArgs = args || [];
|
||||
}
|
||||
} catch (error1) {
|
||||
error = error1 as Error;
|
||||
error = error1;
|
||||
|
||||
// Shouldn't happen, but still guard it.
|
||||
process.nextTick(function () {
|
||||
|
||||
@@ -72,7 +72,10 @@ BrowserWindow.getAllWindows = () => {
|
||||
|
||||
BrowserWindow.getFocusedWindow = () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
const hasWC = window.webContents && !window.webContents.isDestroyed();
|
||||
if (!window.isDestroyed() && hasWC) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -5,8 +5,7 @@ const isWindows = process.platform === 'win32';
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
type RoleId = 'about' | 'close' | 'copy' | 'cut' | 'delete' | 'forcereload' | 'front' | 'help' | 'hide' | 'hideothers' | 'minimize' |
|
||||
'paste' | 'pasteandmatchstyle' | 'quit' | 'redo' | 'reload' | 'resetzoom' | 'selectall' | 'services' | 'recentdocuments' | 'clearrecentdocuments' |
|
||||
'showsubstitutions' | 'togglesmartquotes' | 'togglesmartdashes' | 'toggletextreplacement' | 'startspeaking' | 'stopspeaking' |
|
||||
'paste' | 'pasteandmatchstyle' | 'quit' | 'redo' | 'reload' | 'resetzoom' | 'selectall' | 'services' | 'recentdocuments' | 'clearrecentdocuments' | 'startspeaking' | 'stopspeaking' |
|
||||
'toggledevtools' | 'togglefullscreen' | 'undo' | 'unhide' | 'window' | 'zoom' | 'zoomin' | 'zoomout' | 'togglespellchecker' |
|
||||
'appmenu' | 'filemenu' | 'editmenu' | 'viewmenu' | 'windowmenu' | 'sharemenu'
|
||||
interface Role {
|
||||
@@ -134,18 +133,6 @@ export const roleList: Record<RoleId, Role> = {
|
||||
clearrecentdocuments: {
|
||||
label: 'Clear Menu'
|
||||
},
|
||||
showsubstitutions: {
|
||||
label: 'Show Substitutions'
|
||||
},
|
||||
togglesmartquotes: {
|
||||
label: 'Smart Quotes'
|
||||
},
|
||||
togglesmartdashes: {
|
||||
label: 'Smart Dashes'
|
||||
},
|
||||
toggletextreplacement: {
|
||||
label: 'Text Replacement'
|
||||
},
|
||||
startspeaking: {
|
||||
label: 'Start Speaking'
|
||||
},
|
||||
@@ -250,16 +237,6 @@ export const roleList: Record<RoleId, Role> = {
|
||||
{ role: 'delete' },
|
||||
{ role: 'selectAll' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Substitutions',
|
||||
submenu: [
|
||||
{ role: 'showSubstitutions' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'toggleSmartQuotes' },
|
||||
{ role: 'toggleSmartDashes' },
|
||||
{ role: 'toggleTextReplacement' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Speech',
|
||||
submenu: [
|
||||
|
||||
@@ -61,25 +61,24 @@ class IncomingMessage extends Readable {
|
||||
const filteredHeaders: Record<string, string | string[]> = {};
|
||||
const { rawHeaders } = this._responseHead;
|
||||
rawHeaders.forEach(header => {
|
||||
const keyLowerCase = header.key.toLowerCase();
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, keyLowerCase) &&
|
||||
discardableDuplicateHeaders.has(keyLowerCase)) {
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) &&
|
||||
discardableDuplicateHeaders.has(header.key)) {
|
||||
// do nothing with discardable duplicate headers
|
||||
} else {
|
||||
if (keyLowerCase === 'set-cookie') {
|
||||
if (header.key === 'set-cookie') {
|
||||
// keep set-cookie as an array per Node.js rules
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, keyLowerCase)) {
|
||||
(filteredHeaders[keyLowerCase] as string[]).push(header.value);
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||
(filteredHeaders[header.key] as string[]).push(header.value);
|
||||
} else {
|
||||
filteredHeaders[keyLowerCase] = [header.value];
|
||||
filteredHeaders[header.key] = [header.value];
|
||||
}
|
||||
} else {
|
||||
// for non-cookie headers, the values are joined together with ', '
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, keyLowerCase)) {
|
||||
filteredHeaders[keyLowerCase] += `, ${header.value}`;
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||
filteredHeaders[header.key] += `, ${header.value}`;
|
||||
} else {
|
||||
filteredHeaders[keyLowerCase] = header.value;
|
||||
filteredHeaders[header.key] = header.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,15 +86,6 @@ class IncomingMessage extends Readable {
|
||||
return filteredHeaders;
|
||||
}
|
||||
|
||||
get rawHeaders () {
|
||||
const rawHeadersArr: string[] = [];
|
||||
const { rawHeaders } = this._responseHead;
|
||||
rawHeaders.forEach(header => {
|
||||
rawHeadersArr.push(header.key, header.value);
|
||||
});
|
||||
return rawHeadersArr;
|
||||
}
|
||||
|
||||
get httpVersion () {
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app, ipcMain, session, webFrameMain } from 'electron/main';
|
||||
import { app, ipcMain, session, deprecate, webFrameMain } from 'electron/main';
|
||||
import type { BrowserWindowConstructorOptions, LoadURLOptions } from 'electron/main';
|
||||
|
||||
import * as url from 'url';
|
||||
@@ -613,6 +613,7 @@ WebContents.prototype._init = function () {
|
||||
});
|
||||
|
||||
this.on('-ipc-ports' as any, function (event: Electron.IpcMainEvent, internal: boolean, channel: string, message: any, ports: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
event.ports = ports.map(p => new MessagePortMain(p));
|
||||
ipcMain.emit(channel, event, message);
|
||||
});
|
||||
@@ -692,8 +693,8 @@ WebContents.prototype._init = function () {
|
||||
// TODO(zcbenz): The features string is parsed twice: here where it is
|
||||
// passed to C++, and in |makeBrowserWindowOptions| later where it is
|
||||
// not actually used since the WebContents is created here.
|
||||
// We should be able to remove the latter once the |new-window| event
|
||||
// is removed.
|
||||
// We should be able to remove the latter once the |nativeWindowOpen|
|
||||
// option is removed.
|
||||
const { webPreferences: parsedWebPreferences } = parseFeatures(rawFeatures);
|
||||
// Parameters should keep same with |makeBrowserWindowOptions|.
|
||||
const webPreferences = makeWebPreferences({
|
||||
@@ -705,7 +706,8 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new browser window for "window.open"
|
||||
// Create a new browser window for the native implementation of
|
||||
// "window.open", used in sandbox and nativeWindowOpen mode.
|
||||
this.on('-add-new-contents' as any, (event: ElectronInternal.Event, webContents: Electron.WebContents, disposition: string,
|
||||
_userGesture: boolean, _left: number, _top: number, _width: number, _height: number, url: string, frameName: string,
|
||||
referrer: Electron.Referrer, rawFeatures: string, postData: PostData) => {
|
||||
@@ -733,6 +735,11 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const prefs = this.getLastWebPreferences() || {};
|
||||
if (prefs.nativeWindowOpen === false) {
|
||||
deprecate.log('Deprecation Warning: Disabling nativeWindowOpen is deprecated. The nativeWindowOpen option will be removed in Electron 18.');
|
||||
}
|
||||
}
|
||||
|
||||
this.on('login', (event, ...args) => {
|
||||
|
||||
@@ -31,7 +31,7 @@ export const setDefaultApplicationMenu = () => {
|
||||
{
|
||||
label: 'Community Discussions',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://discord.com/invite/APGC3k5yaH');
|
||||
await shell.openExternal('https://discord.gg/electron');
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -56,6 +56,7 @@ function makeWebPreferences (embedder: Electron.WebContents, params: Record<stri
|
||||
const inheritedWebPreferences = new Map([
|
||||
['contextIsolation', true],
|
||||
['javascript', false],
|
||||
['nativeWindowOpen', true],
|
||||
['nodeIntegration', false],
|
||||
['sandbox', true],
|
||||
['nodeIntegrationInSubFrames', false],
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/**
|
||||
* Create and minimally track guest windows at the direction of the renderer
|
||||
* (via window.open). Here, "guest" roughly means "child" — it's not necessarily
|
||||
* emblematic of its process status; both in-process (same-origin) and
|
||||
* out-of-process (cross-origin) are created here. "Embedder" roughly means
|
||||
* "parent."
|
||||
* emblematic of its process status; both in-process (same-origin
|
||||
* nativeWindowOpen) and out-of-process (cross-origin nativeWindowOpen and
|
||||
* BrowserWindowProxy) are created here. "Embedder" roughly means "parent."
|
||||
*/
|
||||
import { BrowserWindow } from 'electron/main';
|
||||
import type { BrowserWindowConstructorOptions, Referrer, WebContents, LoadURLOptions } from 'electron/main';
|
||||
import { parseFeatures } from '@electron/internal/browser/parse-features-string';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
type PostData = LoadURLOptions['postData']
|
||||
export type WindowOpenArgs = {
|
||||
@@ -22,12 +23,13 @@ const unregisterFrameName = (name: string) => frameNamesToWindow.delete(name);
|
||||
const getGuestWindowByFrameName = (name: string) => frameNamesToWindow.get(name);
|
||||
|
||||
/**
|
||||
* `openGuestWindow` is called to create and setup event handling for the new
|
||||
* window.
|
||||
* `openGuestWindow` is called for both implementations of window.open
|
||||
* (BrowserWindowProxy and nativeWindowOpen) to create and setup event handling
|
||||
* for the new window.
|
||||
*
|
||||
* Until its removal in 12.0.0, the `new-window` event is fired, allowing the
|
||||
* user to preventDefault() on the passed event (which ends up calling
|
||||
* DestroyWebContents).
|
||||
* DestroyWebContents in the nativeWindowOpen code path).
|
||||
*/
|
||||
export function openGuestWindow ({ event, embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs }: {
|
||||
event: { sender: WebContents, defaultPrevented: boolean },
|
||||
@@ -76,6 +78,22 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
|
||||
webContents: guest,
|
||||
...browserWindowOptions
|
||||
});
|
||||
if (!guest) {
|
||||
// We should only call `loadURL` if the webContents was constructed by us in
|
||||
// the case of BrowserWindowProxy (non-sandboxed, nativeWindowOpen: false),
|
||||
// as navigating to the url when creating the window from an existing
|
||||
// webContents is not necessary (it will navigate there anyway).
|
||||
// This can also happen if we enter this function from OpenURLFromTab, in
|
||||
// which case the browser process is responsible for initiating navigation
|
||||
// in the new window.
|
||||
window.loadURL(url, {
|
||||
httpReferrer: referrer,
|
||||
...(postData && {
|
||||
postData,
|
||||
extraHeaders: formatPostDataHeaders(postData as Electron.UploadRawData[])
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
handleWindowLifecycleEvents({ embedder, frameName, guest: window });
|
||||
|
||||
@@ -100,7 +118,9 @@ const handleWindowLifecycleEvents = function ({ embedder, guest, frameName }: {
|
||||
guest.destroy();
|
||||
};
|
||||
|
||||
const cachedGuestId = guest.webContents.id;
|
||||
const closedByUser = function () {
|
||||
embedder._sendInternal(`${IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_CLOSED}_${cachedGuestId}`);
|
||||
embedder.removeListener('current-render-view-deleted' as any, closedByEmbedder);
|
||||
};
|
||||
embedder.once('current-render-view-deleted' as any, closedByEmbedder);
|
||||
@@ -175,6 +195,7 @@ function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs,
|
||||
const securityWebPreferences: { [key: string]: boolean } = {
|
||||
contextIsolation: true,
|
||||
javascript: false,
|
||||
nativeWindowOpen: true,
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
webviewTag: false,
|
||||
@@ -196,10 +217,10 @@ function makeBrowserWindowOptions ({ embedder, features, overrideOptions }: {
|
||||
height: 600,
|
||||
...parsedOptions,
|
||||
...overrideOptions,
|
||||
// Note that for normal code path an existing WebContents created by
|
||||
// Chromium will be used, with web preferences parsed in the
|
||||
// |-will-add-new-contents| event.
|
||||
// The |webPreferences| here is only used by the |new-window| event.
|
||||
// Note that for |nativeWindowOpen: true| the WebContents is created in
|
||||
// |api::WebContents::WebContentsCreatedWithFullParams|, with prefs
|
||||
// parsed in the |-will-add-new-contents| event.
|
||||
// The |webPreferences| here is only used by |nativeWindowOpen: false|.
|
||||
webPreferences: makeWebPreferences({
|
||||
embedder,
|
||||
insecureParsedWebPreferences: parsedWebPreferences,
|
||||
@@ -224,6 +245,7 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||
}
|
||||
return map;
|
||||
}, {} as Electron.WebPreferences));
|
||||
const openerId = parentWebPreferences.nativeWindowOpen ? null : embedder.id;
|
||||
|
||||
return {
|
||||
...parsedWebPreferences,
|
||||
@@ -231,10 +253,22 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||
// ability to change important security options but allow main (via
|
||||
// setWindowOpenHandler) to change them.
|
||||
...securityWebPreferencesFromParent,
|
||||
...secureOverrideWebPreferences
|
||||
...secureOverrideWebPreferences,
|
||||
// Sets correct openerId here to give correct options to 'new-window' event handler
|
||||
// TODO: Figure out another way to pass this?
|
||||
openerId
|
||||
};
|
||||
}
|
||||
|
||||
function formatPostDataHeaders (postData: PostData) {
|
||||
if (!postData) return;
|
||||
|
||||
const { contentType, boundary } = parseContentTypeFormat(postData);
|
||||
if (boundary != null) { return `content-type: ${contentType}; boundary=${boundary}`; }
|
||||
|
||||
return `content-type: ${contentType}`;
|
||||
}
|
||||
|
||||
const MULTIPART_CONTENT_TYPE = 'multipart/form-data';
|
||||
const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
||||
|
||||
|
||||
213
lib/browser/guest-window-proxy.ts
Normal file
213
lib/browser/guest-window-proxy.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Manage guest windows when using the default BrowserWindowProxy version of the
|
||||
* renderer's window.open (i.e. nativeWindowOpen off). This module mostly
|
||||
* consists of marshaling IPC requests from the BrowserWindowProxy to the
|
||||
* WebContents.
|
||||
*/
|
||||
import { webContents, BrowserWindow } from 'electron/main';
|
||||
import type { WebContents } from 'electron/main';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
||||
import { openGuestWindow } from '@electron/internal/browser/guest-window-manager';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
const { isSameOrigin } = process._linkedBinding('electron_common_v8_util');
|
||||
|
||||
const getGuestWindow = function (guestContents: WebContents) {
|
||||
let guestWindow = BrowserWindow.fromWebContents(guestContents);
|
||||
if (guestWindow == null) {
|
||||
const hostContents = guestContents.hostWebContents;
|
||||
if (hostContents != null) {
|
||||
guestWindow = BrowserWindow.fromWebContents(hostContents);
|
||||
}
|
||||
}
|
||||
if (!guestWindow) {
|
||||
throw new Error('getGuestWindow failed');
|
||||
}
|
||||
return guestWindow;
|
||||
};
|
||||
|
||||
const isChildWindow = function (sender: WebContents, target: WebContents) {
|
||||
return target.getLastWebPreferences()!.openerId === sender.id;
|
||||
};
|
||||
|
||||
const isRelatedWindow = function (sender: WebContents, target: WebContents) {
|
||||
return isChildWindow(sender, target) || isChildWindow(target, sender);
|
||||
};
|
||||
|
||||
const isScriptableWindow = function (sender: WebContents, target: WebContents) {
|
||||
return (
|
||||
isRelatedWindow(sender, target) &&
|
||||
isSameOrigin(sender.getURL(), target.getURL())
|
||||
);
|
||||
};
|
||||
|
||||
const isNodeIntegrationEnabled = function (sender: WebContents) {
|
||||
return sender.getLastWebPreferences()!.nodeIntegration === true;
|
||||
};
|
||||
|
||||
// Checks whether |sender| can access the |target|:
|
||||
const canAccessWindow = function (sender: WebContents, target: WebContents) {
|
||||
return (
|
||||
isChildWindow(sender, target) ||
|
||||
isScriptableWindow(sender, target) ||
|
||||
isNodeIntegrationEnabled(sender)
|
||||
);
|
||||
};
|
||||
|
||||
// Routed window.open messages with raw options
|
||||
ipcMainInternal.on(
|
||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_OPEN,
|
||||
(
|
||||
event,
|
||||
url: string,
|
||||
frameName: string,
|
||||
features: string
|
||||
) => {
|
||||
// This should only be allowed for senders that have nativeWindowOpen: false
|
||||
const lastWebPreferences = event.sender.getLastWebPreferences()!;
|
||||
if (lastWebPreferences.nativeWindowOpen || lastWebPreferences.sandbox) {
|
||||
event.returnValue = null;
|
||||
throw new Error(
|
||||
'GUEST_WINDOW_MANAGER_WINDOW_OPEN denied: expected native window.open'
|
||||
);
|
||||
}
|
||||
|
||||
const referrer: Electron.Referrer = { url: '', policy: 'strict-origin-when-cross-origin' };
|
||||
const browserWindowOptions = event.sender._callWindowOpenHandler(event, { url, frameName, features, disposition: 'new-window', referrer });
|
||||
if (event.defaultPrevented) {
|
||||
event.returnValue = null;
|
||||
return;
|
||||
}
|
||||
const guest = openGuestWindow({
|
||||
event,
|
||||
embedder: event.sender,
|
||||
referrer,
|
||||
disposition: 'new-window',
|
||||
overrideBrowserWindowOptions: browserWindowOptions!,
|
||||
windowOpenArgs: {
|
||||
url: url || 'about:blank',
|
||||
frameName: frameName || '',
|
||||
features: features || ''
|
||||
}
|
||||
});
|
||||
|
||||
event.returnValue = guest ? guest.webContents.id : null;
|
||||
}
|
||||
);
|
||||
|
||||
type IpcHandler<T, Event> = (event: Event, guestContents: Electron.WebContents, ...args: any[]) => T;
|
||||
const makeSafeHandler = function<T, Event> (handler: IpcHandler<T, Event>) {
|
||||
return (event: Event, guestId: number, ...args: any[]) => {
|
||||
// Access webContents via electron to prevent circular require.
|
||||
const guestContents = webContents.fromId(guestId);
|
||||
if (!guestContents) {
|
||||
throw new Error(`Invalid guestId: ${guestId}`);
|
||||
}
|
||||
|
||||
return handler(event, guestContents as Electron.WebContents, ...args);
|
||||
};
|
||||
};
|
||||
|
||||
const handleMessage = function (channel: string, handler: IpcHandler<any, Electron.IpcMainInvokeEvent>) {
|
||||
ipcMainInternal.handle(channel, makeSafeHandler(handler));
|
||||
};
|
||||
|
||||
const handleMessageSync = function (channel: string, handler: IpcHandler<any, ElectronInternal.IpcMainInternalEvent>) {
|
||||
ipcMainUtils.handleSync(channel, makeSafeHandler(handler));
|
||||
};
|
||||
|
||||
type ContentsCheck = (contents: WebContents, guestContents: WebContents) => boolean;
|
||||
const securityCheck = function (contents: WebContents, guestContents: WebContents, check: ContentsCheck) {
|
||||
if (!check(contents, guestContents)) {
|
||||
console.error(
|
||||
`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`
|
||||
);
|
||||
throw new Error(`Access denied to guestId: ${guestContents.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
const windowMethods = new Set(['destroy', 'focus', 'blur']);
|
||||
|
||||
handleMessage(
|
||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_METHOD,
|
||||
(event, guestContents, method, ...args) => {
|
||||
securityCheck(event.sender, guestContents, canAccessWindow);
|
||||
|
||||
if (!windowMethods.has(method)) {
|
||||
console.error(
|
||||
`Blocked ${event.senderFrame.url} from calling method: ${method}`
|
||||
);
|
||||
throw new Error(`Invalid method: ${method}`);
|
||||
}
|
||||
|
||||
return (getGuestWindow(guestContents) as any)[method](...args);
|
||||
}
|
||||
);
|
||||
|
||||
handleMessage(
|
||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE,
|
||||
(event, guestContents, message, targetOrigin, sourceOrigin) => {
|
||||
if (targetOrigin == null) {
|
||||
targetOrigin = '*';
|
||||
}
|
||||
|
||||
// The W3C does not seem to have word on how postMessage should work when the
|
||||
// origins do not match, so we do not do |canAccessWindow| check here since
|
||||
// postMessage across origins is useful and not harmful.
|
||||
securityCheck(event.sender, guestContents, isRelatedWindow);
|
||||
|
||||
if (
|
||||
targetOrigin === '*' ||
|
||||
isSameOrigin(guestContents.getURL(), targetOrigin)
|
||||
) {
|
||||
const sourceId = event.sender.id;
|
||||
guestContents._sendInternal(
|
||||
IPC_MESSAGES.GUEST_WINDOW_POSTMESSAGE,
|
||||
sourceId,
|
||||
message,
|
||||
sourceOrigin
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const webContentsMethodsAsync = new Set([
|
||||
'loadURL',
|
||||
'executeJavaScript',
|
||||
'print'
|
||||
]);
|
||||
|
||||
handleMessage(
|
||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD,
|
||||
(event, guestContents, method, ...args) => {
|
||||
securityCheck(event.sender, guestContents, canAccessWindow);
|
||||
|
||||
if (!webContentsMethodsAsync.has(method)) {
|
||||
console.error(
|
||||
`Blocked ${event.sender.getURL()} from calling method: ${method}`
|
||||
);
|
||||
throw new Error(`Invalid method: ${method}`);
|
||||
}
|
||||
|
||||
return (guestContents as any)[method](...args);
|
||||
}
|
||||
);
|
||||
|
||||
const webContentsMethodsSync = new Set(['getURL']);
|
||||
|
||||
handleMessageSync(
|
||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD,
|
||||
(event, guestContents, method, ...args) => {
|
||||
securityCheck(event.sender, guestContents, canAccessWindow);
|
||||
|
||||
if (!webContentsMethodsSync.has(method)) {
|
||||
console.error(
|
||||
`Blocked ${event.sender.getURL()} from calling method: ${method}`
|
||||
);
|
||||
throw new Error(`Invalid method: ${method}`);
|
||||
}
|
||||
|
||||
return (guestContents as any)[method](...args);
|
||||
}
|
||||
);
|
||||
@@ -78,6 +78,7 @@ require('@electron/internal/browser/rpc-server');
|
||||
|
||||
// Load the guest view manager.
|
||||
require('@electron/internal/browser/guest-view-manager');
|
||||
require('@electron/internal/browser/guest-window-proxy');
|
||||
|
||||
// Now we try to load app's package.json.
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
|
||||
@@ -15,7 +15,7 @@ export class IpcMainImpl extends EventEmitter {
|
||||
try {
|
||||
e._reply(await Promise.resolve(fn(e, ...args)));
|
||||
} catch (err) {
|
||||
e._throw(err as Error);
|
||||
e._throw(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,6 +18,13 @@ export const enum IPC_MESSAGES {
|
||||
GUEST_VIEW_MANAGER_PROPERTY_GET = 'GUEST_VIEW_MANAGER_PROPERTY_GET',
|
||||
GUEST_VIEW_MANAGER_PROPERTY_SET = 'GUEST_VIEW_MANAGER_PROPERTY_SET',
|
||||
|
||||
GUEST_WINDOW_MANAGER_WINDOW_OPEN = 'GUEST_WINDOW_MANAGER_WINDOW_OPEN',
|
||||
GUEST_WINDOW_MANAGER_WINDOW_CLOSED = 'GUEST_WINDOW_MANAGER_WINDOW_CLOSED',
|
||||
GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE = 'GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE',
|
||||
GUEST_WINDOW_MANAGER_WINDOW_METHOD = 'GUEST_WINDOW_MANAGER_WINDOW_METHOD',
|
||||
GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD = 'GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD',
|
||||
GUEST_WINDOW_POSTMESSAGE = 'GUEST_WINDOW_POSTMESSAGE',
|
||||
|
||||
RENDERER_WEB_FRAME_METHOD = 'RENDERER_WEB_FRAME_METHOD',
|
||||
|
||||
INSPECTOR_CONFIRM = 'INSPECTOR_CONFIRM',
|
||||
|
||||
@@ -12,7 +12,9 @@ const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const nodeIntegration = mainFrame.getWebPreference('nodeIntegration');
|
||||
const webviewTag = mainFrame.getWebPreference('webviewTag');
|
||||
const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
|
||||
const nativeWindowOpen = mainFrame.getWebPreference('nativeWindowOpen') || process.sandboxed;
|
||||
const isWebView = mainFrame.getWebPreference('isWebView');
|
||||
const openerId = mainFrame.getWebPreference('openerId');
|
||||
|
||||
// ElectronApiServiceImpl will look for the "ipcNative" hidden object when
|
||||
// invoking the 'onMessage' callback.
|
||||
@@ -42,7 +44,7 @@ switch (window.location.protocol) {
|
||||
default: {
|
||||
// Override default web functions.
|
||||
const { windowSetup } = require('@electron/internal/renderer/window-setup') as typeof windowSetupModule;
|
||||
windowSetup(isWebView, isHiddenPage);
|
||||
windowSetup(isWebView, openerId, isHiddenPage, nativeWindowOpen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,249 @@
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
import { internalContextBridge } from '@electron/internal/renderer/api/context-bridge';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
const { contextIsolationEnabled } = internalContextBridge;
|
||||
|
||||
export const windowSetup = (isWebView: boolean, isHiddenPage: boolean) => {
|
||||
// This file implements the following APIs over the ctx bridge:
|
||||
// - window.open()
|
||||
// - window.opener.blur()
|
||||
// - window.opener.close()
|
||||
// - window.opener.eval()
|
||||
// - window.opener.focus()
|
||||
// - window.opener.location
|
||||
// - window.opener.print()
|
||||
// - window.opener.closed
|
||||
// - window.opener.postMessage()
|
||||
// - window.history.back()
|
||||
// - window.history.forward()
|
||||
// - window.history.go()
|
||||
// - window.history.length
|
||||
// - window.prompt()
|
||||
// - document.hidden
|
||||
// - document.visibilityState
|
||||
|
||||
// Helper function to resolve relative url.
|
||||
const resolveURL = (url: string, base: string) => new URL(url, base).href;
|
||||
|
||||
// Use this method to ensure values expected as strings in the main process
|
||||
// are convertible to strings in the renderer process. This ensures exceptions
|
||||
// converting values to strings are thrown in this process.
|
||||
const toString = (value: any) => {
|
||||
return value != null ? `${value}` : value;
|
||||
};
|
||||
|
||||
const windowProxies = new Map<number, BrowserWindowProxy>();
|
||||
|
||||
const getOrCreateProxy = (guestId: number): SafelyBoundBrowserWindowProxy => {
|
||||
let proxy = windowProxies.get(guestId);
|
||||
if (proxy == null) {
|
||||
proxy = new BrowserWindowProxy(guestId);
|
||||
windowProxies.set(guestId, proxy);
|
||||
}
|
||||
return proxy.getSafe();
|
||||
};
|
||||
|
||||
const removeProxy = (guestId: number) => {
|
||||
windowProxies.delete(guestId);
|
||||
};
|
||||
|
||||
type LocationProperties = 'hash' | 'href' | 'host' | 'hostname' | 'origin' | 'pathname' | 'port' | 'protocol' | 'search'
|
||||
|
||||
class LocationProxy {
|
||||
@LocationProxy.ProxyProperty public hash!: string;
|
||||
@LocationProxy.ProxyProperty public href!: string;
|
||||
@LocationProxy.ProxyProperty public host!: string;
|
||||
@LocationProxy.ProxyProperty public hostname!: string;
|
||||
@LocationProxy.ProxyProperty public origin!: string;
|
||||
@LocationProxy.ProxyProperty public pathname!: string;
|
||||
@LocationProxy.ProxyProperty public port!: string;
|
||||
@LocationProxy.ProxyProperty public protocol!: string;
|
||||
@LocationProxy.ProxyProperty public search!: URLSearchParams;
|
||||
|
||||
private guestId: number;
|
||||
|
||||
/**
|
||||
* Beware: This decorator will have the _prototype_ as the `target`. It defines properties
|
||||
* commonly found in URL on the LocationProxy.
|
||||
*/
|
||||
private static ProxyProperty<T> (target: LocationProxy, propertyKey: LocationProperties) {
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function (this: LocationProxy): T | string {
|
||||
const guestURL = this.getGuestURL();
|
||||
const value = guestURL ? guestURL[propertyKey] : '';
|
||||
return value === undefined ? '' : value;
|
||||
},
|
||||
set: function (this: LocationProxy, newVal: T) {
|
||||
const guestURL = this.getGuestURL();
|
||||
if (guestURL) {
|
||||
// TypeScript doesn't want us to assign to read-only variables.
|
||||
// It's right, that's bad, but we're doing it anyway.
|
||||
(guestURL as any)[propertyKey] = newVal;
|
||||
|
||||
return this._invokeWebContentsMethod('loadURL', guestURL.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getSafe = () => {
|
||||
const that = this;
|
||||
return {
|
||||
get href () { return that.href; },
|
||||
set href (newValue) { that.href = newValue; },
|
||||
get hash () { return that.hash; },
|
||||
set hash (newValue) { that.hash = newValue; },
|
||||
get host () { return that.host; },
|
||||
set host (newValue) { that.host = newValue; },
|
||||
get hostname () { return that.hostname; },
|
||||
set hostname (newValue) { that.hostname = newValue; },
|
||||
get origin () { return that.origin; },
|
||||
set origin (newValue) { that.origin = newValue; },
|
||||
get pathname () { return that.pathname; },
|
||||
set pathname (newValue) { that.pathname = newValue; },
|
||||
get port () { return that.port; },
|
||||
set port (newValue) { that.port = newValue; },
|
||||
get protocol () { return that.protocol; },
|
||||
set protocol (newValue) { that.protocol = newValue; },
|
||||
get search () { return that.search; },
|
||||
set search (newValue) { that.search = newValue; }
|
||||
};
|
||||
}
|
||||
|
||||
constructor (guestId: number) {
|
||||
// eslint will consider the constructor "useless"
|
||||
// unless we assign them in the body. It's fine, that's what
|
||||
// TS would do anyway.
|
||||
this.guestId = guestId;
|
||||
this.getGuestURL = this.getGuestURL.bind(this);
|
||||
}
|
||||
|
||||
public toString (): string {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
private getGuestURL (): URL | null {
|
||||
const maybeURL = this._invokeWebContentsMethodSync('getURL') as string;
|
||||
|
||||
// When there's no previous frame the url will be blank, so account for that here
|
||||
// to prevent url parsing errors on an empty string.
|
||||
const urlString = maybeURL !== '' ? maybeURL : 'about:blank';
|
||||
try {
|
||||
return new URL(urlString);
|
||||
} catch (e) {
|
||||
console.error('LocationProxy: failed to parse string', urlString, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private _invokeWebContentsMethod (method: string, ...args: any[]) {
|
||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD, this.guestId, method, ...args);
|
||||
}
|
||||
|
||||
private _invokeWebContentsMethodSync (method: string, ...args: any[]) {
|
||||
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD, this.guestId, method, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
interface SafelyBoundBrowserWindowProxy {
|
||||
location: WindowProxy['location'];
|
||||
blur: WindowProxy['blur'];
|
||||
close: WindowProxy['close'];
|
||||
eval: typeof eval; // eslint-disable-line no-eval
|
||||
focus: WindowProxy['focus'];
|
||||
print: WindowProxy['print'];
|
||||
postMessage: WindowProxy['postMessage'];
|
||||
closed: boolean;
|
||||
}
|
||||
|
||||
class BrowserWindowProxy {
|
||||
public closed: boolean = false
|
||||
|
||||
private _location: LocationProxy
|
||||
private guestId: number
|
||||
|
||||
// TypeScript doesn't allow getters/accessors with different types,
|
||||
// so for now, we'll have to make do with an "any" in the mix.
|
||||
// https://github.com/Microsoft/TypeScript/issues/2521
|
||||
public get location (): LocationProxy | any {
|
||||
return this._location.getSafe();
|
||||
}
|
||||
|
||||
public set location (url: string | any) {
|
||||
url = resolveURL(url, this.location.href);
|
||||
this._invokeWebContentsMethod('loadURL', url);
|
||||
}
|
||||
|
||||
constructor (guestId: number) {
|
||||
this.guestId = guestId;
|
||||
this._location = new LocationProxy(guestId);
|
||||
|
||||
ipcRendererInternal.once(`${IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_CLOSED}_${guestId}`, () => {
|
||||
removeProxy(guestId);
|
||||
this.closed = true;
|
||||
});
|
||||
}
|
||||
|
||||
public getSafe = (): SafelyBoundBrowserWindowProxy => {
|
||||
const that = this;
|
||||
return {
|
||||
postMessage: this.postMessage,
|
||||
blur: this.blur,
|
||||
close: this.close,
|
||||
focus: this.focus,
|
||||
print: this.print,
|
||||
eval: this.eval,
|
||||
get location () {
|
||||
return that.location;
|
||||
},
|
||||
set location (url: string | any) {
|
||||
that.location = url;
|
||||
},
|
||||
get closed () {
|
||||
return that.closed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public close = () => {
|
||||
this._invokeWindowMethod('destroy');
|
||||
}
|
||||
|
||||
public focus = () => {
|
||||
this._invokeWindowMethod('focus');
|
||||
}
|
||||
|
||||
public blur = () => {
|
||||
this._invokeWindowMethod('blur');
|
||||
}
|
||||
|
||||
public print = () => {
|
||||
this._invokeWebContentsMethod('print');
|
||||
}
|
||||
|
||||
public postMessage = (message: any, targetOrigin: string) => {
|
||||
ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE, this.guestId, message, toString(targetOrigin), window.location.origin);
|
||||
}
|
||||
|
||||
public eval = (code: string) => {
|
||||
this._invokeWebContentsMethod('executeJavaScript', code);
|
||||
}
|
||||
|
||||
private _invokeWindowMethod (method: string, ...args: any[]) {
|
||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_METHOD, this.guestId, method, ...args);
|
||||
}
|
||||
|
||||
private _invokeWebContentsMethod (method: string, ...args: any[]) {
|
||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD, this.guestId, method, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
export const windowSetup = (
|
||||
isWebView: boolean, openerId: number, isHiddenPage: boolean, usesNativeWindowOpen: boolean) => {
|
||||
if (!process.sandboxed && !isWebView) {
|
||||
// Override default window.close.
|
||||
window.close = function () {
|
||||
@@ -13,12 +252,72 @@ export const windowSetup = (isWebView: boolean, isHiddenPage: boolean) => {
|
||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['close'], window.close);
|
||||
}
|
||||
|
||||
if (!usesNativeWindowOpen) {
|
||||
// TODO(MarshallOfSound): Make compatible with ctx isolation without hole-punch
|
||||
// Make the browser window or guest view emit "new-window" event.
|
||||
window.open = function (url?: string, frameName?: string, features?: string) {
|
||||
if (url != null && url !== '') {
|
||||
url = resolveURL(url, location.href);
|
||||
}
|
||||
const guestId = ipcRendererInternal.sendSync(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_OPEN, url, toString(frameName), toString(features));
|
||||
if (guestId != null) {
|
||||
return getOrCreateProxy(guestId) as any as WindowProxy;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['open'], window.open);
|
||||
}
|
||||
|
||||
// If this window uses nativeWindowOpen, but its opener window does not, we
|
||||
// need to proxy window.opener in order to let the page communicate with its
|
||||
// opener.
|
||||
// Additionally, windows opened from a nativeWindowOpen child of a
|
||||
// non-nativeWindowOpen parent will initially have their WebPreferences
|
||||
// copied from their opener before having them updated, meaning openerId is
|
||||
// initially incorrect. We detect this situation by checking for
|
||||
// window.opener, which will be non-null for a natively-opened child, so we
|
||||
// can ignore the openerId in that case, since it's incorrectly copied from
|
||||
// the parent. This is, uh, confusing, so here's a diagram that will maybe
|
||||
// help?
|
||||
//
|
||||
// [ grandparent window ] --> [ parent window ] --> [ child window ]
|
||||
// n.W.O = false n.W.O = true n.W.O = true
|
||||
// id = 1 id = 2 id = 3
|
||||
// openerId = 0 openerId = 1 openerId = 1 <- !!wrong!!
|
||||
// opener = null opener = null opener = [parent window]
|
||||
if (openerId && !window.opener) {
|
||||
window.opener = getOrCreateProxy(openerId);
|
||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['opener'], window.opener);
|
||||
}
|
||||
|
||||
// But we do not support prompt().
|
||||
window.prompt = function () {
|
||||
throw new Error('prompt() is and will not be supported.');
|
||||
};
|
||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['prompt'], window.prompt);
|
||||
|
||||
if (!usesNativeWindowOpen || openerId) {
|
||||
ipcRendererInternal.on(IPC_MESSAGES.GUEST_WINDOW_POSTMESSAGE, function (
|
||||
_event, sourceId: number, message: any, sourceOrigin: string
|
||||
) {
|
||||
// Manually dispatch event instead of using postMessage because we also need to
|
||||
// set event.source.
|
||||
//
|
||||
// Why any? We can't construct a MessageEvent and we can't
|
||||
// use `as MessageEvent` because you're not supposed to override
|
||||
// data, origin, and source
|
||||
const event: any = document.createEvent('Event');
|
||||
event.initEvent('message', false, false);
|
||||
|
||||
event.data = message;
|
||||
event.origin = sourceOrigin;
|
||||
event.source = getOrCreateProxy(sourceId);
|
||||
|
||||
window.dispatchEvent(event as MessageEvent);
|
||||
});
|
||||
}
|
||||
|
||||
if (isWebView) {
|
||||
// Webview `document.visibilityState` tracks window visibility (and ignores
|
||||
// the actual <webview> element visibility) for backwards compatibility.
|
||||
|
||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "18.0.0-alpha.4",
|
||||
"version": "17.4.1",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
@@ -31,7 +31,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^4.4.1",
|
||||
"@typescript-eslint/parser": "^4.4.1",
|
||||
"asar": "^3.1.0",
|
||||
"aws-sdk": "^2.814.0",
|
||||
"aws-sdk": "^2.727.1",
|
||||
"check-for-leaks": "^1.2.1",
|
||||
"colors": "1.4.0",
|
||||
"dotenv-safe": "^4.0.4",
|
||||
@@ -67,7 +67,7 @@
|
||||
"timers-browserify": "1.4.2",
|
||||
"ts-loader": "^8.0.2",
|
||||
"ts-node": "6.2.0",
|
||||
"typescript": "^4.5.5",
|
||||
"typescript": "^4.1.3",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"wrapper-webpack-plugin": "^2.1.0"
|
||||
@@ -78,14 +78,14 @@
|
||||
"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 shell/ || (echo \"\\nCode not formatted correctly.\" && exit 1)",
|
||||
"lint:clang-format": "python3 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",
|
||||
"lint:py": "node ./script/lint.js --py",
|
||||
"lint:gn": "node ./script/lint.js --gn",
|
||||
"lint:docs": "remark docs -qf && npm run lint:js-in-markdown && npm run create-typescript-definitions && npm run lint:docs-relative-links && npm run lint:markdownlint",
|
||||
"lint:docs-relative-links": "python ./script/check-relative-doc-links.py",
|
||||
"lint:docs-relative-links": "python3 ./script/check-relative-doc-links.py",
|
||||
"lint:markdownlint": "markdownlint \"*.md\" \"docs/**/*.md\"",
|
||||
"lint:js-in-markdown": "standard-markdown docs",
|
||||
"create-api-json": "electron-docs-parser --dir=./",
|
||||
@@ -116,14 +116,14 @@
|
||||
"ts-node script/gen-filenames.ts"
|
||||
],
|
||||
"*.{cc,mm,c,h}": [
|
||||
"python script/run-clang-format.py -r -c --fix"
|
||||
"python3 script/run-clang-format.py -r -c --fix"
|
||||
],
|
||||
"*.md": [
|
||||
"npm run lint:docs"
|
||||
],
|
||||
"*.{gn,gni}": [
|
||||
"npm run gn-check",
|
||||
"python script/run-gn-format.py"
|
||||
"python3 script/run-gn-format.py"
|
||||
],
|
||||
"*.py": [
|
||||
"node script/lint.js --py --fix --only --"
|
||||
|
||||
@@ -67,7 +67,6 @@ feat_enable_offscreen_rendering_with_viz_compositor.patch
|
||||
gpu_notify_when_dxdiag_request_fails.patch
|
||||
feat_allow_embedders_to_add_observers_on_created_hunspell.patch
|
||||
feat_add_onclose_to_messageport.patch
|
||||
ui_gtk_public_header.patch
|
||||
allow_in-process_windows_to_have_different_web_prefs.patch
|
||||
refactor_expose_cursor_changes_to_the_webcontentsobserver.patch
|
||||
crash_allow_setting_more_options.patch
|
||||
@@ -105,13 +104,24 @@ 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.patch
|
||||
load_v8_snapshot_in_browser_process.patch
|
||||
fix_patch_out_permissions_checks_in_exclusive_access.patch
|
||||
fix_aspect_ratio_with_max_size.patch
|
||||
revert_stop_using_nsrunloop_in_renderer_process.patch
|
||||
fix_dont_delete_SerialPortManager_on_main_thread.patch
|
||||
feat_add_data_transfer_to_requestsingleinstancelock.patch
|
||||
fix_crash_when_saving_edited_pdf_files.patch
|
||||
drop_extra_printingcontext_calls_to_newpage_pagedone.patch
|
||||
cherry-pick-f5101995acd2.patch
|
||||
fix_don_t_restore_maximized_windows_when_calling_showinactive.patch
|
||||
build_disable_partition_alloc_on_mac.patch
|
||||
fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch
|
||||
remove_incorrect_width_height_adjustments.patch
|
||||
set_dpi_correctly_during_main_window_creation.patch
|
||||
ui_gtk_public_header.patch
|
||||
introduce_ozoneplatform_electron_can_call_x11_property.patch
|
||||
revert_motionmark_avoid_unnecessary_ipc_on_mac.patch
|
||||
cherry-pick-a1dc371d6680.patch
|
||||
cherry-pick-1665a1d16d46.patch
|
||||
use-after-free_of_id_and_idref_attributes.patch
|
||||
fix_--without-valid_build.patch
|
||||
cherry-pick-4d26949260aa.patch
|
||||
|
||||
@@ -123,7 +123,7 @@ index 59dd5662dccea2e570b93e7bbdc59fded301979c..30d7bafbf5877d71868fbdec9abbcaca
|
||||
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 42f30c871534bf7037c13d71802443baa20eb850..552909b44f47d64a260b9cd39ea42dac41e31603 100644
|
||||
index 4fa74cc53ddd7557dc5b9e9e6c7ed16214f049b9..cf1e3e898f3b4c9461ed700e0776856689b4c65b 100644
|
||||
--- a/third_party/blink/renderer/core/loader/empty_clients.h
|
||||
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
|
||||
@@ -356,6 +356,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient {
|
||||
|
||||
@@ -85,10 +85,10 @@ index 14d4a00293ab0b11e733676844ce483992d6cd8e..c6c2dbb9dddd1eaa21e8c7b276d871a3
|
||||
// Visibility -----------------------------------------------------------
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||
index 6531f9b8d7720c8d3bb5f5df677cbac13a17454d..d4602eb32f1f14f639df26f866f5406ab36003d5 100644
|
||||
index 567ea9b7bd78a229b113ae11849f3e86942c92d6..d525df56c4e33a19431b2980a932956cb1986865 100644
|
||||
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||
@@ -3662,6 +3662,13 @@ PageScheduler* WebViewImpl::Scheduler() const {
|
||||
@@ -3667,6 +3667,13 @@ PageScheduler* WebViewImpl::Scheduler() const {
|
||||
return GetPage()->GetPageScheduler();
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ index 6531f9b8d7720c8d3bb5f5df677cbac13a17454d..d4602eb32f1f14f639df26f866f5406a
|
||||
void WebViewImpl::SetVisibilityState(
|
||||
mojom::blink::PageVisibilityState visibility_state,
|
||||
bool is_initial_state) {
|
||||
@@ -3673,7 +3680,8 @@ void WebViewImpl::SetVisibilityState(
|
||||
@@ -3678,7 +3685,8 @@ void WebViewImpl::SetVisibilityState(
|
||||
}
|
||||
GetPage()->SetVisibilityState(visibility_state, is_initial_state);
|
||||
GetPage()->GetPageScheduler()->SetPageVisible(
|
||||
@@ -113,10 +113,10 @@ index 6531f9b8d7720c8d3bb5f5df677cbac13a17454d..d4602eb32f1f14f639df26f866f5406a
|
||||
|
||||
mojom::blink::PageVisibilityState WebViewImpl::GetVisibilityState() {
|
||||
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
|
||||
index cce6788b0a529ffb4d6330237c50c8f9a1b2093a..83c95f9867cbf52646a9b093706b05d8d99ce3b6 100644
|
||||
index 63e97c417f1709438319956d9f2ed2f845e950c0..80904ef8478952f38a326fe1cebbff06ff7e25ab 100644
|
||||
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
|
||||
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
|
||||
@@ -420,6 +420,7 @@ class CORE_EXPORT WebViewImpl final : public WebView,
|
||||
@@ -419,6 +419,7 @@ class CORE_EXPORT WebViewImpl final : public WebView,
|
||||
LocalDOMWindow* PagePopupWindow() const;
|
||||
|
||||
PageScheduler* Scheduler() const override;
|
||||
@@ -124,7 +124,7 @@ index cce6788b0a529ffb4d6330237c50c8f9a1b2093a..83c95f9867cbf52646a9b093706b05d8
|
||||
void SetVisibilityState(mojom::blink::PageVisibilityState visibility_state,
|
||||
bool is_initial_state) override;
|
||||
mojom::blink::PageVisibilityState GetVisibilityState() override;
|
||||
@@ -856,6 +857,8 @@ class CORE_EXPORT WebViewImpl final : public WebView,
|
||||
@@ -855,6 +856,8 @@ class CORE_EXPORT WebViewImpl final : public WebView,
|
||||
// If true, we send IPC messages when |preferred_size_| changes.
|
||||
bool send_preferred_size_changes_ = false;
|
||||
|
||||
|
||||
@@ -8,19 +8,21 @@ WebPreferences of in-process child windows, rather than relying on
|
||||
process-level command line switches, as before.
|
||||
|
||||
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
index 6356025f24855b789b0fdb492ca61232bc6d8000..7eab027f4af2a10c77a8754f6faf06a67b2edce0 100644
|
||||
index 6356025f24855b789b0fdb492ca61232bc6d8000..37b08cb04db0cc16636cfa9db6cc91f050cc85ab 100644
|
||||
--- a/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
@@ -144,6 +144,20 @@ WebPreferences::WebPreferences()
|
||||
@@ -144,6 +144,22 @@ WebPreferences::WebPreferences()
|
||||
fake_no_alloc_direct_call_for_testing_enabled(false),
|
||||
v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
|
||||
record_whole_document(false),
|
||||
+ // Begin Electron-specific WebPreferences.
|
||||
+ opener_id(0),
|
||||
+ context_isolation(false),
|
||||
+ is_webview(false),
|
||||
+ hidden_page(false),
|
||||
+ offscreen(false),
|
||||
+ preload(base::FilePath::StringType()),
|
||||
+ native_window_open(false),
|
||||
+ node_integration(false),
|
||||
+ node_integration_in_worker(false),
|
||||
+ node_integration_in_sub_frames(false),
|
||||
@@ -33,7 +35,7 @@ index 6356025f24855b789b0fdb492ca61232bc6d8000..7eab027f4af2a10c77a8754f6faf06a6
|
||||
accelerated_video_decode_enabled(false),
|
||||
animation_policy(
|
||||
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
index 98391c17bc6c3371919966e9caf09efbcc94cec6..c2d3457f17a076e238dc851b5c61a9ed0efb3a19 100644
|
||||
index 98391c17bc6c3371919966e9caf09efbcc94cec6..c4f9f01aaf44a14e13b285fa0f6287f068c5e401 100644
|
||||
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
@@ -22,6 +22,10 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
@@ -47,15 +49,17 @@ index 98391c17bc6c3371919966e9caf09efbcc94cec6..c2d3457f17a076e238dc851b5c61a9ed
|
||||
!data.ReadLazyFrameLoadingDistanceThresholdsPx(
|
||||
&out->lazy_frame_loading_distance_thresholds_px) ||
|
||||
!data.ReadLazyImageLoadingDistanceThresholdsPx(
|
||||
@@ -156,6 +160,19 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
@@ -156,6 +160,21 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
data.fake_no_alloc_direct_call_for_testing_enabled();
|
||||
out->v8_cache_options = data.v8_cache_options();
|
||||
out->record_whole_document = data.record_whole_document();
|
||||
+ // Begin Electron-specific WebPreferences.
|
||||
+ out->opener_id = data.opener_id();
|
||||
+ out->context_isolation = data.context_isolation();
|
||||
+ out->is_webview = data.is_webview();
|
||||
+ out->hidden_page = data.hidden_page();
|
||||
+ out->offscreen = data.offscreen();
|
||||
+ out->native_window_open = data.native_window_open();
|
||||
+ out->node_integration = data.node_integration();
|
||||
+ out->node_integration_in_worker = data.node_integration_in_worker();
|
||||
+ out->node_integration_in_sub_frames = data.node_integration_in_sub_frames();
|
||||
@@ -68,7 +72,7 @@ index 98391c17bc6c3371919966e9caf09efbcc94cec6..c2d3457f17a076e238dc851b5c61a9ed
|
||||
out->accelerated_video_decode_enabled =
|
||||
data.accelerated_video_decode_enabled();
|
||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
index 9eefe05ffbd6f78a869e2a7449a8fa7c7d456dda..cc57fcbe8a1c51db906731c18a5ffe3bc711a755 100644
|
||||
index 9eefe05ffbd6f78a869e2a7449a8fa7c7d456dda..3f84267ed1d7158b168709270be6a01da7dcfa31 100644
|
||||
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
@@ -10,6 +10,7 @@
|
||||
@@ -79,17 +83,19 @@ index 9eefe05ffbd6f78a869e2a7449a8fa7c7d456dda..cc57fcbe8a1c51db906731c18a5ffe3b
|
||||
#include "net/nqe/effective_connection_type.h"
|
||||
#include "third_party/blink/public/common/common_export.h"
|
||||
#include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom-shared.h"
|
||||
@@ -161,6 +162,22 @@ struct BLINK_COMMON_EXPORT WebPreferences {
|
||||
@@ -161,6 +162,24 @@ struct BLINK_COMMON_EXPORT WebPreferences {
|
||||
blink::mojom::V8CacheOptions v8_cache_options;
|
||||
bool record_whole_document;
|
||||
|
||||
+ // Begin Electron-specific WebPreferences.
|
||||
+ std::vector<base::FilePath> preloads;
|
||||
+ int opener_id;
|
||||
+ bool context_isolation;
|
||||
+ bool is_webview;
|
||||
+ bool hidden_page;
|
||||
+ bool offscreen;
|
||||
+ base::FilePath preload;
|
||||
+ bool native_window_open;
|
||||
+ bool node_integration;
|
||||
+ bool node_integration_in_worker;
|
||||
+ bool node_integration_in_sub_frames;
|
||||
@@ -103,7 +109,7 @@ index 9eefe05ffbd6f78a869e2a7449a8fa7c7d456dda..cc57fcbe8a1c51db906731c18a5ffe3b
|
||||
// only controls whether or not the "document.cookie" field is properly
|
||||
// connected to the backing store, for instance if you wanted to be able to
|
||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
index 5124059d0df902d3879f2c96d001472a10c2ead1..9ab4f31275dfebc98ef94ed496a23e8c28168c3a 100644
|
||||
index 5124059d0df902d3879f2c96d001472a10c2ead1..b9a3e3ed17bb227f1e5ec868880a19ef19331b07 100644
|
||||
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
@@ -6,6 +6,7 @@
|
||||
@@ -114,7 +120,7 @@ index 5124059d0df902d3879f2c96d001472a10c2ead1..9ab4f31275dfebc98ef94ed496a23e8c
|
||||
#include "mojo/public/cpp/bindings/struct_traits.h"
|
||||
#include "net/nqe/effective_connection_type.h"
|
||||
#include "third_party/blink/public/common/common_export.h"
|
||||
@@ -446,6 +447,60 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
@@ -446,6 +447,68 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
return r.record_whole_document;
|
||||
}
|
||||
|
||||
@@ -123,6 +129,10 @@ index 5124059d0df902d3879f2c96d001472a10c2ead1..9ab4f31275dfebc98ef94ed496a23e8c
|
||||
+ return r.preloads;
|
||||
+ }
|
||||
+
|
||||
+ static int opener_id(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.opener_id;
|
||||
+ }
|
||||
+
|
||||
+ static bool context_isolation(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.context_isolation;
|
||||
+ }
|
||||
@@ -143,6 +153,10 @@ index 5124059d0df902d3879f2c96d001472a10c2ead1..9ab4f31275dfebc98ef94ed496a23e8c
|
||||
+ return r.preload;
|
||||
+ }
|
||||
+
|
||||
+ static bool native_window_open(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.native_window_open;
|
||||
+ }
|
||||
+
|
||||
+ static bool node_integration(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.node_integration;
|
||||
+ }
|
||||
@@ -176,7 +190,7 @@ index 5124059d0df902d3879f2c96d001472a10c2ead1..9ab4f31275dfebc98ef94ed496a23e8c
|
||||
return r.cookie_enabled;
|
||||
}
|
||||
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
index 1f0ca7565e7df7bb535bb8c779929f6202273471..69c5932f498d2849847ddcc3605f45df3acc596d 100644
|
||||
index 1f0ca7565e7df7bb535bb8c779929f6202273471..3fe9e1089c0cceaa7c6f6e7dff4dac9fc55d2ca2 100644
|
||||
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
@@ -10,6 +10,7 @@ import "third_party/blink/public/mojom/v8_cache_options.mojom";
|
||||
@@ -187,17 +201,19 @@ index 1f0ca7565e7df7bb535bb8c779929f6202273471..69c5932f498d2849847ddcc3605f45df
|
||||
|
||||
enum PointerType {
|
||||
kPointerNone = 1, // 1 << 0
|
||||
@@ -213,6 +214,22 @@ struct WebPreferences {
|
||||
@@ -213,6 +214,24 @@ struct WebPreferences {
|
||||
V8CacheOptions v8_cache_options;
|
||||
bool record_whole_document;
|
||||
|
||||
+ // Begin Electron-specific WebPreferences.
|
||||
+ array<mojo_base.mojom.FilePath> preloads;
|
||||
+ int32 opener_id;
|
||||
+ bool context_isolation;
|
||||
+ bool is_webview;
|
||||
+ bool hidden_page;
|
||||
+ bool offscreen;
|
||||
+ mojo_base.mojom.FilePath preload;
|
||||
+ bool native_window_open;
|
||||
+ bool node_integration;
|
||||
+ bool node_integration_in_worker;
|
||||
+ bool node_integration_in_sub_frames;
|
||||
|
||||
@@ -49,7 +49,7 @@ index 1b022bf6b37982e6321120951de5d8dcc10ecf6a..5cd7cf680244383e466106801103871b
|
||||
// its owning reference back to our owning LocalFrame.
|
||||
client_->Detached(type);
|
||||
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
|
||||
index fc47c47cc197a674d97e77e35a904d3bfb481891..bc1444a76d72f1f40966ddac21e689a4a5995125 100644
|
||||
index e4fa7685539aa22e6bf55bf768aa1f055e1dbdf3..96334220264cdf0afb2014500aeb90001b6fae72 100644
|
||||
--- a/third_party/blink/renderer/core/frame/local_frame.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
|
||||
@@ -533,10 +533,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) {
|
||||
|
||||
23
patches/chromium/build_disable_partition_alloc_on_mac.patch
Normal file
23
patches/chromium/build_disable_partition_alloc_on_mac.patch
Normal file
@@ -0,0 +1,23 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: VerteDinde <vertedinde@electronjs.org>
|
||||
Date: Tue, 1 Mar 2022 16:02:22 -0800
|
||||
Subject: build: disable partition alloc on mac
|
||||
|
||||
Enabling partition alloc caused a crash when spawning
|
||||
a child process. This patch disables partition alloc for mac,
|
||||
and can be removed when the crash in fork is resolved.
|
||||
Related issue: https://github.com/electron/electron/issues/32718
|
||||
|
||||
diff --git a/base/allocator/allocator.gni b/base/allocator/allocator.gni
|
||||
index 2c35e82ec5ad94840cc894cc55bb90e7c4c00d4f..af34f85f686f61a1d4dc2ee1248af4d0b61e4cb6 100644
|
||||
--- a/base/allocator/allocator.gni
|
||||
+++ b/base/allocator/allocator.gni
|
||||
@@ -18,7 +18,7 @@ _is_using_sanitizers = is_asan || is_hwasan || is_lsan || is_tsan || is_msan
|
||||
# - Windows: debug CRT is not compatible, see below.
|
||||
_disable_partition_alloc = is_component_build || (is_win && is_debug)
|
||||
_is_partition_alloc_platform =
|
||||
- is_android || is_win || is_mac || is_linux || is_chromeos ||
|
||||
+ is_android || is_win || is_linux || is_chromeos ||
|
||||
# TODO(crbug.com/1278780): Allow x64 once compatible with safe-stack.
|
||||
(is_fuchsia && target_cpu == "arm64")
|
||||
|
||||
@@ -11,10 +11,10 @@ if we ever align our .pak file generation with Chrome we can remove this
|
||||
patch.
|
||||
|
||||
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
|
||||
index 33e8d4f5b27532d015d8cd4dbe7d2550916eb436..76945ae5d23af853d920810cbcb248c0daf2f544 100644
|
||||
index 473af396e95a4e67242c3775866fdcd4ce1f4d41..8fa395626b972e53a4c7abaef94609e5a662eaab 100644
|
||||
--- a/chrome/BUILD.gn
|
||||
+++ b/chrome/BUILD.gn
|
||||
@@ -170,11 +170,16 @@ if (!is_android && !is_mac) {
|
||||
@@ -171,11 +171,16 @@ if (!is_android && !is_mac) {
|
||||
"common/crash_keys.h",
|
||||
]
|
||||
|
||||
@@ -33,10 +33,10 @@ index 33e8d4f5b27532d015d8cd4dbe7d2550916eb436..76945ae5d23af853d920810cbcb248c0
|
||||
"//base",
|
||||
"//build:branding_buildflags",
|
||||
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
||||
index 8f418d78b7ac7a23d80f44e23d430febe641cf0a..dc378e799bad709709f339864fdf63e6d9a6cb69 100644
|
||||
index 834601238cf5f62f7a43b5a2ccc7a743b85efbfe..d032c1b60fdff2d000f497399ea287a09afd97f0 100644
|
||||
--- a/chrome/browser/BUILD.gn
|
||||
+++ b/chrome/browser/BUILD.gn
|
||||
@@ -4528,7 +4528,7 @@ static_library("browser") {
|
||||
@@ -4522,7 +4522,7 @@ static_library("browser") {
|
||||
|
||||
# On Windows, the hashes are embedded in //chrome:chrome_initial rather
|
||||
# than here in :chrome_dll.
|
||||
@@ -46,10 +46,10 @@ index 8f418d78b7ac7a23d80f44e23d430febe641cf0a..dc378e799bad709709f339864fdf63e6
|
||||
}
|
||||
|
||||
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
|
||||
index 4d35024ba4a2f490f5749449c8772da60fb6eb46..de0d4c18362ba9c8e288e532b11f114f3b3f9532 100644
|
||||
index 19387561ba18f2f9bf3a6ef42fc32b9adc7ac375..9c6ae050454f6e0c5032f3aea43e4123e06f3ff3 100644
|
||||
--- a/chrome/test/BUILD.gn
|
||||
+++ b/chrome/test/BUILD.gn
|
||||
@@ -5531,7 +5531,6 @@ test("unit_tests") {
|
||||
@@ -5529,7 +5529,6 @@ test("unit_tests") {
|
||||
|
||||
deps += [
|
||||
"//chrome:other_version",
|
||||
@@ -57,7 +57,7 @@ index 4d35024ba4a2f490f5749449c8772da60fb6eb46..de0d4c18362ba9c8e288e532b11f114f
|
||||
"//chrome//services/util_win:unit_tests",
|
||||
"//chrome/app:chrome_dll_resources",
|
||||
"//chrome/browser:chrome_process_finder",
|
||||
@@ -5554,6 +5553,10 @@ test("unit_tests") {
|
||||
@@ -5552,6 +5551,10 @@ test("unit_tests") {
|
||||
"//ui/resources",
|
||||
]
|
||||
|
||||
@@ -68,7 +68,7 @@ index 4d35024ba4a2f490f5749449c8772da60fb6eb46..de0d4c18362ba9c8e288e532b11f114f
|
||||
ldflags = [
|
||||
"/DELAYLOAD:api-ms-win-core-winrt-error-l1-1-0.dll",
|
||||
"/DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll",
|
||||
@@ -6245,7 +6248,6 @@ test("unit_tests") {
|
||||
@@ -6243,7 +6246,6 @@ test("unit_tests") {
|
||||
}
|
||||
|
||||
deps += [
|
||||
@@ -76,7 +76,7 @@ index 4d35024ba4a2f490f5749449c8772da60fb6eb46..de0d4c18362ba9c8e288e532b11f114f
|
||||
"//chrome/browser:cart_db_content_proto",
|
||||
"//chrome/browser:coupon_db_content_proto",
|
||||
"//chrome/browser/media/router:test_support",
|
||||
@@ -6286,6 +6288,11 @@ test("unit_tests") {
|
||||
@@ -6284,6 +6286,11 @@ test("unit_tests") {
|
||||
"//ui/native_theme:test_support",
|
||||
"//ui/webui/resources/js/browser_command:mojo_bindings",
|
||||
]
|
||||
|
||||
@@ -9,10 +9,10 @@ potentially prevent a window from being created.
|
||||
TODO(loc): this patch is currently broken.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index 38b4d4da5ea013118d8464d82fd84a551a36a80f..165d6b8eb5cb0ce949c71c1707687960c49a4ebb 100644
|
||||
index 7cb5a62e40953f7cb99ba8225551004649110d9f..6ebceb4b5c47ada6d228ef00825b10fb7326d78c 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -6602,6 +6602,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
@@ -6626,6 +6626,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
last_committed_origin_, params->window_container_type,
|
||||
params->target_url, params->referrer.To<Referrer>(),
|
||||
params->frame_name, params->disposition, *params->features,
|
||||
@@ -21,10 +21,10 @@ index 38b4d4da5ea013118d8464d82fd84a551a36a80f..165d6b8eb5cb0ce949c71c1707687960
|
||||
&no_javascript_access);
|
||||
|
||||
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
|
||||
index c4351bea185f1a7d392ef3c1b95d66d35347e87a..131e74e1789632f59086b2bfb390654207f6d18f 100644
|
||||
index 031ae3493232e571abb96533f379fa0265fa299c..495f2f355984a304e71fe0b2f2fe670bdc8bc833 100644
|
||||
--- a/content/browser/web_contents/web_contents_impl.cc
|
||||
+++ b/content/browser/web_contents/web_contents_impl.cc
|
||||
@@ -3864,6 +3864,14 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
@@ -3868,6 +3868,14 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
}
|
||||
auto* new_contents_impl = new_contents.get();
|
||||
|
||||
@@ -39,7 +39,7 @@ index c4351bea185f1a7d392ef3c1b95d66d35347e87a..131e74e1789632f59086b2bfb3906542
|
||||
new_contents_impl->GetController().SetSessionStorageNamespace(
|
||||
partition_id, session_storage_namespace);
|
||||
|
||||
@@ -3906,12 +3914,6 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
@@ -3910,12 +3918,6 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
AddWebContentsDestructionObserver(new_contents_impl);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ index afd57b6d28f8280d8b140370a36d9ca6ec17b774..da44b637ee5fdf371974f322aaf1a07b
|
||||
|
||||
// Operation result when the renderer asks the browser to create a new window.
|
||||
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
|
||||
index f26ff2199c9f72adfcea9df0db0dc9f0eb70a127..8bbca0ed64bdfc892b3f2e21f0f88fe14344459d 100644
|
||||
index 491c969c55e64267aef2bd78dee96663bca567ee..7a8349a4d1f97b33ffb8391226fbc204c181640f 100644
|
||||
--- a/content/public/browser/content_browser_client.cc
|
||||
+++ b/content/public/browser/content_browser_client.cc
|
||||
@@ -570,6 +570,8 @@ bool ContentBrowserClient::CanCreateWindow(
|
||||
@@ -81,7 +81,7 @@ index f26ff2199c9f72adfcea9df0db0dc9f0eb70a127..8bbca0ed64bdfc892b3f2e21f0f88fe1
|
||||
bool opener_suppressed,
|
||||
bool* no_javascript_access) {
|
||||
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
|
||||
index d50c7e0c618b8292e38b8f8b307d019701f70e49..253c47f41294155bab9a3b0884e65a09fcacd0d0 100644
|
||||
index 2591d560ddbecf5a1bd5cd458394825e6d1ba8e8..f44bb9899f2ab9e3cd72386abe78e330af714fe5 100644
|
||||
--- a/content/public/browser/content_browser_client.h
|
||||
+++ b/content/public/browser/content_browser_client.h
|
||||
@@ -169,6 +169,7 @@ class NetworkService;
|
||||
|
||||
118
patches/chromium/cherry-pick-1665a1d16d46.patch
Normal file
118
patches/chromium/cherry-pick-1665a1d16d46.patch
Normal file
@@ -0,0 +1,118 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: "msw@chromium.org" <msw@chromium.org>
|
||||
Date: Tue, 1 Feb 2022 21:16:10 +0000
|
||||
Subject: Reland "Make web cursor size limits match on browser and renderer"
|
||||
|
||||
This reverts commit 38a8343085e53889eba48fcff78a6c2295927333.
|
||||
|
||||
Reason for revert: Fix without regressing https://crbug.com/1292426
|
||||
(Increased WebCursor limit 128->150px to support DevToolsEyeDropper)
|
||||
|
||||
Original change's description:
|
||||
> Revert "Make web cursor size limits match on browser and renderer"
|
||||
>
|
||||
> This reverts commit 868b44dd8b4a1a3b9698f561ca17f75e4ec78dd2.
|
||||
>
|
||||
> Reason for revert: https://crbug.com/1292426
|
||||
>
|
||||
> Original change's description:
|
||||
> > Make web cursor size limits match on browser and renderer
|
||||
> >
|
||||
> > Use NSCursor arrowCursor on Mac for ui::mojom::CursorType::kNull.
|
||||
> > (i.e. when WebCursor is constructed with an overly large custom cursor)
|
||||
> >
|
||||
> > Bug: 1246188
|
||||
> > Test: Automated unit tests and WPTs
|
||||
> > Change-Id: I89627fa13cba96b755b8f80adbc91cfc865b6b1b
|
||||
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3413912
|
||||
> > Reviewed-by: Henrique Ferreiro <hferreiro@igalia.com>
|
||||
> > Reviewed-by: Charlie Harrison <csharrison@chromium.org>
|
||||
> > Commit-Queue: Mike Wasserman <msw@chromium.org>
|
||||
> > Auto-Submit: Mike Wasserman <msw@chromium.org>
|
||||
> > Cr-Commit-Position: refs/heads/main@{#964378}
|
||||
>
|
||||
> Bug: 1246188
|
||||
> Change-Id: Id7b3b88e65c012993537ce96c2b5064b7b76646e
|
||||
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3428347
|
||||
> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
> Commit-Queue: Mike Wasserman <msw@chromium.org>
|
||||
> Cr-Commit-Position: refs/heads/main@{#965475}
|
||||
|
||||
Fixed: 1246188
|
||||
Bug: 1292426
|
||||
Change-Id: I5a490603c3e21e17f3136a3d792a18429eb3f633
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3428624
|
||||
Auto-Submit: Mike Wasserman <msw@chromium.org>
|
||||
Reviewed-by: Charlie Harrison <csharrison@chromium.org>
|
||||
Commit-Queue: Mike Wasserman <msw@chromium.org>
|
||||
Reviewed-by: Henrique Ferreiro <hferreiro@igalia.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#965857}
|
||||
|
||||
diff --git a/content/common/cursors/webcursor.cc b/content/common/cursors/webcursor.cc
|
||||
index c8b6b9d3f75d0cac98cc4ab8e71b68117837c1dc..4c80616e41e6ba5b171ae09d06d7f4110589f70a 100644
|
||||
--- a/content/common/cursors/webcursor.cc
|
||||
+++ b/content/common/cursors/webcursor.cc
|
||||
@@ -22,16 +22,19 @@ WebCursor::WebCursor(const ui::Cursor& cursor) {
|
||||
WebCursor::WebCursor(const WebCursor& other) = default;
|
||||
|
||||
bool WebCursor::SetCursor(const ui::Cursor& cursor) {
|
||||
- static constexpr int kMaxSize = 1024;
|
||||
+ // This value is just large enough to accommodate:
|
||||
+ // - kMaximumCursorSize in Blink's EventHandler
|
||||
+ // - kCursorSize in Chrome's DevToolsEyeDropper
|
||||
+ static constexpr int kMaximumCursorSize = 150;
|
||||
if (cursor.image_scale_factor() < 0.01f ||
|
||||
cursor.image_scale_factor() > 100.f ||
|
||||
(cursor.type() == ui::mojom::CursorType::kCustom &&
|
||||
- (cursor.custom_bitmap().width() > kMaxSize ||
|
||||
- cursor.custom_bitmap().height() > kMaxSize ||
|
||||
+ (cursor.custom_bitmap().width() > kMaximumCursorSize ||
|
||||
+ cursor.custom_bitmap().height() > kMaximumCursorSize ||
|
||||
cursor.custom_bitmap().width() / cursor.image_scale_factor() >
|
||||
- kMaxSize ||
|
||||
+ kMaximumCursorSize ||
|
||||
cursor.custom_bitmap().height() / cursor.image_scale_factor() >
|
||||
- kMaxSize))) {
|
||||
+ kMaximumCursorSize))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
diff --git a/content/common/cursors/webcursor_mac.mm b/content/common/cursors/webcursor_mac.mm
|
||||
index f85c421f8581abe191738eaee133004b729a817d..fdc70bdff2ddc517f3e341dd16263ae89d8b153f 100644
|
||||
--- a/content/common/cursors/webcursor_mac.mm
|
||||
+++ b/content/common/cursors/webcursor_mac.mm
|
||||
@@ -265,6 +265,7 @@ - (CrCoreCursorType)_coreCursorType {
|
||||
case ui::mojom::CursorType::kCustom:
|
||||
return CreateCustomCursor(cursor_);
|
||||
case ui::mojom::CursorType::kNull:
|
||||
+ return [NSCursor arrowCursor];
|
||||
case ui::mojom::CursorType::kDndNone:
|
||||
case ui::mojom::CursorType::kDndMove:
|
||||
case ui::mojom::CursorType::kDndCopy:
|
||||
diff --git a/content/common/cursors/webcursor_unittest.cc b/content/common/cursors/webcursor_unittest.cc
|
||||
index 8f53ffa052f1f60d4eedccca5a4bc32941966963..9b2d8dec85e73b73108f40bca90237b4465e5043 100644
|
||||
--- a/content/common/cursors/webcursor_unittest.cc
|
||||
+++ b/content/common/cursors/webcursor_unittest.cc
|
||||
@@ -122,11 +122,11 @@ TEST(WebCursorTest, SetCursor) {
|
||||
|
||||
// SetCursor should return false when the image width is too large.
|
||||
cursor.set_image_scale_factor(1.f);
|
||||
- cursor.set_custom_bitmap(CreateTestBitmap(1025, 3));
|
||||
+ cursor.set_custom_bitmap(CreateTestBitmap(151, 3));
|
||||
EXPECT_FALSE(webcursor.SetCursor(cursor));
|
||||
|
||||
// SetCursor should return false when the image height is too large.
|
||||
- cursor.set_custom_bitmap(CreateTestBitmap(3, 1025));
|
||||
+ cursor.set_custom_bitmap(CreateTestBitmap(3, 151));
|
||||
EXPECT_FALSE(webcursor.SetCursor(cursor));
|
||||
|
||||
// SetCursor should return false when the scaled image width is too large.
|
||||
@@ -136,7 +136,7 @@ TEST(WebCursorTest, SetCursor) {
|
||||
|
||||
// SetCursor should return false when the scaled image height is too large.
|
||||
cursor.set_image_scale_factor(0.1f);
|
||||
- cursor.set_custom_bitmap(CreateTestBitmap(5, 200));
|
||||
+ cursor.set_custom_bitmap(CreateTestBitmap(5, 20));
|
||||
EXPECT_FALSE(webcursor.SetCursor(cursor));
|
||||
}
|
||||
|
||||
154
patches/chromium/cherry-pick-4d26949260aa.patch
Normal file
154
patches/chromium/cherry-pick-4d26949260aa.patch
Normal file
@@ -0,0 +1,154 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yutaka Hirano <yhirano@chromium.org>
|
||||
Date: Fri, 4 Feb 2022 10:16:05 +0000
|
||||
Subject: Don't interpret informational response body as HTTP/0.9 response
|
||||
|
||||
An informational (1xx) response with body can be interpreted as an
|
||||
informational response followed by an HTTP/0.9 response. It is confusing
|
||||
so let's stop doing that.
|
||||
|
||||
Bug: 1291482
|
||||
Change-Id: Ic3823838614330d761f11360a783859e5baa260e
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3428433
|
||||
Reviewed-by: Matt Menke <mmenke@chromium.org>
|
||||
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
|
||||
Reviewed-by: Mike West <mkwst@chromium.org>
|
||||
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#967169}
|
||||
|
||||
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
|
||||
index a5f7bb10a01abb00db5d4ae15104f305027145b2..aaa22ac7001420c4d9c44a76af5a58bfbdc7deda 100644
|
||||
--- a/net/http/http_stream_parser.cc
|
||||
+++ b/net/http/http_stream_parser.cc
|
||||
@@ -1014,15 +1014,23 @@ int HttpStreamParser::ParseResponseHeaders(int end_offset) {
|
||||
base::StringPiece(read_buf_->StartOfBuffer(), end_offset));
|
||||
if (!headers)
|
||||
return net::ERR_INVALID_HTTP_RESPONSE;
|
||||
+ has_seen_status_line_ = true;
|
||||
} else {
|
||||
// Enough data was read -- there is no status line, so this is HTTP/0.9, or
|
||||
// the server is broken / doesn't speak HTTP.
|
||||
|
||||
- // If the port is not the default for the scheme, assume it's not a real
|
||||
- // HTTP/0.9 response, and fail the request.
|
||||
+ if (has_seen_status_line_) {
|
||||
+ // If we saw a status line previously, the server can speak HTTP/1.x so it
|
||||
+ // is not reasonable to interpret the response as an HTTP/0.9 response.
|
||||
+ return ERR_INVALID_HTTP_RESPONSE;
|
||||
+ }
|
||||
+
|
||||
base::StringPiece scheme = request_->url.scheme_piece();
|
||||
if (url::DefaultPortForScheme(scheme.data(), scheme.length()) !=
|
||||
request_->url.EffectiveIntPort()) {
|
||||
+ // If the port is not the default for the scheme, assume it's not a real
|
||||
+ // HTTP/0.9 response, and fail the request.
|
||||
+
|
||||
// Allow Shoutcast responses over HTTP, as it's somewhat common and relies
|
||||
// on HTTP/0.9 on weird ports to work.
|
||||
// See
|
||||
diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h
|
||||
index 508b5296d6ff93689facc16939be2c720f42bf48..0c0619bdcf49328d1b737e2c3485d5beff99d0d1 100644
|
||||
--- a/net/http/http_stream_parser.h
|
||||
+++ b/net/http/http_stream_parser.h
|
||||
@@ -275,6 +275,11 @@ class NET_EXPORT_PRIVATE HttpStreamParser {
|
||||
// True if reading a keep-alive response. False if not, or if don't yet know.
|
||||
bool response_is_keep_alive_;
|
||||
|
||||
+ // True if we've seen a response that has an HTTP status line. This is
|
||||
+ // persistent across multiple response parsing. If we see a status line
|
||||
+ // for a response, this will remain true forever.
|
||||
+ bool has_seen_status_line_ = false;
|
||||
+
|
||||
// Keep track of the number of response body bytes read so far.
|
||||
int64_t response_body_read_;
|
||||
|
||||
diff --git a/net/http/http_stream_parser_unittest.cc b/net/http/http_stream_parser_unittest.cc
|
||||
index 3a394e629874d8ffff6eec02609e6009571a6733..e25b78539dfec4b27e6ddf2052967cab91141770 100644
|
||||
--- a/net/http/http_stream_parser_unittest.cc
|
||||
+++ b/net/http/http_stream_parser_unittest.cc
|
||||
@@ -1399,6 +1399,25 @@ TEST(HttpStreamParser, Http09PortTests) {
|
||||
}
|
||||
}
|
||||
|
||||
+TEST(HttpStreamParser, ContinueWithBody) {
|
||||
+ const std::string kResponse =
|
||||
+ "HTTP/1.1 100 Continue\r\n\r\nhello\r\nworld\r\n";
|
||||
+
|
||||
+ SimpleGetRunner get_runner;
|
||||
+ get_runner.set_url(GURL("http://foo.com/"));
|
||||
+ get_runner.AddRead(kResponse);
|
||||
+ get_runner.SetupParserAndSendRequest();
|
||||
+
|
||||
+ get_runner.ReadHeadersExpectingError(OK);
|
||||
+ ASSERT_TRUE(get_runner.response_info()->headers);
|
||||
+ EXPECT_EQ("HTTP/1.1 100 Continue",
|
||||
+ get_runner.response_info()->headers->GetStatusLine());
|
||||
+
|
||||
+ // We ignore informational responses and start reading the next response in
|
||||
+ // the stream. This simulates the behavior.
|
||||
+ get_runner.ReadHeadersExpectingError(ERR_INVALID_HTTP_RESPONSE);
|
||||
+}
|
||||
+
|
||||
TEST(HttpStreamParser, NullFails) {
|
||||
const char kTestHeaders[] =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any-expected.txt
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6ac068363a83816939013bbde88a7584aca4f307
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any-expected.txt
|
||||
@@ -0,0 +1,7 @@
|
||||
+This is a testharness.js-based test.
|
||||
+PASS Status(100) should be ignored.
|
||||
+FAIL Status(101) should be accepted, with removing body. promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
|
||||
+PASS Status(103) should be ignored.
|
||||
+PASS Status(199) should be ignored.
|
||||
+Harness: the test ran to completion.
|
||||
+
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any.js b/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..df4dafcd80b38af93b688dcb318cfaf1978a939f
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any.js
|
||||
@@ -0,0 +1,28 @@
|
||||
+promise_test(async (t) => {
|
||||
+ // The 100 response should be ignored, then the transaction ends, which
|
||||
+ // should lead to an error.
|
||||
+ await promise_rejects_js(
|
||||
+ t, TypeError, fetch('/common/text-plain.txt?pipe=status(100)'));
|
||||
+}, 'Status(100) should be ignored.');
|
||||
+
|
||||
+// This behavior is being discussed at https://github.com/whatwg/fetch/issues/1397.
|
||||
+promise_test(async (t) => {
|
||||
+ const res = await fetch('/common/text-plain.txt?pipe=status(101)');
|
||||
+ assert_equals(res.status, 101);
|
||||
+ const body = await res.text();
|
||||
+ assert_equals(body, '');
|
||||
+}, 'Status(101) should be accepted, with removing body.');
|
||||
+
|
||||
+promise_test(async (t) => {
|
||||
+ // The 103 response should be ignored, then the transaction ends, which
|
||||
+ // should lead to an error.
|
||||
+ await promise_rejects_js(
|
||||
+ t, TypeError, fetch('/common/text-plain.txt?pipe=status(103)'));
|
||||
+}, 'Status(103) should be ignored.');
|
||||
+
|
||||
+promise_test(async (t) => {
|
||||
+ // The 199 response should be ignored, then the transaction ends, which
|
||||
+ // should lead to an error.
|
||||
+ await promise_rejects_js(
|
||||
+ t, TypeError, fetch('/common/text-plain.txt?pipe=status(199)'));
|
||||
+}, 'Status(199) should be ignored.');
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any.worker-expected.txt
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6ac068363a83816939013bbde88a7584aca4f307
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/fetch/security/1xx-response.any.worker-expected.txt
|
||||
@@ -0,0 +1,7 @@
|
||||
+This is a testharness.js-based test.
|
||||
+PASS Status(100) should be ignored.
|
||||
+FAIL Status(101) should be accepted, with removing body. promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
|
||||
+PASS Status(103) should be ignored.
|
||||
+PASS Status(199) should be ignored.
|
||||
+Harness: the test ran to completion.
|
||||
+
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user