mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
176 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69df216a9c | ||
|
|
2917eb1589 | ||
|
|
ec282ff385 | ||
|
|
5d98e5e78a | ||
|
|
251e7bb227 | ||
|
|
d4881625b8 | ||
|
|
348f991563 | ||
|
|
6308fc4c4e | ||
|
|
7fb719f261 | ||
|
|
d7d9d58c8b | ||
|
|
acc861dbaf | ||
|
|
18de6be607 | ||
|
|
1ae50bbeff | ||
|
|
7d129ac5c9 | ||
|
|
2cca38c036 | ||
|
|
f616c85061 | ||
|
|
393045a7e5 | ||
|
|
4a7dd267ec | ||
|
|
f2275ec32e | ||
|
|
e70c39ed60 | ||
|
|
db12e7f08b | ||
|
|
6a1c6f8d7f | ||
|
|
7bd6479573 | ||
|
|
c2de2eaaa0 | ||
|
|
62b3a6b724 | ||
|
|
c3d8337058 | ||
|
|
b3eff8ef13 | ||
|
|
6cef0d9118 | ||
|
|
65d03deacd | ||
|
|
39e55225a0 | ||
|
|
bb83758378 | ||
|
|
2716a25fcf | ||
|
|
2745d70d25 | ||
|
|
b6ffa799c5 | ||
|
|
c9bb6cd134 | ||
|
|
ac2515dd0d | ||
|
|
49436f298b | ||
|
|
542874d13b | ||
|
|
2a8e1a6de1 | ||
|
|
f5bffae462 | ||
|
|
9f80f4aa76 | ||
|
|
736150672c | ||
|
|
7434547d97 | ||
|
|
0436a27d4f | ||
|
|
6627bebdbd | ||
|
|
4a24a28467 | ||
|
|
390ed798f0 | ||
|
|
cdd2176bb6 | ||
|
|
e57b74858f | ||
|
|
3805e3c0fc | ||
|
|
5fb34249c6 | ||
|
|
06997edc3a | ||
|
|
e2b961d73c | ||
|
|
867ea38cd0 | ||
|
|
2e8f2a2f51 | ||
|
|
4da1162ffe | ||
|
|
9d3ebb1888 | ||
|
|
0b8971dec2 | ||
|
|
c8ab43a766 | ||
|
|
64c6737bb2 | ||
|
|
30b6330189 | ||
|
|
177bdd207c | ||
|
|
77fbad0333 | ||
|
|
971ec77b96 | ||
|
|
1b0c15f742 | ||
|
|
52cddbcd92 | ||
|
|
6d0feb8b79 | ||
|
|
2e096ac6bc | ||
|
|
3afe390805 | ||
|
|
d5097dd2e6 | ||
|
|
257b214cb5 | ||
|
|
945ecdd709 | ||
|
|
cb614d15aa | ||
|
|
5fbcee074d | ||
|
|
dc14161a98 | ||
|
|
299fa3a3c4 | ||
|
|
d09e7f851b | ||
|
|
84eea6fa79 | ||
|
|
0cd80cd424 | ||
|
|
78f037f639 | ||
|
|
fc75bf0a9e | ||
|
|
ddc44e1af4 | ||
|
|
5ab2749acc | ||
|
|
642c458c31 | ||
|
|
2195890a46 | ||
|
|
3413e89572 | ||
|
|
7bf69ce4ee | ||
|
|
49f5006ef0 | ||
|
|
fea59c5601 | ||
|
|
6feed17387 | ||
|
|
ec0fa836d2 | ||
|
|
56a34d3b10 | ||
|
|
bde3d403a8 | ||
|
|
8779a3ac0f | ||
|
|
b465ee721e | ||
|
|
50b0750df3 | ||
|
|
1a3da54563 | ||
|
|
fa3767564a | ||
|
|
85db7e7077 | ||
|
|
9b1d2d5b88 | ||
|
|
e8621de3f5 | ||
|
|
9942b2ba80 | ||
|
|
2bf125f82a | ||
|
|
69493ac51e | ||
|
|
6504c4bbc8 | ||
|
|
5c1e1ee5ec | ||
|
|
f39ac0764d | ||
|
|
252805ddb4 | ||
|
|
df27597c05 | ||
|
|
fa683ef8b7 | ||
|
|
16b09a676f | ||
|
|
301108ece4 | ||
|
|
5d36cdf485 | ||
|
|
c94ab729dd | ||
|
|
b35f904e58 | ||
|
|
b143042c7f | ||
|
|
10c238c0ea | ||
|
|
ad2f34491c | ||
|
|
d6c51bc456 | ||
|
|
f313d00083 | ||
|
|
2ef55e1e9e | ||
|
|
62402870fd | ||
|
|
7b2a4c9984 | ||
|
|
7958f3efbb | ||
|
|
3e90aff6fd | ||
|
|
192fc3ee2c | ||
|
|
e0e7b14bdc | ||
|
|
4cecc87c55 | ||
|
|
46e7511af9 | ||
|
|
72733bae8a | ||
|
|
38fce95417 | ||
|
|
54de33464d | ||
|
|
6c4c66d2cd | ||
|
|
839dcbbbdb | ||
|
|
05ae55d131 | ||
|
|
d7736e5924 | ||
|
|
30921548ed | ||
|
|
9059a50f93 | ||
|
|
78569e9b91 | ||
|
|
07b8b2ffdd | ||
|
|
2d18829b43 | ||
|
|
f0a9e56741 | ||
|
|
314e627df1 | ||
|
|
b5c79632ef | ||
|
|
0dcc660794 | ||
|
|
0753e4911c | ||
|
|
ff2a0a4bd3 | ||
|
|
5860b70189 | ||
|
|
d84b7fc4d4 | ||
|
|
1873d8ec81 | ||
|
|
0c19db01d9 | ||
|
|
2156a9064f | ||
|
|
6e169089f0 | ||
|
|
6855004aa5 | ||
|
|
93d9aaa99c | ||
|
|
e4a3f064de | ||
|
|
9265390164 | ||
|
|
8e6819ae71 | ||
|
|
f95d2bdfd1 | ||
|
|
737b08d130 | ||
|
|
b44024d660 | ||
|
|
d0d6e88e5a | ||
|
|
c647007ccc | ||
|
|
134c8b7ed3 | ||
|
|
4f203eee4a | ||
|
|
bb065beaa6 | ||
|
|
8481e5c08a | ||
|
|
7276a1fd8c | ||
|
|
7d18988317 | ||
|
|
68afcd16e4 | ||
|
|
72e336445a | ||
|
|
3e658c4782 | ||
|
|
e7d3b43db0 | ||
|
|
6683606449 | ||
|
|
7bcc2a2762 | ||
|
|
c04fa30944 |
@@ -57,6 +57,16 @@ parameters:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
# Executors
|
||||
executors:
|
||||
linux-arm:
|
||||
resource_class: electronjs/linux-arm
|
||||
machine: true
|
||||
|
||||
linux-arm64:
|
||||
resource_class: electronjs/linux-arm64
|
||||
machine: true
|
||||
|
||||
# The config expects the following environment variables to be set:
|
||||
# - "SLACK_WEBHOOK" Slack hook URL to send notifications.
|
||||
#
|
||||
@@ -239,7 +249,14 @@ step-maybe-cleanup-arm64-mac: &step-maybe-cleanup-arm64-mac
|
||||
killall Safari || echo "No Safari processes left running"
|
||||
rm -rf ~/Library/Application\ Support/Electron*
|
||||
rm -rf ~/Library/Application\ Support/electron*
|
||||
elif [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
XVFB=/usr/bin/Xvfb
|
||||
/sbin/start-stop-daemon --stop --exec $XVFB || echo "Xvfb not running"
|
||||
pkill electron || echo "electron not running"
|
||||
rm -rf ~/.config/Electron*
|
||||
rm -rf ~/.config/electron*
|
||||
fi
|
||||
|
||||
when: always
|
||||
|
||||
step-checkout-electron: &step-checkout-electron
|
||||
@@ -325,6 +342,7 @@ step-setup-goma-for-build: &step-setup-goma-for-build
|
||||
npm install
|
||||
mkdir third_party
|
||||
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
|
||||
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
|
||||
@@ -531,6 +549,7 @@ step-electron-build: &step-electron-build
|
||||
ninja -C out/Default electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
ninja -C out/Default tools/v8_context_snapshot -j $NUMBER_OF_NINJA_PROCESSES
|
||||
gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args
|
||||
(cd out/Default; zip mksnapshot.zip mksnapshot_args clang_x64_v8_arm64/gen/v8/embedded.S)
|
||||
rm -rf out/Default/clang_x64_v8_arm64/gen
|
||||
rm -rf out/Default/clang_x64_v8_arm64/obj
|
||||
rm -rf out/Default/clang_x64/obj
|
||||
@@ -762,7 +781,7 @@ step-verify-mksnapshot: &step-verify-mksnapshot
|
||||
command: |
|
||||
if [ "$IS_ASAN" != "1" ]; then
|
||||
cd src
|
||||
if [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then
|
||||
if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --snapshot-files-dir $PWD/cross-arch-snapshots
|
||||
else
|
||||
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default
|
||||
@@ -907,28 +926,6 @@ step-maybe-cross-arch-snapshot-store: &step-maybe-cross-arch-snapshot-store
|
||||
path: src/cross-arch-snapshots
|
||||
destination: cross-arch-snapshots
|
||||
|
||||
step-maybe-trigger-arm-test: &step-maybe-trigger-arm-test
|
||||
run:
|
||||
name: Trigger an arm test on VSTS if applicable
|
||||
command: |
|
||||
cd src
|
||||
# Only run for non-fork prs
|
||||
if [ "$TRIGGER_ARM_TEST" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then
|
||||
#Trigger VSTS job, passing along CircleCI job number and branch to build
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
if [ x"$MAS_BUILD" == x"true" ]; then
|
||||
export DEVOPS_BUILD="electron-mas-arm64-testing"
|
||||
else
|
||||
export DEVOPS_BUILD="electron-osx-arm64-testing"
|
||||
fi
|
||||
echo "Triggering $DEVOPS_BUILD build on Azure DevOps"
|
||||
node electron/script/release/ci-release-build.js --job=$DEVOPS_BUILD --ci=DevOps --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
|
||||
else
|
||||
echo "Triggering electron-$TARGET_ARCH-testing build on VSTS"
|
||||
node electron/script/release/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
|
||||
fi
|
||||
fi
|
||||
|
||||
step-maybe-generate-typescript-defs: &step-maybe-generate-typescript-defs
|
||||
run:
|
||||
name: Generate type declarations
|
||||
@@ -1343,7 +1340,7 @@ steps-tests: &steps-tests
|
||||
(cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split)) 2>&1 | $ASAN_SYMBOLIZE
|
||||
(cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split)) 2>&1 | $ASAN_SYMBOLIZE
|
||||
else
|
||||
if [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then
|
||||
if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
export ELECTRON_SKIP_NATIVE_MODULE_TESTS=true
|
||||
(cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging)
|
||||
(cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging)
|
||||
@@ -1634,9 +1631,6 @@ commands:
|
||||
steps:
|
||||
- *step-save-out-cache
|
||||
|
||||
# Trigger tests on arm hardware if needed
|
||||
- *step-maybe-trigger-arm-test
|
||||
|
||||
- *step-maybe-notify-slack-failure
|
||||
|
||||
electron-publish:
|
||||
@@ -1980,7 +1974,7 @@ jobs:
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
steps:
|
||||
- electron-build:
|
||||
persist: false
|
||||
persist: true
|
||||
checkout: true
|
||||
use-out-cache: false
|
||||
|
||||
@@ -2039,7 +2033,7 @@ jobs:
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
steps:
|
||||
- electron-build:
|
||||
persist: false
|
||||
persist: true
|
||||
checkout: true
|
||||
use-out-cache: false
|
||||
|
||||
@@ -2436,6 +2430,24 @@ jobs:
|
||||
<<: *env-send-slack-notifications
|
||||
<<: *steps-verify-ffmpeg
|
||||
|
||||
linux-arm-testing-tests:
|
||||
executor: linux-arm
|
||||
environment:
|
||||
<<: *env-arm
|
||||
<<: *env-global
|
||||
<<: *env-headless-testing
|
||||
<<: *env-stack-dumping
|
||||
<<: *steps-tests
|
||||
|
||||
linux-arm64-testing-tests:
|
||||
executor: linux-arm64
|
||||
environment:
|
||||
<<: *env-arm64
|
||||
<<: *env-global
|
||||
<<: *env-headless-testing
|
||||
<<: *env-stack-dumping
|
||||
<<: *steps-tests
|
||||
|
||||
osx-testing-x64-tests:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
@@ -2688,8 +2700,23 @@ workflows:
|
||||
- linux-ia32-testing
|
||||
|
||||
- linux-arm-testing
|
||||
- linux-arm-testing-tests:
|
||||
filters:
|
||||
branches:
|
||||
# Do not run this on forked pull requests
|
||||
ignore: /pull\/[0-9]+/
|
||||
requires:
|
||||
- linux-arm-testing
|
||||
|
||||
- linux-arm64-testing
|
||||
- linux-arm64-testing-tests:
|
||||
filters:
|
||||
branches:
|
||||
# Do not run this on forked pull requests
|
||||
ignore: /pull\/[0-9]+/
|
||||
requires:
|
||||
- linux-arm64-testing
|
||||
|
||||
- linux-arm64-testing-gn-check:
|
||||
requires:
|
||||
- linux-checkout-fast
|
||||
|
||||
8
BUILD.gn
8
BUILD.gn
@@ -1,6 +1,7 @@
|
||||
import("//build/config/locales.gni")
|
||||
import("//build/config/ui.gni")
|
||||
import("//build/config/win/manifest.gni")
|
||||
import("//components/os_crypt/features.gni")
|
||||
import("//components/spellcheck/spellcheck_build_features.gni")
|
||||
import("//content/public/app/mac_helpers.gni")
|
||||
import("//extensions/buildflags/buildflags.gni")
|
||||
@@ -292,7 +293,7 @@ templated_file("electron_version_header") {
|
||||
action("electron_fuses") {
|
||||
script = "build/fuses/build.py"
|
||||
|
||||
inputs = [ "build/fuses/fuses.json" ]
|
||||
inputs = [ "build/fuses/fuses.json5" ]
|
||||
|
||||
outputs = [
|
||||
"$target_gen_dir/fuses.h",
|
||||
@@ -331,6 +332,7 @@ source_set("electron_lib") {
|
||||
"//components/network_hints/common:mojo_bindings",
|
||||
"//components/network_hints/renderer",
|
||||
"//components/network_session_configurator/common",
|
||||
"//components/os_crypt",
|
||||
"//components/pref_registry",
|
||||
"//components/prefs",
|
||||
"//components/upload_list",
|
||||
@@ -678,6 +680,10 @@ source_set("electron_lib") {
|
||||
]
|
||||
libs += [ "uxtheme.lib" ]
|
||||
}
|
||||
|
||||
if (allow_runtime_configurable_key_storage) {
|
||||
defines += [ "ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE" ]
|
||||
}
|
||||
}
|
||||
|
||||
electron_paks("packed_resources") {
|
||||
|
||||
@@ -22,7 +22,7 @@ Issues are created [here](https://github.com/electron/electron/issues/new).
|
||||
|
||||
### Issue Closure
|
||||
|
||||
Bug reports will be closed if the issue has been inactive and the latest affected version no longer receives support. At the moment, Electron maintains its three latest major versions, with a new major version being released every 12 weeks. (For more information on Electron's release cadence, see [this blog post](https://electronjs.org/blog/12-week-cadence).)
|
||||
Bug reports will be closed if the issue has been inactive and the latest affected version no longer receives support. At the moment, Electron maintains its three latest major versions, with a new major version being released every 8 weeks. (For more information on Electron's release cadence, see [this blog post](https://electronjs.org/blog/8-week-cadence).)
|
||||
|
||||
_If an issue has been closed and you still feel it's relevant, feel free to ping a maintainer or add a comment!_
|
||||
|
||||
|
||||
7
DEPS
7
DEPS
@@ -10,17 +10,18 @@ gclient_gn_args = [
|
||||
'checkout_openxr',
|
||||
'checkout_google_benchmark',
|
||||
'mac_xcode_version',
|
||||
'generate_location_tags',
|
||||
]
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'91.0.4472.69',
|
||||
'91.0.4472.164',
|
||||
'node_version':
|
||||
'v14.16.0',
|
||||
'nan_version':
|
||||
'v2.14.2',
|
||||
'squirrel.mac_version':
|
||||
'cdc0729c8bf8576bfef18629186e1e9ecf1b0d9f',
|
||||
'0e5d146ba13101a1302d59ea6e6e0b3cace4ae38',
|
||||
|
||||
'pyyaml_version': '3.12',
|
||||
|
||||
@@ -52,6 +53,8 @@ vars = {
|
||||
|
||||
'mac_xcode_version': 'default',
|
||||
|
||||
'generate_location_tags': False,
|
||||
|
||||
# To allow running hooks without parsing the DEPS tree
|
||||
'process_deps': True,
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
13.0.1
|
||||
13.2.3
|
||||
@@ -28,3 +28,8 @@ libcxx_abi_unstable = false
|
||||
enable_pseudolocales = false
|
||||
|
||||
is_cfi = false
|
||||
|
||||
# Make application name configurable at runtime for cookie crypto
|
||||
allow_runtime_configurable_key_storage = true
|
||||
|
||||
enable_cet_shadow_stack = false
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
@@ -49,8 +50,8 @@ const volatile char kFuseWire[] = { /* sentinel */ {sentinel}, /* fuse_version *
|
||||
}
|
||||
"""
|
||||
|
||||
with open(os.path.join(dir_path, "fuses.json"), 'r') as f:
|
||||
fuse_defaults = json.load(f)
|
||||
with open(os.path.join(dir_path, "fuses.json5"), 'r') as f:
|
||||
fuse_defaults = json.loads(''.join(line for line in f.readlines() if not line.strip()[0] == "/"), object_pairs_hook=OrderedDict)
|
||||
|
||||
fuse_version = fuse_defaults['_version']
|
||||
del fuse_defaults['_version']
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
"_comment": "Modifying the fuse schema in any breaking way should result in the _version prop being incremented. NEVER remove a fuse or change its meaning, instead mark it as removed with 'r'",
|
||||
"_schema": "0 == off, 1 == on, r == removed fuse",
|
||||
"_version": 1,
|
||||
"run_as_node": "1"
|
||||
"run_as_node": "1",
|
||||
"cookie_encryption": "0"
|
||||
}
|
||||
@@ -16,5 +16,5 @@ try:
|
||||
subprocess.check_output(args, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
error_msg = "NPM script '{}' failed with code '{}':\n".format(sys.argv[2], e.returncode)
|
||||
print(error_msg + e.output.decode('ascii'))
|
||||
print(error_msg + e.output.decode('utf8'))
|
||||
sys.exit(e.returncode)
|
||||
|
||||
@@ -145,6 +145,8 @@ static_library("chrome") {
|
||||
|
||||
if (enable_color_chooser) {
|
||||
sources += [
|
||||
"//chrome/browser/devtools/devtools_eye_dropper.cc",
|
||||
"//chrome/browser/devtools/devtools_eye_dropper.h",
|
||||
"//chrome/browser/platform_util.cc",
|
||||
"//chrome/browser/platform_util.h",
|
||||
"//chrome/browser/ui/browser_dialogs.h",
|
||||
|
||||
@@ -1193,8 +1193,8 @@ badge.
|
||||
|
||||
On macOS, it shows on the dock icon. On Linux, it only works for Unity launcher.
|
||||
|
||||
**Note:** Unity launcher requires the existence of a `.desktop` file to work,
|
||||
for more information please read [Desktop Environment Integration][unity-requirement].
|
||||
**Note:** Unity launcher requires a `.desktop` file to work. For more information,
|
||||
please read the [Unity integration documentation][unity-requirement].
|
||||
|
||||
### `app.getBadgeCount()` _Linux_ _macOS_
|
||||
|
||||
@@ -1432,8 +1432,8 @@ An `Integer` property that returns the badge count for current app. Setting the
|
||||
|
||||
On macOS, setting this with any nonzero integer shows on the dock icon. On Linux, this property only works for Unity launcher.
|
||||
|
||||
**Note:** Unity launcher requires the existence of a `.desktop` file to work,
|
||||
for more information please read [Desktop Environment Integration][unity-requirement].
|
||||
**Note:** Unity launcher requires a `.desktop` file to work. For more information,
|
||||
please read the [Unity integration documentation][unity-requirement].
|
||||
|
||||
**Note:** On macOS, you need to ensure that your application has the permission
|
||||
to display notifications for this property to take effect.
|
||||
@@ -1461,7 +1461,7 @@ A `Boolean` property that returns `true` if the app is packaged, `false` otherw
|
||||
[LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme
|
||||
[handoff]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html
|
||||
[activity-type]: https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType
|
||||
[unity-requirement]: ../tutorial/desktop-environment-integration.md#unity-launcher
|
||||
[unity-requirement]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher
|
||||
[mas-builds]: ../tutorial/mac-app-store-submission-guide.md
|
||||
[Squirrel-Windows]: https://github.com/Squirrel/Squirrel.Windows
|
||||
[JumpListBeginListMSDN]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378398(v=vs.85).aspx
|
||||
|
||||
@@ -187,9 +187,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
* `parent` BrowserWindow (optional) - Specify parent window. Default is `null`.
|
||||
* `modal` Boolean (optional) - Whether this is a modal window. This only works when the
|
||||
window is a child window. Default is `false`.
|
||||
* `acceptFirstMouse` Boolean (optional) - Whether the web view accepts a single
|
||||
mouse-down event that simultaneously activates the window. Default is
|
||||
`false`.
|
||||
* `acceptFirstMouse` Boolean (optional) - Whether clicking an inactive window will also
|
||||
click through to the web contents. Default is `false` on macOS. This option is not
|
||||
configurable on other platforms.
|
||||
* `disableAutoHideCursor` Boolean (optional) - Whether to hide cursor when typing.
|
||||
Default is `false`.
|
||||
* `autoHideMenuBar` Boolean (optional) - Auto hide the menu bar unless the `Alt`
|
||||
@@ -339,7 +339,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
more details.
|
||||
* `contextIsolation` Boolean (optional) - Whether to run Electron APIs and
|
||||
the specified `preload` script in a separate JavaScript context. Defaults
|
||||
to `false`. The context that the `preload` script runs in will only have
|
||||
to `true`. The context that the `preload` script runs in will only have
|
||||
access to its own dedicated `document` and `window` globals, as well as
|
||||
its own set of JavaScript builtins (`Array`, `Object`, `JSON`, etc.),
|
||||
which are all invisible to the loaded content. The Electron API will only
|
||||
@@ -351,8 +351,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
context in the dev tools by selecting the 'Electron Isolated Context'
|
||||
entry in the combo box at the top of the Console tab.
|
||||
* `worldSafeExecuteJavaScript` Boolean (optional) - If true, values returned from `webFrame.executeJavaScript` will be sanitized to ensure JS values
|
||||
can't unsafely cross between worlds when using `contextIsolation`. The default
|
||||
is `false`. In Electron 12, the default will be changed to `true`. _Deprecated_
|
||||
can't unsafely cross between worlds when using `contextIsolation`. Defaults to `true`. _Deprecated_
|
||||
* `nativeWindowOpen` Boolean (optional) - Whether to use native
|
||||
`window.open()`. Defaults to `false`. Child windows will always have node
|
||||
integration disabled unless `nodeIntegrationInSubFrames` is true. **Note:** This option is currently
|
||||
|
||||
@@ -71,7 +71,7 @@ const request = net.request({
|
||||
|
||||
Returns:
|
||||
|
||||
* `response` IncomingMessage - An object representing the HTTP response message.
|
||||
* `response` [IncomingMessage](incoming-message.md) - An object representing the HTTP response message.
|
||||
|
||||
#### Event: 'login'
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ When `contextIsolation` is enabled in your `webPreferences` (this is the default
|
||||
|
||||
The `contextBridge` module has the following methods:
|
||||
|
||||
### `contextBridge.exposeInMainWorld(apiKey, api)` _Experimental_
|
||||
### `contextBridge.exposeInMainWorld(apiKey, api)`
|
||||
|
||||
* `apiKey` String - The key to inject the API onto `window` with. The API will be accessible on `window[apiKey]`.
|
||||
* `api` any - Your API, more information on what this API can be and how it works is available below.
|
||||
@@ -50,7 +50,7 @@ The `contextBridge` module has the following methods:
|
||||
|
||||
### API
|
||||
|
||||
The `api` provided to [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api-experimental) must be a `Function`, `String`, `Number`, `Array`, `Boolean`, or an object
|
||||
The `api` provided to [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api) must be a `Function`, `String`, `Number`, `Array`, `Boolean`, or an object
|
||||
whose keys are strings and values are a `Function`, `String`, `Number`, `Array`, `Boolean`, or another nested object that meets the same conditions.
|
||||
|
||||
`Function` values are proxied to the other context and all other values are **copied** and **frozen**. Any data / primitives sent in
|
||||
|
||||
@@ -24,7 +24,7 @@ The `dialog` module has the following methods:
|
||||
* `buttonLabel` String (optional) - Custom label for the confirmation button, when
|
||||
left empty the default label will be used.
|
||||
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
|
||||
* `properties` String[] (optional) - Contains which features the dialog should
|
||||
* `properties` String[] (optional) - Contains which features the dialog should
|
||||
use. The following values are supported:
|
||||
* `openFile` - Allow files to be selected.
|
||||
* `openDirectory` - Allow directories to be selected.
|
||||
@@ -87,7 +87,7 @@ dialog.showOpenDialogSync(mainWindow, {
|
||||
* `buttonLabel` String (optional) - Custom label for the confirmation button, when
|
||||
left empty the default label will be used.
|
||||
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
|
||||
* `properties` String[] (optional) - Contains which features the dialog should
|
||||
* `properties` String[] (optional) - Contains which features the dialog should
|
||||
use. The following values are supported:
|
||||
* `openFile` - Allow files to be selected.
|
||||
* `openDirectory` - Allow directories to be selected.
|
||||
@@ -112,7 +112,7 @@ Returns `Promise<Object>` - Resolve with an object containing the following:
|
||||
|
||||
* `canceled` Boolean - whether or not the dialog was canceled.
|
||||
* `filePaths` String[] - An array of file paths chosen by the user. If the dialog is cancelled this will be an empty array.
|
||||
* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated. (For return values, see [table here](#bookmarks-array).)
|
||||
* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated. (For return values, see [table here](#bookmarks-array).)
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
@@ -165,7 +165,7 @@ dialog.showOpenDialog(mainWindow, {
|
||||
displayed in front of the filename text field.
|
||||
* `showsTagField` Boolean (optional) _macOS_ - Show the tags input box,
|
||||
defaults to `true`.
|
||||
* `properties` String[] (optional)
|
||||
* `properties` String[] (optional)
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
@@ -195,7 +195,7 @@ The `filters` specifies an array of file types that can be displayed, see
|
||||
* `nameFieldLabel` String (optional) _macOS_ - Custom label for the text
|
||||
displayed in front of the filename text field.
|
||||
* `showsTagField` Boolean (optional) _macOS_ - Show the tags input box, defaults to `true`.
|
||||
* `properties` String[] (optional)
|
||||
* `properties` String[] (optional)
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
@@ -227,7 +227,7 @@ expanding and collapsing the dialog.
|
||||
`"warning"`. On Windows, `"question"` displays the same icon as `"info"`, unless
|
||||
you set an icon using the `"icon"` option. On macOS, both `"warning"` and
|
||||
`"error"` display the same warning icon.
|
||||
* `buttons` String[] (optional) - Array of texts for buttons. On Windows, an empty array
|
||||
* `buttons` String[] (optional) - Array of texts for buttons. On Windows, an empty array
|
||||
will result in one button labeled "OK".
|
||||
* `defaultId` Integer (optional) - Index of the button in the buttons array which will
|
||||
be selected by default when the message box opens.
|
||||
@@ -273,7 +273,7 @@ If `browserWindow` is not shown dialog will not be attached to it. In such case
|
||||
`"warning"`. On Windows, `"question"` displays the same icon as `"info"`, unless
|
||||
you set an icon using the `"icon"` option. On macOS, both `"warning"` and
|
||||
`"error"` display the same warning icon.
|
||||
* `buttons` String[] (optional) - Array of texts for buttons. On Windows, an empty array
|
||||
* `buttons` String[] (optional) - Array of texts for buttons. On Windows, an empty array
|
||||
will result in one button labeled "OK".
|
||||
* `defaultId` Integer (optional) - Index of the button in the buttons array which will
|
||||
be selected by default when the message box opens.
|
||||
|
||||
@@ -162,7 +162,7 @@ const template = [
|
||||
{ role: 'services' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideothers' },
|
||||
{ role: 'hideOthers' },
|
||||
{ role: 'unhide' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'quit' }
|
||||
|
||||
@@ -531,7 +531,7 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents
|
||||
#### `ses.setPermissionCheckHandler(handler)`
|
||||
|
||||
* `handler` Function\<Boolean> | null
|
||||
* `webContents` ([WebContents](web-contents.md) | null) - WebContents checking the permission. Please note that if the request comes from a subframe you should use `requestingUrl` to check the request origin. Cross origin sub frames making permission checks will pass a `null` webContents to this handler. You should use `embeddingOrigin` and `requestingOrigin` to determine what origin the owning frame and the requesting frame are on respectively.
|
||||
* `webContents` ([WebContents](web-contents.md) | null) - WebContents checking the permission. Please note that if the request comes from a subframe you should use `requestingUrl` to check the request origin. All cross origin sub frames making permission checks will pass a `null` webContents to this handler, while certain other permission checks such as `notifications` checks will always pass `null`. You should use `embeddingOrigin` and `requestingOrigin` to determine what origin the owning frame and the requesting frame are on respectively.
|
||||
* `permission` String - Type of permission check. Valid values are `midiSysex`, `notifications`, `geolocation`, `media`,`mediaKeySystem`,`midi`, `pointerLock`, `fullscreen`, `openExternal`, or `serial`.
|
||||
* `requestingOrigin` String - The origin URL of the permission check
|
||||
* `details` Object - Some properties are only available on certain permission types.
|
||||
|
||||
@@ -14,7 +14,7 @@ Process: [Main](../glossary.md#main-process)
|
||||
* `highlightedIndex` Integer - The index of the item the user touched.
|
||||
* `selectedStyle` String (optional) - Selected item style. Can be `background`, `outline` or `none`. Defaults to `none`.
|
||||
* `overlayStyle` String (optional) - Selected overlay item style. Can be `background`, `outline` or `none`. Defaults to `none`.
|
||||
* `showArrowButtons` Boolean (optional) - Defaults to `false`.
|
||||
* `showArrowButtons` Boolean (optional) - Whether to show arrow buttons. Defaults to `false` and is only shown if `items` is non-empty.
|
||||
* `mode` String (optional) - Can be `fixed` or `free`. The default is `free`.
|
||||
* `continuous` Boolean (optional) - Defaults to `true`.
|
||||
|
||||
|
||||
@@ -62,6 +62,11 @@ Sets the maximum and minimum pinch-to-zoom level.
|
||||
> webFrame.setVisualZoomLevelLimits(1, 3)
|
||||
> ```
|
||||
|
||||
> **NOTE**: Visual zoom only applies to pinch-to-zoom behavior. Cmd+/-/0 zoom shortcuts are
|
||||
> controlled by the 'zoomIn', 'zoomOut', and 'resetZoom' MenuItem roles in the application
|
||||
> Menu. To disable shortcuts, manually [define the Menu](./menu.md#examples) and omit zoom roles
|
||||
> from the definition.
|
||||
|
||||
### `webFrame.setSpellCheckProvider(language, provider)`
|
||||
|
||||
* `language` String
|
||||
|
||||
@@ -154,7 +154,6 @@ response are visible by the time this listener is fired.
|
||||
* `timestamp` Double
|
||||
* `statusLine` String
|
||||
* `statusCode` Integer
|
||||
* `requestHeaders` Record<string, string>
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `callback` Function
|
||||
* `headersReceivedResponse` Object
|
||||
|
||||
@@ -718,6 +718,10 @@ Corresponds to the points in time when the spinner of the tab starts spinning.
|
||||
|
||||
Corresponds to the points in time when the spinner of the tab stops spinning.
|
||||
|
||||
### Event: 'did-attach'
|
||||
|
||||
Fired when attached to the embedder web contents.
|
||||
|
||||
### Event: 'dom-ready'
|
||||
|
||||
Fired when document in the given frame is loaded.
|
||||
@@ -838,6 +842,19 @@ this purpose.
|
||||
|
||||
Calling `event.preventDefault()` does __NOT__ have any effect.
|
||||
|
||||
### Event: 'did-start-navigation'
|
||||
|
||||
Returns:
|
||||
|
||||
* `url` String
|
||||
* `isInPlace` Boolean
|
||||
* `isMainFrame` Boolean
|
||||
* `frameProcessId` Integer
|
||||
* `frameRoutingId` Integer
|
||||
|
||||
Emitted when any frame (including main) starts navigating. `isInPlace` will be
|
||||
`true` for in-page navigations.
|
||||
|
||||
### Event: 'did-navigate'
|
||||
|
||||
Returns:
|
||||
@@ -850,6 +867,23 @@ This event is not emitted for in-page navigations, such as clicking anchor links
|
||||
or updating the `window.location.hash`. Use `did-navigate-in-page` event for
|
||||
this purpose.
|
||||
|
||||
### Event: 'did-frame-navigate'
|
||||
|
||||
Returns:
|
||||
|
||||
* `url` String
|
||||
* `httpResponseCode` Integer - -1 for non HTTP navigations
|
||||
* `httpStatusText` String - empty for non HTTP navigations,
|
||||
* `isMainFrame` Boolean
|
||||
* `frameProcessId` Integer
|
||||
* `frameRoutingId` Integer
|
||||
|
||||
Emitted when any frame navigation is done.
|
||||
|
||||
This event is not emitted for in-page navigations, such as clicking anchor links
|
||||
or updating the `window.location.hash`. Use `did-navigate-in-page` event for
|
||||
this purpose.
|
||||
|
||||
### Event: 'did-navigate-in-page'
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -117,15 +117,18 @@ const mainWindow = new BrowserWindow({
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url === 'about:blank') {
|
||||
return {
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
backgroundColor: 'black',
|
||||
webPreferences: {
|
||||
preload: 'my-child-window-preload-script.js'
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
backgroundColor: 'black',
|
||||
webPreferences: {
|
||||
preload: 'my-child-window-preload-script.js'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return { action: 'deny' }
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@ files, you need to have built Electron so that it knows which compiler flags
|
||||
were used. There is one required option for the script `--output-dir`, which
|
||||
tells the script which build directory to pull the compilation information
|
||||
from. A typical usage would be:
|
||||
`npm run lint:clang-tiy --out-dir ../out/Testing`
|
||||
`npm run lint:clang-tidy --out-dir ../out/Testing`
|
||||
|
||||
With no filenames provided, all C/C++/Objective-C files will be checked.
|
||||
You can provide a list of files to be checked by passing the filenames after
|
||||
the options:
|
||||
`npm run lint:clang-tiy --out-dir ../out/Testing shell/browser/api/electron_api_app.cc`
|
||||
`npm run lint:clang-tidy --out-dir ../out/Testing shell/browser/api/electron_api_app.cc`
|
||||
|
||||
While `clang-tidy` has a
|
||||
[long list](https://clang.llvm.org/extra/clang-tidy/checks/list.html)
|
||||
|
||||
@@ -25,7 +25,7 @@ You can run `npm run lint` to show any style issues detected by `cpplint` and
|
||||
## C++ and Python
|
||||
|
||||
For C++ and Python, we follow Chromium's [Coding
|
||||
Style](https://www.chromium.org/developers/coding-style). You can use
|
||||
Style](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/styleguide/styleguide.md). You can use
|
||||
[clang-format](clang-format.md) to format the C++ code automatically. There is
|
||||
also a script `script/cpplint.py` to check whether all files conform.
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ function createWindow () {
|
||||
})
|
||||
|
||||
ipcMain.handle('dark-mode:system', () => {
|
||||
nativeTheme.themeSouce = 'system'
|
||||
nativeTheme.themeSource = 'system'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
<p>
|
||||
We are using node <script>document.write(process.versions.node)</script>,
|
||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||
and Electron <script>document.write(process.versions.electron)</script>.
|
||||
</p>
|
||||
<p>After launching this application, you should see the system notification.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,21 +3,17 @@ const { app, BrowserWindow, Notification } = require('electron')
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
|
||||
const NOTIFICATION_TITLE = 'Basic Notification'
|
||||
const NOTIFICATION_BODY = 'Notification from the Main process'
|
||||
|
||||
function showNotification () {
|
||||
const notification = {
|
||||
title: 'Basic Notification',
|
||||
body: 'Notification from the Main process'
|
||||
}
|
||||
new Notification(notification).show()
|
||||
new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY }).show()
|
||||
}
|
||||
|
||||
app.whenReady().then(createWindow).then(showNotification)
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
<p>
|
||||
We are using node <script>document.write(process.versions.node)</script>,
|
||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||
and Electron <script>document.write(process.versions.electron)</script>.
|
||||
</p>
|
||||
<p>After launching this application, you should see the system notification.</p>
|
||||
<p id="output">Click it to see the effect in this interface.</p>
|
||||
|
||||
<script src="renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,10 +3,7 @@ const { app, BrowserWindow } = require('electron')
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const myNotification = new Notification('Title', {
|
||||
body: 'Notification from the Renderer process'
|
||||
})
|
||||
const NOTIFICATION_TITLE = 'Title'
|
||||
const NOTIFICATION_BODY = 'Notification from the Renderer process. Click to log to console.'
|
||||
const CLICK_MESSAGE = 'Notification clicked!'
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
new Notification(NOTIFICATION_TITLE, { body: NOTIFICATION_BODY })
|
||||
.onclick = () => document.getElementById("output").innerText = CLICK_MESSAGE
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
<p>
|
||||
We are using node <script>document.write(process.versions.node)</script>,
|
||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||
and Electron <script>document.write(process.versions.electron)</script>.
|
||||
</p>
|
||||
<p>Keep an eye on the dock (Mac) or taskbar (Windows, Unity) for this application!</p>
|
||||
<p>It should indicate a progress that advances from 0 to 100%.</p>
|
||||
<p>It should then show indeterminate (Windows) or pin at 100% (other operating systems)
|
||||
briefly and then loop.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,21 +1,39 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let progressInterval
|
||||
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
win.setProgressBar(0.5)
|
||||
}
|
||||
|
||||
const INCREMENT = 0.03
|
||||
const INTERVAL_DELAY = 100 // ms
|
||||
|
||||
let c = 0
|
||||
progressInterval = setInterval(() => {
|
||||
// update progress bar to next value
|
||||
// values between 0 and 1 will show progress, >1 will show indeterminate or stick at 100%
|
||||
win.setProgressBar(c)
|
||||
|
||||
// increment or reset progress bar
|
||||
if (c < 2) {
|
||||
c += INCREMENT
|
||||
} else {
|
||||
c = (-INCREMENT * 5) // reset to a bit less than 0 to show reset state
|
||||
}
|
||||
}, INTERVAL_DELAY)
|
||||
}
|
||||
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// before the app is terminated, clear both timers
|
||||
app.on('before-quit', () => {
|
||||
clearInterval(progressInterval)
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello World!</title>
|
||||
<title>Recent Documents</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
<h1>Recent Documents</h1>
|
||||
<p>
|
||||
We are using node <script>document.write(process.versions.node)</script>,
|
||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||
and Electron <script>document.write(process.versions.electron)</script>.
|
||||
Right click on the app icon to see recent documents.
|
||||
You should see `recently-used.md` added to the list of recent files
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,17 +5,15 @@ const path = require('path')
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
|
||||
const fileName = 'recently-used.md'
|
||||
fs.writeFile(fileName, 'Lorem Ipsum', () => {
|
||||
app.addRecentDocument(path.join(process.cwd(), `${fileName}`))
|
||||
app.addRecentDocument(path.join(__dirname, fileName))
|
||||
})
|
||||
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello World!</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
<p>
|
||||
We are using node <script>document.write(process.versions.node)</script>,
|
||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||
and Electron <script>document.write(process.versions.electron)</script>.
|
||||
Click on the title with the <pre>Command</pre> or <pre>Control</pre> key pressed.
|
||||
You should see a popup with the represented file at the top.
|
||||
</p>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -4,10 +4,7 @@ const os = require('os');
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
|
||||
@@ -16,91 +16,12 @@
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/tray">
|
||||
full API documentation (opens in new window)
|
||||
full API documentation
|
||||
</a>
|
||||
in your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<button id="put-in-tray">View Demo</button>
|
||||
<span id="tray-countdown"></span>
|
||||
</div>
|
||||
<p>
|
||||
The demo button sends a message to the main process using the
|
||||
<code>ipc</code> module. In the main process the app is told to
|
||||
place an icon, with a context menu, in the tray.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In this example the tray icon can be removed by clicking 'Remove' in
|
||||
the context menu or selecting the demo button again.
|
||||
</p>
|
||||
<h5>Main Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const path = require('path')
|
||||
const {ipcMain, app, Menu, Tray} = require('electron')
|
||||
|
||||
let appIcon = null
|
||||
|
||||
ipcMain.on('put-in-tray', (event) => {
|
||||
const iconName = process.platform === 'win32' ? 'windows-icon.png' : 'iconTemplate.png'
|
||||
const iconPath = path.join(__dirname, iconName)
|
||||
appIcon = new Tray(iconPath)
|
||||
|
||||
const contextMenu = Menu.buildFromTemplate([{
|
||||
label: 'Remove',
|
||||
click: () => {
|
||||
event.sender.send('tray-removed')
|
||||
}
|
||||
}])
|
||||
|
||||
appIcon.setToolTip('Electron Demo in the tray.')
|
||||
appIcon.setContextMenu(contextMenu)
|
||||
})
|
||||
|
||||
ipcMain.on('remove-tray', () => {
|
||||
appIcon.destroy()
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (appIcon) appIcon.destroy()
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre>
|
||||
<code>
|
||||
const ipc = require('electron').ipcRenderer
|
||||
|
||||
const trayBtn = document.getElementById('put-in-tray')
|
||||
let trayOn = false
|
||||
|
||||
trayBtn.addEventListener('click', function (event) {
|
||||
if (trayOn) {
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
ipc.send('remove-tray')
|
||||
} else {
|
||||
trayOn = true
|
||||
const message = 'Click demo again to remove.'
|
||||
document.getElementById('tray-countdown').innerHTML = message
|
||||
ipc.send('put-in-tray')
|
||||
}
|
||||
})
|
||||
// Tray removed from context menu on icon
|
||||
ipc.on('tray-removed', function () {
|
||||
ipc.send('remove-tray')
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
})
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<div>
|
||||
<h2>ProTip</h2>
|
||||
<strong>Tray support in Linux.</strong>
|
||||
@@ -109,7 +30,7 @@ ipc.on('tray-removed', function () {
|
||||
will need to install <code>libappindicator1</code> to make the
|
||||
tray icon work. See the
|
||||
<a href="https://electronjs.org/docs/api/tray">
|
||||
full API documentation (opens in new window)
|
||||
full API documentation
|
||||
</a>
|
||||
for more details about using Tray on Linux.
|
||||
</p>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,35 +0,0 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const trayBtn = document.getElementById('put-in-tray')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let trayOn = false
|
||||
|
||||
trayBtn.addEventListener('click', function (event) {
|
||||
if (trayOn) {
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
ipcRenderer.send('remove-tray')
|
||||
} else {
|
||||
trayOn = true
|
||||
const message = 'Click demo again to remove.'
|
||||
document.getElementById('tray-countdown').innerHTML = message
|
||||
ipcRenderer.send('put-in-tray')
|
||||
}
|
||||
})
|
||||
// Tray removed from context menu on icon
|
||||
ipcRenderer.on('tray-removed', function () {
|
||||
ipcRenderer.send('remove-tray')
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,92 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
|
||||
<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'">
|
||||
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>app.setAsDefaultProtocol Demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<section>
|
||||
<header>
|
||||
<h1>
|
||||
Protocol Handler
|
||||
</h1>
|
||||
<h3>The <code>app</code> module provides methods for handling protocols.</h3>
|
||||
<p>These methods allow you to set and unset the protocols your app should be the default app for. Similar to when a browser asks to be your default for viewing web pages.</p>
|
||||
<h1>App Default Protocol Demo</h1>
|
||||
|
||||
<p>Open the <a href="https://electronjs.org/docs/api/app">full app API documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> in your browser.</p>
|
||||
</header>
|
||||
<p>The protocol API allows us to register a custom protocol and intercept existing protocol requests.</p>
|
||||
<p>These methods allow you to set and unset the protocols your app should be the default app for. Similar to when a
|
||||
browser asks to be your default for viewing web pages.</p>
|
||||
|
||||
<div >
|
||||
<button id="open-in-browser" class="js-container-target demo-toggle-button">Launch current page in browser
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<section id='open-app-link'>
|
||||
<a href="electron-api-demos://open">Now... launch the app from a web link</a>
|
||||
</section>
|
||||
<div >
|
||||
<p>You can set your app as the default app to open for a specific protocol. For instance, in this demo we set this app as the default for <code>electron-api-demos://</code>. The demo button above will launch a page in your default browser with a link. Click that link and it will re-launch this app.</p>
|
||||
<h5>Packaging</h5>
|
||||
<p>This feature will only work on macOS when your app is packaged. It will not work when you're launching it in development from the command-line. When you package your app you'll need to make sure the macOS <code>plist</code> for the app is updated to include the new protocol handler. If you're using <code>electron-packager</code> then you can add the flag <code>--extend-info</code> with a path to the <code>plist</code> you've created. The one for this app is below.</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code>
|
||||
const {shell} = require('electron')
|
||||
const path = require('path')
|
||||
const protocolHandlerBtn = document.getElementById('protocol-handler')
|
||||
protocolHandlerBtn.addEventListener('click', () => {
|
||||
const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
|
||||
const pagePath = path.join('file://', pageDirectory, '../../sections/system/protocol-link.html')
|
||||
shell.openExternal(pagePath)
|
||||
})
|
||||
</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code>
|
||||
const {app, dialog} = require('electron')
|
||||
const path = require('path')
|
||||
<p>Open the <a href="https://www.electronjs.org/docs/api/protocol">full protocol API documentation</a> in your
|
||||
browser.</p>
|
||||
|
||||
if (process.defaultApp) {
|
||||
if (process.argv.length >= 2) {
|
||||
app.setAsDefaultProtocolClient('electron-api-demos', process.execPath, [path.resolve(process.argv[1])])
|
||||
}
|
||||
} else {
|
||||
app.setAsDefaultProtocolClient('electron-api-demos')
|
||||
}
|
||||
-----
|
||||
|
||||
app.on('open-url', (event, url) => {
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
|
||||
})
|
||||
<h3>Demo</h3>
|
||||
<p>
|
||||
First: Launch current page in browser
|
||||
<button id="open-in-browser" class="js-container-target demo-toggle-button">
|
||||
Click to Launch Browser
|
||||
</button>
|
||||
</p>
|
||||
|
||||
</code></pre>
|
||||
<h5>macOS plist</h5>
|
||||
<pre><code>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>electron-api-demos</string>
|
||||
</array>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Electron API Demos Protocol</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>ElectronTeamID</key>
|
||||
<string>VEKTX9H2N7</string>
|
||||
</dict>
|
||||
</plist>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</section>
|
||||
<p>
|
||||
Then: Launch the app from a web link!
|
||||
<a href="electron-fiddle://open">Click here to launch the app</a>
|
||||
</p>
|
||||
|
||||
----
|
||||
|
||||
<p>You can set your app as the default app to open for a specific protocol. For instance, in this demo we set this app
|
||||
as the default for <code>electron-fiddle://</code>. The demo button above will launch a page in your default
|
||||
browser with a link. Click that link and it will re-launch this app.</p>
|
||||
|
||||
|
||||
<h3>Packaging</h3>
|
||||
<p>This feature will only work on macOS when your app is packaged. It will not work when you're launching it in
|
||||
development from the command-line. When you package your app you'll need to make sure the macOS <code>plist</code>
|
||||
for the app is updated to include the new protocol handler. If you're using <code>electron-packager</code> then you
|
||||
can add the flag <code>--extend-info</code> with a path to the <code>plist</code> you've created. The one for this
|
||||
app is below:</p>
|
||||
|
||||
<p>
|
||||
<h5>macOS plist</h5>
|
||||
<pre><code>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>electron-api-demos</string>
|
||||
</array>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Electron API Demos Protocol</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>ElectronTeamID</key>
|
||||
<string>VEKTX9H2N7</string>
|
||||
</dict>
|
||||
</plist>
|
||||
</code>
|
||||
</pre>
|
||||
<p>
|
||||
|
||||
<!-- You can also require other files to run in this process -->
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,10 +1,39 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, dialog } = require('electron')
|
||||
const { app, BrowserWindow, ipcMain, shell } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
let mainWindow;
|
||||
|
||||
if (process.defaultApp) {
|
||||
if (process.argv.length >= 2) {
|
||||
app.setAsDefaultProtocolClient('electron-fiddle', process.execPath, [path.resolve(process.argv[1])])
|
||||
}
|
||||
} else {
|
||||
app.setAsDefaultProtocolClient('electron-fiddle')
|
||||
}
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore()
|
||||
mainWindow.focus()
|
||||
}
|
||||
})
|
||||
|
||||
// Create mainWindow, load the rest of the app, etc...
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
app.on('open-url', (event, url) => {
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
|
||||
})
|
||||
}
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
@@ -12,58 +41,23 @@ function createWindow () {
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
if (process.defaultApp) {
|
||||
if (process.argv.length >= 2) {
|
||||
app.setAsDefaultProtocolClient('electron-api-demos', process.execPath, [path.resolve(process.argv[1])])
|
||||
}
|
||||
} else {
|
||||
app.setAsDefaultProtocolClient('electron-api-demos')
|
||||
}
|
||||
|
||||
app.on('open-url', (event, url) => {
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
|
||||
// Handle window controls via IPC
|
||||
ipcMain.on('shell:open', () => {
|
||||
const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
|
||||
const pagePath = path.join('file://', pageDirectory, 'index.html')
|
||||
shell.openExternal(pagePath)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// All of the Node.js APIs are available in the preload process.
|
||||
// It has the same sandbox as a Chrome extension.
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
// Set up context bridge between the renderer process and the main process
|
||||
contextBridge.exposeInMainWorld(
|
||||
'shell',
|
||||
{
|
||||
open: () => ipcRenderer.send('shell:open'),
|
||||
}
|
||||
)
|
||||
@@ -1,14 +1,8 @@
|
||||
const { shell } = require('electron')
|
||||
const path = require('path')
|
||||
// This file is required by the index.html file and will
|
||||
// be executed in the renderer process for that window.
|
||||
// All APIs exposed by the context bridge are available here.
|
||||
|
||||
const openInBrowserButton = document.getElementById('open-in-browser')
|
||||
const openAppLink = document.getElementById('open-app-link')
|
||||
// Hides openAppLink when loaded inside Electron
|
||||
openAppLink.style.display = 'none'
|
||||
|
||||
openInBrowserButton.addEventListener('click', () => {
|
||||
console.log('clicked')
|
||||
const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
|
||||
const pagePath = path.join('file://', pageDirectory, 'index.html')
|
||||
shell.openExternal(pagePath)
|
||||
})
|
||||
// Binds the buttons to the context bridge API.
|
||||
document.getElementById('open-in-browser').addEventListener('click', () => {
|
||||
shell.open();
|
||||
});
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 37 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 32 KiB |
0
docs/images/versioning-sketch-2.png
Executable file → Normal file
0
docs/images/versioning-sketch-2.png
Executable file → Normal file
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -133,7 +133,7 @@ are likely using [`electron-packager`], which includes [`electron-osx-sign`] and
|
||||
|
||||
If you're using Packager's API, you can pass [in configuration that both signs
|
||||
and notarizes your
|
||||
application](https://electron.github.io/electron-packager/master/interfaces/electronpackager.options.html).
|
||||
application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html).
|
||||
|
||||
```js
|
||||
const packager = require('electron-packager')
|
||||
|
||||
@@ -159,7 +159,7 @@ function createWindow () {
|
||||
})
|
||||
|
||||
ipcMain.handle('dark-mode:system', () => {
|
||||
nativeTheme.themeSouce = 'system'
|
||||
nativeTheme.themeSource = 'system'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -200,6 +200,6 @@ Run the example using Electron Fiddle and then click the "Toggle Dark Mode" butt
|
||||
[system-wide-dark-mode]: https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/dark-mode/
|
||||
[electron-forge]: https://www.electronforge.io/
|
||||
[electron-packager]: https://github.com/electron/electron-packager
|
||||
[packager-darwindarkmode-api]: https://electron.github.io/electron-packager/master/interfaces/electronpackager.options.html#darwindarkmodesupport
|
||||
[packager-darwindarkmode-api]: https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html#darwindarkmodesupport
|
||||
[prefers-color-scheme]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
|
||||
[event-listeners]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# Desktop Environment Integration
|
||||
|
||||
Different operating systems provide different features for integrating desktop
|
||||
applications into their desktop environments. For example, on Windows,
|
||||
applications can put shortcuts in the JumpList of task bar, and on Mac,
|
||||
applications can put a custom menu in the dock menu.
|
||||
|
||||
This guide explains how to integrate your application into those desktop
|
||||
environments with Electron APIs.
|
||||
|
||||
## Notifications
|
||||
|
||||
See the [Notifications documentation](notifications.md).
|
||||
|
||||
## Recent Documents
|
||||
|
||||
See [Recent Documents documentation](recent-documents.md).
|
||||
|
||||
## Progress Bar
|
||||
|
||||
See the [Progress Bar documentation](progress-bar.md).
|
||||
|
||||
## Unity Launcher
|
||||
|
||||
See the [Unity Launcher documentation][unity-launcher].
|
||||
|
||||
## Represented File for macOS Window
|
||||
|
||||
See the [Represented File documentation](represented-file.md).
|
||||
|
||||
## Dragging files out of the window
|
||||
|
||||
See the [Native File Drag & Drop documentation](native-file-drag-drop.md).
|
||||
|
||||
[unity-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher
|
||||
@@ -1,22 +1,27 @@
|
||||
# Electron Release Timelines
|
||||
|
||||
Special notes:
|
||||
|
||||
* The `-beta.1` and `stable` dates are our solid release dates.
|
||||
* We strive for weekly beta releases, however we often release more betas than scheduled.
|
||||
* All dates are our goals but there may be reasons for adjusting the stable deadline, such as security bugs.
|
||||
* Take a look at the [5.0.0 Timeline blog post](https://electronjs.org/blog/electron-5-0-timeline) for info about publicizing our release dates.
|
||||
* Since Electron 6.0, we've been targeting every other Chromium version and releasing our stable on the same day as Chrome stable. You can reference Chromium's release schedule [here](https://chromiumdash.appspot.com/schedule). See [Electron's new release cadence blog post](https://www.electronjs.org/blog/12-week-cadence) for more details on our release schedule.
|
||||
* Electron 15.0 only will include a special Alpha release. Starting in Electron 16.0, we will release on an 8-week cadence. See [Electron's new 8-week cadence blog post](https://www.electronjs.org/blog/8-week-cadence) for more details.
|
||||
|
||||
| Version | -beta.1 | Stable | Chrome | Node |
|
||||
| ------- | ------- | ------ | ------ | ---- |
|
||||
| 2.0.0 | 2018-02-21 | 2018-05-01 | M61 | v8.9 |
|
||||
| 3.0.0 | 2018-06-21 | 2018-09-18 | M66 | v10.2 |
|
||||
| 4.0.0 | 2018-10-11 | 2018-12-20 | M69 | v10.11 |
|
||||
| 5.0.0 | 2019-01-22 | 2019-04-24 | M73 | v12.0 |
|
||||
| 6.0.0 | 2019-05-01 | 2019-07-30 | M76 | v12.4 |
|
||||
| 7.0.0 | 2019-08-01 | 2019-10-22 | M78 | v12.8 |
|
||||
| 8.0.0 | 2019-10-24 | 2020-02-04 | M80 | v12.13 |
|
||||
| 9.0.0 | 2020-02-06 | 2020-05-19 | M83 | v12.14 |
|
||||
| 10.0.0 | 2020-05-21 | 2020-08-25 | M85 | v12.16 |
|
||||
| 11.0.0 | 2020-08-27 | 2020-11-17 | M87 | v12.18 |
|
||||
| 12.0.0 | 2020-11-19 | 2021-03-02 | M89 | v14.16 |
|
||||
| 13.0.0 | 2021-03-04 | 2021-05-25 | M91 | v14.x |
|
||||
| Electron | Alpha | Beta | Stable | Chrome | Node |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- |
|
||||
| 2.0.0 | -- | 2018-Feb-21 | 2018-May-01 | M61 | v8.9 |
|
||||
| 3.0.0 | -- | 2018-Jun-21 | 2018-Sep-18 | M66 | v10.2 |
|
||||
| 4.0.0 | -- | 2018-Oct-11 | 2018-Dec-20 | M69 | v10.11 |
|
||||
| 5.0.0 | -- | 2019-Jan-22 | 2019-Apr-24 | M73 | v12.0 |
|
||||
| 6.0.0 | -- | 2019-May-01 | 2019-Jul-30 | M76 | v12.4 |
|
||||
| 7.0.0 | -- | 2019-Aug-01 | 2019-Oct-22 | M78 | v12.8 |
|
||||
| 8.0.0 | -- | 2019-Oct-24 | 2020-Feb-04 | M80 | v12.13 |
|
||||
| 9.0.0 | -- | 2020-Feb-06 | 2020-May-19 | M83 | v12.14 |
|
||||
| 10.0.0 | -- | 2020-May-21 | 2020-Aug-25 | M85 | v12.16 |
|
||||
| 11.0.0 | -- | 2020-Aug-27 | 2020-Nov-17 | M87 | v12.18 |
|
||||
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | M89 | v14.16 |
|
||||
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | M91 | v14.16 |
|
||||
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | M93 | TBD |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | M94 | TBD |
|
||||
|
||||
@@ -51,4 +51,4 @@ Somewhere in the Electron binary there will be a sequence of bytes that look lik
|
||||
|
||||
To flip a fuse you find its position in the fuse wire and change it to "0" or "1" depending on the state you'd like.
|
||||
|
||||
You can view the current schema [here](https://github.com/electron/electron/blob/master/build/fuses/fuses.json).
|
||||
You can view the current schema [here](https://github.com/electron/electron/blob/master/build/fuses/fuses.json5).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# In-App Purchase (macOS)
|
||||
# In-App Purchases (macOS)
|
||||
|
||||
## Preparing
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Installation
|
||||
# Advanced Installation Instructions
|
||||
|
||||
To install prebuilt Electron binaries, use [`npm`][npm].
|
||||
The preferred method is to install Electron as a development dependency in your
|
||||
@@ -135,14 +135,18 @@ a text file. A typical cache might look like this:
|
||||
|
||||
## Skip binary download
|
||||
|
||||
When installing the `electron` NPM package, it automatically downloads the electron binary.
|
||||
Under the hood, Electron's JavaScript API binds to a binary that contains its
|
||||
implementations. Because this binary is crucial to the function of any Electron app,
|
||||
it is downloaded by default in the `postinstall` step every time you install `electron`
|
||||
from the npm registry.
|
||||
|
||||
This can sometimes be unnecessary, e.g. in a CI environment, when testing another component.
|
||||
However, if you want to install your project's dependencies but don't need to use
|
||||
Electron functionality, you can set the `ELECTRON_SKIP_BINARY_DOWNLOAD` environment
|
||||
variable to prevent the binary from being downloaded. For instance, this feature can
|
||||
be useful in continuous integration environments when running unit tests that mock
|
||||
out the `electron` module.
|
||||
|
||||
To prevent the binary from being downloaded when you install all npm dependencies you can set the environment variable `ELECTRON_SKIP_BINARY_DOWNLOAD`.
|
||||
E.g.:
|
||||
|
||||
```sh
|
||||
```sh npm2yarn
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD=1 npm install
|
||||
```
|
||||
|
||||
|
||||
@@ -49,11 +49,11 @@ is a great place to get advice from other Electron app developers.
|
||||
the [GitHub issue tracker][issue-tracker] to see if any existing issues match your
|
||||
problem. If not, feel free to fill out our bug report template and submit a new issue.
|
||||
|
||||
[chromium](https://www.chromium.org/)
|
||||
[node](https://nodejs.org/)
|
||||
[mdn-guide](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web)
|
||||
[node-guide](https://nodejs.dev/learn)
|
||||
[comic](https://www.google.com/googlebooks/chrome/)
|
||||
[fiddle](https://electronjs.org/fiddle)
|
||||
[issue-tracker](https://github.com/electron/electron/issues)
|
||||
[discord](https://discord.gg/electron)
|
||||
[chromium]: https://www.chromium.org/
|
||||
[node]: https://nodejs.org/
|
||||
[mdn-guide]: https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web
|
||||
[node-guide]: https://nodejs.dev/learn
|
||||
[comic]: https://www.google.com/googlebooks/chrome/
|
||||
[fiddle]: https://electronjs.org/fiddle
|
||||
[issue-tracker]: https://github.com/electron/electron/issues
|
||||
[discord]: https://discord.gg/electron
|
||||
|
||||
169
docs/tutorial/launch-app-from-url-in-another-app.md
Normal file
169
docs/tutorial/launch-app-from-url-in-another-app.md
Normal file
@@ -0,0 +1,169 @@
|
||||
---
|
||||
title: launch-app-from-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.
|
||||
slug: launch-app-from-url-in-another-app
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Launching Your Electron App From A URL In Another App
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- ✍ Update this section if you want to provide more details -->
|
||||
|
||||
This guide will take you through the process of setting your electron app as the default
|
||||
handler for a specific [protocol](https://www.electronjs.org/docs/api/protocol).
|
||||
|
||||
By the end of this tutorial, we will have set our app to intercept and handle
|
||||
any clicked URLs that start with a specific protocol. In this guide, the protocol
|
||||
we will use will be "`electron-fiddle://`".
|
||||
|
||||
## Examples
|
||||
|
||||
### Main Process (main.js)
|
||||
|
||||
First we will import the required modules from `electron`. These modules help control our application life and create a native browser window.
|
||||
|
||||
```js
|
||||
const { app, BrowserWindow, shell } = require('electron')
|
||||
const path = require('path')
|
||||
```
|
||||
|
||||
Next, we will proceed to register our application to handle all "`electron-fiddle://`" protocols.
|
||||
|
||||
```js
|
||||
if (process.defaultApp) {
|
||||
if (process.argv.length >= 2) {
|
||||
app.setAsDefaultProtocolClient('electron-fiddle', process.execPath, [path.resolve(process.argv[1])])
|
||||
}
|
||||
} else {
|
||||
app.setAsDefaultProtocolClient('electron-fiddle')
|
||||
}
|
||||
```
|
||||
|
||||
We will now define the function in charge of creating our browser window and load our application's `index.html` file.
|
||||
|
||||
```js
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
```
|
||||
|
||||
In this next step, we will create our `BrowserWindow` and tell our application how to handle an event in which an external protocol is clicked.
|
||||
|
||||
This code will be different in WindowsOS compared to MacOS and Linux. This is due to Windows requiring additional code in order to open the contents of the protocol link within the same electron instance. Read more about this [here](https://www.electronjs.org/docs/api/app#apprequestsingleinstancelock).
|
||||
|
||||
### Windows code:
|
||||
|
||||
```js
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore()
|
||||
mainWindow.focus()
|
||||
}
|
||||
})
|
||||
|
||||
// Create mainWindow, load the rest of the app, etc...
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
// handling the protocol. In this case, we choose to show an Error Box.
|
||||
app.on('open-url', (event, url) => {
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### MacOS and Linux code:
|
||||
|
||||
```js
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
// handling the protocol. In this case, we choose to show an Error Box.
|
||||
app.on('open-url', (event, url) => {
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
|
||||
})
|
||||
```
|
||||
|
||||
Finally, we will add some additional code to handle when someone closes our application
|
||||
|
||||
```js
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', function () {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
```
|
||||
|
||||
## Important Note:
|
||||
|
||||
### Packaging
|
||||
|
||||
This feature will only work on macOS when your app is packaged. It will not work when you're launching it in development from the command-line. When you package your app you'll need to make sure the macOS `plist` for the app is updated to include the new protocol handler. If you're using [`electron-packager`](https://github.com/electron/electron-packager) then you
|
||||
can add the flag `--extend-info` with a path to the `plist` you've created. The one for this app is below:
|
||||
|
||||
### Plist
|
||||
|
||||
```XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>electron-api-demos</string>
|
||||
</array>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Electron API Demos Protocol</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>ElectronTeamID</key>
|
||||
<string>VEKTX9H2N7</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
After you start your electron app, you can now enter in a URL in your browser that contains the custom protocol, for example `"electron-fiddle://open"` and observe that the application will respond and show an error dialog box.
|
||||
|
||||
<!--
|
||||
Because Electron examples usually require multiple files (HTML, CSS, JS
|
||||
for the main and renderer process, etc.), we use this custom code block
|
||||
for Fiddle (https://www.electronjs.org/fiddle).
|
||||
Please modify any of the files in the referenced folder to fit your
|
||||
example.
|
||||
The content in this codeblock will not be rendered in the website so you
|
||||
can leave it empty.
|
||||
-->
|
||||
|
||||
```fiddle docs/fiddles/system/protocol-handler/launch-app-from-URL-in-another-app
|
||||
|
||||
```
|
||||
|
||||
<!-- ✍ Explanation of the code below -->
|
||||
@@ -1,4 +1,4 @@
|
||||
# Custom Linux Desktop Launcher Actions
|
||||
# Desktop Launcher Actions (Linux)
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Configuring the macOS Dock
|
||||
# Dock (macOS)
|
||||
|
||||
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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Notifications (Windows, Linux, macOS)
|
||||
# Notifications
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +18,7 @@ To show notifications in the Main process, you need to use the
|
||||
|
||||
### Show notifications in the Renderer process
|
||||
|
||||
Assuming you have a working Electron application from the
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), add the following line to the
|
||||
`index.html` file before the closing `</body>` tag:
|
||||
|
||||
@@ -26,26 +26,22 @@ Assuming you have a working Electron application from the
|
||||
<script src="renderer.js"></script>
|
||||
```
|
||||
|
||||
and add the `renderer.js` file:
|
||||
...and add the `renderer.js` file:
|
||||
|
||||
```javascript fiddle='docs/fiddles/features/notifications/renderer'
|
||||
const myNotification = new Notification('Title', {
|
||||
body: 'Notification from the Renderer process'
|
||||
})
|
||||
const NOTIFICATION_TITLE = 'Title'
|
||||
const NOTIFICATION_BODY = 'Notification from the Renderer process. Click to log to console.'
|
||||
const CLICK_MESSAGE = 'Notification clicked'
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
new Notification(NOTIFICATION_TITLE, { body: NOTIFICATION_BODY })
|
||||
.onclick = () => console.log(CLICK_MESSAGE)
|
||||
```
|
||||
|
||||
After launching the Electron application, you should see the notification:
|
||||
|
||||

|
||||
|
||||
If you open the Console and then click the notification, you will see the
|
||||
message that was generated after triggering the `onclick` event:
|
||||
|
||||

|
||||
Additionally, if you click on the notification, the DOM will update to show "Notification clicked!".
|
||||
|
||||
### Show notifications in the Main process
|
||||
|
||||
@@ -55,18 +51,17 @@ Starting with a working application from the
|
||||
```javascript fiddle='docs/fiddles/features/notifications/main'
|
||||
const { Notification } = require('electron')
|
||||
|
||||
const NOTIFICATION_TITLE = 'Basic Notification'
|
||||
const NOTIFICATION_BODY = 'Notification from the Main process'
|
||||
|
||||
function showNotification () {
|
||||
const notification = {
|
||||
title: 'Basic Notification',
|
||||
body: 'Notification from the Main process'
|
||||
}
|
||||
new Notification(notification).show()
|
||||
new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY }).show()
|
||||
}
|
||||
|
||||
app.whenReady().then(createWindow).then(showNotification)
|
||||
```
|
||||
|
||||
After launching the Electron application, you should see the notification:
|
||||
After launching the Electron application, you should see the system notification:
|
||||
|
||||

|
||||
|
||||
@@ -135,14 +130,6 @@ if you exceed that limit.
|
||||
|
||||
[apple-notification-guidelines]: https://developer.apple.com/macos/human-interface-guidelines/system-capabilities/notifications/
|
||||
|
||||
#### Advanced Notifications
|
||||
|
||||
Later versions of macOS allow for notifications with an input field, allowing the user
|
||||
to quickly reply to a notification. In order to send notifications with an input field,
|
||||
use the userland module [node-mac-notifier][node-mac-notifier].
|
||||
|
||||
[node-mac-notifier]: https://github.com/CharlieHess/node-mac-notifier
|
||||
|
||||
#### Do not disturb / Session State
|
||||
|
||||
To detect whether or not you're allowed to send a notification, use the userland module
|
||||
|
||||
@@ -120,9 +120,9 @@ file in the directory you executed it in. Both files can be analyzed using
|
||||
the Chrome Developer Tools, using the `Performance` and `Memory` tabs
|
||||
respectively.
|
||||
|
||||
![performance-cpu-prof]
|
||||
![Performance CPU Profile][performance-cpu-prof]
|
||||
|
||||
![performance-heap-prof]
|
||||
![Performance Heap Memory Profile][performance-heap-prof]
|
||||
|
||||
In this example, on the author's machine, we saw that loading `request` took
|
||||
almost half a second, whereas `node-fetch` took dramatically less memory
|
||||
|
||||
@@ -138,7 +138,7 @@ way to import Electron's content scripts.
|
||||
<!-- Note: This guide doesn't take sandboxing into account, which might fundamentally
|
||||
change the statements here. -->
|
||||
Preload scripts contain code that executes in a renderer process before its web content
|
||||
begins loading. These scripts runs within the renderer context, but are granted more
|
||||
begins loading. These scripts run within the renderer context, but are granted more
|
||||
privileges by having access to Node.js APIs.
|
||||
|
||||
A preload script can be attached to the main process in the `BrowserWindow` constructor's
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Progress Bar in Taskbar (Windows, macOS, Unity)
|
||||
# Taskbar Progress Bar (Windows & macOS)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -31,30 +31,70 @@ currently at 63% towards completion, you would call it as
|
||||
`setProgressBar(0.63)`.
|
||||
|
||||
Setting the parameter to negative values (e.g. `-1`) will remove the progress
|
||||
bar, whereas setting it to values greater than `1` (e.g. `2`) will switch the
|
||||
progress bar to indeterminate mode (Windows-only -- it will clamp to 100%
|
||||
otherwise). In this mode, a progress bar remains active but does not show an
|
||||
actual percentage. Use this mode for situations when you do not know how long
|
||||
an operation will take to complete.
|
||||
bar. Setting it to a value greater than `1` will indicate an indeterminate progress bar
|
||||
in Windows or clamp to 100% in other operating systems. An indeterminate progress bar
|
||||
remains active but does not show an actual percentage, and is used for situations when
|
||||
you do not know how long an operation will take to complete.
|
||||
|
||||
See the [API documentation for more options and modes][setprogressbar].
|
||||
|
||||
## Example
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), add the following lines to the
|
||||
`main.js` file:
|
||||
In this example, we add a progress bar to the main window that increments over time
|
||||
using Node.js timers.
|
||||
|
||||
```javascript fiddle='docs/fiddles/features/progress-bar'
|
||||
const { BrowserWindow } = require('electron')
|
||||
const win = new BrowserWindow()
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
win.setProgressBar(0.5)
|
||||
let progressInterval
|
||||
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
|
||||
const INCREMENT = 0.03
|
||||
const INTERVAL_DELAY = 100 // ms
|
||||
|
||||
let c = 0
|
||||
progressInterval = setInterval(() => {
|
||||
// update progress bar to next value
|
||||
// values between 0 and 1 will show progress, >1 will show indeterminate or stick at 100%
|
||||
win.setProgressBar(c)
|
||||
|
||||
// increment or reset progress bar
|
||||
if (c < 2) c += INCREMENT
|
||||
else c = 0
|
||||
}, INTERVAL_DELAY)
|
||||
}
|
||||
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// before the app is terminated, clear both timers
|
||||
app.on('before-quit', () => {
|
||||
clearInterval(progressInterval)
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
After launching the Electron application, you should see the bar in
|
||||
the dock (macOS) or taskbar (Windows, Unity), indicating the progress
|
||||
percentage you just defined.
|
||||
After launching the Electron application, the dock (macOS) or taskbar (Windows, Unity)
|
||||
should show a progress bar that starts at zero and progresses through 100% to completion.
|
||||
It should then show indeterminate (Windows) or pin to 100% (other operating systems)
|
||||
briefly and then loop.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ app.on('window-all-closed', function () {
|
||||
|
||||
[node-platform]: https://nodejs.org/api/process.html#process_process_platform
|
||||
[window-all-closed]: ../api/app.md#event-window-all-closed
|
||||
[window-all-closed]: ../api/app.md#appquit
|
||||
[app-quit]: ../api/app.md#appquit
|
||||
|
||||
#### Open a window if none are open (macOS)
|
||||
|
||||
@@ -299,7 +299,9 @@ function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
@@ -440,7 +442,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
</html>
|
||||
```
|
||||
|
||||
```fiddle docs/fiddles/quickstart
|
||||
```fiddle docs/fiddles/quick-start
|
||||
```
|
||||
|
||||
To summarize all the steps we've done:
|
||||
|
||||
@@ -13,39 +13,62 @@ __Application dock menu:__
|
||||
|
||||
![macOS Dock Menu][dock-menu-image]
|
||||
|
||||
To add a file to recent documents, you need to use the
|
||||
[app.addRecentDocument][addrecentdocument] API.
|
||||
|
||||
## Example
|
||||
|
||||
### Add an item to recent documents
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), add the following lines to the
|
||||
`main.js` file:
|
||||
### Managing recent documents
|
||||
|
||||
```javascript fiddle='docs/fiddles/features/recent-documents'
|
||||
const { app } = require('electron')
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
app.addRecentDocument('/Users/USERNAME/Desktop/work.type')
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
|
||||
const fileName = 'recently-used.md'
|
||||
fs.writeFile(fileName, 'Lorem Ipsum', () => {
|
||||
app.addRecentDocument(path.join(__dirname, fileName))
|
||||
})
|
||||
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
app.clearRecentDocuments()
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Adding a recent document
|
||||
|
||||
To add a file to recent documents, use the
|
||||
[app.addRecentDocument][addrecentdocument] API.
|
||||
|
||||
After launching the Electron application, right click the application icon.
|
||||
You should see the item you just added. In this guide, the item is a Markdown
|
||||
file located in the root of the project:
|
||||
In this guide, the item is a Markdown file located in the root of the project.
|
||||
You should see `recently-used.md` added to the list of recent files:
|
||||
|
||||

|
||||
|
||||
### Clear the list of recent documents
|
||||
#### Clearing the list of recent documents
|
||||
|
||||
To clear the list of recent documents, you need to use
|
||||
[app.clearRecentDocuments][clearrecentdocuments] API in the `main.js` file:
|
||||
|
||||
```javascript
|
||||
const { app } = require('electron')
|
||||
|
||||
app.clearRecentDocuments()
|
||||
```
|
||||
To clear the list of recent documents, use the
|
||||
[app.clearRecentDocuments][clearrecentdocuments] API.
|
||||
In this guide, the list of documents is cleared once all windows have been
|
||||
closed.
|
||||
|
||||
## Additional information
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Represented File for macOS BrowserWindows
|
||||
# Representing Files in a BrowserWindow (macOS)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -20,23 +20,40 @@ To set the represented file of window, you can use the
|
||||
|
||||
## Example
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), add the following lines to the
|
||||
`main.js` file:
|
||||
|
||||
```javascript fiddle='docs/fiddles/features/represented-file'
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const os = require('os');
|
||||
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const win = new BrowserWindow()
|
||||
|
||||
win.setRepresentedFilename('/etc/passwd')
|
||||
win.setRepresentedFilename(os.homedir())
|
||||
win.setDocumentEdited(true)
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
After launching the Electron application, click on the title with `Command` or
|
||||
`Control` key pressed. You should see a popup with the file you just defined:
|
||||
`Control` key pressed. You should see a popup with the represented file at the top.
|
||||
In this guide, this is the current user's home directory:
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Snapcraft Guide (Ubuntu Software Center & More)
|
||||
# Snapcraft Guide (Linux)
|
||||
|
||||
This guide provides information on how to package your Electron application
|
||||
for any Snapcraft environment, including the Ubuntu Software Center.
|
||||
|
||||
@@ -16,7 +16,6 @@ you can interact with the community in these locations:
|
||||
* Sharing ideas with other Electron app developers
|
||||
* And more!
|
||||
* [`electron`](https://discuss.atom.io/c/electron) category on the Atom forums
|
||||
* `#atom-shell` channel on Freenode
|
||||
* `#electron` channel on [Atom's Slack](https://discuss.atom.io/t/join-us-on-slack/16638?source_topic_id=25406)
|
||||
* [`electron-ru`](https://telegram.me/electron_ru) *(Russian)*
|
||||
* [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)*
|
||||
@@ -38,6 +37,13 @@ tools and resources.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
_**Note:** Beginning in September 2021 with Electron 15, the Electron team
|
||||
will temporarily support the latest **four** stable major versions. This
|
||||
extended support is intended to help Electron developers transition to
|
||||
the [new eight week release cadence](https://electronjs.org/blog/8-week-cadence), and will continue until May 2022, with
|
||||
the release of Electron 19. At that time, the Electron team will drop support
|
||||
back to the latest three stable major versions._
|
||||
|
||||
The latest three *stable* major versions are supported by the Electron team.
|
||||
For example, if the latest release is 6.1.x, then the 5.0.x as well
|
||||
as the 4.2.x series are supported. We only support the latest minor release
|
||||
@@ -64,9 +70,9 @@ until the maintainers feel the maintenance burden is too high to continue doing
|
||||
|
||||
### Currently supported versions
|
||||
|
||||
* 13.x.y
|
||||
* 12.x.y
|
||||
* 11.x.y
|
||||
* 10.x.y
|
||||
|
||||
### End-of-life
|
||||
|
||||
|
||||
83
docs/tutorial/tray.md
Normal file
83
docs/tutorial/tray.md
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: Tray
|
||||
description: This guide will take you through the process of creating
|
||||
a Tray icon with its own context menu to the system's notification area.
|
||||
slug: tray
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Tray
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- ✍ Update this section if you want to provide more details -->
|
||||
|
||||
This guide will take you through the process of creating a
|
||||
[Tray](https://www.electronjs.org/docs/api/tray) icon with
|
||||
its own context menu to the system's notification area.
|
||||
|
||||
On MacOS and Ubuntu, the Tray will be located on the top
|
||||
right corner of your screen, adjacent to your battery and wifi icons.
|
||||
On Windows, the Tray will usually be located in the bottom right corner.
|
||||
|
||||
## Example
|
||||
|
||||
### main.js
|
||||
|
||||
First we must import `app`, `Tray`, `Menu`, `nativeImage` from `electron`.
|
||||
|
||||
```js
|
||||
const { app, Tray, Menu, nativeImage } = require('electron')
|
||||
```
|
||||
|
||||
Next we will create our Tray. To do this, we will use a
|
||||
[`NativeImage`](https://www.electronjs.org/docs/api/native-image) icon,
|
||||
which can be created through any one of these
|
||||
[methods](https://www.electronjs.org/docs/api/native-image#methods).
|
||||
Note that we wrap our Tray creation code within an
|
||||
[`app.whenReady`](https://www.electronjs.org/docs/api/app#appwhenready)
|
||||
as we will need to wait for our electron app to finish initializing.
|
||||
|
||||
```js title='main.js'
|
||||
let tray
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const icon = nativeImage.createFromPath('path/to/asset.png')
|
||||
tray = new Tray(icon)
|
||||
|
||||
// note: your contextMenu, Tooltip and Title code will go here!
|
||||
})
|
||||
```
|
||||
|
||||
Great! Now we can start attaching a context menu to our Tray, like so:
|
||||
|
||||
```js
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{ label: 'Item1', type: 'radio' },
|
||||
{ label: 'Item2', type: 'radio' },
|
||||
{ label: 'Item3', type: 'radio', checked: true },
|
||||
{ label: 'Item4', type: 'radio' }
|
||||
])
|
||||
|
||||
tray.setContextMenu(contextMenu)
|
||||
```
|
||||
|
||||
The code above will create 4 separate radio-type items in the context menu.
|
||||
To read more about constructing native menus, click
|
||||
[here](https://www.electronjs.org/docs/api/menu#menubuildfromtemplatetemplate).
|
||||
|
||||
Finally, let's give our tray a tooltip and a title.
|
||||
|
||||
```js
|
||||
tray.setToolTip('This is my application')
|
||||
tray.setTitle('This is my title')
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
After you start your electron app, you should see the Tray residing
|
||||
in either the top or bottom right of your screen, depending on your
|
||||
operating system.
|
||||
|
||||
```fiddle docs/fiddles/native-ui/tray
|
||||
```
|
||||
@@ -49,7 +49,7 @@ update server.
|
||||
Depending on your needs, you can choose from one of these:
|
||||
|
||||
- [Hazel][hazel] – Update server for private or open-source apps which can be
|
||||
deployed for free on [Now][now]. It pulls from [GitHub Releases][gh-releases]
|
||||
deployed for free on [Vercel][vercel]. It pulls from [GitHub Releases][gh-releases]
|
||||
and leverages the power of GitHub's CDN.
|
||||
- [Nuts][nuts] – Also uses [GitHub Releases][gh-releases], but caches app
|
||||
updates on disk and supports private repositories.
|
||||
@@ -64,7 +64,7 @@ to minify server cost.
|
||||
Once you've deployed your update server, continue with importing the required
|
||||
modules in your code. The following code might vary for different server
|
||||
software, but it works like described when using
|
||||
[Hazel](https://github.com/zeit/hazel).
|
||||
[Hazel][hazel].
|
||||
|
||||
**Important:** Please ensure that the code below will only be executed in
|
||||
your packaged app, and not in development. You can use
|
||||
@@ -136,8 +136,8 @@ autoUpdater.on('error', message => {
|
||||
|
||||
Because the requests made by Auto Update aren't under your direct control, you may find situations that are difficult to handle (such as if the update server is behind authentication). The `url` field does support files, which means that with some effort, you can sidestep the server-communication aspect of the process. [Here's an example of how this could work](https://github.com/electron/electron/issues/5020#issuecomment-477636990).
|
||||
|
||||
[now]: https://zeit.co/now
|
||||
[hazel]: https://github.com/zeit/hazel
|
||||
[vercel]: https://vercel.com
|
||||
[hazel]: https://github.com/vercel/hazel
|
||||
[nuts]: https://github.com/GitbookIO/nuts
|
||||
[gh-releases]: https://help.github.com/articles/creating-releases/
|
||||
[electron-release-server]: https://github.com/ArekSredzki/electron-release-server
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Using Native Node Modules
|
||||
# Native Node Modules
|
||||
|
||||
Native Node.js modules are supported by Electron, but since Electron has a different
|
||||
[application binary interface (ABI)][abi] from a given Node.js binary (due to
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Using Pepper Flash Plugin
|
||||
# Pepper Flash Plugin
|
||||
|
||||
Electron no longer supports the Pepper Flash plugin, as Chrome has removed support.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Using Selenium and WebDriver
|
||||
# Selenium and WebDriver
|
||||
|
||||
From [ChromeDriver - WebDriver for Chrome][chrome-driver]:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Web embeds
|
||||
# Web Embeds
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Windows 10 on Arm
|
||||
# Windows on ARM
|
||||
|
||||
If your app runs with Electron 6.0.8 or later, you can now build it for Windows 10 on Arm. This considerably improves performance, but requires recompilation of any native modules used in your app. It may also require small fixups to your build and packaging scripts.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Windows Taskbar
|
||||
# Taskbar Customization (Windows)
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -180,8 +180,8 @@ filenames = {
|
||||
"shell/browser/ui/cocoa/root_view_mac.mm",
|
||||
"shell/browser/ui/cocoa/views_delegate_mac.h",
|
||||
"shell/browser/ui/cocoa/views_delegate_mac.mm",
|
||||
"shell/browser/ui/cocoa/window_buttons_view.h",
|
||||
"shell/browser/ui/cocoa/window_buttons_view.mm",
|
||||
"shell/browser/ui/cocoa/window_buttons_proxy.h",
|
||||
"shell/browser/ui/cocoa/window_buttons_proxy.mm",
|
||||
"shell/browser/ui/drag_util_mac.mm",
|
||||
"shell/browser/ui/file_dialog_mac.mm",
|
||||
"shell/browser/ui/inspectable_web_contents_view_mac.h",
|
||||
|
||||
@@ -490,63 +490,83 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
}
|
||||
};
|
||||
|
||||
function fsReadFileAsar (pathArgument: string, options: any, callback: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (pathInfo.isAsar) {
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = { encoding: null };
|
||||
} else if (typeof options === 'string') {
|
||||
options = { encoding: options };
|
||||
} else if (options === null || options === undefined) {
|
||||
options = { encoding: null };
|
||||
} else if (typeof options !== 'object') {
|
||||
throw new TypeError('Bad arguments');
|
||||
}
|
||||
|
||||
const { encoding } = options;
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.size === 0) {
|
||||
nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFile(realPath, options, callback);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => {
|
||||
callback(error, encoding ? buffer.toString(encoding) : buffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { readFile } = fs;
|
||||
fs.readFile = function (pathArgument: string, options: any, callback: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return readFile.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = { encoding: null };
|
||||
} else if (typeof options === 'string') {
|
||||
options = { encoding: options };
|
||||
} else if (options === null || options === undefined) {
|
||||
options = { encoding: null };
|
||||
} else if (typeof options !== 'object') {
|
||||
throw new TypeError('Bad arguments');
|
||||
if (!pathInfo.isAsar) {
|
||||
return readFile.apply(this, arguments);
|
||||
}
|
||||
|
||||
const { encoding } = options;
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.size === 0) {
|
||||
nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFile(realPath, options, callback);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => {
|
||||
callback(error, encoding ? buffer.toString(encoding) : buffer);
|
||||
});
|
||||
return fsReadFileAsar(pathArgument, options, callback);
|
||||
};
|
||||
|
||||
fs.promises.readFile = util.promisify(fs.readFile);
|
||||
const { readFile: readFilePromise } = fs.promises;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
fs.promises.readFile = function (pathArgument: string, options: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) {
|
||||
return readFilePromise.apply(this, arguments);
|
||||
}
|
||||
|
||||
const p = util.promisify(fsReadFileAsar);
|
||||
return p(pathArgument, options);
|
||||
};
|
||||
|
||||
const { readFileSync } = fs;
|
||||
fs.readFileSync = function (pathArgument: string, options: any) {
|
||||
|
||||
@@ -20,20 +20,6 @@ BrowserWindow.prototype._init = function (this: BWT) {
|
||||
nativeSetBounds.call(this, bounds, ...opts);
|
||||
};
|
||||
|
||||
// Sometimes the webContents doesn't get focus when window is shown, so we
|
||||
// have to force focusing on webContents in this case. The safest way is to
|
||||
// focus it when we first start to load URL, if we do it earlier it won't
|
||||
// have effect, if we do it later we might move focus in the page.
|
||||
//
|
||||
// Though this hack is only needed on macOS when the app is launched from
|
||||
// Finder, we still do it on all platforms in case of other bugs we don't
|
||||
// know.
|
||||
if (this.webContents._initiallyShown) {
|
||||
this.webContents.once('load-url' as any, function (this: WebContents) {
|
||||
this.focus();
|
||||
});
|
||||
}
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event: Event) => {
|
||||
app.emit('browser-window-blur', event, this);
|
||||
|
||||
@@ -76,6 +76,7 @@ ipcMainInternal.on(
|
||||
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({
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
import { internalContextBridge } from '@electron/internal/renderer/api/context-bridge';
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
import { webFrame } from 'electron/renderer';
|
||||
import { IPC_MESSAGES } from '../common/ipc-messages';
|
||||
|
||||
const { contextIsolationEnabled } = internalContextBridge;
|
||||
|
||||
/* Corrects for some Inspector adaptations needed in Electron.
|
||||
* 1) Use menu API to show context menu.
|
||||
* 2) Correct for Chromium returning undefined for filesystem.
|
||||
* 3) Use dialog API to override file chooser dialog.
|
||||
*/
|
||||
window.onload = function () {
|
||||
// Use menu API to show context menu.
|
||||
window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu;
|
||||
|
||||
// correct for Chromium returning undefined for filesystem
|
||||
window.Persistence!.FileSystemWorkspaceBinding.completeURL = completeURL;
|
||||
|
||||
// Use dialog API to override file chooser dialog.
|
||||
window.UI!.createFileSelectorElement = createFileSelectorElement;
|
||||
if (contextIsolationEnabled) {
|
||||
internalContextBridge.overrideGlobalValueFromIsolatedWorld([
|
||||
'InspectorFrontendHost', 'showContextMenuAtPoint'
|
||||
], createMenu);
|
||||
internalContextBridge.overrideGlobalValueFromIsolatedWorld([
|
||||
'Persistence', 'FileSystemWorkspaceBinding', 'completeURL'
|
||||
], completeURL);
|
||||
internalContextBridge.overrideGlobalValueFromIsolatedWorld([
|
||||
'UI', 'createFileSelectorElement'
|
||||
], createFileSelectorElement);
|
||||
} else {
|
||||
window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu;
|
||||
window.Persistence!.FileSystemWorkspaceBinding.completeURL = completeURL;
|
||||
window.UI!.createFileSelectorElement = createFileSelectorElement;
|
||||
}
|
||||
};
|
||||
|
||||
// Extra / is needed as a result of MacOS requiring absolute paths
|
||||
@@ -36,9 +52,10 @@ const createMenu = function (x: number, y: number, items: ContextMenuItem[]) {
|
||||
const isEditMenu = useEditMenuItems(x, y, items);
|
||||
ipcRendererInternal.invoke<number>(IPC_MESSAGES.INSPECTOR_CONTEXT_MENU, items, isEditMenu).then(id => {
|
||||
if (typeof id === 'number') {
|
||||
window.DevToolsAPI!.contextMenuItemSelected(id);
|
||||
webFrame.executeJavaScript(`window.DevToolsAPI.contextMenuItemSelected(${JSON.stringify(id)})`);
|
||||
}
|
||||
window.DevToolsAPI!.contextMenuCleared();
|
||||
|
||||
webFrame.executeJavaScript('window.DevToolsAPI.contextMenuCleared()');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -270,9 +270,7 @@ const warnAboutAllowedPopups = function () {
|
||||
// Logs a warning message about the remote module
|
||||
|
||||
const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) {
|
||||
if (!webPreferences || isLocalhost()) return;
|
||||
const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true;
|
||||
if (!remoteModuleEnabled) return;
|
||||
if (!webPreferences || !webPreferences.enableRemoteModule || isLocalhost()) return;
|
||||
|
||||
if (getIsRemoteProtocol()) {
|
||||
const warning = `This renderer process has "enableRemoteModule" enabled
|
||||
@@ -298,7 +296,9 @@ const logSecurityWarnings = function (
|
||||
warnAboutEnableBlinkFeatures(webPreferences);
|
||||
warnAboutInsecureCSP();
|
||||
warnAboutAllowedPopups();
|
||||
warnAboutRemoteModuleWithRemoteContent(webPreferences);
|
||||
if (BUILDFLAG(ENABLE_REMOTE_MODULE)) {
|
||||
warnAboutRemoteModuleWithRemoteContent(webPreferences);
|
||||
}
|
||||
};
|
||||
|
||||
const getWebPreferences = async function () {
|
||||
|
||||
@@ -12,6 +12,9 @@ require('../common/reset-search-paths');
|
||||
// Import common settings.
|
||||
require('@electron/internal/common/init');
|
||||
|
||||
// Process command line arguments.
|
||||
const { hasSwitch, getSwitchValue } = process._linkedBinding('electron_common_command_line');
|
||||
|
||||
// Export node bindings to global.
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/cjs/helpers') // eslint-disable-line
|
||||
global.module = new Module('electron/js2c/worker_init');
|
||||
@@ -32,4 +35,10 @@ if (self.location.protocol === 'file:') {
|
||||
// For backwards compatibility we fake these two paths here
|
||||
global.__filename = path.join(process.resourcesPath, 'electron.asar', 'worker', 'init.js');
|
||||
global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'worker');
|
||||
|
||||
const appPath = hasSwitch('app-path') ? getSwitchValue('app-path') : null;
|
||||
if (appPath) {
|
||||
// Search for module under the app directory.
|
||||
global.module.paths = Module._nodeModulePaths(appPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "13.0.1",
|
||||
"version": "13.2.3",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
2
patches/angle/.patches
Normal file
2
patches/angle/.patches
Normal file
@@ -0,0 +1,2 @@
|
||||
cherry-pick-d8cb996.patch
|
||||
cherry-pick-1fb846c.patch
|
||||
90
patches/angle/cherry-pick-1fb846c.patch
Normal file
90
patches/angle/cherry-pick-1fb846c.patch
Normal file
@@ -0,0 +1,90 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Alexey Knyazev <lexa.knyazev@gmail.com>
|
||||
Date: Tue, 3 Aug 2021 01:57:49 +0400
|
||||
Subject: Validate texStorage dimensions with compressed formats
|
||||
|
||||
Bug: angleproject:6230
|
||||
Change-Id: I501ec1e6974bdc7e6731dcb88045edb0aa22b888
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3067329
|
||||
Commit-Queue: Alexey Knyazev <lexa.knyazev@gmail.com>
|
||||
Reviewed-by: Kenneth Russell <kbr@chromium.org>
|
||||
Reviewed-by: Jamie Madill <jmadill@chromium.org>
|
||||
|
||||
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
|
||||
index 53e1afd6e26ef8cc099e51c66457a968ca82b1c8..5c69d568ee45b9e8bf1d4d0da7b9a48b123a47c0 100644
|
||||
--- a/src/libANGLE/validationES3.cpp
|
||||
+++ b/src/libANGLE/validationES3.cpp
|
||||
@@ -1339,17 +1339,26 @@ bool ValidateES3TexStorageParametersBase(const Context *context,
|
||||
return false;
|
||||
}
|
||||
|
||||
- if (formatInfo.compressed && target == TextureType::Rectangle)
|
||||
+ if (formatInfo.compressed)
|
||||
{
|
||||
- context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed);
|
||||
- return false;
|
||||
- }
|
||||
+ if (target == TextureType::Rectangle)
|
||||
+ {
|
||||
+ context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed);
|
||||
+ return false;
|
||||
+ }
|
||||
|
||||
- if (formatInfo.compressed && target == TextureType::_3D)
|
||||
- {
|
||||
- if (!ValidateES3CompressedFormatForTexture3D(context, formatInfo.internalFormat))
|
||||
+ if (target == TextureType::_3D)
|
||||
{
|
||||
- // Error already generated.
|
||||
+ if (!ValidateES3CompressedFormatForTexture3D(context, formatInfo.internalFormat))
|
||||
+ {
|
||||
+ // Error already generated.
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!ValidCompressedImageSize(context, formatInfo.internalFormat, 0, width, height, depth))
|
||||
+ {
|
||||
+ context->validationError(GL_INVALID_OPERATION, kInvalidCompressedImageSize);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
diff --git a/src/tests/gl_tests/SRGBTextureTest.cpp b/src/tests/gl_tests/SRGBTextureTest.cpp
|
||||
index 271fe6dbaa1d5684f7c2e16bdef18bce30943e8b..a0eab1af9e34a5de75e226efef9c0af4a50c63ff 100644
|
||||
--- a/src/tests/gl_tests/SRGBTextureTest.cpp
|
||||
+++ b/src/tests/gl_tests/SRGBTextureTest.cpp
|
||||
@@ -340,7 +340,7 @@ TEST_P(SRGBTextureTestES3, SRGBOverrideFormats)
|
||||
{
|
||||
GLTexture tex;
|
||||
glBindTexture(GL_TEXTURE_2D, tex.get());
|
||||
- glTexStorage2D(GL_TEXTURE_2D, 1, format, 1, 1);
|
||||
+ glTexStorage2D(GL_TEXTURE_2D, 1, format, 4, 4);
|
||||
GLenum error = glGetError();
|
||||
if (error == GL_INVALID_ENUM)
|
||||
{
|
||||
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
index fabaefbe33c94a048ec7c974951ac9ef886826ef..efbd9ebdb2bb29cf9673750e1e3e7b492bfbf87e 100644
|
||||
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
@@ -5073,6 +5073,21 @@ void WebGLCompatibilityTest::testCompressedTexLevelDimension(GLenum format,
|
||||
{
|
||||
EXPECT_GL_ERROR(expectedError) << explanation;
|
||||
}
|
||||
+
|
||||
+ if (level == 0 && width > 0 && getClientMajorVersion() >= 3)
|
||||
+ {
|
||||
+ GLTexture sourceTextureStorage;
|
||||
+ glBindTexture(GL_TEXTURE_2D, sourceTextureStorage);
|
||||
+ glTexStorage2D(GL_TEXTURE_2D, 1, format, width, height);
|
||||
+ if (expectedError == 0)
|
||||
+ {
|
||||
+ EXPECT_GL_NO_ERROR() << explanation << " (texStorage)";
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ EXPECT_GL_ERROR(expectedError) << explanation << " (texStorage)";
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
void WebGLCompatibilityTest::testCompressedTexImage(GLenum format)
|
||||
300
patches/angle/cherry-pick-d8cb996.patch
Normal file
300
patches/angle/cherry-pick-d8cb996.patch
Normal file
@@ -0,0 +1,300 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Kenneth Russell <kbr@chromium.org>
|
||||
Date: Wed, 4 Aug 2021 18:15:51 -0700
|
||||
Subject: In WebGL, constrain base level of compressed textures.
|
||||
|
||||
Enforce that if a mipmap level > 0 is specified for a compressed
|
||||
texture, that it implies that the size of the base level of the
|
||||
texture is a multiple of the format's block size.
|
||||
|
||||
Makes the test changes in
|
||||
https://github.com/KhronosGroup/WebGL/pull/3304 largely pass. There
|
||||
are some needed follow-on fixes to that PR, and this CL changes a
|
||||
sub-test result in the existing S3TC and S3TC-sRGB tests which will
|
||||
need to be suppressed Chromium-side first.
|
||||
|
||||
Bug: angleproject:6245
|
||||
Change-Id: I7723d7882091b78a353d8d273e80b819dd384021
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3072568
|
||||
Commit-Queue: Kenneth Russell <kbr@chromium.org>
|
||||
Reviewed-by: Jamie Madill <jmadill@chromium.org>
|
||||
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
|
||||
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
|
||||
index 656e51f17724af9e0959fcd741ceeebf25ee0f24..412b9aa0f1d75c40ce02522589c53e943d049228 100644
|
||||
--- a/src/libANGLE/validationES.cpp
|
||||
+++ b/src/libANGLE/validationES.cpp
|
||||
@@ -1006,6 +1006,15 @@ bool ValidCompressedDimension(GLsizei size, GLuint blockSize, GLint level)
|
||||
return (level > 0) || (size % blockSize == 0);
|
||||
}
|
||||
|
||||
+bool ValidCompressedBaseLevelForWebGL(GLsizei size, GLuint blockSize, GLint level)
|
||||
+{
|
||||
+ // Avoid C++ undefined behavior.
|
||||
+ constexpr int maxValidShifts = 31;
|
||||
+ if (level > maxValidShifts)
|
||||
+ return false;
|
||||
+ return ((size << level) % blockSize) == 0;
|
||||
+}
|
||||
+
|
||||
bool ValidCompressedImageSize(const Context *context,
|
||||
GLenum internalFormat,
|
||||
GLint level,
|
||||
@@ -1043,11 +1052,27 @@ bool ValidCompressedImageSize(const Context *context,
|
||||
|
||||
if (CompressedTextureFormatRequiresExactSize(internalFormat))
|
||||
{
|
||||
- if (!ValidCompressedDimension(width, formatInfo.compressedBlockWidth, level) ||
|
||||
- !ValidCompressedDimension(height, formatInfo.compressedBlockHeight, level) ||
|
||||
- !ValidCompressedDimension(depth, formatInfo.compressedBlockDepth, level))
|
||||
+ // In WebGL compatibility mode, enforce that the base level implied
|
||||
+ // by the compressed texture's mip level would conform to the block
|
||||
+ // size. This is more strict than the non-WebGL check.
|
||||
+ if (context->getExtensions().webglCompatibility)
|
||||
{
|
||||
- return false;
|
||||
+ if (!ValidCompressedBaseLevelForWebGL(width, formatInfo.compressedBlockWidth, level) ||
|
||||
+ !ValidCompressedBaseLevelForWebGL(height, formatInfo.compressedBlockHeight,
|
||||
+ level) ||
|
||||
+ !ValidCompressedBaseLevelForWebGL(depth, formatInfo.compressedBlockDepth, level))
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ if (!ValidCompressedDimension(width, formatInfo.compressedBlockWidth, level) ||
|
||||
+ !ValidCompressedDimension(height, formatInfo.compressedBlockHeight, level) ||
|
||||
+ !ValidCompressedDimension(depth, formatInfo.compressedBlockDepth, level))
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
index 40c9a02e3eafe9d437e20e72a391bad73c783516..fabaefbe33c94a048ec7c974951ac9ef886826ef 100644
|
||||
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
|
||||
@@ -296,6 +296,16 @@ void main()
|
||||
GLsizei blockSize,
|
||||
const std::string &extName,
|
||||
bool subImageAllowed);
|
||||
+
|
||||
+ GLint expectedByteLength(GLenum format, GLsizei width, GLsizei height);
|
||||
+ void testCompressedTexLevelDimension(GLenum format,
|
||||
+ GLint level,
|
||||
+ GLsizei width,
|
||||
+ GLsizei height,
|
||||
+ GLsizei expectedByteLength,
|
||||
+ GLenum expectedError,
|
||||
+ const char *explanation);
|
||||
+ void testCompressedTexImage(GLenum format);
|
||||
};
|
||||
|
||||
class WebGL2CompatibilityTest : public WebGLCompatibilityTest
|
||||
@@ -3040,6 +3050,84 @@ TEST_P(WebGLCompatibilityTest, CompressedTextureS3TC)
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
|
||||
+// Test WebGL-specific constraints on sizes of S3TC textures' mipmap levels.
|
||||
+TEST_P(WebGLCompatibilityTest, CompressedTexImageS3TC)
|
||||
+{
|
||||
+ const char *extensions[] = {
|
||||
+ "GL_EXT_texture_compression_dxt1",
|
||||
+ "GL_ANGLE_texture_compression_dxt3",
|
||||
+ "GL_ANGLE_texture_compression_dxt5",
|
||||
+ };
|
||||
+
|
||||
+ for (const char *extension : extensions)
|
||||
+ {
|
||||
+ if (IsGLExtensionRequestable(extension))
|
||||
+ {
|
||||
+ glRequestExtensionANGLE(extension);
|
||||
+ }
|
||||
+
|
||||
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(extension));
|
||||
+ }
|
||||
+
|
||||
+ // Ported from WebGL conformance suite:
|
||||
+ // sdk/tests/conformance/extensions/s3tc-and-srgb.html
|
||||
+ constexpr GLenum formats[] = {
|
||||
+ GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
|
||||
+ GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
|
||||
+ GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
|
||||
+ GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
|
||||
+ };
|
||||
+
|
||||
+ for (GLenum format : formats)
|
||||
+ {
|
||||
+ testCompressedTexImage(format);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// Test WebGL-specific constraints on sizes of RGTC textures' mipmap levels.
|
||||
+TEST_P(WebGLCompatibilityTest, CompressedTexImageRGTC)
|
||||
+{
|
||||
+ if (IsGLExtensionRequestable("GL_EXT_texture_compression_rgtc"))
|
||||
+ {
|
||||
+ glRequestExtensionANGLE("GL_EXT_texture_compression_rgtc");
|
||||
+ }
|
||||
+
|
||||
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_compression_rgtc"));
|
||||
+
|
||||
+ // Ported from WebGL conformance suite:
|
||||
+ // sdk/tests/conformance/extensions/ext-texture-compression-rgtc.html
|
||||
+ constexpr GLenum formats[] = {GL_COMPRESSED_RED_RGTC1_EXT, GL_COMPRESSED_SIGNED_RED_RGTC1_EXT,
|
||||
+ GL_COMPRESSED_RED_GREEN_RGTC2_EXT,
|
||||
+ GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT};
|
||||
+
|
||||
+ for (GLenum format : formats)
|
||||
+ {
|
||||
+ testCompressedTexImage(format);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// Test WebGL-specific constraints on sizes of BPTC textures' mipmap levels.
|
||||
+TEST_P(WebGLCompatibilityTest, CompressedTexImageBPTC)
|
||||
+{
|
||||
+ if (IsGLExtensionRequestable("GL_EXT_texture_compression_bptc"))
|
||||
+ {
|
||||
+ glRequestExtensionANGLE("GL_EXT_texture_compression_bptc");
|
||||
+ }
|
||||
+
|
||||
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_compression_bptc"));
|
||||
+
|
||||
+ // Ported from WebGL conformance suite:
|
||||
+ // sdk/tests/conformance/extensions/ext-texture-compression-bptc.html
|
||||
+ constexpr GLenum formats[] = {
|
||||
+ GL_COMPRESSED_RGBA_BPTC_UNORM_EXT, GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT,
|
||||
+ GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT, GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT};
|
||||
+
|
||||
+ for (GLenum format : formats)
|
||||
+ {
|
||||
+ testCompressedTexImage(format);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
TEST_P(WebGLCompatibilityTest, L32FTextures)
|
||||
{
|
||||
constexpr float textureData[] = {15.1f, 0.0f, 0.0f, 0.0f};
|
||||
@@ -4937,6 +5025,119 @@ void WebGLCompatibilityTest::validateCompressedTexImageExtensionFormat(GLenum fo
|
||||
}
|
||||
}
|
||||
|
||||
+GLint WebGLCompatibilityTest::expectedByteLength(GLenum format, GLsizei width, GLsizei height)
|
||||
+{
|
||||
+ switch (format)
|
||||
+ {
|
||||
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
|
||||
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
+ case GL_COMPRESSED_RED_RGTC1_EXT:
|
||||
+ case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
|
||||
+ return ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
+ case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
|
||||
+ case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
|
||||
+ case GL_COMPRESSED_RGBA_BPTC_UNORM_EXT:
|
||||
+ case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:
|
||||
+ case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT:
|
||||
+ case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT:
|
||||
+ return ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||
+ }
|
||||
+
|
||||
+ UNREACHABLE();
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void WebGLCompatibilityTest::testCompressedTexLevelDimension(GLenum format,
|
||||
+ GLint level,
|
||||
+ GLsizei width,
|
||||
+ GLsizei height,
|
||||
+ GLsizei expectedByteLength,
|
||||
+ GLenum expectedError,
|
||||
+ const char *explanation)
|
||||
+{
|
||||
+ std::vector<uint8_t> tempVector(expectedByteLength, 0);
|
||||
+
|
||||
+ EXPECT_GL_NO_ERROR();
|
||||
+
|
||||
+ GLTexture sourceTexture;
|
||||
+ glBindTexture(GL_TEXTURE_2D, sourceTexture);
|
||||
+ glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, expectedByteLength,
|
||||
+ tempVector.data());
|
||||
+ if (expectedError == 0)
|
||||
+ {
|
||||
+ EXPECT_GL_NO_ERROR() << explanation;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ EXPECT_GL_ERROR(expectedError) << explanation;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void WebGLCompatibilityTest::testCompressedTexImage(GLenum format)
|
||||
+{
|
||||
+ struct TestCase
|
||||
+ {
|
||||
+ GLint level;
|
||||
+ GLsizei width;
|
||||
+ GLsizei height;
|
||||
+ GLenum expectedError;
|
||||
+ const char *explanation;
|
||||
+ };
|
||||
+
|
||||
+ constexpr TestCase testCases[] = {
|
||||
+ {0, 4, 3, GL_INVALID_OPERATION, "level is 0, height is not a multiple of 4"},
|
||||
+ {0, 3, 4, GL_INVALID_OPERATION, "level is 0, width is not a multiple of 4"},
|
||||
+ {0, 2, 2, GL_INVALID_OPERATION, "level is 0, width is not a multiple of 4"},
|
||||
+ {0, 4, 4, GL_NO_ERROR, "is valid"},
|
||||
+ {1, 1, 1, GL_INVALID_OPERATION, "implied base mip 2x2 is invalid"},
|
||||
+ {1, 1, 2, GL_INVALID_OPERATION, "implied base mip 2x4 is invalid"},
|
||||
+ {1, 2, 1, GL_INVALID_OPERATION, "implied base mip 4x2 is invalid"},
|
||||
+ {1, 2, 2, GL_NO_ERROR, "implied base mip 4x4 is valid"},
|
||||
+ };
|
||||
+
|
||||
+ constexpr TestCase webgl2TestCases[] = {
|
||||
+ {0, 0, 0, GL_NO_ERROR, "0: 0x0 is valid"},
|
||||
+ {0, 1, 1, GL_INVALID_OPERATION, "0: 1x1 is invalid"},
|
||||
+ {0, 2, 2, GL_INVALID_OPERATION, "0: 2x2 is invalid"},
|
||||
+ {0, 3, 3, GL_INVALID_OPERATION, "0: 3x3 is invalid"},
|
||||
+ {0, 10, 10, GL_INVALID_OPERATION, "0: 10x10 is invalid"},
|
||||
+ {0, 11, 11, GL_INVALID_OPERATION, "0: 11x11 is invalid"},
|
||||
+ {0, 11, 12, GL_INVALID_OPERATION, "0: 11x12 is invalid"},
|
||||
+ {0, 12, 11, GL_INVALID_OPERATION, "0: 12x11 is invalid"},
|
||||
+ {0, 12, 12, GL_NO_ERROR, "0: 12x12 is valid"},
|
||||
+ {1, 0, 0, GL_NO_ERROR, "1: 0x0 is valid"},
|
||||
+ {1, 3, 3, GL_INVALID_OPERATION, "1: 3x3 is invalid"},
|
||||
+ {1, 5, 5, GL_INVALID_OPERATION, "1: 5x5 is invalid"},
|
||||
+ {1, 5, 6, GL_INVALID_OPERATION, "1: 5x6 is invalid"},
|
||||
+ {1, 6, 5, GL_INVALID_OPERATION, "1: 6x5 is invalid"},
|
||||
+ {1, 6, 6, GL_NO_ERROR, "1: 6x6 is valid"},
|
||||
+ {2, 0, 0, GL_NO_ERROR, "2: 0x0 is valid"},
|
||||
+ {2, 3, 3, GL_NO_ERROR, "2: 3x3 is valid"},
|
||||
+ {3, 1, 3, GL_NO_ERROR, "3: 1x3 is valid"},
|
||||
+ {3, 1, 1, GL_NO_ERROR, "3: 1x1 is valid"},
|
||||
+ {2, 1, 3, GL_NO_ERROR, "implied base mip 4x12 is valid"},
|
||||
+ };
|
||||
+
|
||||
+ for (const TestCase &test : testCases)
|
||||
+ {
|
||||
+ testCompressedTexLevelDimension(format, test.level, test.width, test.height,
|
||||
+ expectedByteLength(format, test.width, test.height),
|
||||
+ test.expectedError, test.explanation);
|
||||
+ }
|
||||
+
|
||||
+ if (getClientMajorVersion() >= 3)
|
||||
+ {
|
||||
+ for (const TestCase &test : webgl2TestCases)
|
||||
+ {
|
||||
+ testCompressedTexLevelDimension(format, test.level, test.width, test.height,
|
||||
+ expectedByteLength(format, test.width, test.height),
|
||||
+ test.expectedError, test.explanation);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
// Test enabling GL_EXT_texture_compression_dxt1 for GL_COMPRESSED_RGB_S3TC_DXT1_EXT
|
||||
TEST_P(WebGLCompatibilityTest, EnableCompressedTextureExtensionDXT1RGB)
|
||||
{
|
||||
@@ -1,2 +1,3 @@
|
||||
expose_ripemd160.patch
|
||||
expose_aes-cfb.patch
|
||||
expose_des-ede3.patch
|
||||
|
||||
40
patches/boringssl/expose_des-ede3.patch
Normal file
40
patches/boringssl/expose_des-ede3.patch
Normal file
@@ -0,0 +1,40 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Rose <nornagon@nornagon.net>
|
||||
Date: Wed, 24 Feb 2021 11:08:34 -0800
|
||||
Subject: expose des-ede3
|
||||
|
||||
This should be upstreamed.
|
||||
|
||||
diff --git a/crypto/cipher_extra/cipher_extra.c b/crypto/cipher_extra/cipher_extra.c
|
||||
index 588a4773437c311877f275bf3679f9688cda3c46..e771ed6589b4579cc35300d5b2a1b68d92e444f5 100644
|
||||
--- a/crypto/cipher_extra/cipher_extra.c
|
||||
+++ b/crypto/cipher_extra/cipher_extra.c
|
||||
@@ -93,6 +93,8 @@ const EVP_CIPHER *EVP_get_cipherbyname(const char *name) {
|
||||
return EVP_rc4();
|
||||
} else if (OPENSSL_strcasecmp(name, "des-cbc") == 0) {
|
||||
return EVP_des_cbc();
|
||||
+ } else if (OPENSSL_strcasecmp(name, "des-ede3") == 0) {
|
||||
+ return EVP_des_ede3();
|
||||
} else if (OPENSSL_strcasecmp(name, "des-ede3-cbc") == 0 ||
|
||||
// This is not a name used by OpenSSL, but tcpdump registers it
|
||||
// with |EVP_add_cipher_alias|. Our |EVP_add_cipher_alias| is a
|
||||
diff --git a/decrepit/evp/evp_do_all.c b/decrepit/evp/evp_do_all.c
|
||||
index 84af06fc56e4aa72d4d48801d7c037add0221747..fe412e350f43ad20758025da6b9754952d164938 100644
|
||||
--- a/decrepit/evp/evp_do_all.c
|
||||
+++ b/decrepit/evp/evp_do_all.c
|
||||
@@ -39,6 +39,7 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
callback(EVP_des_cbc(), "DES-CBC", NULL, arg);
|
||||
callback(EVP_des_ecb(), "DES-ECB", NULL, arg);
|
||||
callback(EVP_des_ede(), "DES-EDE", NULL, arg);
|
||||
+ callback(EVP_des_ede3(), "DES-EDE3", NULL, arg);
|
||||
callback(EVP_des_ede_cbc(), "DES-EDE-CBC", NULL, arg);
|
||||
callback(EVP_des_ede3_cbc(), "DES-EDE3-CBC", NULL, arg);
|
||||
callback(EVP_rc2_cbc(), "RC2-CBC", NULL, arg);
|
||||
@@ -65,6 +66,7 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
callback(EVP_des_cbc(), "des-cbc", NULL, arg);
|
||||
callback(EVP_des_ecb(), "des-ecb", NULL, arg);
|
||||
callback(EVP_des_ede(), "des-ede", NULL, arg);
|
||||
+ callback(EVP_des_ede3(), "des-ede3", NULL, arg);
|
||||
callback(EVP_des_ede_cbc(), "des-ede-cbc", NULL, arg);
|
||||
callback(EVP_des_ede3_cbc(), "des-ede3-cbc", NULL, arg);
|
||||
callback(EVP_rc2_cbc(), "rc2-cbc", NULL, arg);
|
||||
@@ -102,6 +102,7 @@ fix_export_zlib_symbols.patch
|
||||
don_t_use_potentially_null_getwebframe_-_view_when_get_blink.patch
|
||||
web_contents.patch
|
||||
fix_route_mouse_event_navigations_through_the_web_contents_delegate.patch
|
||||
webview_fullscreen.patch
|
||||
disable_unload_metrics.patch
|
||||
fix_add_check_for_sandbox_then_result.patch
|
||||
moves_background_color_setter_of_webview_to_blinks_webprefs_logic.patch
|
||||
@@ -110,3 +111,17 @@ add_setter_for_browsermainloop_result_code.patch
|
||||
cherry-pick-8089dbfc616f.patch
|
||||
x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch
|
||||
build_libc_as_static_library.patch
|
||||
cherry-pick-3299d70b7d0f.patch
|
||||
support_runtime_configurable_key_storage_on_linux_os_crypto.patch
|
||||
make_keychain_service_account_optionally_configurable_at_runtime.patch
|
||||
don_t_run_pcscan_notifythreadcreated_if_pcscan_is_disabled.patch
|
||||
cherry-pick-cc20b36a5845.patch
|
||||
set_svgimage_page_after_document_install.patch
|
||||
cherry-pick-5487040a284a.patch
|
||||
cherry-pick-e60cc80ff744.patch
|
||||
add_gin_wrappable_crash_key.patch
|
||||
cherry-pick-cd98d7c0dae9.patch
|
||||
cherry-pick-ac9dc1235e28.patch
|
||||
cherry-pick-4ce2abc17078.patch
|
||||
cherry-pick-e2123a8e0943.patch
|
||||
cherry-pick-1227933.patch
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Cheng Zhao <zcbenz@gmail.com>
|
||||
Date: Thu, 4 Oct 2018 14:57:02 -0700
|
||||
Subject: accelerator.patch
|
||||
Subject: fix: improve shortcut text of Accelerator
|
||||
|
||||
This patch makes three changes to Accelerator::GetShortcutText to improve shortcut display text in menus:
|
||||
|
||||
1. Ctrl-Alt-<Key> accelerators show as Ctrl-Alt-<Key> instead of as Ctrl-<Key>
|
||||
2. F2-F24 accelerators show up as such
|
||||
3. Ctrl-Shift-= should show as Ctrl-+
|
||||
3. Ctrl-Shift-= and Ctrl-Plus show up as such
|
||||
|
||||
diff --git a/ui/base/accelerators/accelerator.cc b/ui/base/accelerators/accelerator.cc
|
||||
index c44f3d3752025bd3f11db790a97a48e8ba856034..8e0c1446315823a391614b19aa2c4ba2e5faed0d 100644
|
||||
index c44f3d3752025bd3f11db790a97a48e8ba856034..8b1859b18aa7ebcecf4c2a90b4ced0186d258ddc 100644
|
||||
--- a/ui/base/accelerators/accelerator.cc
|
||||
+++ b/ui/base/accelerators/accelerator.cc
|
||||
@@ -11,6 +11,7 @@
|
||||
@@ -21,61 +21,39 @@ index c44f3d3752025bd3f11db790a97a48e8ba856034..8e0c1446315823a391614b19aa2c4ba2
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "build/chromeos_buildflags.h"
|
||||
@@ -27,9 +28,7 @@
|
||||
#include <windows.h>
|
||||
@@ -209,6 +210,11 @@ std::u16string Accelerator::GetShortcutText() const {
|
||||
#endif
|
||||
|
||||
-#if !defined(OS_WIN) && (defined(USE_AURA) || defined(OS_MAC))
|
||||
#include "ui/events/keycodes/keyboard_code_conversion.h"
|
||||
-#endif
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
#include "ui/base/ui_base_features.h"
|
||||
@@ -208,7 +207,15 @@ std::u16string Accelerator::GetShortcutText() const {
|
||||
shortcut = KeyCodeToName();
|
||||
#endif
|
||||
|
||||
+ unsigned int flags = 0;
|
||||
if (shortcut.empty()) {
|
||||
+ const uint16_t c = DomCodeToUsLayoutCharacter(
|
||||
+ UsLayoutKeyboardCodeToDomCode(key_code_), flags);
|
||||
+ if (c != 0) {
|
||||
+ shortcut =
|
||||
+ static_cast<std::u16string::value_type>(
|
||||
+ base::ToUpperASCII(static_cast<char16_t>(c)));
|
||||
+ }
|
||||
+ // When a shifted char is explicitly specified, for example Ctrl+Plus,
|
||||
+ // use the shifted char directly.
|
||||
+ if (shifted_char) {
|
||||
+ shortcut += *shifted_char;
|
||||
+ } else {
|
||||
#if defined(OS_WIN)
|
||||
// Our fallback is to try translate the key code to a regular character
|
||||
// unless it is one of digits (VK_0 to VK_9). Some keyboard
|
||||
@@ -217,21 +224,14 @@ std::u16string Accelerator::GetShortcutText() const {
|
||||
// accent' for '0'). For display in the menu (e.g. Ctrl-0 for the
|
||||
// default zoom level), we leave VK_[0-9] alone without translation.
|
||||
wchar_t key;
|
||||
- if (base::IsAsciiDigit(key_code_))
|
||||
+ if (base::IsAsciiDigit(key_code_)) {
|
||||
key = static_cast<wchar_t>(key_code_);
|
||||
- else
|
||||
- key = LOWORD(::MapVirtualKeyW(key_code_, MAPVK_VK_TO_CHAR));
|
||||
- // If there is no translation for the given |key_code_| (e.g.
|
||||
- // VKEY_UNKNOWN), |::MapVirtualKeyW| returns 0.
|
||||
- if (key != 0)
|
||||
- shortcut += key;
|
||||
-#elif defined(USE_AURA) || defined(OS_MAC) || defined(OS_ANDROID)
|
||||
- const uint16_t c = DomCodeToUsLayoutCharacter(
|
||||
- UsLayoutKeyboardCodeToDomCode(key_code_), false);
|
||||
- if (c != 0)
|
||||
- shortcut +=
|
||||
- static_cast<std::u16string::value_type>(base::ToUpperASCII(c));
|
||||
+ shortcut = key;
|
||||
+ }
|
||||
@@ -232,6 +238,10 @@ std::u16string Accelerator::GetShortcutText() const {
|
||||
shortcut +=
|
||||
static_cast<std::u16string::value_type>(base::ToUpperASCII(c));
|
||||
#endif
|
||||
+ }
|
||||
+ if (key_code_ > VKEY_F1 && key_code_ <= VKEY_F24)
|
||||
+ shortcut = base::UTF8ToUTF16(
|
||||
+ base::StringPrintf("F%d", key_code_ - VKEY_F1 + 1));
|
||||
}
|
||||
|
||||
#if defined(OS_MAC)
|
||||
@@ -427,7 +427,7 @@ std::u16string Accelerator::ApplyLongFormModifiers(
|
||||
@@ -419,7 +429,7 @@ std::u16string Accelerator::ApplyLongFormModifiers(
|
||||
const std::u16string& shortcut) const {
|
||||
std::u16string result = shortcut;
|
||||
|
||||
- if (IsShiftDown())
|
||||
+ if (!shifted_char && IsShiftDown())
|
||||
result = ApplyModifierToAcceleratorString(result, IDS_APP_SHIFT_KEY);
|
||||
|
||||
// Note that we use 'else-if' in order to avoid using Ctrl+Alt as a shortcut.
|
||||
@@ -427,7 +437,7 @@ std::u16string Accelerator::ApplyLongFormModifiers(
|
||||
// more information.
|
||||
if (IsCtrlDown())
|
||||
result = ApplyModifierToAcceleratorString(result, IDS_APP_CTRL_KEY);
|
||||
@@ -84,3 +62,24 @@ index c44f3d3752025bd3f11db790a97a48e8ba856034..8e0c1446315823a391614b19aa2c4ba2
|
||||
result = ApplyModifierToAcceleratorString(result, IDS_APP_ALT_KEY);
|
||||
|
||||
if (IsCmdDown()) {
|
||||
diff --git a/ui/base/accelerators/accelerator.h b/ui/base/accelerators/accelerator.h
|
||||
index 62eacbe23f949cf62b86a775d331459ae3934048..008b753809f34b72d0c1de46916c0d36eb90a8f5 100644
|
||||
--- a/ui/base/accelerators/accelerator.h
|
||||
+++ b/ui/base/accelerators/accelerator.h
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/component_export.h"
|
||||
+#include "base/optional.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
#include "ui/events/event_constants.h"
|
||||
@@ -100,6 +101,8 @@ class COMPONENT_EXPORT(UI_BASE) Accelerator {
|
||||
return interrupted_by_mouse_event_;
|
||||
}
|
||||
|
||||
+ base::Optional<char16_t> shifted_char;
|
||||
+
|
||||
private:
|
||||
std::u16string ApplyLongFormModifiers(const std::u16string& shortcut) const;
|
||||
std::u16string ApplyShortFormModifiers(const std::u16string& shortcut) const;
|
||||
|
||||
48
patches/chromium/add_gin_wrappable_crash_key.patch
Normal file
48
patches/chromium/add_gin_wrappable_crash_key.patch
Normal file
@@ -0,0 +1,48 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: VerteDinde <khammond@slack-corp.com>
|
||||
Date: Thu, 15 Jul 2021 12:16:50 -0700
|
||||
Subject: chore: add gin::wrappable wrapperinfo crash key
|
||||
|
||||
This patch adds an additional crash key for gin::Wrappable, to help
|
||||
debug a crash that is occurring during garbage collection in SecondWeakCallback.
|
||||
|
||||
The crash seems to be due to a class that is holding a reference to
|
||||
gin::Wrappable even after being deleted. This added crash key compares
|
||||
the soon-to-be-deleted WrapperInfo with known WrapperInfo components to
|
||||
help determine where the crash originated from.
|
||||
|
||||
This patch should not be upstreamed, and can be removed in Electron 15 and
|
||||
beyond once we identify the cause of the crash.
|
||||
|
||||
diff --git a/gin/wrappable.cc b/gin/wrappable.cc
|
||||
index fe07eb94a8e679859bba6d76ff0d6ee86bd0c67e..ecb0aa2c4ec57e1814f4c94194e775440f4e35ee 100644
|
||||
--- a/gin/wrappable.cc
|
||||
+++ b/gin/wrappable.cc
|
||||
@@ -8,6 +8,11 @@
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "gin/per_isolate_data.h"
|
||||
|
||||
+#if !defined(MAS_BUILD)
|
||||
+#include "components/crash/core/common/crash_key.h"
|
||||
+#include "electron/shell/common/crash_keys.h"
|
||||
+#endif
|
||||
+
|
||||
namespace gin {
|
||||
|
||||
WrappableBase::WrappableBase() = default;
|
||||
@@ -36,6 +41,15 @@ void WrappableBase::FirstWeakCallback(
|
||||
void WrappableBase::SecondWeakCallback(
|
||||
const v8::WeakCallbackInfo<WrappableBase>& data) {
|
||||
WrappableBase* wrappable = data.GetParameter();
|
||||
+
|
||||
+#if !defined(MAS_BUILD)
|
||||
+ WrapperInfo* wrapperInfo = static_cast<WrapperInfo*>(data.GetInternalField(0));
|
||||
+ std::string location = electron::crash_keys::GetCrashValueForGinWrappable(wrapperInfo);
|
||||
+
|
||||
+ static crash_reporter::CrashKeyString<32> crash_key("gin-wrappable-fatal.location");
|
||||
+ crash_reporter::ScopedCrashKeyString auto_clear(&crash_key, location);
|
||||
+#endif
|
||||
+
|
||||
delete wrappable;
|
||||
}
|
||||
|
||||
@@ -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 08a3eb686360e7a53cca75437cbe5f4d15cb9a17..78d3287ef7a708f44bd8ef0290618ef781f09d9c 100644
|
||||
index 7f7e6adf57128f1e8b0890aa14102fe4db452cb2..59d855ec17efe9117ee9bca5074b1ab28dfb40fa 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -5440,6 +5440,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
@@ -5430,6 +5430,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,
|
||||
|
||||
215
patches/chromium/cherry-pick-1227933.patch
Normal file
215
patches/chromium/cherry-pick-1227933.patch
Normal file
@@ -0,0 +1,215 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Koji Ishii <kojii@chromium.org>
|
||||
Date: Mon, 26 Jul 2021 07:09:18 +0000
|
||||
Subject: Fix nested inline box fragmentation
|
||||
|
||||
This patch fixes when nested inline boxes are fragmented in a
|
||||
line due to bidi reordering.
|
||||
|
||||
Before this change, the fragmented boxes are appended to the
|
||||
end of |box_data_list_|. Then when |NGInlineLayoutStateStack::
|
||||
CreateBoxFragments| creates inline boxes in the ascending
|
||||
order of |box_data_list_|, it failed to add the fragmented
|
||||
boxes into their parent inline boxes.
|
||||
|
||||
This is critical for out-of-flow positioned objects whose
|
||||
containing block is an inline box, because they expect to be
|
||||
propagated through all ancestor inline boxes.
|
||||
|
||||
|UpdateBoxDataFragmentRange| is a little tricky by appending
|
||||
to a vector it is iterating. Changing it to insert to the
|
||||
correct position makes the function even trickier. This patch
|
||||
changes it to add fragmented boxes to a separate vector, and
|
||||
let later process |UpdateFragmentedBoxDataEdges| to merge the
|
||||
vector to |box_data_list_|.
|
||||
|
||||
(cherry picked from commit 9c8a39c14a9c80556468593cddf436f5047a16ce)
|
||||
|
||||
Bug: 1227933, 1229999
|
||||
Change-Id: I7edcd209e1fdac06bab01b16d660383e7e9c37bd
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3038308
|
||||
Commit-Queue: Koji Ishii <kojii@chromium.org>
|
||||
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#903356}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3053212
|
||||
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Auto-Submit: Koji Ishii <kojii@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4577@{#145}
|
||||
Cr-Branched-From: 761ddde228655e313424edec06497d0c56b0f3c4-refs/heads/master@{#902210}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc
|
||||
index b257014513cb881d4531694b86c05fd21edb6732..9a3f6f3af7839ebed24f7d8a32b7f95fba66cd9a 100644
|
||||
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc
|
||||
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h"
|
||||
|
||||
+#include "base/containers/adapters.h"
|
||||
#include "third_party/blink/renderer/core/layout/geometry/logical_offset.h"
|
||||
#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
|
||||
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h"
|
||||
@@ -387,13 +388,14 @@ void NGInlineLayoutStateStack::UpdateAfterReorder(
|
||||
box_data.fragment_start = box_data.fragment_end = 0;
|
||||
|
||||
// Scan children and update start/end from their box_data_index.
|
||||
- unsigned box_count = box_data_list_.size();
|
||||
+ Vector<BoxData> fragmented_boxes;
|
||||
for (unsigned index = 0; index < line_box->size();)
|
||||
- index = UpdateBoxDataFragmentRange(line_box, index);
|
||||
+ index = UpdateBoxDataFragmentRange(line_box, index, &fragmented_boxes);
|
||||
|
||||
- // If any inline fragmentation due to BiDi reorder, adjust box edges.
|
||||
- if (box_count != box_data_list_.size())
|
||||
- UpdateFragmentedBoxDataEdges();
|
||||
+ // If any inline fragmentation occurred due to BiDi reorder, append them and
|
||||
+ // adjust box edges.
|
||||
+ if (UNLIKELY(!fragmented_boxes.IsEmpty()))
|
||||
+ UpdateFragmentedBoxDataEdges(&fragmented_boxes);
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
// Check all BoxData have ranges.
|
||||
@@ -410,7 +412,8 @@ void NGInlineLayoutStateStack::UpdateAfterReorder(
|
||||
|
||||
unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
|
||||
NGLogicalLineItems* line_box,
|
||||
- unsigned index) {
|
||||
+ unsigned index,
|
||||
+ Vector<BoxData>* fragmented_boxes) {
|
||||
// Find the first line box item that should create a box fragment.
|
||||
for (; index < line_box->size(); index++) {
|
||||
NGLogicalLineItem* start = &(*line_box)[index];
|
||||
@@ -438,7 +441,7 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
|
||||
// It also changes other BoxData, but not the one we're dealing with here
|
||||
// because the update is limited only when its |box_data_index| is lower.
|
||||
while (end->box_data_index && end->box_data_index < box_data_index) {
|
||||
- UpdateBoxDataFragmentRange(line_box, index);
|
||||
+ UpdateBoxDataFragmentRange(line_box, index, fragmented_boxes);
|
||||
}
|
||||
|
||||
if (box_data_index != end->box_data_index)
|
||||
@@ -453,14 +456,9 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
|
||||
} else {
|
||||
// This box is fragmented by BiDi reordering. Add a new BoxData for the
|
||||
// fragmented range.
|
||||
- box_data_list_[box_data_index - 1].fragmented_box_data_index =
|
||||
- box_data_list_.size();
|
||||
- // Do not use `emplace_back()` here because adding to |box_data_list_| may
|
||||
- // reallocate the buffer, but the `BoxData` ctor must run before the
|
||||
- // reallocation. Create a new instance and |push_back()| instead.
|
||||
- BoxData fragmented_box_data(box_data_list_[box_data_index - 1],
|
||||
- start_index, index);
|
||||
- box_data_list_.push_back(fragmented_box_data);
|
||||
+ BoxData& fragmented_box = fragmented_boxes->emplace_back(
|
||||
+ box_data_list_[box_data_index - 1], start_index, index);
|
||||
+ fragmented_box.fragmented_box_data_index = box_data_index;
|
||||
}
|
||||
// If this box has parent boxes, we need to process it again.
|
||||
if (box_data_list_[box_data_index - 1].parent_box_data_index)
|
||||
@@ -470,7 +468,43 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
|
||||
return index;
|
||||
}
|
||||
|
||||
-void NGInlineLayoutStateStack::UpdateFragmentedBoxDataEdges() {
|
||||
+void NGInlineLayoutStateStack::UpdateFragmentedBoxDataEdges(
|
||||
+ Vector<BoxData>* fragmented_boxes) {
|
||||
+ DCHECK(!fragmented_boxes->IsEmpty());
|
||||
+ // Append in the descending order of |fragmented_box_data_index| because the
|
||||
+ // indices will change as boxes are inserted into |box_data_list_|.
|
||||
+ std::sort(fragmented_boxes->begin(), fragmented_boxes->end(),
|
||||
+ [](const BoxData& a, const BoxData& b) {
|
||||
+ if (a.fragmented_box_data_index != b.fragmented_box_data_index) {
|
||||
+ return a.fragmented_box_data_index <
|
||||
+ b.fragmented_box_data_index;
|
||||
+ }
|
||||
+ DCHECK_NE(a.fragment_start, b.fragment_start);
|
||||
+ return a.fragment_start < b.fragment_start;
|
||||
+ });
|
||||
+ for (BoxData& fragmented_box : base::Reversed(*fragmented_boxes)) {
|
||||
+ // Insert the fragmented box to right after the box it was fragmented from.
|
||||
+ // The order in the |box_data_list_| is critical when propagating child
|
||||
+ // fragment data such as OOF to ancestors.
|
||||
+ const unsigned insert_at = fragmented_box.fragmented_box_data_index;
|
||||
+ DCHECK_GT(insert_at, 0u);
|
||||
+ fragmented_box.fragmented_box_data_index = 0;
|
||||
+ box_data_list_.insert(insert_at, fragmented_box);
|
||||
+
|
||||
+ // Adjust box data indices by the insertion.
|
||||
+ for (BoxData& box_data : box_data_list_) {
|
||||
+ if (box_data.fragmented_box_data_index >= insert_at)
|
||||
+ ++box_data.fragmented_box_data_index;
|
||||
+ }
|
||||
+
|
||||
+ // Set the index of the last fragment to the original box. This is needed to
|
||||
+ // update fragment edges.
|
||||
+ const unsigned fragmented_from = insert_at - 1;
|
||||
+ if (!box_data_list_[fragmented_from].fragmented_box_data_index)
|
||||
+ box_data_list_[fragmented_from].fragmented_box_data_index = insert_at;
|
||||
+ }
|
||||
+
|
||||
+ // Move the line-right edge to the last fragment.
|
||||
for (BoxData& box_data : box_data_list_) {
|
||||
if (box_data.fragmented_box_data_index)
|
||||
box_data.UpdateFragmentEdges(box_data_list_);
|
||||
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h
|
||||
index 82ecfef8fe4d404d5713f0f67d83b38ecfbfca4c..9d079266efd7f2ccc43cef40d8d89e4fc6edda9e 100644
|
||||
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h
|
||||
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h
|
||||
@@ -156,17 +156,6 @@ class CORE_EXPORT NGInlineLayoutStateStack {
|
||||
// reordering.
|
||||
void UpdateAfterReorder(NGLogicalLineItems*);
|
||||
|
||||
- // Update start/end of the first BoxData found at |index|.
|
||||
- //
|
||||
- // If inline fragmentation is found, a new BoxData is added.
|
||||
- //
|
||||
- // Returns the index to process next. It should be given to the next call to
|
||||
- // this function.
|
||||
- unsigned UpdateBoxDataFragmentRange(NGLogicalLineItems*, unsigned index);
|
||||
-
|
||||
- // Update edges of inline fragmented boxes.
|
||||
- void UpdateFragmentedBoxDataEdges();
|
||||
-
|
||||
// Compute inline positions of fragments and boxes.
|
||||
LayoutUnit ComputeInlinePositions(NGLogicalLineItems*, LayoutUnit position);
|
||||
|
||||
@@ -259,6 +248,19 @@ class CORE_EXPORT NGInlineLayoutStateStack {
|
||||
scoped_refptr<const NGLayoutResult> CreateBoxFragment(NGLogicalLineItems*);
|
||||
};
|
||||
|
||||
+ // Update start/end of the first BoxData found at |index|.
|
||||
+ //
|
||||
+ // If inline fragmentation is found, a new BoxData is added.
|
||||
+ //
|
||||
+ // Returns the index to process next. It should be given to the next call to
|
||||
+ // this function.
|
||||
+ unsigned UpdateBoxDataFragmentRange(NGLogicalLineItems*,
|
||||
+ unsigned index,
|
||||
+ Vector<BoxData>* fragmented_boxes);
|
||||
+
|
||||
+ // Update edges of inline fragmented boxes.
|
||||
+ void UpdateFragmentedBoxDataEdges(Vector<BoxData>* fragmented_boxes);
|
||||
+
|
||||
Vector<NGInlineBoxState, 4> stack_;
|
||||
Vector<BoxData, 4> box_data_list_;
|
||||
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/text/crashtests/bidi-inline-fragment-oof-crash.html b/third_party/blink/web_tests/external/wpt/css/CSS2/text/crashtests/bidi-inline-fragment-oof-crash.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b701d2b5688ace54aa99530c12fa8143f1e6a508
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/text/crashtests/bidi-inline-fragment-oof-crash.html
|
||||
@@ -0,0 +1,13 @@
|
||||
+<!DOCTYPE html>
|
||||
+<link rel="author" href="mailto:mstensho@chromium.org">
|
||||
+<link rel="help" href="https://crbug.com/1229999">
|
||||
+<div style="direction:rtl; width:500px">
|
||||
+ <span style="border:solid">
|
||||
+ <span style="position:relative">
|
||||
+ <div style="display:inline-block; width:1000%; height:10px"></div>
|
||||
+ <span dir="ltr">
|
||||
+ <div style="position:absolute"></div>
|
||||
+ </span>
|
||||
+ </span>
|
||||
+ </span>
|
||||
+</div>
|
||||
337
patches/chromium/cherry-pick-3299d70b7d0f.patch
Normal file
337
patches/chromium/cherry-pick-3299d70b7d0f.patch
Normal file
@@ -0,0 +1,337 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Hongchan Choi <hongchan@chromium.org>
|
||||
Date: Wed, 21 Apr 2021 01:58:33 +0000
|
||||
Subject: Make PushPullFIFO adapt to irregular audio callbacks from the browser
|
||||
process
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
For background and rationale:
|
||||
go/webaudio-worklet-glitch-aaudio (Googler only)
|
||||
|
||||
The current implementation of PushPullFIFO doesn’t dynamically increase
|
||||
or decrease the amount frames to keep in the FIFO. So it is vulnerable
|
||||
to callback bursts from the device thread and it does not recover from
|
||||
subsequent callbacks when underrun happens.
|
||||
|
||||
The proposed solution is to implement the “earmark” counter in
|
||||
PushPullFIFO, so it can dynamically adapt when the buffer
|
||||
underrun happens by increasing the frame amount to keep. This design is
|
||||
inspired by the buffer optimization technique [1] from the AAudio
|
||||
reference.
|
||||
|
||||
[1] https://developer.android.com/ndk/guides/audio/aaudio/aaudio#optimizing-performance
|
||||
|
||||
Tested on:
|
||||
https://googlechromelabs.github.io/web-audio-samples/audio-worklet/ [Android, MacOS]
|
||||
https://learningsynths.ableton.com/en/playground [Android, MacOS]
|
||||
https://mfcc64.github.io/0000001/audio-worklet-test.html [Android, MacOS]
|
||||
https://dev.anthum.com/audio-worklet/chrome-android-debug [Android, MacOS]
|
||||
|
||||
Potentially this patch might be able to fix:
|
||||
825823,1198540,1188901,1188104
|
||||
|
||||
Bug: 1090441,1173656,1181434,1188117
|
||||
Change-Id: I69e5f100caf2896e268234d41e4e18c3b1648719
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2797664
|
||||
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
|
||||
Reviewed-by: Raymond Toy <rtoy@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#874535}
|
||||
|
||||
diff --git a/third_party/blink/renderer/platform/audio/audio_destination.cc b/third_party/blink/renderer/platform/audio/audio_destination.cc
|
||||
index 126783eb5a6e4f738555d49803dc908c39d101ed..a16975a4ece7a8e8fd8e9870360e815efddcc920 100644
|
||||
--- a/third_party/blink/renderer/platform/audio/audio_destination.cc
|
||||
+++ b/third_party/blink/renderer/platform/audio/audio_destination.cc
|
||||
@@ -212,10 +212,10 @@ void AudioDestination::Render(const WebVector<float*>& destination_data,
|
||||
for (unsigned i = 0; i < number_of_output_channels_; ++i)
|
||||
output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames);
|
||||
|
||||
- size_t frames_to_render = fifo_->Pull(output_bus_.get(), number_of_frames);
|
||||
-
|
||||
- // Use the dual-thread rendering model if the AudioWorklet is activated.
|
||||
if (worklet_task_runner_) {
|
||||
+ // Use the dual-thread rendering if the AudioWorklet is activated.
|
||||
+ size_t frames_to_render =
|
||||
+ fifo_->PullAndUpdateEarmark(output_bus_.get(), number_of_frames);
|
||||
PostCrossThreadTask(
|
||||
*worklet_task_runner_, FROM_HERE,
|
||||
CrossThreadBindOnce(&AudioDestination::RequestRender,
|
||||
@@ -223,7 +223,8 @@ void AudioDestination::Render(const WebVector<float*>& destination_data,
|
||||
frames_to_render, delay, delay_timestamp,
|
||||
prior_frames_skipped));
|
||||
} else {
|
||||
- // Otherwise use the single-thread rendering with AudioDeviceThread.
|
||||
+ // Otherwise use the single-thread rendering.
|
||||
+ size_t frames_to_render = fifo_->Pull(output_bus_.get(), number_of_frames);
|
||||
RequestRender(number_of_frames, frames_to_render, delay,
|
||||
delay_timestamp, prior_frames_skipped);
|
||||
}
|
||||
@@ -316,6 +317,11 @@ void AudioDestination::StartWithWorkletTaskRunner(
|
||||
|
||||
if (device_state_ != DeviceState::kStopped)
|
||||
return;
|
||||
+
|
||||
+ // The dual-thread rendering kicks off, so updates the earmark frames
|
||||
+ // accordingly.
|
||||
+ fifo_->SetEarmarkFrames(callback_buffer_size_);
|
||||
+
|
||||
worklet_task_runner_ = std::move(worklet_task_runner);
|
||||
web_audio_device_->Start();
|
||||
SetDeviceState(DeviceState::kRunning);
|
||||
diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo.cc b/third_party/blink/renderer/platform/audio/push_pull_fifo.cc
|
||||
index f89e51ee5544d2c265d81c1247c4a16723d7e153..0dfc6d8707da99f6ceb9a58a78d67bcb1af10ee1 100644
|
||||
--- a/third_party/blink/renderer/platform/audio/push_pull_fifo.cc
|
||||
+++ b/third_party/blink/renderer/platform/audio/push_pull_fifo.cc
|
||||
@@ -210,6 +210,99 @@ size_t PushPullFIFO::Pull(AudioBus* output_bus, size_t frames_requested) {
|
||||
: 0;
|
||||
}
|
||||
|
||||
+size_t PushPullFIFO::PullAndUpdateEarmark(AudioBus* output_bus,
|
||||
+ size_t frames_requested) {
|
||||
+ TRACE_EVENT2("webaudio",
|
||||
+ "PushPullFIFO::PullAndUpdateEarmark",
|
||||
+ "frames_requested", frames_requested,
|
||||
+ "pull_count_", pull_count_);
|
||||
+
|
||||
+ CHECK(output_bus);
|
||||
+ SECURITY_CHECK(frames_requested <= output_bus->length());
|
||||
+
|
||||
+ MutexLocker locker(lock_);
|
||||
+ TRACE_EVENT0("webaudio", "PushPullFIFO::PullAndUpdateEarmark (under lock)");
|
||||
+
|
||||
+ SECURITY_CHECK(frames_requested <= fifo_length_);
|
||||
+ SECURITY_CHECK(index_read_ < fifo_length_);
|
||||
+
|
||||
+ // The frames available was not enough to fulfill |frames_requested|. Fill
|
||||
+ // the output buffer with silence and update |earmark_frames_|.
|
||||
+ if (frames_requested > frames_available_) {
|
||||
+ const size_t missing_frames = frames_requested - frames_available_;
|
||||
+
|
||||
+ if (underflow_count_++ < kMaxMessagesToLog) {
|
||||
+ LOG(WARNING) << "PushPullFIFO::PullAndUpdateEarmark"
|
||||
+ << "underflow while pulling ("
|
||||
+ << "underflowCount=" << underflow_count_
|
||||
+ << ", availableFrames=" << frames_available_
|
||||
+ << ", requestedFrames=" << frames_requested
|
||||
+ << ", fifoLength=" << fifo_length_ << ")";
|
||||
+ }
|
||||
+
|
||||
+ TRACE_EVENT2("webaudio",
|
||||
+ "PushPullFIFO::PullAndUpdateEarmark (underrun)",
|
||||
+ "missing frames", missing_frames,
|
||||
+ "underflow_count_", underflow_count_);
|
||||
+
|
||||
+ // We assume that the next |frames_requested| from |AudioOutputDevice| will
|
||||
+ // be the same.
|
||||
+ earmark_frames_ += frames_requested;
|
||||
+
|
||||
+ // |earmark_frames_| can't be bigger than the half of the FIFO size.
|
||||
+ if (earmark_frames_ > fifo_length_ * 0.5) {
|
||||
+ earmark_frames_ = fifo_length_ * 0.5;
|
||||
+ }
|
||||
+
|
||||
+ // Note that it silences when underrun happens now, and ship the remaining
|
||||
+ // frames in subsequent callbacks without silence in between.
|
||||
+ for (unsigned i = 0; i < fifo_bus_->NumberOfChannels(); ++i) {
|
||||
+ float* output_bus_channel = output_bus->Channel(i)->MutableData();
|
||||
+ memset(output_bus_channel, 0,
|
||||
+ frames_requested * sizeof(*output_bus_channel));
|
||||
+ }
|
||||
+
|
||||
+ // The producer (WebAudio) needs to prepare the next pull plus what's
|
||||
+ // missing.
|
||||
+ return frames_requested + missing_frames;
|
||||
+ }
|
||||
+
|
||||
+ const size_t remainder = fifo_length_ - index_read_;
|
||||
+ const size_t frames_to_fill = std::min(frames_available_, frames_requested);
|
||||
+
|
||||
+ for (unsigned i = 0; i < fifo_bus_->NumberOfChannels(); ++i) {
|
||||
+ const float* fifo_bus_channel = fifo_bus_->Channel(i)->Data();
|
||||
+ float* output_bus_channel = output_bus->Channel(i)->MutableData();
|
||||
+
|
||||
+ // Fill up the output bus with the available frames first.
|
||||
+ if (remainder >= frames_to_fill) {
|
||||
+ // The remainder is big enough for the frames to pull.
|
||||
+ memcpy(output_bus_channel, fifo_bus_channel + index_read_,
|
||||
+ frames_to_fill * sizeof(*fifo_bus_channel));
|
||||
+ } else {
|
||||
+ // The frames to pull is bigger than the remainder size.
|
||||
+ // Wrap around the index.
|
||||
+ memcpy(output_bus_channel, fifo_bus_channel + index_read_,
|
||||
+ remainder * sizeof(*fifo_bus_channel));
|
||||
+ memcpy(output_bus_channel + remainder, fifo_bus_channel,
|
||||
+ (frames_to_fill - remainder) * sizeof(*fifo_bus_channel));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Update the read index; wrap it around if necessary.
|
||||
+ index_read_ = (index_read_ + frames_to_fill) % fifo_length_;
|
||||
+
|
||||
+ // Update the number of frames in FIFO.
|
||||
+ frames_available_ -= frames_to_fill;
|
||||
+ DCHECK_EQ((index_read_ + frames_available_) % fifo_length_, index_write_);
|
||||
+
|
||||
+ pull_count_++;
|
||||
+
|
||||
+ // Ask the producer to fill the FIFO up to |earmark_frames_|.
|
||||
+ return earmark_frames_ > frames_available_
|
||||
+ ? earmark_frames_ - frames_available_ : 0;
|
||||
+}
|
||||
+
|
||||
const PushPullFIFOStateForTest PushPullFIFO::GetStateForTest() {
|
||||
MutexLocker locker(lock_);
|
||||
return {length(), NumberOfChannels(), frames_available_, index_read_,
|
||||
diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo.h b/third_party/blink/renderer/platform/audio/push_pull_fifo.h
|
||||
index 8b822b17f66bdc27a61d09bc272631846ea82dcb..ad34a8b0fc39cf566c971323a8dade13a53a1420 100644
|
||||
--- a/third_party/blink/renderer/platform/audio/push_pull_fifo.h
|
||||
+++ b/third_party/blink/renderer/platform/audio/push_pull_fifo.h
|
||||
@@ -71,18 +71,33 @@ class PLATFORM_EXPORT PushPullFIFO {
|
||||
// the request from the consumer without causing error, but with a glitch.
|
||||
size_t Pull(AudioBus* output_bus, size_t frames_requested);
|
||||
|
||||
+ // Pull and update |ear_mark_frames_| to make the dual thread rendering mode
|
||||
+ // (i.e. AudioWorklet) more smooth. The single thread rendering does not need
|
||||
+ // this treatment.
|
||||
+ size_t PullAndUpdateEarmark(AudioBus* output_bus, size_t frames_requested);
|
||||
+
|
||||
+ void SetEarmarkFrames(size_t earmark_frames) {
|
||||
+ DCHECK(IsMainThread());
|
||||
+ MutexLocker locker(lock_);
|
||||
+ earmark_frames_ = earmark_frames;
|
||||
+ }
|
||||
+
|
||||
size_t length() const { return fifo_length_; }
|
||||
unsigned NumberOfChannels() const {
|
||||
lock_.AssertAcquired();
|
||||
return fifo_bus_->NumberOfChannels();
|
||||
}
|
||||
|
||||
- // TODO(hongchan): For single thread unit test only. Consider refactoring.
|
||||
AudioBus* GetFIFOBusForTest() {
|
||||
MutexLocker locker(lock_);
|
||||
return fifo_bus_.get();
|
||||
}
|
||||
|
||||
+ size_t GetEarmarkFramesForTest() {
|
||||
+ MutexLocker locker(lock_);
|
||||
+ return earmark_frames_;
|
||||
+ }
|
||||
+
|
||||
// For single thread unit test only. Get the current configuration that
|
||||
// consists of FIFO length, number of channels, read/write index position and
|
||||
// under/overflow count.
|
||||
@@ -101,6 +116,11 @@ class PLATFORM_EXPORT PushPullFIFO {
|
||||
unsigned underflow_count_ = 0;
|
||||
|
||||
Mutex lock_;
|
||||
+
|
||||
+ // To adapt the unstable callback timing. Every buffer underrun from
|
||||
+ // PullAndUpdateEarmark() will increase this number.
|
||||
+ size_t earmark_frames_ GUARDED_BY(lock_) = 0;
|
||||
+
|
||||
// The number of frames in the FIFO actually available for pulling.
|
||||
size_t frames_available_ GUARDED_BY(lock_) = 0;
|
||||
size_t index_read_ GUARDED_BY(lock_) = 0;
|
||||
diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc b/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc
|
||||
index 13c6a4d5078e6e1970cd976a3c5829aa7c915460..7abc6af203e7e84e3acda71440c035dfc1b66226 100644
|
||||
--- a/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc
|
||||
+++ b/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc
|
||||
@@ -364,6 +364,96 @@ INSTANTIATE_TEST_SUITE_P(PushPullFIFOFeatureTest,
|
||||
PushPullFIFOFeatureTest,
|
||||
testing::ValuesIn(g_feature_test_params));
|
||||
|
||||
+
|
||||
+struct FIFOEarmarkTestParam {
|
||||
+ FIFOTestSetup setup;
|
||||
+ size_t callback_buffer_size;
|
||||
+ size_t expected_earmark_frames;
|
||||
+};
|
||||
+
|
||||
+class PushPullFIFOEarmarkFramesTest
|
||||
+ : public testing::TestWithParam<FIFOEarmarkTestParam> {};
|
||||
+
|
||||
+TEST_P(PushPullFIFOEarmarkFramesTest, FeatureTests) {
|
||||
+ const FIFOTestSetup setup = GetParam().setup;
|
||||
+ const size_t callback_buffer_size = GetParam().callback_buffer_size;
|
||||
+ const size_t expected_earmark_frames = GetParam().expected_earmark_frames;
|
||||
+
|
||||
+ // Create a FIFO with a specified configuration.
|
||||
+ std::unique_ptr<PushPullFIFO> fifo = std::make_unique<PushPullFIFO>(
|
||||
+ setup.number_of_channels, setup.fifo_length);
|
||||
+ fifo->SetEarmarkFrames(callback_buffer_size);
|
||||
+
|
||||
+ scoped_refptr<AudioBus> output_bus;
|
||||
+
|
||||
+ // Iterate all the scheduled push/pull actions.
|
||||
+ size_t frame_counter = 0;
|
||||
+ for (const auto& action : setup.fifo_actions) {
|
||||
+ if (strcmp(action.action, "PUSH") == 0) {
|
||||
+ scoped_refptr<AudioBus> input_bus =
|
||||
+ AudioBus::Create(setup.number_of_channels, action.number_of_frames);
|
||||
+ frame_counter = FillBusWithLinearRamp(input_bus.get(), frame_counter);
|
||||
+ fifo->Push(input_bus.get());
|
||||
+ LOG(INFO) << "PUSH " << action.number_of_frames
|
||||
+ << " frames (frameCounter=" << frame_counter << ")";
|
||||
+ } else if (strcmp(action.action, "PULL_EARMARK") == 0) {
|
||||
+ output_bus =
|
||||
+ AudioBus::Create(setup.number_of_channels, action.number_of_frames);
|
||||
+ fifo->PullAndUpdateEarmark(output_bus.get(), action.number_of_frames);
|
||||
+ LOG(INFO) << "PULL_EARMARK " << action.number_of_frames << " frames";
|
||||
+ } else {
|
||||
+ NOTREACHED();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Test the earmark frames.
|
||||
+ const size_t actual_earmark_frames = fifo->GetEarmarkFramesForTest();
|
||||
+ EXPECT_EQ(expected_earmark_frames, actual_earmark_frames);
|
||||
+}
|
||||
+
|
||||
+FIFOEarmarkTestParam g_earmark_test_params[] = {
|
||||
+ // When there's no underrun, the earmark is equal to the callback size.
|
||||
+ {{8192, 2, {
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PULL_EARMARK", 256},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PULL_EARMARK", 256}
|
||||
+ }}, 256, 256},
|
||||
+ // The first underrun increases the earmark by the callback size.
|
||||
+ {{8192, 2, {
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PULL_EARMARK", 384}, // udnerrun; updating earmark and skipping pull.
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PULL_EARMARK", 384} // OK
|
||||
+ }}, 384, 768},
|
||||
+ // Simulating "bursty and irregular" callbacks.
|
||||
+ {{8192, 2, {
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PULL_EARMARK", 480}, // OK
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PULL_EARMARK", 480}, // underrun; updating earmark and skipping pull.
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PUSH", 128},
|
||||
+ {"PULL_EARMARK", 480}, // OK
|
||||
+ {"PUSH", 128},
|
||||
+ {"PULL_EARMARK", 480} // underrun; updating earmark and skipping pull.
|
||||
+ }}, 480, 1440}
|
||||
+};
|
||||
+
|
||||
+INSTANTIATE_TEST_SUITE_P(PushPullFIFOEarmarkFramesTest,
|
||||
+ PushPullFIFOEarmarkFramesTest,
|
||||
+ testing::ValuesIn(g_earmark_test_params));
|
||||
+
|
||||
} // namespace
|
||||
|
||||
} // namespace blink
|
||||
135
patches/chromium/cherry-pick-4ce2abc17078.patch
Normal file
135
patches/chromium/cherry-pick-4ce2abc17078.patch
Normal file
@@ -0,0 +1,135 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Flack <flackr@chromium.org>
|
||||
Date: Fri, 30 Jul 2021 18:51:38 +0000
|
||||
Subject: Forbid script execution for entire lifecycle update
|
||||
|
||||
We should not execute script during the lifecycle update except in cases where we we know it is safe to do so, either because we will rerun the lifecycle steps if anything is invalidated (resize observers, intersection observers) or because the script does not have access to invalidate the DOM (e.g. paint worklets).
|
||||
|
||||
(cherry picked from commit a73237da91de8aa49aaa5d9479bae51cf387f090)
|
||||
|
||||
Bug: 1196853
|
||||
Change-Id: Id1fdbbb25107cfdc6c234123f845406c28d32914
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2815619
|
||||
Reviewed-by: Stefan Zager <szager@chromium.org>
|
||||
Commit-Queue: Robert Flack <flackr@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#901110}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3058973
|
||||
Auto-Submit: Robert Flack <flackr@chromium.org>
|
||||
Commit-Queue: Stefan Zager <szager@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4472@{#1588}
|
||||
Cr-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
|
||||
index 1901d2128035abdcdefcf9747db0c18580ec2073..55dccfbe7d9046a479afc9c0b141accd0b998c87 100644
|
||||
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
|
||||
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
|
||||
@@ -95,6 +95,7 @@
|
||||
#include "third_party/blink/renderer/core/script/classic_script.h"
|
||||
#include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
|
||||
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
|
||||
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
|
||||
#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
|
||||
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
|
||||
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
|
||||
@@ -803,6 +804,8 @@ void WebPluginContainerImpl::Dispose() {
|
||||
}
|
||||
|
||||
if (web_plugin_) {
|
||||
+ // Plugins may execute script on being detached during the lifecycle update.
|
||||
+ ScriptForbiddenScope::AllowUserAgentScript allow_script;
|
||||
CHECK(web_plugin_->Container() == this);
|
||||
web_plugin_->Destroy();
|
||||
web_plugin_ = nullptr;
|
||||
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
|
||||
index 30c2b00056873410c46177ccfe5fba1f7155dbb2..3538b4841a9f752fa0c5e8966cd10ab78d369f5c 100644
|
||||
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
|
||||
@@ -2539,6 +2539,7 @@ bool LocalFrameView::UpdateLifecyclePhases(
|
||||
|
||||
void LocalFrameView::UpdateLifecyclePhasesInternal(
|
||||
DocumentLifecycle::LifecycleState target_state) {
|
||||
+ ScriptForbiddenScope forbid_script;
|
||||
// RunScrollTimelineSteps must not run more than once.
|
||||
bool should_run_scroll_timeline_steps = true;
|
||||
|
||||
@@ -2636,6 +2637,10 @@ void LocalFrameView::UpdateLifecyclePhasesInternal(
|
||||
continue;
|
||||
}
|
||||
|
||||
+ // At this point in time, script is allowed to run as we will repeat the
|
||||
+ // lifecycle update if anything is invalidated.
|
||||
+ ScriptForbiddenScope::AllowUserAgentScript allow_script;
|
||||
+
|
||||
// ResizeObserver and post-layout IntersectionObserver observation
|
||||
// deliveries may dirty style and layout. RunResizeObserverSteps will return
|
||||
// true if any observer ran that may have dirtied style or layout;
|
||||
@@ -2894,6 +2899,7 @@ bool LocalFrameView::AnyFrameIsPrintingOrPaintingPreview() {
|
||||
}
|
||||
|
||||
void LocalFrameView::RunPaintLifecyclePhase(PaintBenchmarkMode benchmark_mode) {
|
||||
+ DCHECK(ScriptForbiddenScope::IsScriptForbidden());
|
||||
TRACE_EVENT0("blink,benchmark", "LocalFrameView::RunPaintLifecyclePhase");
|
||||
// While printing or capturing a paint preview of a document, the paint walk
|
||||
// is done into a special canvas. There is no point doing a normal paint step
|
||||
@@ -2925,17 +2931,11 @@ void LocalFrameView::RunPaintLifecyclePhase(PaintBenchmarkMode benchmark_mode) {
|
||||
for (PaintLayerScrollableArea* area : *animating_scrollable_areas)
|
||||
area->UpdateCompositorScrollAnimations();
|
||||
}
|
||||
- {
|
||||
- // Updating animations can notify ready promises which could mutate
|
||||
- // the DOM. We should delay these until we have finished the lifecycle
|
||||
- // update. https://crbug.com/1196781
|
||||
- ScriptForbiddenScope forbid_script;
|
||||
- frame_view.GetLayoutView()
|
||||
- ->GetDocument()
|
||||
- .GetDocumentAnimations()
|
||||
- .UpdateAnimations(DocumentLifecycle::kPaintClean,
|
||||
- paint_artifact_compositor_.get());
|
||||
- }
|
||||
+ frame_view.GetLayoutView()
|
||||
+ ->GetDocument()
|
||||
+ .GetDocumentAnimations()
|
||||
+ .UpdateAnimations(DocumentLifecycle::kPaintClean,
|
||||
+ paint_artifact_compositor_.get());
|
||||
Document& document = frame_view.GetLayoutView()->GetDocument();
|
||||
total_animations_count +=
|
||||
document.GetDocumentAnimations().GetAnimationsCount();
|
||||
@@ -4512,6 +4512,7 @@ void LocalFrameView::RenderThrottlingStatusChanged() {
|
||||
// so painting the tree should just clear the previous painted output.
|
||||
DCHECK(!IsUpdatingLifecycle());
|
||||
AllowThrottlingScope allow_throtting(*this);
|
||||
+ ScriptForbiddenScope forbid_script;
|
||||
RunPaintLifecyclePhase();
|
||||
}
|
||||
|
||||
@@ -5018,6 +5019,7 @@ void LocalFrameView::RunPaintBenchmark(int repeat_count,
|
||||
// quantization when the time is very small.
|
||||
base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
|
||||
do {
|
||||
+ ScriptForbiddenScope forbid_script;
|
||||
RunPaintLifecyclePhase(mode);
|
||||
timer.NextLap();
|
||||
} while (!timer.HasTimeLimitExpired());
|
||||
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
|
||||
index e6e0c5b909c4d073963bcbb074bfb091a6ccb83b..618e08fbb5157c06348feee5f0120bd28ed0bc44 100644
|
||||
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
|
||||
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h"
|
||||
#include "third_party/blink/renderer/modules/csspaint/paint_worklet_id_generator.h"
|
||||
#include "third_party/blink/renderer/modules/csspaint/paint_worklet_messaging_proxy.h"
|
||||
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
|
||||
#include "third_party/blink/renderer/platform/graphics/paint_generated_image.h"
|
||||
|
||||
namespace blink {
|
||||
@@ -126,6 +127,10 @@ scoped_refptr<Image> PaintWorklet::Paint(const String& name,
|
||||
layout_object.GetDocument(), layout_object.StyleRef(),
|
||||
paint_definition->NativeInvalidationProperties(),
|
||||
paint_definition->CustomInvalidationProperties());
|
||||
+ // The PaintWorkletGlobalScope is sufficiently isolated that it is safe to
|
||||
+ // run during the lifecycle update without concern for it causing
|
||||
+ // invalidations to the lifecycle.
|
||||
+ ScriptForbiddenScope::AllowUserAgentScript allow_script;
|
||||
sk_sp<PaintRecord> paint_record = paint_definition->Paint(
|
||||
container_size, zoom, style_map, data, device_scale_factor);
|
||||
if (!paint_record)
|
||||
60
patches/chromium/cherry-pick-5487040a284a.patch
Normal file
60
patches/chromium/cherry-pick-5487040a284a.patch
Normal file
@@ -0,0 +1,60 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dale Curtis <dalecurtis@chromium.org>
|
||||
Date: Mon, 12 Jul 2021 19:29:39 +0000
|
||||
Subject: Don't TexImage via GPU when OOP raster is disabled on Windows.
|
||||
|
||||
For some reason it's much slower than the the non-GPU based path. The
|
||||
test page https://koush.github.io/webcodecs/ goes from ~270fps decoding
|
||||
and ~5fps rendering to ~300fps decoding and >> 60fps rendering when
|
||||
running with --disable-oop-rasterization.
|
||||
|
||||
R=kbr
|
||||
|
||||
Bug: 1227921
|
||||
Change-Id: I1abbcfd5c4a515540753c8c5e681949f6f236c9c
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3018801
|
||||
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
|
||||
Reviewed-by: Kenneth Russell <kbr@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#900587}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
|
||||
index 31a22fa48c001a07d1b848c107d22c7d1342c2a6..f70dd9b9d0eb405736e8830feb9cb32bbdb5af4a 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
|
||||
@@ -5955,6 +5955,14 @@ void WebGLRenderingContextBase::TexImageHelperMediaVideoFrame(
|
||||
CanUseTexImageViaGPU(format, type) &&
|
||||
transform == media::kNoTransformation;
|
||||
|
||||
+#if defined(OS_WIN)
|
||||
+ // TODO(crbug.com/1227921): When OOP GPU rasterization is disabled, uploading
|
||||
+ // via the GPU becomes extremely slow.
|
||||
+ const bool gpu_teximage_is_slow = !caps.supports_oop_raster;
|
||||
+#else
|
||||
+ const bool gpu_teximage_is_slow = false;
|
||||
+#endif
|
||||
+
|
||||
// Callers may chose to provide a renderer which ensures that generated
|
||||
// intermediates will be cached across TexImage calls for the same frame.
|
||||
std::unique_ptr<media::PaintCanvasVideoRenderer> local_video_renderer;
|
||||
@@ -6000,7 +6008,7 @@ void WebGLRenderingContextBase::TexImageHelperMediaVideoFrame(
|
||||
// TODO(crbug.com/1180879): I420A should be supported, but currently fails
|
||||
// conformance/textures/misc/texture-video-transparent.html.
|
||||
if (!media_video_frame->HasTextures() &&
|
||||
- media::IsOpaque(media_video_frame->format()) &&
|
||||
+ media::IsOpaque(media_video_frame->format()) && !gpu_teximage_is_slow &&
|
||||
video_renderer->CopyVideoFrameYUVDataToGLTexture(
|
||||
raster_context_provider, ContextGL(), media_video_frame, target,
|
||||
texture->Object(), adjusted_internalformat, format, type, level,
|
||||
@@ -6068,9 +6076,9 @@ void WebGLRenderingContextBase::TexImageHelperMediaVideoFrame(
|
||||
function_id == kTexImage2D || function_id == kTexSubImage2D;
|
||||
#endif
|
||||
|
||||
- const bool can_upload_via_gpu = function_supports_gpu_teximage &&
|
||||
- CanUseTexImageViaGPU(format, type) &&
|
||||
- source_image_rect_is_default;
|
||||
+ const bool can_upload_via_gpu =
|
||||
+ function_supports_gpu_teximage && CanUseTexImageViaGPU(format, type) &&
|
||||
+ source_image_rect_is_default && !gpu_teximage_is_slow;
|
||||
|
||||
// If we can upload via GPU, try to to use an accelerated resource provider
|
||||
// configured appropriately for video. Otherwise use the software cache.
|
||||
103
patches/chromium/cherry-pick-ac9dc1235e28.patch
Normal file
103
patches/chromium/cherry-pick-ac9dc1235e28.patch
Normal file
@@ -0,0 +1,103 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ilya Nikolaevskiy <ilnik@chromium.org>
|
||||
Date: Mon, 17 May 2021 08:34:41 +0000
|
||||
Subject: Add locks and empty string checks to FakeV4L2Impl
|
||||
|
||||
FakeV4L2Impl is crashed by fuzzer with some weird ASAN errors, which
|
||||
turned out to be a threading issue.
|
||||
|
||||
Bug: 1205059,1196302
|
||||
Change-Id: Ieb3a917c9a4549b655862e69214774e183a70bc3
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2883613
|
||||
Reviewed-by: Ricky Liang <jcliang@chromium.org>
|
||||
Commit-Queue: Ilya Nikolaevskiy <ilnik@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#883390}
|
||||
|
||||
diff --git a/media/capture/video/linux/fake_v4l2_impl.cc b/media/capture/video/linux/fake_v4l2_impl.cc
|
||||
index 4a976815d009e68b3740aa71e8c8fd641bf91493..09647474bed3c7c7b34cd9fb161edd2a7d2e170f 100644
|
||||
--- a/media/capture/video/linux/fake_v4l2_impl.cc
|
||||
+++ b/media/capture/video/linux/fake_v4l2_impl.cc
|
||||
@@ -380,10 +380,16 @@ FakeV4L2Impl::~FakeV4L2Impl() = default;
|
||||
|
||||
void FakeV4L2Impl::AddDevice(const std::string& device_name,
|
||||
const FakeV4L2DeviceConfig& config) {
|
||||
+ base::AutoLock lock(lock_);
|
||||
device_configs_.emplace(device_name, config);
|
||||
}
|
||||
|
||||
int FakeV4L2Impl::open(const char* device_name, int flags) {
|
||||
+ if (!device_name)
|
||||
+ return kInvalidId;
|
||||
+
|
||||
+ base::AutoLock lock(lock_);
|
||||
+
|
||||
std::string device_name_as_string(device_name);
|
||||
auto device_configs_iter = device_configs_.find(device_name_as_string);
|
||||
if (device_configs_iter == device_configs_.end())
|
||||
@@ -403,6 +409,7 @@ int FakeV4L2Impl::open(const char* device_name, int flags) {
|
||||
}
|
||||
|
||||
int FakeV4L2Impl::close(int fd) {
|
||||
+ base::AutoLock lock(lock_);
|
||||
auto device_iter = opened_devices_.find(fd);
|
||||
if (device_iter == opened_devices_.end())
|
||||
return kErrorReturnValue;
|
||||
@@ -412,6 +419,7 @@ int FakeV4L2Impl::close(int fd) {
|
||||
}
|
||||
|
||||
int FakeV4L2Impl::ioctl(int fd, int request, void* argp) {
|
||||
+ base::AutoLock lock(lock_);
|
||||
auto device_iter = opened_devices_.find(fd);
|
||||
if (device_iter == opened_devices_.end())
|
||||
return EBADF;
|
||||
@@ -518,6 +526,7 @@ void* FakeV4L2Impl::mmap(void* /*start*/,
|
||||
int flags,
|
||||
int fd,
|
||||
off_t offset) {
|
||||
+ base::AutoLock lock(lock_);
|
||||
if (flags & MAP_FIXED) {
|
||||
errno = EINVAL;
|
||||
return MAP_FAILED;
|
||||
@@ -543,10 +552,12 @@ void* FakeV4L2Impl::mmap(void* /*start*/,
|
||||
}
|
||||
|
||||
int FakeV4L2Impl::munmap(void* start, size_t length) {
|
||||
+ base::AutoLock lock(lock_);
|
||||
return kSuccessReturnValue;
|
||||
}
|
||||
|
||||
int FakeV4L2Impl::poll(struct pollfd* ufds, unsigned int nfds, int timeout) {
|
||||
+ base::AutoLock lock(lock_);
|
||||
if (nfds != 1) {
|
||||
// We only support polling of a single device.
|
||||
errno = EINVAL;
|
||||
diff --git a/media/capture/video/linux/fake_v4l2_impl.h b/media/capture/video/linux/fake_v4l2_impl.h
|
||||
index 0a035a97dd8761b08eb4cfdbe2865efc346f0e23..ae7167f95163581c756ab4951717fd4352c67757 100644
|
||||
--- a/media/capture/video/linux/fake_v4l2_impl.h
|
||||
+++ b/media/capture/video/linux/fake_v4l2_impl.h
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
+#include "base/synchronization/lock.h"
|
||||
#include "media/capture/capture_export.h"
|
||||
#include "media/capture/video/linux/v4l2_capture_device.h"
|
||||
#include "media/capture/video/video_capture_device_descriptor.h"
|
||||
@@ -52,11 +53,13 @@ class CAPTURE_EXPORT FakeV4L2Impl : public V4L2CaptureDevice {
|
||||
private:
|
||||
class OpenedDevice;
|
||||
|
||||
- int next_id_to_return_from_open_;
|
||||
- std::map<std::string, FakeV4L2DeviceConfig> device_configs_;
|
||||
- std::map<std::string, int> device_name_to_open_id_map_;
|
||||
+ base::Lock lock_;
|
||||
+
|
||||
+ int next_id_to_return_from_open_ GUARDED_BY(lock_);
|
||||
+ std::map<std::string, FakeV4L2DeviceConfig> device_configs_ GUARDED_BY(lock_);
|
||||
+ std::map<std::string, int> device_name_to_open_id_map_ GUARDED_BY(lock_);
|
||||
std::map<int /*value returned by open()*/, std::unique_ptr<OpenedDevice>>
|
||||
- opened_devices_;
|
||||
+ opened_devices_ GUARDED_BY(lock_);
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
41
patches/chromium/cherry-pick-cc20b36a5845.patch
Normal file
41
patches/chromium/cherry-pick-cc20b36a5845.patch
Normal file
@@ -0,0 +1,41 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Rogers <pdr@chromium.org>
|
||||
Date: Wed, 19 May 2021 16:11:29 +0000
|
||||
Subject: Use a 100ms timer in
|
||||
MacScrollbarAnimatorImpl::StartScrollbarPaintTimer
|
||||
|
||||
MacScrollbarAnimatorImpl::StartScrollbarPaintTimer has a timer that
|
||||
runs while ShouldSuspendScrollAnimations is true. The timer was changed
|
||||
from 100ms to 1ms in https://crrev.com/376453. This was later changed to
|
||||
0ms in https://crrev.com/434428 which caused a performance regression
|
||||
(https://crbug.com/713669). The timer was then changed back to 1ms in
|
||||
https://crrev.com/470823 with a comment about giving other tasks time to
|
||||
cancel the timer. This patch restores the 100ms timer. The comment has
|
||||
been removed because this timer is more of a polling mechanism.
|
||||
|
||||
Bug: 1090000
|
||||
Change-Id: I4041d9ba5869abbe4ff7d55d56d921c7d90a72ad
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2903830
|
||||
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
|
||||
Commit-Queue: Philip Rogers <pdr@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#884506}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm b/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm
|
||||
index cb3c5b0493410a1cb8d099ec3349e8cbb8dd629d..b7257f954d4f7f85128b5fd24651c27d2ce8bd85 100644
|
||||
--- a/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm
|
||||
+++ b/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm
|
||||
@@ -814,13 +814,11 @@ - (void)invalidate {
|
||||
}
|
||||
|
||||
void MacScrollbarAnimatorImpl::StartScrollbarPaintTimer() {
|
||||
- // Post a task with 1 ms delay to give a chance to run other immediate tasks
|
||||
- // that may cancel this.
|
||||
initial_scrollbar_paint_task_handle_ = PostDelayedCancellableTask(
|
||||
*task_runner_, FROM_HERE,
|
||||
WTF::Bind(&MacScrollbarAnimatorImpl::InitialScrollbarPaintTask,
|
||||
WrapWeakPersistent(this)),
|
||||
- base::TimeDelta::FromMilliseconds(1));
|
||||
+ base::TimeDelta::FromMilliseconds(100));
|
||||
}
|
||||
|
||||
bool MacScrollbarAnimatorImpl::ScrollbarPaintTimerIsActive() const {
|
||||
50
patches/chromium/cherry-pick-cd98d7c0dae9.patch
Normal file
50
patches/chromium/cherry-pick-cd98d7c0dae9.patch
Normal file
@@ -0,0 +1,50 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Peng Huang <penghuang@chromium.org>
|
||||
Date: Wed, 7 Jul 2021 20:50:53 +0000
|
||||
Subject: Fix UAF problem in SharedImageInterfaceInProcess
|
||||
|
||||
(cherry picked from commit 38b4905f8d877b27bc2d4ccd4cfc0f82b636deea)
|
||||
|
||||
Bug: 1216822
|
||||
Change-Id: I8ae1f7c406e1899e500ee7ddeaaf18230b1cbcb2
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2971144
|
||||
Commit-Queue: Peng Huang <penghuang@chromium.org>
|
||||
Commit-Queue: Vasiliy Telezhnikov <vasilyt@chromium.org>
|
||||
Auto-Submit: Peng Huang <penghuang@chromium.org>
|
||||
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#893931}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3011895
|
||||
Auto-Submit: Mason Freed <masonf@chromium.org>
|
||||
Reviewed-by: Peng Huang <penghuang@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4515@{#1369}
|
||||
Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287}
|
||||
|
||||
diff --git a/gpu/ipc/shared_image_interface_in_process.cc b/gpu/ipc/shared_image_interface_in_process.cc
|
||||
index 04b96e99968df70b9d7475733cccdc5db4f857fe..14b5a3077219e4acbe2b304c9e5db7fa29174c9c 100644
|
||||
--- a/gpu/ipc/shared_image_interface_in_process.cc
|
||||
+++ b/gpu/ipc/shared_image_interface_in_process.cc
|
||||
@@ -86,6 +86,8 @@ void SharedImageInterfaceInProcess::DestroyOnGpu(
|
||||
sync_point_client_state_->Destroy();
|
||||
sync_point_client_state_ = nullptr;
|
||||
}
|
||||
+
|
||||
+ context_state_ = nullptr;
|
||||
completion->Signal();
|
||||
}
|
||||
|
||||
diff --git a/gpu/ipc/shared_image_interface_in_process.h b/gpu/ipc/shared_image_interface_in_process.h
|
||||
index 0a2246fcfde338717e38c39e49376dc6912300a8..287def52c8ed59ccd722d8f69aac590aa88b91b5 100644
|
||||
--- a/gpu/ipc/shared_image_interface_in_process.h
|
||||
+++ b/gpu/ipc/shared_image_interface_in_process.h
|
||||
@@ -228,10 +228,7 @@ class GL_IN_PROCESS_CONTEXT_EXPORT SharedImageInterfaceInProcess
|
||||
|
||||
// Accessed on GPU thread.
|
||||
MailboxManager* mailbox_manager_;
|
||||
- // Used to check if context is lost at destruction time.
|
||||
- // TODO(weiliangc): SharedImageInterface should become active observer of
|
||||
- // whether context is lost.
|
||||
- SharedContextState* context_state_;
|
||||
+ scoped_refptr<SharedContextState> context_state_;
|
||||
// Created and only used by this SharedImageInterface.
|
||||
SyncPointManager* sync_point_manager_;
|
||||
scoped_refptr<SyncPointClientState> sync_point_client_state_;
|
||||
64
patches/chromium/cherry-pick-e2123a8e0943.patch
Normal file
64
patches/chromium/cherry-pick-e2123a8e0943.patch
Normal file
@@ -0,0 +1,64 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tal Pressman <talp@chromium.org>
|
||||
Date: Wed, 21 Jul 2021 09:11:13 +0000
|
||||
Subject: Manually post task to bind FileUtilitiesHost.
|
||||
|
||||
The FileUtilitiesHost binder is posted to a separate sequence, and the
|
||||
ServiceWorkerHost may be destroyed by the time the it runs, causing a
|
||||
UAF.
|
||||
This CL changes it so that, when we try to bind a new receiver, the
|
||||
host's worker_process_id() is obtained first (on the service worker's
|
||||
core thread) and then a task is posted to do the actual binding on a
|
||||
USER_VISIBLE task runner.
|
||||
|
||||
Credit: This issue was first reported (with analysis) by
|
||||
soulchen8650@gmail.com.
|
||||
|
||||
Bug: 1229298
|
||||
Change-Id: I6d5c05a830ba30f6cb98bf2df70a3df3333f3dd9
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3041006
|
||||
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
|
||||
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
|
||||
Commit-Queue: Tal Pressman <talp@google.com>
|
||||
Cr-Commit-Position: refs/heads/master@{#903832}
|
||||
|
||||
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
|
||||
index 9fbd499d113784dda58a5470510acbdb65c1c119..2b297d200277e857579deff7f41affca6fc7877a 100644
|
||||
--- a/content/browser/browser_interface_binders.cc
|
||||
+++ b/content/browser/browser_interface_binders.cc
|
||||
@@ -376,10 +376,22 @@ void BindTextSuggestionHostForFrame(
|
||||
}
|
||||
#endif
|
||||
|
||||
+// Get the service worker's worker process ID and post a task to bind the
|
||||
+// receiver on a USER_VISIBLE task runner.
|
||||
+// This is necessary because:
|
||||
+// - Binding the host itself and checking the ID on the task's thread may cause
|
||||
+// a UAF if the host has been deleted in the meantime.
|
||||
+// - The process ID is not yet populated at the time `PopulateInterfaceBinders`
|
||||
+// is called.
|
||||
void BindFileUtilitiesHost(
|
||||
- const ServiceWorkerHost* host,
|
||||
+ ServiceWorkerHost* host,
|
||||
mojo::PendingReceiver<blink::mojom::FileUtilitiesHost> receiver) {
|
||||
- FileUtilitiesHostImpl::Create(host->worker_process_id(), std::move(receiver));
|
||||
+ auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
|
||||
+ {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
|
||||
+ task_runner->PostTask(
|
||||
+ FROM_HERE,
|
||||
+ base::BindOnce(&FileUtilitiesHostImpl::Create, host->worker_process_id(),
|
||||
+ std::move(receiver)));
|
||||
}
|
||||
|
||||
template <typename WorkerHost, typename Interface>
|
||||
@@ -1175,9 +1187,7 @@ void PopulateServiceWorkerBinders(ServiceWorkerHost* host,
|
||||
|
||||
// static binders
|
||||
map->Add<blink::mojom::FileUtilitiesHost>(
|
||||
- base::BindRepeating(&BindFileUtilitiesHost, host),
|
||||
- base::ThreadPool::CreateSequencedTaskRunner(
|
||||
- {base::MayBlock(), base::TaskPriority::USER_VISIBLE}));
|
||||
+ base::BindRepeating(&BindFileUtilitiesHost, host));
|
||||
map->Add<shape_detection::mojom::BarcodeDetectionProvider>(
|
||||
base::BindRepeating(&BindBarcodeDetectionProvider));
|
||||
map->Add<shape_detection::mojom::FaceDetectionProvider>(
|
||||
35
patches/chromium/cherry-pick-e60cc80ff744.patch
Normal file
35
patches/chromium/cherry-pick-e60cc80ff744.patch
Normal file
@@ -0,0 +1,35 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shrek Shao <shrekshao@google.com>
|
||||
Date: Tue, 29 Jun 2021 01:17:03 +0000
|
||||
Subject: Fix multidraw validation drawcount + offset out of bounds
|
||||
|
||||
(cherry picked from commit 7d0a12ce19fed024d56b95a692d888fe3ef14e2f)
|
||||
|
||||
Bug: 1219886
|
||||
Change-Id: I8a84664150758370d9a77ee22ac5549bead0e37e
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2977850
|
||||
Reviewed-by: Kenneth Russell <kbr@chromium.org>
|
||||
Commit-Queue: Kenneth Russell <kbr@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#895423}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2988885
|
||||
Reviewed-by: Shrek Shao <shrekshao@google.com>
|
||||
Reviewed-by: Austin Eng <enga@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4515@{#1101}
|
||||
Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_multi_draw_common.cc b/third_party/blink/renderer/modules/webgl/webgl_multi_draw_common.cc
|
||||
index e0da2d4c89872387d4a7a0ad98d499252e8494bc..af5352e2cc9f60ceb79170f3ab94318b01942ed7 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_multi_draw_common.cc
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_multi_draw_common.cc
|
||||
@@ -34,6 +34,11 @@ bool WebGLMultiDrawCommon::ValidateArray(WebGLExtensionScopedContext* scoped,
|
||||
outOfBoundsDescription);
|
||||
return false;
|
||||
}
|
||||
+ if (static_cast<uint64_t>(drawcount) + offset > size) {
|
||||
+ scoped->Context()->SynthesizeGLError(GL_INVALID_OPERATION, function_name,
|
||||
+ "drawcount plus offset out of bounds");
|
||||
+ return false;
|
||||
+ }
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user