mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
52 Commits
v34.0.0-be
...
v34.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a1c029f94 | ||
|
|
a1816f3587 | ||
|
|
db84f39e5e | ||
|
|
7ecb8ad7a5 | ||
|
|
973c344f1b | ||
|
|
01c5db032a | ||
|
|
2c22507dfb | ||
|
|
41c1161dd9 | ||
|
|
b36aaf36e7 | ||
|
|
5728e6dc81 | ||
|
|
c86896312f | ||
|
|
b9b6aebe32 | ||
|
|
6bc6d10308 | ||
|
|
1323e223a4 | ||
|
|
bc00450a31 | ||
|
|
1ecc57465d | ||
|
|
0b12d0e6c5 | ||
|
|
68a410de9b | ||
|
|
314bc3e1a1 | ||
|
|
33065a8654 | ||
|
|
52484b0ccd | ||
|
|
b5ed025267 | ||
|
|
bcbd3063a3 | ||
|
|
686bc86042 | ||
|
|
fd117af7ce | ||
|
|
40dd1b76bc | ||
|
|
521835d70b | ||
|
|
cfe549c739 | ||
|
|
2d45b7df34 | ||
|
|
9a7848ced1 | ||
|
|
c54f1f98fe | ||
|
|
a7066af0f9 | ||
|
|
7f6594654c | ||
|
|
f383391c0e | ||
|
|
ed0946c880 | ||
|
|
d1eee44b19 | ||
|
|
498a7d82cc | ||
|
|
dc53f6e73b | ||
|
|
fa98b4d542 | ||
|
|
1c2015ed1f | ||
|
|
d0668e6a3a | ||
|
|
d7284f0262 | ||
|
|
b33ea0d72d | ||
|
|
c63613f290 | ||
|
|
dc0c6c6f3f | ||
|
|
a92b3944a1 | ||
|
|
5ca3f950e9 | ||
|
|
99f2bab4a4 | ||
|
|
c0d1c3481a | ||
|
|
5a0e1ccf73 | ||
|
|
d8a7c57506 | ||
|
|
a30cf5b551 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,6 +1,9 @@
|
||||
# `git apply` and friends don't understand CRLF, even on windows. Force those
|
||||
# files to be checked out with LF endings even if core.autocrlf is true.
|
||||
*.patch text eol=lf
|
||||
DEPS text eol=lf
|
||||
yarn.lock text eol=lf
|
||||
script/zip_manifests/*.manifest text eol=lf
|
||||
patches/**/.patches merge=union
|
||||
|
||||
# Source code and markdown files should always use LF as line ending.
|
||||
|
||||
18
.github/actions/build-electron/action.yml
vendored
18
.github/actions/build-electron/action.yml
vendored
@@ -5,10 +5,10 @@ inputs:
|
||||
description: 'Target arch'
|
||||
required: true
|
||||
target-platform:
|
||||
description: 'Target platform'
|
||||
description: 'Target platform, should be linux, win, macos'
|
||||
required: true
|
||||
artifact-platform:
|
||||
description: 'Artifact platform, should be linux, darwin or mas'
|
||||
description: 'Artifact platform, should be linux, win, darwin or mas'
|
||||
required: true
|
||||
step-suffix:
|
||||
description: 'Suffix for build steps'
|
||||
@@ -71,7 +71,7 @@ runs:
|
||||
cd src
|
||||
e build --target electron:electron_dist_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
if [ "${{ inputs.is-asan }}" != "true" ]; then
|
||||
target_os=${{ inputs.target-platform == 'linux' && 'linux' || 'mac'}}
|
||||
target_os=${{ inputs.target-platform == 'macos' && 'mac' || inputs.target-platform }}
|
||||
if [ "${{ inputs.artifact-platform }}" = "mas" ]; then
|
||||
target_os="${target_os}_mas"
|
||||
fi
|
||||
@@ -82,7 +82,7 @@ runs:
|
||||
run: |
|
||||
cd src
|
||||
e build --target electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES
|
||||
gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args
|
||||
ELECTRON_DEPOT_TOOLS_DISABLE_LOG=1 e d gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args
|
||||
# Remove unused args from mksnapshot_args
|
||||
SEDOPTION="-i"
|
||||
if [ "`uname`" = "Darwin" ]; then
|
||||
@@ -91,7 +91,7 @@ runs:
|
||||
sed $SEDOPTION '/.*builtins-pgo/d' out/Default/mksnapshot_args
|
||||
sed $SEDOPTION '/--turbo-profiling-input/d' out/Default/mksnapshot_args
|
||||
|
||||
if [ "`uname`" = "Linux" ]; then
|
||||
if [ "${{ inputs.target-platform }}" = "linux" ]; then
|
||||
if [ "${{ inputs.target-arch }}" = "arm" ]; then
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/mksnapshot
|
||||
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/v8_context_snapshot_generator
|
||||
@@ -105,7 +105,13 @@ runs:
|
||||
fi
|
||||
|
||||
e build --target electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
(cd out/Default; zip mksnapshot.zip mksnapshot_args gen/v8/embedded.S)
|
||||
if [ "${{ inputs.target-platform }}" = "win" ]; then
|
||||
cd out/Default
|
||||
powershell Compress-Archive -update mksnapshot_args mksnapshot.zip
|
||||
powershell Compress-Archive -update gen/v8/embedded.S mksnapshot.zip
|
||||
else
|
||||
(cd out/Default; zip mksnapshot.zip mksnapshot_args gen/v8/embedded.S)
|
||||
fi
|
||||
- name: Generate Cross-Arch Snapshot (arm/arm64) ${{ inputs.step-suffix }}
|
||||
shell: bash
|
||||
if: ${{ (inputs.target-arch == 'arm' || inputs.target-arch == 'arm64') && inputs.target-platform == 'linux' }}
|
||||
|
||||
70
.github/actions/checkout/action.yml
vendored
70
.github/actions/checkout/action.yml
vendored
@@ -5,6 +5,10 @@ inputs:
|
||||
description: 'Whether to generate and persist a SAS token for the item in the cache'
|
||||
required: false
|
||||
default: 'false'
|
||||
use-cache:
|
||||
description: 'Whether to persist the cache to the shared drive'
|
||||
required: false
|
||||
default: 'true'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
@@ -13,31 +17,27 @@ runs:
|
||||
run: |
|
||||
echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Install Build Tools
|
||||
uses: ./src/electron/.github/actions/install-build-tools
|
||||
- name: Get Depot Tools
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
if [[ ! -d depot_tools ]]; then
|
||||
git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
|
||||
sed -i '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
# Remove swift-format dep from cipd on macOS until we send a patch upstream.
|
||||
cd depot_tools
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
touch .disable_auto_update
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
touch .disable_auto_update
|
||||
fi
|
||||
- name: Add Depot Tools to PATH
|
||||
shell: bash
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
- name: Generate DEPS Hash
|
||||
shell: bash
|
||||
run: |
|
||||
node src/electron/script/generate-deps-hash.js && cat src/electron/.depshash-target
|
||||
echo "DEPSHASH=v1-src-cache-$(shasum src/electron/.depshash | cut -f1 -d' ')" >> $GITHUB_ENV
|
||||
node src/electron/script/generate-deps-hash.js
|
||||
echo "DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)" >> $GITHUB_ENV
|
||||
- name: Generate SAS Key
|
||||
if: ${{ inputs.generate-sas-token == 'true' }}
|
||||
shell: bash
|
||||
@@ -54,18 +54,23 @@ runs:
|
||||
id: check-cache
|
||||
shell: bash
|
||||
run: |
|
||||
cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar
|
||||
echo "Using cache key: $DEPSHASH"
|
||||
echo "Checking for cache in: $cache_path"
|
||||
if [ ! -f "$cache_path" ] || [ `du $cache_path | cut -f1` = "0" ]; then
|
||||
if [[ "${{ inputs.use-cache }}" == "false" ]]; then
|
||||
echo "Not using cache this time..."
|
||||
echo "cache_exists=false" >> $GITHUB_OUTPUT
|
||||
echo "Cache Does Not Exist for $DEPSHASH"
|
||||
else
|
||||
echo "cache_exists=true" >> $GITHUB_OUTPUT
|
||||
echo "Cache Already Exists for $DEPSHASH, Skipping.."
|
||||
cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar
|
||||
echo "Using cache key: $DEPSHASH"
|
||||
echo "Checking for cache in: $cache_path"
|
||||
if [ ! -f "$cache_path" ] || [ `du $cache_path | cut -f1` = "0" ]; then
|
||||
echo "cache_exists=false" >> $GITHUB_OUTPUT
|
||||
echo "Cache Does Not Exist for $DEPSHASH"
|
||||
else
|
||||
echo "cache_exists=true" >> $GITHUB_OUTPUT
|
||||
echo "Cache Already Exists for $DEPSHASH, Skipping.."
|
||||
fi
|
||||
fi
|
||||
- name: Check cross instance cache disk space
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
if: steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
# if there is less than 20 GB free space then creating the cache might fail so exit early
|
||||
@@ -81,13 +86,17 @@ runs:
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
gclient config \
|
||||
e d gclient config \
|
||||
--name "src/electron" \
|
||||
--unmanaged \
|
||||
${GCLIENT_EXTRA_ARGS} \
|
||||
"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY"
|
||||
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 gclient sync --with_branch_heads --with_tags -vvvvv
|
||||
if [ "$TARGET_OS" != "" ]; then
|
||||
echo "target_os=['$TARGET_OS']" >> ./.gclient
|
||||
fi
|
||||
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags -vv
|
||||
if [ "${{ inputs.is-release }}" != "true" && -n "${{ env.PATCH_UP_APP_CREDS }}" ]; then
|
||||
# Re-export all the patches to check if there were changes.
|
||||
python3 src/electron/script/export_all_patches.py src/electron/patches/config.json
|
||||
@@ -128,13 +137,13 @@ runs:
|
||||
# https://dawn-review.googlesource.com/c/dawn/+/83901
|
||||
# TODO: maybe better to always leave out */.git/HEAD file for all targets ?
|
||||
- name: Delete .git directories under src to free space
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
cd src
|
||||
( find . -type d -name ".git" -not -path "./third_party/angle/*" -not -path "./third_party/dawn/*" -not -path "./electron/*" ) | xargs rm -rf
|
||||
- name: Minimize Cache Size for Upload
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
rm -rf src/android_webview
|
||||
@@ -145,9 +154,12 @@ runs:
|
||||
rm -rf src/third_party/angle/third_party/VK-GL-CTS/src
|
||||
rm -rf src/third_party/swift-toolchain
|
||||
rm -rf src/third_party/swiftshader/tests/regres/testlists
|
||||
cp src/electron/.github/actions/checkout/action.yml ./
|
||||
rm -rf src/electron
|
||||
mkdir -p src/electron/.github/actions/checkout
|
||||
mv action.yml src/electron/.github/actions/checkout
|
||||
- name: Compress Src Directory
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Uncompressed src size: $(du -sh src | cut -f1 -d' ')"
|
||||
@@ -155,7 +167,7 @@ runs:
|
||||
echo "Compressed src to $(du -sh $DEPSHASH.tar | cut -f1 -d' ')"
|
||||
cp ./$DEPSHASH.tar /mnt/cross-instance-cache/
|
||||
- name: Persist Src Cache
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
final_cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar
|
||||
|
||||
11
.github/actions/install-build-tools/action.yml
vendored
11
.github/actions/install-build-tools/action.yml
vendored
@@ -6,6 +6,15 @@ runs:
|
||||
- name: Install Build Tools
|
||||
shell: bash
|
||||
run: |
|
||||
export BUILD_TOOLS_SHA=eeb1a11392e4cec08fd926c93b31ab556dc0c23b
|
||||
if [ "$(expr substr $(uname -s) 1 10)" == "MSYS_NT-10" ]; then
|
||||
git config --global core.filemode false
|
||||
git config --global core.autocrlf false
|
||||
git config --global branch.autosetuprebase always
|
||||
fi
|
||||
export BUILD_TOOLS_SHA=8246e57791b0af4ae5975eb96f09855f9269b1cd
|
||||
npm i -g @electron/build-tools
|
||||
e auto-update disable
|
||||
if [ "$(expr substr $(uname -s) 1 10)" == "MSYS_NT-10" ]; then
|
||||
e d cipd.bat --version
|
||||
cp "C:\Python37\python.exe" "C:\Python37\python3.exe"
|
||||
fi
|
||||
|
||||
21
.github/actions/install-dependencies/action.yml
vendored
Normal file
21
.github/actions/install-dependencies/action.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: 'Install Dependencies'
|
||||
description: 'Installs yarn depdencies using cache when available'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Get yarn cache directory path
|
||||
shell: bash
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(node src/electron/script/yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('src/electron/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile --prefer-offline
|
||||
140
.github/workflows/build.yml
vendored
140
.github/workflows/build.yml
vendored
@@ -18,6 +18,11 @@ on:
|
||||
description: 'Skip Linux builds'
|
||||
default: false
|
||||
required: false
|
||||
skip-windows:
|
||||
type: boolean
|
||||
description: 'Skip Windows builds'
|
||||
default: false
|
||||
required: false
|
||||
skip-lint:
|
||||
type: boolean
|
||||
description: 'Skip lint check'
|
||||
@@ -28,7 +33,11 @@ on:
|
||||
- main
|
||||
- '[1-9][0-9]-x-y'
|
||||
pull_request:
|
||||
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -40,7 +49,9 @@ jobs:
|
||||
build-image-sha: ${{ steps.set-output.outputs.build-image-sha }}
|
||||
docs-only: ${{ steps.set-output.outputs.docs-only }}
|
||||
steps:
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 #v4.0.2
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.0.2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
id: filter
|
||||
with:
|
||||
@@ -98,6 +109,7 @@ jobs:
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Checkout & Sync & Save
|
||||
uses: ./src/electron/.github/actions/checkout
|
||||
with:
|
||||
@@ -124,9 +136,68 @@ jobs:
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Checkout & Sync & Save
|
||||
uses: ./src/electron/.github/actions/checkout
|
||||
|
||||
checkout-windows:
|
||||
needs: setup
|
||||
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
|
||||
runs-on: electron-arc-linux-amd64-32core
|
||||
container:
|
||||
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
|
||||
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
env:
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True'
|
||||
TARGET_OS: 'win'
|
||||
ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN: '1'
|
||||
outputs:
|
||||
build-image-sha: ${{ needs.setup.outputs.build-image-sha}}
|
||||
steps:
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Checkout & Sync & Save
|
||||
uses: ./src/electron/.github/actions/checkout
|
||||
|
||||
# GN Check Jobs
|
||||
macos-gn-check:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
|
||||
needs: checkout-macos
|
||||
with:
|
||||
target-platform: macos
|
||||
target-archs: x64 arm64
|
||||
check-runs-on: macos-14
|
||||
gn-build-type: testing
|
||||
secrets: inherit
|
||||
|
||||
linux-gn-check:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
|
||||
needs: checkout-linux
|
||||
with:
|
||||
target-platform: linux
|
||||
target-archs: x64 arm arm64
|
||||
check-runs-on: electron-arc-linux-amd64-8core
|
||||
check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
|
||||
gn-build-type: testing
|
||||
secrets: inherit
|
||||
|
||||
windows-gn-check:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
|
||||
needs: checkout-windows
|
||||
with:
|
||||
target-platform: win
|
||||
target-archs: x64 x86 arm64
|
||||
check-runs-on: electron-arc-linux-amd64-8core
|
||||
check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
|
||||
gn-build-type: testing
|
||||
secrets: inherit
|
||||
|
||||
# Build Jobs - These cascade into testing jobs
|
||||
macos-x64:
|
||||
permissions:
|
||||
@@ -137,7 +208,6 @@ jobs:
|
||||
needs: checkout-macos
|
||||
with:
|
||||
build-runs-on: macos-14-xlarge
|
||||
check-runs-on: macos-14
|
||||
test-runs-on: macos-13
|
||||
target-platform: macos
|
||||
target-arch: x64
|
||||
@@ -156,7 +226,6 @@ jobs:
|
||||
needs: checkout-macos
|
||||
with:
|
||||
build-runs-on: macos-14-xlarge
|
||||
check-runs-on: macos-14
|
||||
test-runs-on: macos-14
|
||||
target-platform: macos
|
||||
target-arch: arm64
|
||||
@@ -175,7 +244,6 @@ jobs:
|
||||
needs: checkout-linux
|
||||
with:
|
||||
build-runs-on: electron-arc-linux-amd64-32core
|
||||
check-runs-on: electron-arc-linux-amd64-8core
|
||||
test-runs-on: electron-arc-linux-amd64-4core
|
||||
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
|
||||
test-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}'
|
||||
@@ -196,7 +264,6 @@ jobs:
|
||||
needs: checkout-linux
|
||||
with:
|
||||
build-runs-on: electron-arc-linux-amd64-32core
|
||||
check-runs-on: electron-arc-linux-amd64-8core
|
||||
test-runs-on: electron-arc-linux-amd64-4core
|
||||
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
|
||||
test-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}'
|
||||
@@ -218,7 +285,6 @@ jobs:
|
||||
needs: checkout-linux
|
||||
with:
|
||||
build-runs-on: electron-arc-linux-amd64-32core
|
||||
check-runs-on: electron-arc-linux-amd64-8core
|
||||
test-runs-on: electron-arc-linux-arm64-4core
|
||||
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
|
||||
test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init","volumes":["/home/runner/externals:/mnt/runner-externals"]}'
|
||||
@@ -239,7 +305,6 @@ jobs:
|
||||
needs: checkout-linux
|
||||
with:
|
||||
build-runs-on: electron-arc-linux-amd64-32core
|
||||
check-runs-on: electron-arc-linux-amd64-8core
|
||||
test-runs-on: electron-arc-linux-arm64-4core
|
||||
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
|
||||
test-container: '{"image":"ghcr.io/electron/test:arm64v8-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}'
|
||||
@@ -251,10 +316,67 @@ jobs:
|
||||
upload-to-storage: '0'
|
||||
secrets: inherit
|
||||
|
||||
windows-x64:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
|
||||
needs: setup
|
||||
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
|
||||
with:
|
||||
build-runs-on: electron-arc-windows-amd64-16core
|
||||
test-runs-on: windows-latest
|
||||
target-platform: win
|
||||
target-arch: x64
|
||||
is-release: false
|
||||
gn-build-type: testing
|
||||
generate-symbols: false
|
||||
upload-to-storage: '0'
|
||||
secrets: inherit
|
||||
|
||||
windows-x86:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
|
||||
needs: setup
|
||||
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
|
||||
with:
|
||||
build-runs-on: electron-arc-windows-amd64-16core
|
||||
test-runs-on: windows-latest
|
||||
target-platform: win
|
||||
target-arch: x86
|
||||
is-release: false
|
||||
gn-build-type: testing
|
||||
generate-symbols: false
|
||||
upload-to-storage: '0'
|
||||
secrets: inherit
|
||||
|
||||
windows-arm64:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
|
||||
needs: setup
|
||||
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
|
||||
with:
|
||||
build-runs-on: electron-arc-windows-amd64-16core
|
||||
test-runs-on: electron-hosted-windows-arm64-4core
|
||||
target-platform: win
|
||||
target-arch: arm64
|
||||
is-release: false
|
||||
gn-build-type: testing
|
||||
generate-symbols: false
|
||||
upload-to-storage: '0'
|
||||
secrets: inherit
|
||||
|
||||
gha-done:
|
||||
name: GitHub Actions Completed
|
||||
runs-on: ubuntu-latest
|
||||
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64]
|
||||
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, windows-x64, windows-x86, windows-arm64]
|
||||
if: always() && !contains(needs.*.result, 'failure')
|
||||
steps:
|
||||
- name: GitHub Actions Jobs Done
|
||||
|
||||
14
.github/workflows/config/gclient.diff
vendored
14
.github/workflows/config/gclient.diff
vendored
@@ -1,14 +0,0 @@
|
||||
diff --git a/gclient.py b/gclient.py
|
||||
index 59e2b4c5197928bdba1ef69bdbe637d7dfe471c1..b4bae5e48c83c84bd867187afaf40eed16e69851 100755
|
||||
--- a/gclient.py
|
||||
+++ b/gclient.py
|
||||
@@ -783,7 +783,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
|
||||
not condition or "non_git_source" not in condition):
|
||||
continue
|
||||
cipd_root = self.GetCipdRoot()
|
||||
- for package in dep_value.get('packages', []):
|
||||
+ packages = dep_value.get('packages', [])
|
||||
+ for package in (x for x in packages if "infra/3pp/tools/swift-format" not in x.get('package')):
|
||||
deps_to_add.append(
|
||||
CipdDependency(parent=self,
|
||||
name=name,
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
inputs:
|
||||
target-platform:
|
||||
type: string
|
||||
description: 'Platform to run on, can be macos or linux'
|
||||
description: 'Platform to run on, can be macos, win or linux.'
|
||||
required: true
|
||||
target-arch:
|
||||
type: string
|
||||
@@ -15,10 +15,6 @@ on:
|
||||
type: string
|
||||
description: 'What host to run the build'
|
||||
required: true
|
||||
check-runs-on:
|
||||
type: string
|
||||
description: 'What host to run the gn-check'
|
||||
required: true
|
||||
test-runs-on:
|
||||
type: string
|
||||
description: 'What host to run the tests on'
|
||||
@@ -76,16 +72,6 @@ jobs:
|
||||
generate-symbols: ${{ inputs.generate-symbols }}
|
||||
upload-to-storage: ${{ inputs.upload-to-storage }}
|
||||
secrets: inherit
|
||||
gn-check:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
|
||||
with:
|
||||
target-platform: ${{ inputs.target-platform }}
|
||||
target-arch: ${{ inputs.target-arch }}
|
||||
check-runs-on: ${{ inputs.check-runs-on }}
|
||||
check-container: ${{ inputs.build-container }}
|
||||
gn-build-type: ${{ inputs.gn-build-type }}
|
||||
is-asan: ${{ inputs.is-asan }}
|
||||
secrets: inherit
|
||||
test:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-test.yml
|
||||
needs: build
|
||||
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
inputs:
|
||||
target-platform:
|
||||
type: string
|
||||
description: 'Platform to run on, can be macos or linux'
|
||||
description: 'Platform to run on, can be macos, win or linux'
|
||||
required: true
|
||||
target-arch:
|
||||
type: string
|
||||
@@ -15,10 +15,6 @@ on:
|
||||
type: string
|
||||
description: 'What host to run the build'
|
||||
required: true
|
||||
check-runs-on:
|
||||
type: string
|
||||
description: 'What host to run the gn-check'
|
||||
required: true
|
||||
test-runs-on:
|
||||
type: string
|
||||
description: 'What host to run the tests on'
|
||||
@@ -82,16 +78,6 @@ jobs:
|
||||
upload-to-storage: ${{ inputs.upload-to-storage }}
|
||||
is-asan: ${{ inputs.is-asan}}
|
||||
secrets: inherit
|
||||
gn-check:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
|
||||
with:
|
||||
target-platform: ${{ inputs.target-platform }}
|
||||
target-arch: ${{ inputs.target-arch }}
|
||||
check-runs-on: ${{ inputs.check-runs-on }}
|
||||
check-container: ${{ inputs.build-container }}
|
||||
gn-build-type: ${{ inputs.gn-build-type }}
|
||||
is-asan: ${{ inputs.is-asan }}
|
||||
secrets: inherit
|
||||
test:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-test.yml
|
||||
needs: build
|
||||
|
||||
@@ -24,10 +24,9 @@ jobs:
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Run TS/JS compile
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
5
.github/workflows/pipeline-electron-lint.yml
vendored
5
.github/workflows/pipeline-electron-lint.yml
vendored
@@ -24,10 +24,9 @@ jobs:
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Setup third_party Depot Tools
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
type: string
|
||||
target-platform:
|
||||
type: string
|
||||
description: 'Platform to run on, can be macos or linux'
|
||||
description: 'Platform to run on, can be macos, win or linux'
|
||||
required: true
|
||||
target-arch:
|
||||
type: string
|
||||
@@ -69,11 +69,14 @@ env:
|
||||
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
|
||||
SUDOWOODO_EXCHANGE_URL: ${{ secrets.SUDOWOODO_EXCHANGE_URL }}
|
||||
SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }}
|
||||
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
|
||||
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || inputs.target-platform == 'win' && '--custom-var=checkout_win=True' || '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
|
||||
ELECTRON_OUT_DIR: Default
|
||||
|
||||
jobs:
|
||||
build:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
runs-on: ${{ inputs.build-runs-on }}
|
||||
container: ${{ fromJSON(inputs.build-container) }}
|
||||
environment: ${{ inputs.environment }}
|
||||
@@ -81,12 +84,14 @@ jobs:
|
||||
TARGET_ARCH: ${{ inputs.target-arch }}
|
||||
steps:
|
||||
- name: Create src dir
|
||||
run: mkdir src
|
||||
run: |
|
||||
mkdir src
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Free up space (macOS)
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: ./src/electron/.github/actions/free-space-macos
|
||||
@@ -101,9 +106,7 @@ jobs:
|
||||
cache: yarn
|
||||
cache-dependency-path: src/electron/yarn.lock
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Install AZCopy
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
run: brew install azcopy
|
||||
@@ -137,16 +140,13 @@ jobs:
|
||||
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
if [ "`uname`" = "Linux" ]; then
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
fi
|
||||
touch .disable_auto_update
|
||||
- name: Add Depot Tools to PATH
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
- name: Generate DEPS Hash
|
||||
run: |
|
||||
node src/electron/script/generate-deps-hash.js && cat src/electron/.depshash-target
|
||||
DEPSHASH=v1-src-cache-$(shasum src/electron/.depshash | cut -f1 -d' ')
|
||||
node src/electron/script/generate-deps-hash.js
|
||||
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
|
||||
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
|
||||
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
|
||||
- name: Restore src cache via AZCopy
|
||||
@@ -155,11 +155,17 @@ jobs:
|
||||
- name: Restore src cache via AKS
|
||||
if: ${{ inputs.target-platform == 'linux' }}
|
||||
uses: ./src/electron/.github/actions/restore-cache-aks
|
||||
- name: Checkout src via gclient sync
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
uses: ./src/electron/.github/actions/checkout
|
||||
with:
|
||||
use-cache: 'false'
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install Build Tools
|
||||
uses: ./src/electron/.github/actions/install-build-tools
|
||||
- name: Init Build Tools
|
||||
@@ -167,11 +173,11 @@ jobs:
|
||||
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu ${{ inputs.target-arch }}
|
||||
- name: Run Electron Only Hooks
|
||||
run: |
|
||||
gclient runhooks --spec="solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]"
|
||||
e d gclient runhooks --spec="solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]"
|
||||
- name: Regenerate DEPS Hash
|
||||
run: |
|
||||
(cd src/electron && git checkout .) && node src/electron/script/generate-deps-hash.js && cat src/electron/.depshash-target
|
||||
echo "DEPSHASH=$(shasum src/electron/.depshash | cut -f1 -d' ')" >> $GITHUB_ENV
|
||||
(cd src/electron && git checkout .) && node src/electron/script/generate-deps-hash.js
|
||||
echo "DEPSHASH=$(cat src/electron/.depshash)" >> $GITHUB_ENV
|
||||
- name: Add CHROMIUM_BUILDTOOLS_PATH to env
|
||||
run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV
|
||||
- name: Fix Sync (macOS)
|
||||
@@ -179,7 +185,7 @@ jobs:
|
||||
uses: ./src/electron/.github/actions/fix-sync-macos
|
||||
- name: Setup Number of Ninja Processes
|
||||
run: |
|
||||
echo "NUMBER_OF_NINJA_PROCESSES=${{ inputs.target-platform == 'linux' && '300' || '200' }}" >> $GITHUB_ENV
|
||||
echo "NUMBER_OF_NINJA_PROCESSES=${{ inputs.target-platform != 'macos' && '300' || '200' }}" >> $GITHUB_ENV
|
||||
- name: Free up space (macOS)
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: ./src/electron/.github/actions/free-space-macos
|
||||
@@ -189,7 +195,7 @@ jobs:
|
||||
with:
|
||||
target-arch: ${{ inputs.target-arch }}
|
||||
target-platform: ${{ inputs.target-platform }}
|
||||
artifact-platform: ${{ inputs.target-platform == 'linux' && 'linux' || 'darwin' }}
|
||||
artifact-platform: ${{ inputs.target-platform == 'macos' && 'darwin' || inputs.target-platform }}
|
||||
is-release: '${{ inputs.is-release }}'
|
||||
generate-symbols: '${{ inputs.generate-symbols }}'
|
||||
strip-binaries: '${{ inputs.strip-binaries }}'
|
||||
|
||||
@@ -5,11 +5,11 @@ on:
|
||||
inputs:
|
||||
target-platform:
|
||||
type: string
|
||||
description: 'Platform to run on, can be macos or linux'
|
||||
description: 'Platform to run on, can be macos, win or linux'
|
||||
required: true
|
||||
target-arch:
|
||||
target-archs:
|
||||
type: string
|
||||
description: 'Arch to build for, can be x64, arm64 or arm'
|
||||
description: 'Archs to check for, can be x64, x86, arm64 or arm space separated'
|
||||
required: true
|
||||
check-runs-on:
|
||||
type: string
|
||||
@@ -25,35 +25,30 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
default: testing
|
||||
is-asan:
|
||||
description: 'Building the Address Sanitizer (ASan) Linux build'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: electron-gn-check-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ inputs.is-asan }}-${{ github.ref }}
|
||||
group: electron-gn-check-${{ inputs.target-platform }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
|
||||
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
|
||||
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || (inputs.target-platform == 'linux' && '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' || '--custom-var=checkout_win=True') }}
|
||||
ELECTRON_OUT_DIR: Default
|
||||
TARGET_ARCH: ${{ inputs.target-arch }}
|
||||
|
||||
jobs:
|
||||
gn-check:
|
||||
# TODO(codebytere): Change this to medium VM
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
runs-on: ${{ inputs.check-runs-on }}
|
||||
container: ${{ fromJSON(inputs.check-container) }}
|
||||
env:
|
||||
TARGET_ARCH: ${{ inputs.target-arch }}
|
||||
steps:
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Cleanup disk space on macOS
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
shell: bash
|
||||
@@ -73,58 +68,40 @@ jobs:
|
||||
run: df -h
|
||||
- name: Install Build Tools
|
||||
uses: ./src/electron/.github/actions/install-build-tools
|
||||
- name: Init Build Tools
|
||||
- name: Enable windows toolchain
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
run: |
|
||||
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu ${{ inputs.target-arch }}
|
||||
- name: Get Depot Tools
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
|
||||
SEDOPTION="-i"
|
||||
if [ "`uname`" = "Darwin" ]; then
|
||||
SEDOPTION="-i ''"
|
||||
fi
|
||||
|
||||
# remove ninjalog_uploader_wrapper.py from autoninja since we don't use it and it causes problems
|
||||
sed $SEDOPTION '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
if [ "`uname`" = "Linux" ]; then
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
fi
|
||||
touch .disable_auto_update
|
||||
- name: Add Depot Tools to PATH
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
- name: Set GN_EXTRA_ARGS for Linux
|
||||
if: ${{ inputs.target-platform == 'linux' }}
|
||||
run: |
|
||||
if [ "${{ inputs.target-arch }}" = "arm" ]; then
|
||||
GN_EXTRA_ARGS='build_tflite_with_xnnpack=false'
|
||||
elif [ "${{ inputs.target-arch }}" = "arm64" ]; then
|
||||
GN_EXTRA_ARGS='fatal_linker_warnings=false enable_linux_installer=false'
|
||||
fi
|
||||
echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV
|
||||
echo "ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN=1" >> $GITHUB_ENV
|
||||
- name: Generate DEPS Hash
|
||||
run: |
|
||||
node src/electron/script/generate-deps-hash.js && cat src/electron/.depshash-target
|
||||
DEPSHASH=v1-src-cache-$(shasum src/electron/.depshash | cut -f1 -d' ')
|
||||
node src/electron/script/generate-deps-hash.js
|
||||
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
|
||||
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
|
||||
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
|
||||
- name: Restore src cache via AZCopy
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: ./src/electron/.github/actions/restore-cache-azcopy
|
||||
- name: Restore src cache via AKS
|
||||
if: ${{ inputs.target-platform == 'linux' }}
|
||||
if: ${{ inputs.target-platform == 'linux' || inputs.target-platform == 'win' }}
|
||||
uses: ./src/electron/.github/actions/restore-cache-aks
|
||||
- name: Run Electron Only Hooks
|
||||
run: |
|
||||
gclient runhooks --spec="solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]"
|
||||
echo "solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]" > tmpgclient
|
||||
if [ "${{ inputs.target-platform }}" = "win" ]; then
|
||||
echo "solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False,'install_sysroot':False,'checkout_win':True},'managed':False}]" > tmpgclient
|
||||
echo "target_os=['win']" >> tmpgclient
|
||||
fi
|
||||
e d gclient runhooks --gclientfile=tmpgclient
|
||||
|
||||
# Fix VS Toolchain
|
||||
if [ "${{ inputs.target-platform }}" = "win" ]; then
|
||||
rm -rf src/third_party/depot_tools/win_toolchain/vs_files
|
||||
e d python3 src/build/vs_toolchain.py update --force
|
||||
fi
|
||||
- name: Regenerate DEPS Hash
|
||||
run: |
|
||||
(cd src/electron && git checkout .) && node src/electron/script/generate-deps-hash.js && cat src/electron/.depshash-target
|
||||
echo "DEPSHASH=$(shasum src/electron/.depshash | cut -f1 -d' ')" >> $GITHUB_ENV
|
||||
(cd src/electron && git checkout .) && node src/electron/script/generate-deps-hash.js
|
||||
echo "DEPSHASH=$(cat src/electron/.depshash)" >> $GITHUB_ENV
|
||||
- name: Add CHROMIUM_BUILDTOOLS_PATH to env
|
||||
run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV
|
||||
- name: Checkout Electron
|
||||
@@ -132,30 +109,46 @@ jobs:
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Default GN gen
|
||||
run: |
|
||||
cd src/electron
|
||||
git pack-refs
|
||||
cd ..
|
||||
|
||||
e build --only-gen
|
||||
- name: Run GN Check
|
||||
- name: Run GN Check for ${{ inputs.target-archs }}
|
||||
run: |
|
||||
cd src
|
||||
gn check out/Default //electron:electron_lib
|
||||
gn check out/Default //electron:electron_app
|
||||
gn check out/Default //electron/shell/common:mojo
|
||||
gn check out/Default //electron/shell/common:plugin
|
||||
for target_cpu in ${{ inputs.target-archs }}
|
||||
do
|
||||
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu $target_cpu
|
||||
cd src
|
||||
export GN_EXTRA_ARGS="target_cpu=\"$target_cpu\""
|
||||
if [ "${{ inputs.target-platform }}" = "linux" ]; then
|
||||
if [ "$target_cpu" = "arm" ]; then
|
||||
export GN_EXTRA_ARGS="$GN_EXTRA_ARGS build_tflite_with_xnnpack=false"
|
||||
elif [ "$target_cpu" = "arm64" ]; then
|
||||
export GN_EXTRA_ARGS="$GN_EXTRA_ARGS fatal_linker_warnings=false enable_linux_installer=false"
|
||||
fi
|
||||
fi
|
||||
if [ "${{ inputs.target-platform }}" = "win" ]; then
|
||||
export GN_EXTRA_ARGS="$GN_EXTRA_ARGS use_v8_context_snapshot=true target_os=\"win\""
|
||||
fi
|
||||
|
||||
# Check the hunspell filenames
|
||||
node electron/script/gen-hunspell-filenames.js --check
|
||||
node electron/script/gen-libc++-filenames.js --check
|
||||
e build --only-gen
|
||||
|
||||
e d gn check out/Default //electron:electron_lib
|
||||
e d gn check out/Default //electron:electron_app
|
||||
e d gn check out/Default //electron/shell/common:mojo
|
||||
e d gn check out/Default //electron/shell/common:plugin
|
||||
|
||||
# Check the hunspell filenames
|
||||
node electron/script/gen-hunspell-filenames.js --check
|
||||
node electron/script/gen-libc++-filenames.js --check
|
||||
cd ..
|
||||
done
|
||||
- name: Wait for active SSH sessions
|
||||
if: always() && !cancelled()
|
||||
shell: bash
|
||||
run: |
|
||||
while [ -f /var/.ssh-lock ]
|
||||
do
|
||||
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
inputs:
|
||||
target-platform:
|
||||
type: string
|
||||
description: 'Platform to run on, can be macos or linux'
|
||||
description: 'Platform to run on, can be macos, win or linux'
|
||||
required: true
|
||||
target-arch:
|
||||
type: string
|
||||
@@ -41,22 +41,44 @@ env:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
runs-on: ${{ inputs.test-runs-on }}
|
||||
container: ${{ fromJSON(inputs.test-container) }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build-type: ${{ inputs.target-platform == 'macos' && fromJSON('["darwin","mas"]') || fromJSON('["linux"]') }}
|
||||
shard: ${{ inputs.target-platform == 'macos' && fromJSON('[1, 2]') || fromJSON('[1, 2, 3]') }}
|
||||
build-type: ${{ inputs.target-platform == 'macos' && fromJSON('["darwin","mas"]') || (inputs.target-platform == 'win' && fromJSON('["win"]') || fromJSON('["linux"]')) }}
|
||||
shard: ${{ inputs.target-platform == 'linux' && fromJSON('[1, 2, 3]') || fromJSON('[1, 2]') }}
|
||||
env:
|
||||
BUILD_TYPE: ${{ matrix.build-type }}
|
||||
TARGET_ARCH: ${{ inputs.target-arch }}
|
||||
ARTIFACT_KEY: ${{ matrix.build-type }}_${{ inputs.target-arch }}
|
||||
steps:
|
||||
- name: Fix node20 on arm32 runners
|
||||
if: ${{ inputs.target-arch == 'arm' }}
|
||||
if: ${{ inputs.target-arch == 'arm' && inputs.target-platform == 'linux' }}
|
||||
run: |
|
||||
cp $(which node) /mnt/runner-externals/node20/bin/
|
||||
- name: Install Git on Windows arm64 runners
|
||||
if: ${{ inputs.target-arch == 'arm64' && inputs.target-platform == 'win' }}
|
||||
shell: powershell
|
||||
run: |
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||
choco install -y --no-progress git.install --params "'/GitAndUnixToolsOnPath'"
|
||||
choco install -y --no-progress git
|
||||
choco install -y --no-progress python --version 3.11.9
|
||||
choco install -y --no-progress visualstudio2022-workload-vctools --package-parameters "--add Microsoft.VisualStudio.Component.VC.Tools.ARM64"
|
||||
echo "C:\Program Files\Git\cmd" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "C:\Program Files\Git\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "C:\Python311" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: Setup Node.js/npm
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6
|
||||
with:
|
||||
node-version: 20.11.x
|
||||
- name: Add TCC permissions on macOS
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
run: |
|
||||
@@ -95,24 +117,18 @@ jobs:
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Get Depot Tools
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
git config --global core.filemode false
|
||||
git config --global core.autocrlf false
|
||||
git config --global branch.autosetuprebase always
|
||||
git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
if [ "`uname`" = "Darwin" ]; then
|
||||
# remove ninjalog_uploader_wrapper.py from autoninja since we don't use it and it causes problems
|
||||
sed -i '' '/ninjalog_uploader_wrapper.py/d' ./autoninja
|
||||
else
|
||||
sed -i '/ninjalog_uploader_wrapper.py/d' ./autoninja
|
||||
# Remove swift-format dep from cipd on macOS until we send a patch upstream.
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
fi
|
||||
touch .disable_auto_update
|
||||
- name: Add Depot Tools to PATH
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
@@ -134,7 +150,17 @@ jobs:
|
||||
path: ./src_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}
|
||||
- name: Restore Generated Artifacts
|
||||
run: ./src/electron/script/actions/restore-artifacts.sh
|
||||
- name: Unzip Dist, Mksnapshot & Chromedriver
|
||||
- name: Unzip Dist, Mksnapshot & Chromedriver (win)
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
shell: powershell
|
||||
run: |
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||
cd src/out/Default
|
||||
Expand-Archive -Force dist.zip -DestinationPath ./
|
||||
Expand-Archive -Force chromedriver.zip -DestinationPath ./
|
||||
Expand-Archive -Force mksnapshot.zip -DestinationPath ./
|
||||
- name: Unzip Dist, Mksnapshot & Chromedriver (unix)
|
||||
if: ${{ inputs.target-platform != 'win' }}
|
||||
run: |
|
||||
cd src/out/Default
|
||||
unzip -:o dist.zip
|
||||
@@ -158,15 +184,24 @@ jobs:
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
||||
ELECTRON_SKIP_NATIVE_MODULE_TESTS: true
|
||||
DISPLAY: ':99.0'
|
||||
NPM_CONFIG_MSVS_VERSION: '2022'
|
||||
run: |
|
||||
cd src/electron
|
||||
export ELECTRON_TEST_RESULTS_DIR=`pwd`/junit
|
||||
# Get which tests are on this shard
|
||||
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ inputs.target-platform == 'macos' && 2 || 3 }})
|
||||
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ inputs.target-platform == 'linux' && 3 || 2 }})
|
||||
|
||||
# Run tests
|
||||
if [ "`uname`" = "Darwin" ]; then
|
||||
if [ "${{ inputs.target-platform }}" != "linux" ]; then
|
||||
echo "About to start tests"
|
||||
if [ "${{ inputs.target-platform }}" = "win" ]; then
|
||||
if [ "${{ inputs.target-arch }}" = "x86" ]; then
|
||||
export npm_config_arch="ia32"
|
||||
fi
|
||||
if [ "${{ inputs.target-arch }}" = "arm64" ]; then
|
||||
export ELECTRON_FORCE_TEST_SUITE_EXIT="true"
|
||||
fi
|
||||
fi
|
||||
node script/yarn test --runners=main --trace-uncaught --enable-logging --files $tests_files
|
||||
else
|
||||
chown :builduser .. && chmod g+w ..
|
||||
@@ -197,19 +232,21 @@ jobs:
|
||||
DD_CIVISIBILITY_LOGS_ENABLED: true
|
||||
DD_TAGS: "os.architecture:${{ inputs.target-arch }},os.family:${{ inputs.target-platform }},os.platform:${{ inputs.target-platform }},asan:${{ inputs.is-asan }}"
|
||||
run: |
|
||||
if ! [ -z $DD_API_KEY ]; then
|
||||
datadog-ci junit upload src/electron/junit/test-results-main.xml
|
||||
if ! [ -z $DD_API_KEY ] && [ -f src/electron/junit/test-results-main.xml ]; then
|
||||
export DATADOG_PATH=`node src/electron/script/yarn global bin`
|
||||
$DATADOG_PATH/datadog-ci junit upload src/electron/junit/test-results-main.xml
|
||||
fi
|
||||
if: always() && !cancelled()
|
||||
- name: Upload Test Artifacts
|
||||
if: always() && !cancelled()
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874
|
||||
with:
|
||||
name: test_artifacts_${{ env.ARTIFACT_KEY }}
|
||||
name: test_artifacts_${{ env.ARTIFACT_KEY }}_${{ matrix.shard }}
|
||||
path: src/electron/spec/artifacts
|
||||
if-no-files-found: ignore
|
||||
- name: Wait for active SSH sessions
|
||||
if: always() && !cancelled()
|
||||
shell: bash
|
||||
run: |
|
||||
while [ -f /var/.ssh-lock ]
|
||||
do
|
||||
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
inputs:
|
||||
target-platform:
|
||||
type: string
|
||||
description: 'Platform to run on, can be macos or linux'
|
||||
description: 'Platform to run on, can be macos, win or linux'
|
||||
required: true
|
||||
target-arch:
|
||||
type: string
|
||||
@@ -49,23 +49,20 @@ jobs:
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install Build Tools
|
||||
uses: ./src/electron/.github/actions/install-build-tools
|
||||
- name: Init Build Tools
|
||||
run: |
|
||||
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu ${{ inputs.target-arch }}
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Get Depot Tools
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
sed -i '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
touch .disable_auto_update
|
||||
- name: Add Depot Tools to PATH
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
@@ -93,6 +90,7 @@ jobs:
|
||||
node electron/script/node-spec-runner.js --default --jUnitDir=junit
|
||||
- name: Wait for active SSH sessions
|
||||
if: always() && !cancelled()
|
||||
shell: bash
|
||||
run: |
|
||||
while [ -f /var/.ssh-lock ]
|
||||
do
|
||||
@@ -112,23 +110,20 @@ jobs:
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install Build Tools
|
||||
uses: ./src/electron/.github/actions/install-build-tools
|
||||
- name: Init Build Tools
|
||||
run: |
|
||||
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }}
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Get Depot Tools
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
sed -i '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
touch .disable_auto_update
|
||||
- name: Add Depot Tools to PATH
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
@@ -155,6 +150,7 @@ jobs:
|
||||
cd src
|
||||
node electron/script/nan-spec-runner.js
|
||||
- name: Wait for active SSH sessions
|
||||
shell: bash
|
||||
if: always() && !cancelled()
|
||||
run: |
|
||||
while [ -f /var/.ssh-lock ]
|
||||
|
||||
5
.github/workflows/update_appveyor_image.yml
vendored
5
.github/workflows/update_appveyor_image.yml
vendored
@@ -23,6 +23,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
with:
|
||||
node-version: 20.11.x
|
||||
- name: Yarn install
|
||||
run: |
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -48,7 +48,6 @@ ts-gen
|
||||
|
||||
# Used to accelerate CI builds
|
||||
.depshash
|
||||
.depshash-target
|
||||
|
||||
# Used to accelerate builds after sync
|
||||
patches/mtime-cache.json
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'132.0.6834.15',
|
||||
'132.0.6834.83',
|
||||
'node_version':
|
||||
'v20.18.1',
|
||||
'nan_version':
|
||||
|
||||
@@ -187,7 +187,12 @@ for:
|
||||
7z a pdb.zip out\Default\*.pdb
|
||||
}
|
||||
- ps: |
|
||||
$manifest_file = "electron/script/zip_manifests/dist_zip.win.$env:TARGET_ARCH.manifest"
|
||||
if ($env:TARGET_ARCH -eq 'ia32') {
|
||||
$env:MANIFEST_ARCH = "x86"
|
||||
} else {
|
||||
$env:MANIFEST_ARCH = $env:TARGET_ARCH
|
||||
}
|
||||
$manifest_file = "electron/script/zip_manifests/dist_zip.win.$env:MANIFEST_ARCH.manifest"
|
||||
python3 electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip $manifest_file
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Zip contains files not listed in the manifest $manifest_file"
|
||||
|
||||
@@ -59,7 +59,7 @@ def skip_path(dep, dist_zip, target_cpu):
|
||||
and dep == "snapshot_blob.bin"
|
||||
)
|
||||
)
|
||||
if should_skip:
|
||||
if should_skip and os.environ.get('ELECTRON_DEBUG_ZIP_SKIP') == '1':
|
||||
print("Skipping {}".format(dep))
|
||||
return should_skip
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ an issue:
|
||||
### Getting started
|
||||
|
||||
* [Introduction](tutorial/introduction.md)
|
||||
* [Quick Start](tutorial/quick-start.md)
|
||||
* [Process Model](tutorial/process-model.md)
|
||||
|
||||
### Learning the basics
|
||||
|
||||
@@ -514,20 +514,20 @@ and `will-quit` events will not be emitted.
|
||||
* `args` string[] (optional)
|
||||
* `execPath` string (optional)
|
||||
|
||||
Relaunches the app when current instance exits.
|
||||
Relaunches the app when the current instance exits.
|
||||
|
||||
By default, the new instance will use the same working directory and command line
|
||||
arguments with current instance. When `args` is specified, the `args` will be
|
||||
passed as command line arguments instead. When `execPath` is specified, the
|
||||
`execPath` will be executed for relaunch instead of current app.
|
||||
arguments as the current instance. When `args` is specified, the `args` will be
|
||||
passed as the command line arguments instead. When `execPath` is specified, the
|
||||
`execPath` will be executed for the relaunch instead of the current app.
|
||||
|
||||
Note that this method does not quit the app when executed, you have to call
|
||||
Note that this method does not quit the app when executed. You have to call
|
||||
`app.quit` or `app.exit` after calling `app.relaunch` to make the app restart.
|
||||
|
||||
When `app.relaunch` is called for multiple times, multiple instances will be
|
||||
started after current instance exited.
|
||||
When `app.relaunch` is called multiple times, multiple instances will be
|
||||
started after the current instance exits.
|
||||
|
||||
An example of restarting current instance immediately and adding a new command
|
||||
An example of restarting the current instance immediately and adding a new command
|
||||
line argument to the new instance:
|
||||
|
||||
```js
|
||||
|
||||
@@ -72,7 +72,7 @@ The `menu` object has the following instance methods:
|
||||
#### `menu.popup([options])`
|
||||
|
||||
* `options` Object (optional)
|
||||
* `window` [BrowserWindow](browser-window.md) (optional) - Default is the focused window.
|
||||
* `window` [BaseWindow](base-window.md) (optional) - Default is the focused window.
|
||||
* `x` number (optional) - Default is the current mouse cursor position.
|
||||
Must be declared if `y` is declared.
|
||||
* `y` number (optional) - Default is the current mouse cursor position.
|
||||
@@ -86,13 +86,13 @@ The `menu` object has the following instance methods:
|
||||
Can be `none`, `mouse`, `keyboard`, `touch`, `touchMenu`, `longPress`, `longTap`, `touchHandle`, `stylus`, `adjustSelection`, or `adjustSelectionReset`.
|
||||
* `callback` Function (optional) - Called when menu is closed.
|
||||
|
||||
Pops up this menu as a context menu in the [`BrowserWindow`](browser-window.md).
|
||||
Pops up this menu as a context menu in the [`BaseWindow`](base-window.md).
|
||||
|
||||
#### `menu.closePopup([browserWindow])`
|
||||
#### `menu.closePopup([window])`
|
||||
|
||||
* `browserWindow` [BrowserWindow](browser-window.md) (optional) - Default is the focused window.
|
||||
* `window` [BaseWindow](base-window.md) (optional) - Default is the focused window.
|
||||
|
||||
Closes the context menu in the `browserWindow`.
|
||||
Closes the context menu in the `window`.
|
||||
|
||||
#### `menu.append(menuItem)`
|
||||
|
||||
|
||||
@@ -1360,6 +1360,36 @@ specified when registering the protocol.
|
||||
|
||||
Returns `Promise<void>` - resolves when the code cache clear operation is complete.
|
||||
|
||||
#### `ses.getSharedDictionaryUsageInfo()`
|
||||
|
||||
Returns `Promise<SharedDictionaryUsageInfo[]>` - an array of shared dictionary information entries in Chromium's networking service's storage.
|
||||
|
||||
Shared dictionaries are used to power advanced compression of data sent over the wire, specifically with Brotli and ZStandard. You don't need to call any of the shared dictionary APIs in Electron to make use of this advanced web feature, but if you do, they allow deeper control and inspection of the shared dictionaries used during decompression.
|
||||
|
||||
To get detailed information about a specific shared dictionary entry, call `getSharedDictionaryInfo(options)`.
|
||||
|
||||
#### `ses.getSharedDictionaryInfo(options)`
|
||||
|
||||
* `options` Object
|
||||
* `frameOrigin` string - The origin of the frame where the request originates. It’s specific to the individual frame making the request and is defined by its scheme, host, and port. In practice, will look like a URL.
|
||||
* `topFrameSite` string - The site of the top-level browsing context (the main frame or tab that contains the request). It’s less granular than `frameOrigin` and focuses on the broader "site" scope. In practice, will look like a URL.
|
||||
|
||||
Returns `Promise<SharedDictionaryInfo[]>` - an array of shared dictionary information entries in Chromium's networking service's storage.
|
||||
|
||||
To get information about all present shared dictionaries, call `getSharedDictionaryUsageInfo()`.
|
||||
|
||||
#### `ses.clearSharedDictionaryCache()`
|
||||
|
||||
Returns `Promise<void>` - resolves when the dictionary cache has been cleared, both in memory and on disk.
|
||||
|
||||
#### `ses.clearSharedDictionaryCacheForIsolationKey(options)`
|
||||
|
||||
* `options` Object
|
||||
* `frameOrigin` string - The origin of the frame where the request originates. It’s specific to the individual frame making the request and is defined by its scheme, host, and port. In practice, will look like a URL.
|
||||
* `topFrameSite` string - The site of the top-level browsing context (the main frame or tab that contains the request). It’s less granular than `frameOrigin` and focuses on the broader "site" scope. In practice, will look like a URL.
|
||||
|
||||
Returns `Promise<void>` - resolves when the dictionary cache has been cleared for the specified isolation key, both in memory and on disk.
|
||||
|
||||
#### `ses.setSpellCheckerEnabled(enable)`
|
||||
|
||||
* `enable` boolean
|
||||
@@ -1509,9 +1539,11 @@ session is persisted on disk. For in memory sessions this returns `null`.
|
||||
#### `ses.clearData([options])`
|
||||
|
||||
* `options` Object (optional)
|
||||
* `dataTypes` String[] (optional) - The types of data to clear. By default, this will clear all types of data.
|
||||
* `dataTypes` String[] (optional) - The types of data to clear. By default, this will clear all types of data. This
|
||||
can potentially include data types not explicitly listed here. (See Chromium's
|
||||
[`BrowsingDataRemover`][browsing-data-remover] for the full list.)
|
||||
* `backgroundFetch` - Background Fetch
|
||||
* `cache` - Cache
|
||||
* `cache` - Cache (includes `cachestorage` and `shadercache`)
|
||||
* `cookies` - Cookies
|
||||
* `downloads` - Downloads
|
||||
* `fileSystems` - File Systems
|
||||
@@ -1535,7 +1567,9 @@ This method clears more types of data and is more thorough than the
|
||||
|
||||
**Note:** Cookies are stored at a broader scope than origins. When removing cookies and filtering by `origins` (or `excludeOrigins`), the cookies will be removed at the [registrable domain](https://url.spec.whatwg.org/#host-registrable-domain) level. For example, clearing cookies for the origin `https://really.specific.origin.example.com/` will end up clearing all cookies for `example.com`. Clearing cookies for the origin `https://my.website.example.co.uk/` will end up clearing all cookies for `example.co.uk`.
|
||||
|
||||
For more information, refer to Chromium's [`BrowsingDataRemover` interface](https://source.chromium.org/chromium/chromium/src/+/main:content/public/browser/browsing_data_remover.h).
|
||||
**Note:** Clearing cache data will also clear the shared dictionary cache. This means that any dictionaries used for compression may be reloaded after clearing the cache. If you wish to clear the shared dictionary cache but leave other cached data intact, you may want to use the `clearSharedDictionaryCache` method.
|
||||
|
||||
For more information, refer to Chromium's [`BrowsingDataRemover` interface][browsing-data-remover].
|
||||
|
||||
### Instance Properties
|
||||
|
||||
@@ -1601,3 +1635,5 @@ app.whenReady().then(async () => {
|
||||
console.log('Net-logs written to', path)
|
||||
})
|
||||
```
|
||||
|
||||
[browsing-data-remover]: https://source.chromium.org/chromium/chromium/src/+/main:content/public/browser/browsing_data_remover.h
|
||||
|
||||
12
docs/api/structures/shared-dictionary-info.md
Normal file
12
docs/api/structures/shared-dictionary-info.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# SharedDictionaryInfo Object
|
||||
|
||||
* `match` string - The matching path pattern for the dictionary which was declared in 'use-as-dictionary' response header's `match` option.
|
||||
* `matchDestinations` string[] - An array of matching destinations for the dictionary which was declared in 'use-as-dictionary' response header's `match-dest` option.
|
||||
* `id` string - The Id for the dictionary which was declared in 'use-as-dictionary' response header's `id` option.
|
||||
* `dictionaryUrl` string - URL of the dictionary.
|
||||
* `lastFetchTime` Date - The time of when the dictionary was received from the network layer.
|
||||
* `responseTime` Date - The time of when the dictionary was received from the server. For cached responses, this time could be "far" in the past.
|
||||
* `expirationDuration` number - The expiration time for the dictionary which was declared in 'use-as-dictionary' response header's `expires` option in seconds.
|
||||
* `lastUsedTime` Date - The time when the dictionary was last used.
|
||||
* `size` number - The amount of bytes stored for this shared dictionary information object in Chromium's internal storage (usually Sqlite).
|
||||
* `hash` string - The sha256 hash of the dictionary binary.
|
||||
5
docs/api/structures/shared-dictionary-usage-info.md
Normal file
5
docs/api/structures/shared-dictionary-usage-info.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# SharedDictionaryUsageInfo Object
|
||||
|
||||
* `frameOrigin` string - The origin of the frame where the request originates. It’s specific to the individual frame making the request and is defined by its scheme, host, and port. In practice, will look like a URL.
|
||||
* `topFrameSite` string - The site of the top-level browsing context (the main frame or tab that contains the request). It’s less granular than `frameOrigin` and focuses on the broader "site" scope. In practice, will look like a URL.
|
||||
* `totalSizeBytes` number - The amount of bytes stored for this shared dictionary information object in Chromium's internal storage (usually Sqlite).
|
||||
@@ -37,7 +37,10 @@ Process: [Main](../glossary.md#main-process)<br />
|
||||
to load unsigned libraries. Unless you specifically need this capability, it is best to leave this disabled.
|
||||
Default is `false`.
|
||||
* `respondToAuthRequestsFromMainProcess` boolean (optional) - With this flag, all HTTP 401 and 407 network
|
||||
requests created via the [net module](net.md) will allow responding to them via the [`app#login`](app.md#event-login) event in the main process instead of the default [`login`](client-request.md#event-login) event on the [`ClientRequest`](client-request.md) object.
|
||||
requests created via the [net module](net.md) will allow responding to them via the
|
||||
[`app#login`](app.md#event-login) event in the main process instead of the default
|
||||
[`login`](client-request.md#event-login) event on the [`ClientRequest`](client-request.md) object. Default is
|
||||
`false`.
|
||||
|
||||
Returns [`UtilityProcess`](utility-process.md#class-utilityprocess)
|
||||
|
||||
|
||||
@@ -142,6 +142,29 @@ ipcRenderer.on('port', (e, msg) => {
|
||||
})
|
||||
```
|
||||
|
||||
#### `frame.collectJavaScriptCallStack()` _Experimental_
|
||||
|
||||
Returns `Promise<string> | Promise<void>` - A promise that resolves with the currently running JavaScript call
|
||||
stack. If no JavaScript runs in the frame, the promise will never resolve. In cases where the call stack is
|
||||
otherwise unable to be collected, it will return `undefined`.
|
||||
|
||||
This can be useful to determine why the frame is unresponsive in cases where there's long-running JavaScript.
|
||||
For more information, see the [proposed Crash Reporting API.](https://wicg.github.io/crash-reporting/)
|
||||
|
||||
```js
|
||||
const { app } = require('electron')
|
||||
|
||||
app.commandLine.appendSwitch('enable-features', 'DocumentPolicyIncludeJSCallStacksInCrashReports')
|
||||
|
||||
app.on('web-contents-created', (_, webContents) => {
|
||||
webContents.on('unresponsive', async () => {
|
||||
// Interrupt execution and collect call stack from unresponsive renderer
|
||||
const callStack = await webContents.mainFrame.collectJavaScriptCallStack()
|
||||
console.log('Renderer unresponsive\n', callStack)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Instance Properties
|
||||
|
||||
#### `frame.ipc` _Readonly_
|
||||
|
||||
@@ -12,6 +12,31 @@ This document uses the following convention to categorize breaking changes:
|
||||
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
|
||||
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
|
||||
|
||||
## Planned Breaking API Changes (35.0)
|
||||
|
||||
### Deprecated: `level`, `message`, `line`, and `sourceId` arguments in `console-message` event on `WebContents`
|
||||
|
||||
The `console-message` event on `WebContents` has been updated to provide details on the `Event`
|
||||
argument.
|
||||
|
||||
```js
|
||||
// Deprecated
|
||||
webContents.on('console-message', (event, level, message, line, sourceId) => {})
|
||||
|
||||
// Replace with:
|
||||
webContents.on('console-message', ({ level, message, lineNumber, sourceId, frame }) => {})
|
||||
```
|
||||
|
||||
Additionally, `level` is now a string with possible values of `info`, `warning`, `error`, and `debug`.
|
||||
|
||||
## Planned Breaking API Changes (34.0)
|
||||
|
||||
### Behavior Changed: menu bar will be hidden during fullscreen on Windows
|
||||
|
||||
This brings the behavior to parity with Linux. Prior behavior: Menu bar is still visible during fullscreen on Windows. New behavior: Menu bar is hidden during fullscreen on Windows.
|
||||
|
||||
**Correction**: This was previously listed as a breaking change in Electron 33, but was first released in Electron 34.
|
||||
|
||||
## Planned Breaking API Changes (33.0)
|
||||
|
||||
### Behavior Changed: frame properties may retrieve detached WebFrameMain instances or none at all
|
||||
@@ -69,10 +94,6 @@ mainWindow.loadURL('data:text/html,<script src="loaded-from-dataurl.js"></script
|
||||
mainWindow.loadURL('other://index.html')
|
||||
```
|
||||
|
||||
### Behavior Changed: menu bar will be hidden during fullscreen on Windows
|
||||
|
||||
This brings the behavior to parity with Linux. Prior behavior: Menu bar is still visible during fullscreen on Windows. New behavior: Menu bar is hidden during fullscreen on Windows.
|
||||
|
||||
### Behavior Changed: `webContents` property on `login` on `app`
|
||||
|
||||
The `webContents` property in the `login` event from `app` will be `null`
|
||||
@@ -111,14 +132,14 @@ The nonstandard `path` property of the Web `File` object was added in an early v
|
||||
```js
|
||||
// Before (renderer)
|
||||
|
||||
const file = document.querySelector('input[type=file]')
|
||||
const file = document.querySelector('input[type=file]').files[0]
|
||||
alert(`Uploaded file path was: ${file.path}`)
|
||||
```
|
||||
|
||||
```js
|
||||
// After (renderer)
|
||||
|
||||
const file = document.querySelector('input[type=file]')
|
||||
const file = document.querySelector('input[type=file]').files[0]
|
||||
electron.showFilePath(file)
|
||||
|
||||
// (preload)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<button id="clickme">Test Bluetooth</button>
|
||||
<button id="cancel">Cancel Bluetooth Request</button>
|
||||
|
||||
<p>Currently selected bluetooth device: <strong id="device-name""></strong></p>
|
||||
<p>Currently selected bluetooth device: <strong id="device-name"></strong></p>
|
||||
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -9,10 +9,11 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
|
||||
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
|
||||
| 34.0.0 | 2024-Oct-17 | 2024-Nov-13 | 2024-Jan-14 | 2025-Jun-24 | M132 | TBD | ✅ |
|
||||
| 35.0.0 | 2025-Jan-16 | 2025-Feb-05 | 2025-Mar-04 | 2025-Sep-02 | M134 | TBD | ✅ |
|
||||
| 34.0.0 | 2024-Oct-17 | 2024-Nov-13 | 2025-Jan-14 | 2025-Jun-24 | M132 | v20.18 | ✅ |
|
||||
| 33.0.0 | 2024-Aug-22 | 2024-Sep-18 | 2024-Oct-15 | 2025-Apr-29 | M130 | v20.18 | ✅ |
|
||||
| 32.0.0 | 2024-Jun-14 | 2024-Jul-24 | 2024-Aug-20 | 2025-Mar-04 | M128 | v20.16 | ✅ |
|
||||
| 31.0.0 | 2024-Apr-18 | 2024-May-15 | 2024-Jun-11 | 2025-Jan-14 | M126 | v20.14 | ✅ |
|
||||
| 31.0.0 | 2024-Apr-18 | 2024-May-15 | 2024-Jun-11 | 2025-Jan-14 | M126 | v20.14 | 🚫 |
|
||||
| 30.0.0 | 2024-Feb-22 | 2024-Mar-20 | 2024-Apr-16 | 2024-Oct-15 | M124 | v20.11 | 🚫 |
|
||||
| 29.0.0 | 2023-Dec-07 | 2024-Jan-24 | 2024-Feb-20 | 2024-Aug-20 | M122 | v20.9 | 🚫 |
|
||||
| 28.0.0 | 2023-Oct-11 | 2023-Nov-06 | 2023-Dec-05 | 2024-Jun-11 | M120 | v18.18 | 🚫 |
|
||||
|
||||
@@ -14,7 +14,7 @@ To configure a local keyboard shortcut, you need to specify an [`accelerator`][]
|
||||
property when creating a [MenuItem][] within the [Menu][] module.
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), update the `main.js` to be:
|
||||
[tutorial starter code][tutorial-starter-code], update the `main.js` to be:
|
||||
|
||||
```fiddle docs/fiddles/features/keyboard-shortcuts/local
|
||||
const { app, BrowserWindow, Menu, MenuItem } = require('electron/main')
|
||||
@@ -75,7 +75,7 @@ module to detect keyboard events even when the application does not have
|
||||
keyboard focus.
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), update the `main.js` to be:
|
||||
[tutorial starter code][tutorial-starter-code], update the `main.js` to be:
|
||||
|
||||
```fiddle docs/fiddles/features/keyboard-shortcuts/global
|
||||
const { app, BrowserWindow, globalShortcut } = require('electron/main')
|
||||
@@ -144,7 +144,7 @@ is emitted before dispatching `keydown` and `keyup` events in the page. It can
|
||||
be used to catch and handle custom shortcuts that are not visible in the menu.
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), update the `main.js` file with the
|
||||
[tutorial starter code][tutorial-starter-code], update the `main.js` file with the
|
||||
following lines:
|
||||
|
||||
```fiddle docs/fiddles/features/keyboard-shortcuts/interception-from-main
|
||||
@@ -207,3 +207,4 @@ Mousetrap.bind('up up down down left right left right b a enter', () => {
|
||||
[mousetrap]: https://github.com/ccampbell/mousetrap
|
||||
[dom-events]: https://developer.mozilla.org/en-US/docs/Web/Events
|
||||
[addEventListener-api]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
||||
[tutorial-starter-code]: tutorial-2-first-app.md#final-starter-code
|
||||
|
||||
@@ -86,7 +86,7 @@ The main process also controls your application's lifecycle through Electron's
|
||||
that you can use to add custom application behavior (for instance, programmatically
|
||||
quitting your application, modifying the application dock, or showing an About panel).
|
||||
|
||||
As a practical example, the app shown in the [quick start guide][quick-start-lifecycle]
|
||||
As a practical example, the app shown in the [tutorial starter code][tutorial-lifecycle]
|
||||
uses `app` APIs to create a more native application window experience.
|
||||
|
||||
```js title='main.js'
|
||||
@@ -97,7 +97,7 @@ app.on('window-all-closed', () => {
|
||||
```
|
||||
|
||||
[app]: ../api/app.md
|
||||
[quick-start-lifecycle]: ../tutorial/quick-start.md#manage-your-windows-lifecycle
|
||||
[tutorial-lifecycle]: ../tutorial/tutorial-2-first-app.md#quit-the-app-when-all-windows-are-closed-windows--linux
|
||||
|
||||
### Native APIs
|
||||
|
||||
|
||||
@@ -1,513 +0,0 @@
|
||||
# Quick Start
|
||||
|
||||
This guide will step you through the process of creating a barebones Hello World app in
|
||||
Electron, similar to [`electron/electron-quick-start`][quick-start].
|
||||
|
||||
By the end of this tutorial, your app will open a browser window that displays a web page
|
||||
with information about which Chromium, Node.js, and Electron versions are running.
|
||||
|
||||
[quick-start]: https://github.com/electron/electron-quick-start
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To use Electron, you need to install [Node.js][node-download]. We recommend that you
|
||||
use the latest `LTS` version available.
|
||||
|
||||
> Please install Node.js using pre-built installers for your platform.
|
||||
> You may encounter incompatibility issues with different development tools otherwise.
|
||||
|
||||
To check that Node.js was installed correctly, type the following commands in your
|
||||
terminal client:
|
||||
|
||||
```sh
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
|
||||
The commands should print the versions of Node.js and npm accordingly.
|
||||
|
||||
**Note:** Since Electron embeds Node.js into its binary, the version of Node.js running
|
||||
your code is unrelated to the version running on your system.
|
||||
|
||||
[node-download]: https://nodejs.org/en/download/
|
||||
|
||||
## Create your application
|
||||
|
||||
### Scaffold the project
|
||||
|
||||
Electron apps follow the same general structure as other Node.js projects.
|
||||
Start by creating a folder and initializing an npm package.
|
||||
|
||||
```sh npm2yarn
|
||||
mkdir my-electron-app && cd my-electron-app
|
||||
npm init
|
||||
```
|
||||
|
||||
The interactive `init` command will prompt you to set some fields in your config.
|
||||
There are a few rules to follow for the purposes of this tutorial:
|
||||
|
||||
* `entry point` should be `main.js`.
|
||||
* `author` and `description` can be any value, but are necessary for
|
||||
[app packaging](#package-and-distribute-your-application).
|
||||
|
||||
Your `package.json` file should look something like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-electron-app",
|
||||
"version": "1.0.0",
|
||||
"description": "Hello World!",
|
||||
"main": "main.js",
|
||||
"author": "Jane Doe",
|
||||
"license": "MIT"
|
||||
}
|
||||
```
|
||||
|
||||
Then, install the `electron` package into your app's `devDependencies`.
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev electron
|
||||
```
|
||||
|
||||
> Note: If you're encountering any issues with installing Electron, please
|
||||
> refer to the [Advanced Installation][advanced-installation] guide.
|
||||
|
||||
Finally, you want to be able to execute Electron. In the [`scripts`][package-scripts]
|
||||
field of your `package.json` config, add a `start` command like so:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"start": "electron ."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This `start` command will let you open your app in development mode.
|
||||
|
||||
```sh npm2yarn
|
||||
npm start
|
||||
```
|
||||
|
||||
> Note: This script tells Electron to run on your project's root folder. At this stage,
|
||||
> your app will immediately throw an error telling you that it cannot find an app to run.
|
||||
|
||||
[advanced-installation]: ./installation.md
|
||||
[package-scripts]: https://docs.npmjs.com/cli/v7/using-npm/scripts
|
||||
|
||||
### Run the main process
|
||||
|
||||
The entry point of any Electron application is its `main` script. This script controls the
|
||||
**main process**, which runs in a full Node.js environment and is responsible for
|
||||
controlling your app's lifecycle, displaying native interfaces, performing privileged
|
||||
operations, and managing renderer processes (more on that later).
|
||||
|
||||
During execution, Electron will look for this script in the [`main`][package-json-main]
|
||||
field of the app's `package.json` config, which you should have configured during the
|
||||
[app scaffolding](#scaffold-the-project) step.
|
||||
|
||||
To initialize the `main` script, create an empty file named `main.js` in the root folder
|
||||
of your project.
|
||||
|
||||
> Note: If you run the `start` script again at this point, your app will no longer throw
|
||||
> any errors! However, it won't do anything yet because we haven't added any code into
|
||||
> `main.js`.
|
||||
|
||||
[package-json-main]: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#main
|
||||
|
||||
### Create a web page
|
||||
|
||||
Before we can create a window for our application, we need to create the content that
|
||||
will be loaded into it. In Electron, each window displays web contents that can be loaded
|
||||
from either a local HTML file or a remote URL.
|
||||
|
||||
For this tutorial, you will be doing the former. Create an `index.html` file in the root
|
||||
folder of your project:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
We are using Node.js <span id="node-version"></span>,
|
||||
Chromium <span id="chrome-version"></span>,
|
||||
and Electron <span id="electron-version"></span>.
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
> Note: Looking at this HTML document, you can observe that the version numbers are
|
||||
> missing from the body text. We'll manually insert them later using JavaScript.
|
||||
|
||||
### Opening your web page in a browser window
|
||||
|
||||
Now that you have a web page, load it into an application window. To do so, you'll
|
||||
need two Electron modules:
|
||||
|
||||
* The [`app`][app] module, which controls your application's event lifecycle.
|
||||
* The [`BrowserWindow`][browser-window] module, which creates and manages application
|
||||
windows.
|
||||
|
||||
Because the main process runs Node.js, you can import these as [CommonJS][commonjs]
|
||||
modules at the top of your `main.js` file:
|
||||
|
||||
```js
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
```
|
||||
|
||||
Then, add a `createWindow()` function that loads `index.html` into a new `BrowserWindow`
|
||||
instance.
|
||||
|
||||
```js
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
```
|
||||
|
||||
Next, call this `createWindow()` function to open your window.
|
||||
|
||||
In Electron, browser windows can only be created after the `app` module's
|
||||
[`ready`][app-ready] event is fired. You can wait for this event by using the
|
||||
[`app.whenReady()`][app-when-ready] API. Call `createWindow()` after `whenReady()`
|
||||
resolves its Promise.
|
||||
|
||||
```js @ts-type={createWindow:()=>void}
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
```
|
||||
|
||||
> Note: At this point, your Electron application should successfully
|
||||
> open a window that displays your web page!
|
||||
|
||||
[app]: ../api/app.md
|
||||
[browser-window]: ../api/browser-window.md
|
||||
[commonjs]: https://nodejs.org/docs/latest/api/modules.html#modules_modules_commonjs_modules
|
||||
[app-ready]: ../api/app.md#event-ready
|
||||
[app-when-ready]: ../api/app.md#appwhenready
|
||||
|
||||
### Manage your window's lifecycle
|
||||
|
||||
Although you can now open a browser window, you'll need some additional boilerplate code
|
||||
to make it feel more native to each platform. Application windows behave differently on
|
||||
each OS, and Electron puts the responsibility on developers to implement these
|
||||
conventions in their app.
|
||||
|
||||
In general, you can use the `process` global's [`platform`][node-platform] attribute
|
||||
to run code specifically for certain operating systems.
|
||||
|
||||
#### Quit the app when all windows are closed (Windows & Linux)
|
||||
|
||||
On Windows and Linux, exiting all windows generally quits an application entirely.
|
||||
|
||||
To implement this, listen for the `app` module's [`'window-all-closed'`][window-all-closed]
|
||||
event, and call [`app.quit()`][app-quit] if the user is not on macOS (`darwin`).
|
||||
|
||||
```js
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
```
|
||||
|
||||
[node-platform]: https://nodejs.org/api/process.html#process_process_platform
|
||||
[window-all-closed]: ../api/app.md#event-window-all-closed
|
||||
[app-quit]: ../api/app.md#appquit
|
||||
|
||||
#### Open a window if none are open (macOS)
|
||||
|
||||
Whereas Linux and Windows apps quit when they have no windows open, macOS apps generally
|
||||
continue running even without any windows open, and activating the app when no windows
|
||||
are available should open a new one.
|
||||
|
||||
To implement this feature, listen for the `app` module's [`activate`][activate]
|
||||
event, and call your existing `createWindow()` method if no browser windows are open.
|
||||
|
||||
Because windows cannot be created before the `ready` event, you should only listen for
|
||||
`activate` events after your app is initialized. Do this by attaching your event listener
|
||||
from within your existing `whenReady()` callback.
|
||||
|
||||
[activate]: ../api/app.md#event-activate-macos
|
||||
|
||||
```js @ts-type={createWindow:()=>void}
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
> Note: At this point, your window controls should be fully functional!
|
||||
|
||||
### Access Node.js from the renderer with a preload script
|
||||
|
||||
Now, the last thing to do is print out the version numbers for Electron and its
|
||||
dependencies onto your web page.
|
||||
|
||||
Accessing this information is trivial to do in the main process through Node's
|
||||
global `process` object. However, you can't just edit the DOM from the main
|
||||
process because it has no access to the renderer's `document` context.
|
||||
They're in entirely different processes!
|
||||
|
||||
> Note: If you need a more in-depth look at Electron processes, see the
|
||||
> [Process Model][] document.
|
||||
|
||||
This is where attaching a **preload** script to your renderer comes in handy.
|
||||
A preload script runs before the renderer process is loaded, and has access to both
|
||||
renderer globals (e.g. `window` and `document`) and a Node.js environment.
|
||||
|
||||
Create a new script named `preload.js` as such:
|
||||
|
||||
```js
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const replaceText = (selector, text) => {
|
||||
const element = document.getElementById(selector)
|
||||
if (element) element.innerText = text
|
||||
}
|
||||
|
||||
for (const dependency of ['chrome', 'node', 'electron']) {
|
||||
replaceText(`${dependency}-version`, process.versions[dependency])
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The above code accesses the Node.js `process.versions` object and runs a basic `replaceText`
|
||||
helper function to insert the version numbers into the HTML document.
|
||||
|
||||
To attach this script to your renderer process, pass in the path to your preload script
|
||||
to the `webPreferences.preload` option in your existing `BrowserWindow` constructor.
|
||||
|
||||
```js
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
// include the Node.js 'path' module at the top of your file
|
||||
const path = require('node:path')
|
||||
|
||||
// modify your existing createWindow() function
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
There are two Node.js concepts that are used here:
|
||||
|
||||
* The [`__dirname`][dirname] string points to the path of the currently executing script
|
||||
(in this case, your project's root folder).
|
||||
* The [`path.join`][path-join] API joins multiple path segments together, creating a
|
||||
combined path string that works across all platforms.
|
||||
|
||||
We use a path relative to the currently executing JavaScript file so that your relative
|
||||
path will work in both development and packaged mode.
|
||||
|
||||
[Process Model]: ./process-model.md
|
||||
[dirname]: https://nodejs.org/api/modules.html#modules_dirname
|
||||
[path-join]: https://nodejs.org/api/path.html#path_path_join_paths
|
||||
|
||||
### Bonus: Add functionality to your web contents
|
||||
|
||||
At this point, you might be wondering how to add more functionality to your application.
|
||||
|
||||
For any interactions with your web contents, you want to add scripts to your
|
||||
renderer process. Because the renderer runs in a normal web environment, you can add a
|
||||
`<script>` tag right before your `index.html` file's closing `</body>` tag to include
|
||||
any arbitrary scripts you want:
|
||||
|
||||
```html
|
||||
<script src="./renderer.js"></script>
|
||||
```
|
||||
|
||||
The code contained in `renderer.js` can then use the same JavaScript APIs and tooling
|
||||
you use for typical front-end development, such as using [`webpack`][webpack] to bundle
|
||||
and minify your code or [React][react] to manage your user interfaces.
|
||||
|
||||
[webpack]: https://webpack.js.org
|
||||
[react]: https://reactjs.org
|
||||
|
||||
### Recap
|
||||
|
||||
After following the above steps, you should have a fully functional
|
||||
Electron application that looks like this:
|
||||
|
||||

|
||||
|
||||
<!--TODO(erickzhao): Remove the individual code blocks for static website -->
|
||||
The full code is available below:
|
||||
|
||||
```js
|
||||
// main.js
|
||||
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const path = require('node:path')
|
||||
|
||||
const createWindow = () => {
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
// 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', () => {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
// 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.
|
||||
```
|
||||
|
||||
```js
|
||||
// preload.js
|
||||
|
||||
// All the Node.js APIs are available in the preload process.
|
||||
// It has the same sandbox as a Chrome extension.
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const replaceText = (selector, text) => {
|
||||
const element = document.getElementById(selector)
|
||||
if (element) element.innerText = text
|
||||
}
|
||||
|
||||
for (const dependency of ['chrome', 'node', 'electron']) {
|
||||
replaceText(`${dependency}-version`, process.versions[dependency])
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```html
|
||||
<!--index.html-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
We are using Node.js <span id="node-version"></span>,
|
||||
Chromium <span id="chrome-version"></span>,
|
||||
and Electron <span id="electron-version"></span>.
|
||||
|
||||
<!-- You can also require other files to run in this process -->
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```fiddle docs/fiddles/quick-start
|
||||
```
|
||||
|
||||
To summarize all the steps we've done:
|
||||
|
||||
* We bootstrapped a Node.js application and added Electron as a dependency.
|
||||
* We created a `main.js` script that runs our main process, which controls our app
|
||||
and runs in a Node.js environment. In this script, we used Electron's `app` and
|
||||
`BrowserWindow` modules to create a browser window that displays web content
|
||||
in a separate process (the renderer).
|
||||
* In order to access certain Node.js functionality in the renderer, we attached
|
||||
a preload script to our `BrowserWindow` constructor.
|
||||
|
||||
## Package and distribute your application
|
||||
|
||||
The fastest way to distribute your newly created app is using
|
||||
[Electron Forge](https://www.electronforge.io).
|
||||
|
||||
:::info
|
||||
|
||||
To build an RPM package for Linux, you will need to [install its required system dependencies](https://www.electronforge.io/config/makers/rpm).
|
||||
|
||||
:::
|
||||
|
||||
1. Add a description to your `package.json` file, otherwise rpmbuild will fail. Blank description are not valid.
|
||||
2. Add Electron Forge as a development dependency of your app, and use its `import` command to set up
|
||||
Forge's scaffolding:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @electron-forge/cli
|
||||
npx electron-forge import
|
||||
|
||||
✔ Checking your system
|
||||
✔ Initializing Git Repository
|
||||
✔ Writing modified package.json file
|
||||
✔ Installing dependencies
|
||||
✔ Writing modified package.json file
|
||||
✔ Fixing .gitignore
|
||||
|
||||
We have ATTEMPTED to convert your app to be in a format that electron-forge understands.
|
||||
|
||||
Thanks for using "electron-forge"!!!
|
||||
```
|
||||
|
||||
3. Create a distributable using Forge's `make` command:
|
||||
|
||||
```sh npm2yarn
|
||||
npm run make
|
||||
|
||||
> my-electron-app@1.0.0 make /my-electron-app
|
||||
> electron-forge make
|
||||
|
||||
✔ Checking your system
|
||||
✔ Resolving Forge Config
|
||||
We need to package your application before we can make it
|
||||
✔ Preparing to Package Application for arch: x64
|
||||
✔ Preparing native dependencies
|
||||
✔ Packaging Application
|
||||
Making for the following targets: zip
|
||||
✔ Making for target: zip - On platform: darwin - For arch: x64
|
||||
```
|
||||
|
||||
Electron Forge creates the `out` folder where your package will be located:
|
||||
|
||||
```plain
|
||||
// Example for macOS
|
||||
out/
|
||||
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
|
||||
├── ...
|
||||
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app
|
||||
```
|
||||
@@ -1,3 +1,5 @@
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
|
||||
# Window Customization
|
||||
|
||||
The [`BrowserWindow`][] module is the foundation of your Electron application, and
|
||||
@@ -5,13 +7,15 @@ it exposes many APIs that let you customize the look and behavior of your app’
|
||||
This section covers how to implement various use cases for window customization on macOS,
|
||||
Windows, and Linux.
|
||||
|
||||
:::info
|
||||
`BrowserWindow` is a subclass of the [`BaseWindow`][] module. Both modules allow
|
||||
you to create and manage application windows in Electron, with the main difference
|
||||
being that `BrowserWindow` supports a single, full size web view while `BaseWindow`
|
||||
supports composing many web views. `BaseWindow` can be used interchangeably with `BrowserWindow`
|
||||
in the examples of the documents in this section.
|
||||
:::
|
||||
> [!NOTE]
|
||||
> `BrowserWindow` is a subclass of the [`BaseWindow`][] module. Both modules allow
|
||||
> you to create and manage application windows in Electron, with the main difference
|
||||
> being that `BrowserWindow` supports a single, full size web view while `BaseWindow`
|
||||
> supports composing many web views. `BaseWindow` can be used interchangeably with `BrowserWindow`
|
||||
> in the examples of the documents in this section.
|
||||
|
||||
<!-- markdownlint-disable-next-line MD033 -->
|
||||
<DocCardList />
|
||||
|
||||
[`BaseWindow`]: ../api/base-window.md
|
||||
[`BrowserWindow`]: ../api/browser-window.md
|
||||
|
||||
@@ -8,7 +8,7 @@ If your app doesn't use any native modules, then it's really easy to create an A
|
||||
|
||||
1. Make sure that your app's `node_modules` directory is empty.
|
||||
2. Using a _Command Prompt_, run `set npm_config_arch=arm64` before running `npm install`/`yarn install` as usual.
|
||||
3. [If you have Electron installed as a development dependency](quick-start.md#prerequisites), npm will download and unpack the arm64 version. You can then package and distribute your app as normal.
|
||||
3. [If you have Electron installed as a development dependency](tutorial-2-first-app.md#initializing-your-npm-project), npm will download and unpack the arm64 version. You can then package and distribute your app as normal.
|
||||
|
||||
## General considerations
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ To set user tasks for your application, you can use
|
||||
##### Set user tasks
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), update the `main.js` file with the
|
||||
[tutorial starter code][tutorial-starter-code], update the `main.js` file with the
|
||||
following lines:
|
||||
|
||||
```js
|
||||
@@ -121,7 +121,7 @@ To set thumbnail toolbar in your application, you need to use
|
||||
##### Set thumbnail toolbar
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), update the `main.js` file with the
|
||||
[tutorial starter code][tutorial-starter-code], update the `main.js` file with the
|
||||
following lines:
|
||||
|
||||
```js
|
||||
@@ -185,7 +185,7 @@ To set the overlay icon for a window, you need to use the
|
||||
#### Example
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), update the `main.js` file with the
|
||||
[tutorial starter code][tutorial-starter-code], update the `main.js` file with the
|
||||
following lines:
|
||||
|
||||
```js
|
||||
@@ -214,7 +214,7 @@ To flash the BrowserWindow taskbar button, you need to use the
|
||||
#### Example
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), update the `main.js` file with the
|
||||
[tutorial starter code][tutorial-starter-code], update the `main.js` file with the
|
||||
following lines:
|
||||
|
||||
```js
|
||||
@@ -231,10 +231,10 @@ In the above example, it is called when the window comes into focus,
|
||||
but you might use a timeout or some other event to disable it.
|
||||
|
||||
[msdn-flash-frame]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-flashwindow#remarks
|
||||
|
||||
[setthumbarbuttons]: ../api/browser-window.md#winsetthumbarbuttonsbuttons-windows
|
||||
[setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows
|
||||
[setoverlayicon]: ../api/browser-window.md#winsetoverlayiconoverlay-description-windows
|
||||
[flashframe]: ../api/browser-window.md#winflashframeflag
|
||||
[recent-documents]: ./recent-documents.md
|
||||
[progress-bar]: ./progress-bar.md
|
||||
[tutorial-starter-code]: ../tutorial/tutorial-2-first-app.md#final-starter-code
|
||||
|
||||
103
docs/why-electron.md
Normal file
103
docs/why-electron.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Why Electron
|
||||
|
||||
Electron is a framework enabling developers to build cross-platform desktop applications for macOS, Windows, and Linux by combining web technologies (HTML, JavaScript, CSS) with Node.js and native code. It is open-source, MIT-licensed, and free for both commercial and personal use. In this document, we’ll explain why companies and developers choose Electron.
|
||||
|
||||
We can split up the benefits of Electron in two questions: First, why should you use web technologies to build your application? Then, why should you choose Electron as the framework to do so?
|
||||
|
||||
If you’re already using web technologies for your application, you can skip straight to the `Why Electron?` section below.
|
||||
|
||||
## Why choose web technologies
|
||||
|
||||
Web technologies include HTML, CSS, JavaScript, and WebAssembly. They’re the storefront of the modern Internet. Those technologies have emerged as the best choice for building user interfaces — both for consumer applications as well as mission-critical business applications. This is true both for applications that need to run in a browser as well as desktop applications that are not accessible from a browser. Our bold claim here is that this isn’t just true for cross-platform applications that need to run on multiple operating systems but true overall.
|
||||
|
||||
As an example, NASA’s actual [Mission Control](https://github.com/nasa/openmct) is written with web technologies. The [Bloomberg Terminal](https://en.wikipedia.org/wiki/Bloomberg_Terminal), the computer system found at every financial institution, is written with web technologies and runs inside Chromium. It costs $25,000 per user, per year. The McDonald’s ordering kiosk, powering the world’s biggest food retailer, is entirely built with Chromium. The [SpaceX’s Dragon 2 space capsule](https://old.reddit.com/r/spacex/comments/gxb7j1/we_are_the_spacex_software_team_ask_us_anything/ft62781/?context=3) uses Chromium to display its interface. You get the point: web technologies are a great tech stack to build user interfaces.
|
||||
|
||||
Here are the reasons we, the Electron maintainers, are betting on the web.
|
||||
|
||||
### Versatility
|
||||
|
||||
Modern versions of HTML and CSS enable your developers and designers to fully express themselves. The web’s showcase includes Google Earth, Netflix, Spotify, Gmail, Facebook, Airbnb, or GitHub. Whatever interface your application needs, you will be able to express it with HTML, CSS, and JavaScript.
|
||||
|
||||
If you want to focus on building a great product without figuring out how you can realize your designer’s vision in a specific UI framework, the web is a safe bet.
|
||||
|
||||
### Reliability
|
||||
|
||||
Web technologies are the most-used foundation for user interfaces on the planet. The have been hardened accordingly. Modern computers have been optimized from the CPU to the operating system to be good at running web technologies. The manufacturers of your user’s devices—be that an Android phone or the latest MacBook—will ensure that they can visit websites, play videos on YouTube, or display emails. In turn, they’ll also ensure that your app has a stable foundation, even if you have just one user.
|
||||
|
||||
If you want to focus on building a great product without debugging a weird quirk that nobody has found before, the web is a safe bet.
|
||||
|
||||
### Interoperability
|
||||
|
||||
Whatever provider or customer data you need to interact with, they will have probably thought of an integration path with the web. Depending on your technology choice, embedding a YouTube video either takes 30 seconds or requires you to hire a team devoted to streaming and hardware-accelerated video decoding. In the case of YouTube, using anything other than the provided players is actually against their terms and conditions, so you’ll likely embed a browser frame before you implement your own video streaming decoder.
|
||||
|
||||
There will be virtually no platform where your app cannot run if you build it with web technologies. Virtually all devices with a display—be that an ATM, a car infotainment system, a smart TV, a fridge, or a Nintendo Switch—come with means to display web technologies. The web is safe bet if you want to be cross-platform.
|
||||
|
||||
### Ubiquity
|
||||
|
||||
It’s easy to find developers with experience building with web technologies. If you’re a developer, it’ll be easy to find answers to your questions on Google, Stack Overflow, GitHub, or a coding AI of your choice. Whatever problem you need to solve, it’s likely that somebody has solved it well before—and that you can find the answer to the puzzle online.
|
||||
|
||||
If you want to focus on building a great product with ample access to resources and materials, the web is a safe bet.
|
||||
|
||||
## Why choose Electron
|
||||
|
||||
Electron combines Chromium, Node.js, and the ability to write custom native code into one framework for building powerful desktop applications. There are three main reasons to use Electron:
|
||||
|
||||
### Enterprise-grade
|
||||
|
||||
Electron is reliable, secure, stable, and mature. It is the premier choice for companies building their flagship product. We have a list of some of those companies on our homepage, but just among chat apps, Slack, Discord, and Skype are built with Electron. Among AI applications, both OpenAI’s ChatGPT and Anthropic’s Claude use Electron. Visual Studio Code, Loom, Canva, Notion, Docker, and countless other leading developers of software bet on Electron.
|
||||
|
||||
We did make it a priority to make Electron easy to work with and a delight for developers. That’s likely the main reason why Electron became as popular as it is today — but what keeps Electron alive and thriving is the maintainer’s focus on making Electron as stable, secure, performant, and capable of mission-critical use cases for end users as possible. We’re building an Electron that is ready to be used in scenarios where unfixable bugs, unpatched security holes, and outages of any kind are worst-case scenarios.
|
||||
|
||||
### Mature
|
||||
|
||||
Our current estimation is that most desktop computers on the planet run at least one Electron app. Electron has grown by prioritizing talent in its maintainer group, fostering excellent and sustainable engineering practices in managing the ongoing maintenance, and proactively inviting companies betting on Electron to directly contribute to the project. We’re an impact project with the OpenJS foundation, which is itself a part of the Linux foundation. We share resources and expertise with other foundation projects like Node.js, ESLint, Webpack - or the Linux Kernel or Kubernetes.
|
||||
|
||||
What does all of that mean for you, a developer, in practice?
|
||||
|
||||
- **Reliable release schedule**: Electron will release a new major version in lockstep with every second major Chromium release, usually on the same day as Chromium. A lot of work, both in the form of building processes and tools, but also in terms of raw invested hours every week, has to go into making that happen.
|
||||
- **No dictators**: Sometimes, betting on a technology also requires you to bet on a single person or company. In turn, it requires you to trust that the person or company never has a breakdown, starts fighting you directly, or does anything else drastic that’ll force you rethink your entire tech stack. Electron is maintained by a diverse set of companies (Microsoft, Slack/Salesforce, Notion, and more) and will continue to welcome more companies interested in ensuring their “seat at the decision-making table”.
|
||||
|
||||
### Stability, security, performance
|
||||
|
||||
Electron delivers the best experience on all target platforms (macOS, Windows, Linux) by bundling the latest version of Chromium, V8, and Node.js directly with the application binary. When it comes to running and rendering web content with upmost stability, security, and performance, we currently believe that stack to be “best in class”.
|
||||
|
||||
#### Why bundle anything at all
|
||||
|
||||
You might wonder why we bundle Chromium’s web stack with our apps when most modern operating systems already ship a browser and some form of web view. Bundling doesn’t just increase the amount of work for Electron maintainers dramatically, it also increases the total disk size of Electron apps (most apps are >100MB). Many Electron maintainers once developed applications that did make use of embedded web views — and have since accepted the increased disk size and maintainer work as a worthy trade-off.
|
||||
|
||||
When using an operating system's built-in web view, you're limited by the browser version included in the oldest operating system version you need to support. We have found the following problems with this approach:
|
||||
|
||||
- **Stability**: The modern web technology stack is complex, and as a result, you’ll sooner or later encounter bugs. If you use the operating system’s web view, your only recourse will be to ask your customers to upgrade their operating system. If no upgrade is available for that machine (because of no ability to upgrade to the latest macOS or Windows 11), you’ll have to ask them to buy a new computer. If you’re unlucky, you’re now losing a major customer because they will not upgrade their entire fleet of thousands of machines just because one team wanted to try your startup’s app. You have _no recourse_ in this situation. Even the risk of that happening is unacceptable to the companies that employ the Electron maintainers.
|
||||
- **Security:** Similar to how you can fix stability bugs by releasing an app update, you can also release security fixes to your application without asking your customer to upgrade their operating system. Even if operating system providers prioritize updates to their built-in browser, we have not seen them reliably update the built-in web views with similar urgency. Bundling a web renderer gives you, the developer, control.
|
||||
- **Performance:** For simple HTML documents, a built-in web view will sometimes use fewer resources than an app with a bundled framework. For bigger apps, it is our experience that we can deliver better performance with the latest version of Chromium than we can with built-in web views. You might think that the built-in view can share a lot of resources with other apps and the operating system— but for security reasons, apps have to run in their own sandboxes, isolated from each other. At that point, the question is whether the OS’ web view is more performant than Chromium. Across many apps, our experience is that bundling Chromium and Node.js enables us to build better and more performant experiences.
|
||||
|
||||
#### Why bundle Chromium and Node.js
|
||||
|
||||
Electron aims to enable the apps it supports to deliver the best possible user experience, followed by the best possible developer experience. Chromium is currently the best cross-platform rendering stack available. Node.js uses Chromium’s JavaScript engine V8, allowing us to combine the powers of both.
|
||||
|
||||
- **Native code when you want it**: Thanks to Node.js’ mature native addon system, you can always write native code. There is no system API out of reach for you. Whatever macOS, Windows, or Linux feature you’ll want to integrate with —as long as you can do it in C, C++, Objective-C, Rust, or another native language, you’ll be able to do it in Electron. Again, this gives you, the developer, maximum control. With Electron, you can use web technologies without choosing _only_ web technologies.
|
||||
|
||||
### Developer experience
|
||||
|
||||
To summarize, we aim to build an Electron that is mature, enterprise-grade, and ready for mission-critical applications. We prioritize reliability, stability, security, and performance. That said, you might also choose Electron for its developer experience:
|
||||
|
||||
- **Powerful ecosystem**: Anything you find on npm will run inside Electron. Any resource available to you about how to work with Node.js also applies to Electron. In addition, Electron itself has a [thriving ecosystem](https://www.npmjs.com/search?q=electron) — including plenty of choices for installers, updaters, deeper operating system-integration, and more.
|
||||
- **Plenty of built-in capabilities:** Over the last ten years, Electron’s core has gained plenty of native capabilities that you might need to build your application. Written in C++ and Objective-C, Electron has [dozens of easy-to-use APIs for deeper operating-system integration](https://www.electronjs.org/docs/latest/api/app) — like advanced window customization for transparent or oddly shaped widgets, receiving push notifications from the Apple Push Notification Network, or handling a custom URL protocol for your app.
|
||||
- **Open source**: The entire stack is open source and open to your inspection. This ensures your freedom to add any feature or fix any bug you might encounter in the future.
|
||||
- **Native code when you need it:** It bears repeating that Electron allows you to mix and match web technologies and C++, C, Objective-C, Rust, and other native languages. Whether it be SQLite, a whole LLM, or just the ability to call one specific native API, Electron will make it easy.
|
||||
|
||||
---
|
||||
|
||||
## Why choose something else
|
||||
|
||||
As outlined above, the web is an amazing platform for building interfaces. That doesn’t mean that we, the maintainers, would build _everything_ with HTML and CSS. Here are some notable exceptions:
|
||||
|
||||
**Resource-Constrained Environments and IoT:** In scenarios with very limited memory or processing power (say, one megabyte of memory and 100MHz of processing power on a low-powered ARM Cortex-M), you will likely need to use a low-level language to directly talk to the display to output basic text and images. Even on slightly higher-powered single-chip devices you might want to consider an embedded UI framework. A classic example is a smart watch.
|
||||
|
||||
**Small Disk Footprint**: Zipped Electron apps are usually around 80 to 100 Megabytes. If a smaller disk footprint is a hard requirement, you’ll have to use something else.
|
||||
|
||||
**Operating System UI Frameworks and Libraries**: By allowing you to write native code, Electron can do anything a native application can do, including the use of the operating system’s UI components, like WinUI, SwiftUI, or AppKit. In practice, most Electron apps make rare use of that ability. If you want the majority of your app to be built with operating system-provided interface components, you’ll likely be better off building fully native apps for each operating system you’d like to target. It’s not that it’s impossible with Electron, it’ll just likely be an overall easier development process.
|
||||
|
||||
**Games and Real-Time Graphics:** If you're building a high-performance game or application requiring complex real-time 3D graphics, native frameworks like Unity, Unreal Engine, or DirectX/OpenGL will provide better performance and more direct access to graphics hardware. Web fans might point out caveats, like the fact that even Unreal Engine ships with Chromium — or that WebGPU and WebGL are developing rapidly and many game engines, including the ones listed here, can now output their games in a format that runs in a browser. That said, if you asked us to build the next AAA game, we’d likely use something else than just web technologies.
|
||||
|
||||
**Embedding Lightweight Websites**: Electron apps typically are mostly web apps with native code sprinkled in where useful. Processing-heavy Electron applications tend to write the UI in HTML/CSS and build the backend in Rust, C++, or another native language. If you’re planning to build a primarily native application that also wants to display a little website in a specific view, you might be better off using the OS-provided web view or something like [ultralight](https://ultralig.ht/).
|
||||
@@ -133,6 +133,8 @@ auto_filenames = {
|
||||
"docs/api/structures/segmented-control-segment.md",
|
||||
"docs/api/structures/serial-port.md",
|
||||
"docs/api/structures/service-worker-info.md",
|
||||
"docs/api/structures/shared-dictionary-info.md",
|
||||
"docs/api/structures/shared-dictionary-usage-info.md",
|
||||
"docs/api/structures/shared-worker-info.md",
|
||||
"docs/api/structures/sharing-item.md",
|
||||
"docs/api/structures/shortcut-details.md",
|
||||
|
||||
@@ -514,9 +514,9 @@ WebContents.prototype.canGoForward = function () {
|
||||
};
|
||||
|
||||
const canGoToOffsetDeprecated = deprecate.warnOnce('webContents.canGoToOffset', 'webContents.navigationHistory.canGoToOffset');
|
||||
WebContents.prototype.canGoToOffset = function () {
|
||||
WebContents.prototype.canGoToOffset = function (index: number) {
|
||||
canGoToOffsetDeprecated();
|
||||
return this._canGoToOffset();
|
||||
return this._canGoToOffset(index);
|
||||
};
|
||||
|
||||
const clearHistoryDeprecated = deprecate.warnOnce('webContents.clearHistory', 'webContents.navigationHistory.clear');
|
||||
|
||||
@@ -15,7 +15,7 @@ export const windowSetup = (isWebView: boolean, isHiddenPage: boolean) => {
|
||||
|
||||
// But we do not support prompt().
|
||||
window.prompt = function () {
|
||||
throw new Error('prompt() is and will not be supported.');
|
||||
throw new Error('prompt() is not supported.');
|
||||
};
|
||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['prompt'], window.prompt);
|
||||
|
||||
|
||||
@@ -135,3 +135,5 @@ chore_partial_revert_of.patch
|
||||
fix_software_compositing_infinite_loop.patch
|
||||
refactor_unfilter_unresponsive_events.patch
|
||||
support_bstr_pkey_appusermodel_id_in_windows_shortcuts.patch
|
||||
cherry-pick-1282289030ab.patch
|
||||
cherry-pick-3dc17c461b12.patch
|
||||
|
||||
265
patches/chromium/cherry-pick-1282289030ab.patch
Normal file
265
patches/chromium/cherry-pick-1282289030ab.patch
Normal file
@@ -0,0 +1,265 @@
|
||||
From 1282289030ab2026a8db4fb82dc6e20d3bf028be Mon Sep 17 00:00:00 2001
|
||||
From: Kevin Ellis <kevers@google.com>
|
||||
Date: Thu, 12 Dec 2024 06:47:30 -0800
|
||||
Subject: [PATCH] Prune superfluous calls to SetCompositorPending
|
||||
|
||||
We only need to call SetCompositorPending with the pending cancel
|
||||
reason if the animation is running on the compositor.
|
||||
|
||||
The stack trace on the bug report showed a significant time being
|
||||
spent in HasProperty. The timing was also optimized in this CL to
|
||||
prevent unnecessary duplicate calculations.
|
||||
|
||||
Bug: 382394791
|
||||
Change-Id: I03ffa1b486b267e05f63328212d192dfca26eb53
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6076354
|
||||
Reviewed-by: Robert Flack <flackr@chromium.org>
|
||||
Commit-Queue: Kevin Ellis <kevers@chromium.org>
|
||||
Reviewed-by: Claire Chambers <clchambers@microsoft.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#1395390}
|
||||
---
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
|
||||
index 9e162a6f..b58b017 100644
|
||||
--- a/third_party/blink/renderer/core/animation/animation.cc
|
||||
+++ b/third_party/blink/renderer/core/animation/animation.cc
|
||||
@@ -1271,14 +1271,7 @@
|
||||
ResolveTimelineOffsets(timeline_ ? timeline_->GetTimelineRange()
|
||||
: TimelineRange());
|
||||
|
||||
- SetOutdated();
|
||||
-
|
||||
- // 7. Run the procedure to update an animation’s finished state for animation
|
||||
- // with the did seek flag set to false (continuous), and the synchronously
|
||||
- // notify flag set to false (async).
|
||||
- UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
|
||||
-
|
||||
- SetCompositorPending(CompositorPendingReason::kPendingEffectChange);
|
||||
+ EffectInvalidated();
|
||||
|
||||
// Notify of a potential state change.
|
||||
NotifyProbe();
|
||||
@@ -2375,6 +2368,26 @@
|
||||
timeline()->IsMonotonicallyIncreasing(), boundary_aligned);
|
||||
}
|
||||
|
||||
+Animation::NativePaintWorkletReasons Animation::GetNativePaintWorkletReasons() {
|
||||
+ if (native_paint_worklet_reasons_) {
|
||||
+ return native_paint_worklet_reasons_.value();
|
||||
+ }
|
||||
+ NativePaintWorkletReasons reasons = kNoPaintWorklet;
|
||||
+ if (KeyframeEffect* keyframe_effect = DynamicTo<KeyframeEffect>(effect())) {
|
||||
+ if (RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled() &&
|
||||
+ keyframe_effect->Affects(
|
||||
+ PropertyHandle(GetCSSPropertyBackgroundColor()))) {
|
||||
+ reasons |= kBackgroundColorPaintWorklet;
|
||||
+ }
|
||||
+ if (RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled() &&
|
||||
+ keyframe_effect->Affects(PropertyHandle(GetCSSPropertyClipPath()))) {
|
||||
+ reasons |= kClipPathPaintWorklet;
|
||||
+ }
|
||||
+ }
|
||||
+ native_paint_worklet_reasons_ = reasons;
|
||||
+ return reasons;
|
||||
+}
|
||||
+
|
||||
// TODO(crbug.com/960944): Rename to SetPendingCommit. This method handles both
|
||||
// composited and non-composited animations. The use of 'compositor' in the name
|
||||
// is confusing.
|
||||
@@ -2833,7 +2846,7 @@
|
||||
// After updating the animation time if the animation is no longer current
|
||||
// blink will no longer composite the element (see
|
||||
// CompositingReasonFinder::RequiresCompositingFor*Animation).
|
||||
- if (!content_->IsCurrent()) {
|
||||
+ if (!content_->IsCurrent() && HasActiveAnimationsOnCompositor()) {
|
||||
SetCompositorPending(CompositorPendingReason::kPendingCancel);
|
||||
}
|
||||
}
|
||||
@@ -2874,6 +2887,9 @@
|
||||
}
|
||||
|
||||
void Animation::EffectInvalidated() {
|
||||
+ prior_native_paint_worklet_reasons_ = native_paint_worklet_reasons_;
|
||||
+ native_paint_worklet_reasons_ = std::nullopt;
|
||||
+
|
||||
SetOutdated();
|
||||
UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
|
||||
// FIXME: Needs to consider groups when added.
|
||||
@@ -3381,15 +3397,22 @@
|
||||
}
|
||||
|
||||
void Animation::UpdateCompositedPaintStatus() {
|
||||
- if (!NativePaintImageGenerator::NativePaintWorkletAnimationsEnabled()) {
|
||||
- return;
|
||||
+ if (GetNativePaintWorkletReasons() == Animation::kNoPaintWorklet) {
|
||||
+ if (!prior_native_paint_worklet_reasons_ ||
|
||||
+ prior_native_paint_worklet_reasons_ == Animation::kNoPaintWorklet) {
|
||||
+ return;
|
||||
+ }
|
||||
}
|
||||
|
||||
+ prior_native_paint_worklet_reasons_ = GetNativePaintWorkletReasons();
|
||||
+
|
||||
KeyframeEffect* keyframe_effect = DynamicTo<KeyframeEffect>(content_.Get());
|
||||
if (!keyframe_effect) {
|
||||
return;
|
||||
}
|
||||
|
||||
+ // TODO(crbug.com/383562308): If the target changed since the last update, we
|
||||
+ // need to trigger an update for the previous and current target.
|
||||
Element* target = keyframe_effect->EffectTarget();
|
||||
if (!target) {
|
||||
return;
|
||||
@@ -3398,14 +3421,7 @@
|
||||
ElementAnimations* element_animations = target->GetElementAnimations();
|
||||
DCHECK(element_animations);
|
||||
|
||||
- if (RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled()) {
|
||||
- element_animations->RecalcCompositedStatus(target,
|
||||
- GetCSSPropertyBackgroundColor());
|
||||
- }
|
||||
- if (RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled()) {
|
||||
- element_animations->RecalcCompositedStatus(target,
|
||||
- GetCSSPropertyClipPath());
|
||||
- }
|
||||
+ element_animations->RecalcCompositedStatus(target);
|
||||
}
|
||||
|
||||
void Animation::Trace(Visitor* visitor) const {
|
||||
diff --git a/third_party/blink/renderer/core/animation/animation.h b/third_party/blink/renderer/core/animation/animation.h
|
||||
index 61007d7..bc0a891 100644
|
||||
--- a/third_party/blink/renderer/core/animation/animation.h
|
||||
+++ b/third_party/blink/renderer/core/animation/animation.h
|
||||
@@ -393,6 +393,15 @@
|
||||
start_time_ = start_time;
|
||||
}
|
||||
|
||||
+ enum NativePaintWorkletProperties {
|
||||
+ kNoPaintWorklet = 0,
|
||||
+ kBackgroundColorPaintWorklet = 1,
|
||||
+ kClipPathPaintWorklet = 2
|
||||
+ };
|
||||
+
|
||||
+ using NativePaintWorkletReasons = uint32_t;
|
||||
+ NativePaintWorkletReasons GetNativePaintWorkletReasons();
|
||||
+
|
||||
protected:
|
||||
DispatchEventResult DispatchEventInternal(Event&) override;
|
||||
void AddedEventListener(const AtomicString& event_type,
|
||||
@@ -583,6 +592,13 @@
|
||||
|
||||
Member<Event> pending_remove_event_;
|
||||
|
||||
+ // Cache whether animation can potentially have native paint worklets.
|
||||
+ // In the event of the keyframes changing, we need a new evaluation, of
|
||||
+ // the composited status for native paint worklet eligible properties.
|
||||
+ // A change in the playState can also necessitate a composited style update.
|
||||
+ std::optional<NativePaintWorkletReasons> native_paint_worklet_reasons_;
|
||||
+ std::optional<NativePaintWorkletReasons> prior_native_paint_worklet_reasons_;
|
||||
+
|
||||
// TODO(crbug.com/960944): Consider reintroducing kPause and cleanup use of
|
||||
// mutually exclusive pending_play_ and pending_pause_ flags.
|
||||
enum class CompositorAction { kNone, kStart, kCancel };
|
||||
diff --git a/third_party/blink/renderer/core/animation/element_animations.cc b/third_party/blink/renderer/core/animation/element_animations.cc
|
||||
index ca2864f..b1f3b32 100644
|
||||
--- a/third_party/blink/renderer/core/animation/element_animations.cc
|
||||
+++ b/third_party/blink/renderer/core/animation/element_animations.cc
|
||||
@@ -97,43 +97,59 @@
|
||||
Element& element,
|
||||
AnimationEffect* effect) {
|
||||
if (KeyframeEffect* keyframe_effect = DynamicTo<KeyframeEffect>(effect)) {
|
||||
- if (CompositedBackgroundColorStatus() ==
|
||||
- ElementAnimations::CompositedPaintStatus::kComposited &&
|
||||
- keyframe_effect->Affects(
|
||||
- PropertyHandle(GetCSSPropertyBackgroundColor())) &&
|
||||
- element.GetLayoutObject()) {
|
||||
- SetCompositedBackgroundColorStatus(
|
||||
- ElementAnimations::CompositedPaintStatus::kNeedsRepaint);
|
||||
- element.GetLayoutObject()->SetShouldDoFullPaintInvalidation();
|
||||
+ if (RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled()) {
|
||||
+ if (CompositedBackgroundColorStatus() ==
|
||||
+ ElementAnimations::CompositedPaintStatus::kComposited &&
|
||||
+ keyframe_effect->Affects(
|
||||
+ PropertyHandle(GetCSSPropertyBackgroundColor())) &&
|
||||
+ element.GetLayoutObject()) {
|
||||
+ SetCompositedBackgroundColorStatus(
|
||||
+ ElementAnimations::CompositedPaintStatus::kNeedsRepaint);
|
||||
+ element.GetLayoutObject()->SetShouldDoFullPaintInvalidation();
|
||||
+ }
|
||||
}
|
||||
|
||||
- if (CompositedClipPathStatus() ==
|
||||
- ElementAnimations::CompositedPaintStatus::kComposited &&
|
||||
- keyframe_effect->Affects(PropertyHandle(GetCSSPropertyClipPath())) &&
|
||||
- element.GetLayoutObject()) {
|
||||
- SetCompositedClipPathStatus(
|
||||
- ElementAnimations::CompositedPaintStatus::kNeedsRepaint);
|
||||
- element.GetLayoutObject()->SetShouldDoFullPaintInvalidation();
|
||||
- // For clip paths, we also need to update the paint properties to switch
|
||||
- // from path based to mask based clip.
|
||||
- element.GetLayoutObject()->SetNeedsPaintPropertyUpdate();
|
||||
+ if (RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled()) {
|
||||
+ if (CompositedClipPathStatus() ==
|
||||
+ ElementAnimations::CompositedPaintStatus::kComposited &&
|
||||
+ keyframe_effect->Affects(PropertyHandle(GetCSSPropertyClipPath())) &&
|
||||
+ element.GetLayoutObject()) {
|
||||
+ SetCompositedClipPathStatus(
|
||||
+ ElementAnimations::CompositedPaintStatus::kNeedsRepaint);
|
||||
+ element.GetLayoutObject()->SetShouldDoFullPaintInvalidation();
|
||||
+ // For clip paths, we also need to update the paint properties to switch
|
||||
+ // from path based to mask based clip.
|
||||
+ element.GetLayoutObject()->SetNeedsPaintPropertyUpdate();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-void ElementAnimations::RecalcCompositedStatus(Element* element,
|
||||
- const CSSProperty& property) {
|
||||
- ElementAnimations::CompositedPaintStatus status =
|
||||
- HasAnimationForProperty(property)
|
||||
- ? ElementAnimations::CompositedPaintStatus::kNeedsRepaint
|
||||
- : ElementAnimations::CompositedPaintStatus::kNoAnimation;
|
||||
+void ElementAnimations::RecalcCompositedStatus(Element* element) {
|
||||
+ Animation::NativePaintWorkletReasons reasons = Animation::kNoPaintWorklet;
|
||||
+ for (auto& entry : Animations()) {
|
||||
+ if (entry.key->CalculateAnimationPlayState() ==
|
||||
+ V8AnimationPlayState::Enum::kIdle) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ reasons |= entry.key->GetNativePaintWorkletReasons();
|
||||
+ }
|
||||
|
||||
- if (property.PropertyID() == CSSPropertyID::kBackgroundColor) {
|
||||
+ if (RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled()) {
|
||||
+ ElementAnimations::CompositedPaintStatus status =
|
||||
+ reasons & Animation::kBackgroundColorPaintWorklet
|
||||
+ ? ElementAnimations::CompositedPaintStatus::kNeedsRepaint
|
||||
+ : ElementAnimations::CompositedPaintStatus::kNoAnimation;
|
||||
if (SetCompositedBackgroundColorStatus(status) &&
|
||||
element->GetLayoutObject()) {
|
||||
element->GetLayoutObject()->SetShouldDoFullPaintInvalidation();
|
||||
}
|
||||
- } else if (property.PropertyID() == CSSPropertyID::kClipPath) {
|
||||
+ }
|
||||
+ if (RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled()) {
|
||||
+ ElementAnimations::CompositedPaintStatus status =
|
||||
+ reasons & Animation::kClipPathPaintWorklet
|
||||
+ ? ElementAnimations::CompositedPaintStatus::kNeedsRepaint
|
||||
+ : ElementAnimations::CompositedPaintStatus::kNoAnimation;
|
||||
if (SetCompositedClipPathStatus(status) && element->GetLayoutObject()) {
|
||||
element->GetLayoutObject()->SetShouldDoFullPaintInvalidation();
|
||||
// For clip paths, we also need to update the paint properties to switch
|
||||
diff --git a/third_party/blink/renderer/core/animation/element_animations.h b/third_party/blink/renderer/core/animation/element_animations.h
|
||||
index 624f542..3171061 100644
|
||||
--- a/third_party/blink/renderer/core/animation/element_animations.h
|
||||
+++ b/third_party/blink/renderer/core/animation/element_animations.h
|
||||
@@ -120,7 +120,7 @@
|
||||
|
||||
void RecalcCompositedStatusForKeyframeChange(Element& element,
|
||||
AnimationEffect* effect);
|
||||
- void RecalcCompositedStatus(Element* element, const CSSProperty& property);
|
||||
+ void RecalcCompositedStatus(Element* element);
|
||||
|
||||
// TODO(crbug.com/1301961): Consider converting to an array or flat map of
|
||||
// fields for paint properties that can be composited.
|
||||
115
patches/chromium/cherry-pick-3dc17c461b12.patch
Normal file
115
patches/chromium/cherry-pick-3dc17c461b12.patch
Normal file
@@ -0,0 +1,115 @@
|
||||
From 3dc17c461b12fd735c1173a90a2ca62c086a4110 Mon Sep 17 00:00:00 2001
|
||||
From: Orko Garai <orko@igalia.com>
|
||||
Date: Fri, 29 Nov 2024 16:17:04 +0000
|
||||
Subject: [PATCH] Wayland IME: Underline composition text fallback
|
||||
|
||||
At this time text-input-v3 does not provide any styling information.
|
||||
As a quality-of-life improvement, ensure that a default composition
|
||||
style is applied so that the composition text is underlined.
|
||||
|
||||
This will also ensure that the user experience is consistent with
|
||||
ozone/x11.
|
||||
|
||||
Bug: 355238629
|
||||
Change-Id: I8d4bce5e5700510d72f114bb57171f43646be098
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5741768
|
||||
Commit-Queue: Orko Garai <orko@igalia.com>
|
||||
Reviewed-by: Darren Shen <shend@chromium.org>
|
||||
Reviewed-by: Kramer Ge <fangzhoug@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1389833}
|
||||
---
|
||||
|
||||
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
|
||||
index 715e181a..2cfa5cc 100644
|
||||
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
|
||||
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
|
||||
@@ -530,6 +530,7 @@
|
||||
const gfx::Range& preedit_cursor) {
|
||||
CompositionText composition_text;
|
||||
composition_text.text = base::UTF8ToUTF16(text);
|
||||
+ bool has_composition_style = false;
|
||||
for (const auto& span : spans) {
|
||||
auto start_offset = OffsetFromUTF8Offset(text, span.index);
|
||||
if (!start_offset)
|
||||
@@ -540,9 +541,18 @@
|
||||
const auto& style = span.style;
|
||||
if (!style.has_value())
|
||||
continue;
|
||||
+ if (style->type == ImeTextSpan::Type::kComposition) {
|
||||
+ has_composition_style = true;
|
||||
+ }
|
||||
composition_text.ime_text_spans.emplace_back(style->type, *start_offset,
|
||||
*end_offset, style->thickness);
|
||||
}
|
||||
+ if (!composition_text.text.empty() && !has_composition_style) {
|
||||
+ // If no explicit composition style is specified, add default composition
|
||||
+ // style to the composition text.
|
||||
+ composition_text.ime_text_spans.emplace_back(
|
||||
+ ImeTextSpan::Type::kComposition, 0, composition_text.text.length());
|
||||
+ }
|
||||
if (!preedit_cursor.IsValid()) {
|
||||
// This is the case if a preceding preedit_cursor event in text-input-v1 was
|
||||
// not received or an explicit negative value was requested to hide the
|
||||
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
|
||||
index d005de4..dbb339d 100644
|
||||
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
|
||||
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
|
||||
@@ -1174,6 +1174,34 @@
|
||||
});
|
||||
}
|
||||
|
||||
+TEST_P(WaylandInputMethodContextTest, OnPreeditChangedDefaultCompositionStyle) {
|
||||
+ constexpr std::string_view kPreeditString("PreeditString");
|
||||
+ constexpr gfx::Range kSelection{7, 13};
|
||||
+ input_method_context_->OnPreeditString(
|
||||
+ kPreeditString,
|
||||
+ // No composition style provided.
|
||||
+ {{1,
|
||||
+ 3,
|
||||
+ {{ImeTextSpan::Type::kMisspellingSuggestion,
|
||||
+ ImeTextSpan::Thickness::kNone}}}},
|
||||
+ kSelection);
|
||||
+ EXPECT_TRUE(input_method_context_delegate_->was_on_preedit_changed_called());
|
||||
+ EXPECT_EQ(input_method_context_delegate_->last_preedit()->ime_text_spans,
|
||||
+ (ImeTextSpans{ImeTextSpan(ImeTextSpan::Type::kMisspellingSuggestion,
|
||||
+ 1, 4, ImeTextSpan::Thickness::kNone),
|
||||
+ // Default composition should be applied.
|
||||
+ ImeTextSpan(ImeTextSpan::Type::kComposition, 0,
|
||||
+ kPreeditString.size(),
|
||||
+ ImeTextSpan::Thickness::kThin)}));
|
||||
+ EXPECT_EQ(
|
||||
+ input_method_context_->predicted_state_for_testing().surrounding_text,
|
||||
+ u"PreeditString");
|
||||
+ EXPECT_EQ(input_method_context_->predicted_state_for_testing().composition,
|
||||
+ gfx::Range(0, kPreeditString.size()));
|
||||
+ EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
|
||||
+ kSelection);
|
||||
+}
|
||||
+
|
||||
TEST_P(WaylandInputMethodContextTest, OnPreeditChanged) {
|
||||
constexpr std::string_view kPreeditString("PreeditString");
|
||||
constexpr gfx::Range kSelection{7, 13};
|
||||
@@ -1181,13 +1209,19 @@
|
||||
kPreeditString,
|
||||
{{0,
|
||||
static_cast<uint32_t>(kPreeditString.size()),
|
||||
- {{ImeTextSpan::Type::kComposition, ImeTextSpan::Thickness::kThin}}}},
|
||||
+ {{ImeTextSpan::Type::kComposition, ImeTextSpan::Thickness::kThick}}},
|
||||
+ {1,
|
||||
+ 3,
|
||||
+ {{ImeTextSpan::Type::kMisspellingSuggestion,
|
||||
+ ImeTextSpan::Thickness::kNone}}}},
|
||||
kSelection);
|
||||
EXPECT_TRUE(input_method_context_delegate_->was_on_preedit_changed_called());
|
||||
EXPECT_EQ(input_method_context_delegate_->last_preedit()->ime_text_spans,
|
||||
- ImeTextSpans{ImeTextSpan(ImeTextSpan::Type::kComposition, 0,
|
||||
- kPreeditString.size(),
|
||||
- ImeTextSpan::Thickness::kThin)});
|
||||
+ (ImeTextSpans{ImeTextSpan(ImeTextSpan::Type::kComposition, 0,
|
||||
+ kPreeditString.size(),
|
||||
+ ImeTextSpan::Thickness::kThick),
|
||||
+ ImeTextSpan(ImeTextSpan::Type::kMisspellingSuggestion,
|
||||
+ 1, 4, ImeTextSpan::Thickness::kNone)}));
|
||||
EXPECT_EQ(
|
||||
input_method_context_->predicted_state_for_testing().surrounding_text,
|
||||
u"PreeditString");
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
if [ "$(expr substr $(uname -s) 1 10)" == "MSYS_NT-10" ]; then
|
||||
BUILD_TYPE="win"
|
||||
elif [ "`uname`" == "Darwin" ]; then
|
||||
if [ -z "$MAS_BUILD" ]; then
|
||||
BUILD_TYPE="darwin"
|
||||
else
|
||||
@@ -46,23 +48,47 @@ cp_if_exist() {
|
||||
move_src_dirs_if_exist() {
|
||||
mkdir src_artifacts
|
||||
|
||||
for dir in \
|
||||
src/out/Default/gen/node_headers \
|
||||
src/out/Default/overlapped-checker \
|
||||
src/out/Default/ffmpeg \
|
||||
src/out/Default/hunspell_dictionaries \
|
||||
src/third_party/electron_node \
|
||||
src/third_party/nan \
|
||||
src/cross-arch-snapshots \
|
||||
src/third_party/llvm-build \
|
||||
src/build/linux \
|
||||
src/buildtools/mac \
|
||||
src/buildtools/third_party/libc++ \
|
||||
src/buildtools/third_party/libc++abi \
|
||||
src/third_party/libc++ \
|
||||
src/third_party/libc++abi \
|
||||
src/out/Default/obj/buildtools/third_party \
|
||||
src/v8/tools/builtins-pgo
|
||||
dirs=("src/out/Default/gen/node_headers" \
|
||||
"src/out/Default/overlapped-checker" \
|
||||
"src/out/Default/ffmpeg" \
|
||||
"src/out/Default/hunspell_dictionaries" \
|
||||
"src/third_party/electron_node" \
|
||||
"src/third_party/nan" \
|
||||
"src/cross-arch-snapshots" \
|
||||
"src/buildtools/mac" \
|
||||
"src/buildtools/third_party/libc++" \
|
||||
"src/buildtools/third_party/libc++abi" \
|
||||
"src/third_party/libc++" \
|
||||
"src/third_party/libc++abi" \
|
||||
"src/out/Default/obj/buildtools/third_party" \
|
||||
"src/v8/tools/builtins-pgo")
|
||||
|
||||
# Only do this for linux build type, this folder
|
||||
# exists for windows builds on linux hosts but we do
|
||||
# not need it
|
||||
if [ "$BUILD_TYPE" == "linux" ]; then
|
||||
dirs+=('src/build/linux')
|
||||
fi
|
||||
|
||||
# llvm-build is the host toolchain, for windows we need
|
||||
# a different toolchain so no point copying this one
|
||||
if [ "$BUILD_TYPE" != "win" ]; then
|
||||
dirs+=('src/third_party/llvm-build')
|
||||
fi
|
||||
|
||||
# On windows we should clean up two symlinks that aren't
|
||||
# compatible with the windows test runner
|
||||
if [ "$BUILD_TYPE" == "win" ]; then
|
||||
rm -f src/third_party/electron_node/tools/node_modules/eslint/node_modules/eslint
|
||||
rm -f src/third_party/electron_node/tools/node_modules/eslint/node_modules/.bin/eslint
|
||||
rm -f src/third_party/electron_node/out/tools/bin/python
|
||||
|
||||
# Also need to copy electron.lib to node.lib for native module testing purposes
|
||||
mkdir -p src/out/Default/gen/node_headers/Release
|
||||
cp src/out/Default/electron.lib src/out/Default/gen/node_headers/Release/node.lib
|
||||
fi
|
||||
|
||||
for dir in "${dirs[@]}"
|
||||
do
|
||||
if [ -d "$dir" ]; then
|
||||
mkdir -p src_artifacts/$(dirname $dir)
|
||||
@@ -70,7 +96,7 @@ move_src_dirs_if_exist() {
|
||||
fi
|
||||
done
|
||||
|
||||
tar -C src_artifacts -cf src_artifacts.tar ./
|
||||
tar -C src_artifacts -cf src_artifacts.tar .
|
||||
|
||||
echo Storing src_artifacts.tar
|
||||
mv src_artifacts.tar $SRC_ARTIFACTS
|
||||
|
||||
@@ -2,21 +2,19 @@ const crypto = require('node:crypto');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
// Fallback to blow away old cache keys
|
||||
const FALLBACK_HASH_VERSION = 3;
|
||||
|
||||
// Per platform hash versions to bust the cache on different platforms
|
||||
const HASH_VERSIONS = {
|
||||
darwin: 3,
|
||||
darwin: 4,
|
||||
win32: 4,
|
||||
linux: 3
|
||||
linux: 4
|
||||
};
|
||||
|
||||
// Base files to hash
|
||||
const filesToHash = [
|
||||
path.resolve(__dirname, '../DEPS'),
|
||||
path.resolve(__dirname, '../yarn.lock'),
|
||||
path.resolve(__dirname, '../script/sysroots.json')
|
||||
path.resolve(__dirname, '../script/sysroots.json'),
|
||||
path.resolve(__dirname, '../.github/actions/checkout/action.yml')
|
||||
];
|
||||
|
||||
const addAllFiles = (dir) => {
|
||||
@@ -38,7 +36,7 @@ const hasher = crypto.createHash('SHA256');
|
||||
const addToHashAndLog = (s) => {
|
||||
return hasher.update(s);
|
||||
};
|
||||
addToHashAndLog(`HASH_VERSION:${HASH_VERSIONS[process.platform] || FALLBACK_HASH_VERSION}`);
|
||||
addToHashAndLog(`HASH_VERSION:${HASH_VERSIONS[process.platform]}`);
|
||||
for (const file of filesToHash) {
|
||||
hasher.update(fs.readFileSync(file));
|
||||
}
|
||||
@@ -47,15 +45,5 @@ for (const file of filesToHash) {
|
||||
const extraArgs = process.env.GCLIENT_EXTRA_ARGS || 'no_extra_args';
|
||||
addToHashAndLog(extraArgs);
|
||||
|
||||
const effectivePlatform = extraArgs.includes('host_os=mac') ? 'darwin' : process.platform;
|
||||
|
||||
// Write the hash to disk
|
||||
fs.writeFileSync(path.resolve(__dirname, '../.depshash'), hasher.digest('hex'));
|
||||
|
||||
let targetContent = `${effectivePlatform}\n${process.env.TARGET_ARCH}\n${process.env.GN_CONFIG}\n${undefined}\n${process.env.GN_EXTRA_ARGS}\n${process.env.GN_BUILDFLAG_ARGS}`;
|
||||
const argsDir = path.resolve(__dirname, '../build/args');
|
||||
for (const argFile of fs.readdirSync(argsDir).sort()) {
|
||||
targetContent += `\n${argFile}--${crypto.createHash('SHA1').update(fs.readFileSync(path.resolve(argsDir, argFile))).digest('hex')}`;
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.resolve(__dirname, '../.depshash-target'), targetContent);
|
||||
|
||||
@@ -31,9 +31,9 @@ async function main () {
|
||||
const outDir = utils.getOutDir({ shouldLog: true });
|
||||
const nodeDir = path.resolve(BASE, 'out', outDir, 'gen', 'node_headers');
|
||||
const env = {
|
||||
npm_config_msvs_version: '2022',
|
||||
...process.env,
|
||||
npm_config_nodedir: nodeDir,
|
||||
npm_config_msvs_version: '2022',
|
||||
npm_config_arch: process.env.NPM_CONFIG_ARCH,
|
||||
npm_config_yes: 'true'
|
||||
};
|
||||
|
||||
@@ -369,9 +369,6 @@ def main():
|
||||
patch_file.close()
|
||||
os.unlink(patch_file.name)
|
||||
else:
|
||||
print(
|
||||
'To patch these files, run:',
|
||||
f"$ git apply {patch_file.name}", sep='\n')
|
||||
filename=patch_file.name
|
||||
print(f"\nTo patch these files, run:\n$ git apply {filename}\n")
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ const unknownFlags = [];
|
||||
const pass = chalk.green('✓');
|
||||
const fail = chalk.red('✗');
|
||||
|
||||
const FAILURE_STATUS_KEY = 'Electron_Spec_Runner_Failures';
|
||||
|
||||
const args = minimist(process.argv, {
|
||||
string: ['runners', 'target', 'electronVersion'],
|
||||
unknown: arg => unknownFlags.push(arg)
|
||||
@@ -156,6 +158,36 @@ async function runElectronTests () {
|
||||
}
|
||||
}
|
||||
|
||||
async function asyncSpawn (exe, runnerArgs) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let forceExitResult = 0;
|
||||
const child = childProcess.spawn(exe, runnerArgs, {
|
||||
cwd: path.resolve(__dirname, '../..')
|
||||
});
|
||||
child.stdout.pipe(process.stdout);
|
||||
child.stderr.pipe(process.stderr);
|
||||
if (process.env.ELECTRON_FORCE_TEST_SUITE_EXIT) {
|
||||
child.stdout.on('data', data => {
|
||||
const failureRE = RegExp(`${FAILURE_STATUS_KEY}: (\\d.*)`);
|
||||
const failures = data.toString().match(failureRE);
|
||||
if (failures) {
|
||||
forceExitResult = parseInt(failures[1], 10);
|
||||
}
|
||||
});
|
||||
}
|
||||
child.on('error', error => reject(error));
|
||||
child.on('close', (status, signal) => {
|
||||
let returnStatus = 0;
|
||||
if (process.env.ELECTRON_FORCE_TEST_SUITE_EXIT) {
|
||||
returnStatus = forceExitResult;
|
||||
} else {
|
||||
returnStatus = status;
|
||||
}
|
||||
resolve({ status: returnStatus, signal });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function runTestUsingElectron (specDir, testName) {
|
||||
let exe;
|
||||
if (args.electronVersion) {
|
||||
@@ -169,10 +201,7 @@ async function runTestUsingElectron (specDir, testName) {
|
||||
runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe);
|
||||
exe = 'python3';
|
||||
}
|
||||
const { status, signal } = childProcess.spawnSync(exe, runnerArgs, {
|
||||
cwd: path.resolve(__dirname, '../..'),
|
||||
stdio: 'inherit'
|
||||
});
|
||||
const { status, signal } = await asyncSpawn(exe, runnerArgs);
|
||||
if (status !== 0) {
|
||||
if (status) {
|
||||
const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString();
|
||||
@@ -191,9 +220,9 @@ async function runMainProcessElectronTests () {
|
||||
|
||||
async function installSpecModules (dir) {
|
||||
const env = {
|
||||
npm_config_msvs_version: '2022',
|
||||
...process.env,
|
||||
CXXFLAGS: process.env.CXXFLAGS,
|
||||
npm_config_msvs_version: '2022',
|
||||
npm_config_yes: 'true'
|
||||
};
|
||||
if (args.electronVersion) {
|
||||
|
||||
0
script/zip_manifests/dist_zip.win.arm64.manifest
Executable file → Normal file
0
script/zip_manifests/dist_zip.win.arm64.manifest
Executable file → Normal file
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/containers/extend.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "content/public/common/content_constants.h"
|
||||
@@ -98,21 +99,6 @@ bool IsWidevineAvailable(
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_WIDEVINE)
|
||||
|
||||
void AppendDelimitedSwitchToVector(const std::string_view cmd_switch,
|
||||
std::vector<std::string>* append_me) {
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
auto switch_value = command_line->GetSwitchValueASCII(cmd_switch);
|
||||
if (!switch_value.empty()) {
|
||||
constexpr std::string_view delimiter{",", 1};
|
||||
auto tokens =
|
||||
base::SplitString(switch_value, delimiter, base::TRIM_WHITESPACE,
|
||||
base::SPLIT_WANT_NONEMPTY);
|
||||
append_me->reserve(append_me->size() + tokens.size());
|
||||
std::move(std::begin(tokens), std::end(tokens),
|
||||
std::back_inserter(*append_me));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronContentClient::ElectronContentClient() = default;
|
||||
@@ -149,16 +135,19 @@ void ElectronContentClient::AddAdditionalSchemes(Schemes* schemes) {
|
||||
//
|
||||
// We use this for registration to network utility process
|
||||
if (IsUtilityProcess()) {
|
||||
AppendDelimitedSwitchToVector(switches::kServiceWorkerSchemes,
|
||||
&schemes->service_worker_schemes);
|
||||
AppendDelimitedSwitchToVector(switches::kStandardSchemes,
|
||||
&schemes->standard_schemes);
|
||||
AppendDelimitedSwitchToVector(switches::kSecureSchemes,
|
||||
&schemes->secure_schemes);
|
||||
AppendDelimitedSwitchToVector(switches::kBypassCSPSchemes,
|
||||
&schemes->csp_bypassing_schemes);
|
||||
AppendDelimitedSwitchToVector(switches::kCORSSchemes,
|
||||
&schemes->cors_enabled_schemes);
|
||||
const auto& cmd = *base::CommandLine::ForCurrentProcess();
|
||||
auto append_cli_schemes = [&cmd](auto& appendme, const auto key) {
|
||||
base::Extend(appendme, base::SplitString(cmd.GetSwitchValueASCII(key),
|
||||
",", base::TRIM_WHITESPACE,
|
||||
base::SPLIT_WANT_NONEMPTY));
|
||||
};
|
||||
|
||||
using namespace switches;
|
||||
append_cli_schemes(schemes->cors_enabled_schemes, kCORSSchemes);
|
||||
append_cli_schemes(schemes->csp_bypassing_schemes, kBypassCSPSchemes);
|
||||
append_cli_schemes(schemes->secure_schemes, kSecureSchemes);
|
||||
append_cli_schemes(schemes->service_worker_schemes, kServiceWorkerSchemes);
|
||||
append_cli_schemes(schemes->standard_schemes, kStandardSchemes);
|
||||
}
|
||||
|
||||
if (electron::fuses::IsGrantFileProtocolExtraPrivilegesEnabled()) {
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kRelauncherProcess[] = "relauncher";
|
||||
constexpr std::string_view kRelauncherProcess = "relauncher";
|
||||
|
||||
constexpr std::string_view kElectronDisableSandbox{"ELECTRON_DISABLE_SANDBOX"};
|
||||
constexpr std::string_view kElectronEnableStackDumping{
|
||||
|
||||
@@ -776,7 +776,7 @@ base::OnceClosure App::SelectClientCertificate(
|
||||
std::move((*shared_identities)[0]),
|
||||
base::BindRepeating(&GotPrivateKey, shared_delegate, std::move(cert)));
|
||||
}
|
||||
return base::OnceClosure();
|
||||
return {};
|
||||
}
|
||||
|
||||
void App::OnGpuInfoUpdate() {
|
||||
@@ -944,7 +944,7 @@ std::string App::GetSystemLocale(gin_helper::ErrorThrower thrower) const {
|
||||
thrower.ThrowError(
|
||||
"app.getSystemLocale() can only be called "
|
||||
"after app is ready");
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
return static_cast<BrowserProcessImpl*>(g_browser_process)->GetSystemLocale();
|
||||
}
|
||||
|
||||
@@ -640,7 +640,7 @@ void BaseWindow::SetBackgroundColor(const std::string& color_name) {
|
||||
window_->SetBackgroundColor(color);
|
||||
}
|
||||
|
||||
std::string BaseWindow::GetBackgroundColor(gin_helper::Arguments* args) const {
|
||||
std::string BaseWindow::GetBackgroundColor() const {
|
||||
return ToRGBHex(window_->GetBackgroundColor());
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||
bool IsKiosk() const;
|
||||
bool IsTabletMode() const;
|
||||
virtual void SetBackgroundColor(const std::string& color_name);
|
||||
std::string GetBackgroundColor(gin_helper::Arguments* args) const;
|
||||
std::string GetBackgroundColor() const;
|
||||
void InvalidateShadow();
|
||||
void SetHasShadow(bool has_shadow);
|
||||
bool HasShadow() const;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
@@ -20,6 +21,7 @@
|
||||
#include "shell/common/node_includes.h"
|
||||
|
||||
using content::TracingController;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace gin {
|
||||
|
||||
@@ -69,9 +71,9 @@ void StopTracing(gin_helper::Promise<base::FilePath> promise,
|
||||
std::optional<base::FilePath> file_path) {
|
||||
auto resolve_or_reject = base::BindOnce(
|
||||
[](gin_helper::Promise<base::FilePath> promise,
|
||||
const base::FilePath& path, std::optional<std::string> error) {
|
||||
if (error) {
|
||||
promise.RejectWithErrorMessage(error.value());
|
||||
const base::FilePath& path, const std::string_view error) {
|
||||
if (!std::empty(error)) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
} else {
|
||||
promise.Resolve(path);
|
||||
}
|
||||
@@ -81,21 +83,17 @@ void StopTracing(gin_helper::Promise<base::FilePath> promise,
|
||||
auto* instance = TracingController::GetInstance();
|
||||
if (!instance->IsTracing()) {
|
||||
std::move(resolve_or_reject)
|
||||
.Run(std::make_optional(
|
||||
"Failed to stop tracing - no trace in progress"));
|
||||
.Run("Failed to stop tracing - no trace in progress"sv);
|
||||
} else if (file_path) {
|
||||
auto split_callback = base::SplitOnceCallback(std::move(resolve_or_reject));
|
||||
auto endpoint = TracingController::CreateFileEndpoint(
|
||||
*file_path,
|
||||
base::BindOnce(std::move(split_callback.first), std::nullopt));
|
||||
*file_path, base::BindOnce(std::move(split_callback.first), ""sv));
|
||||
if (!instance->StopTracing(endpoint)) {
|
||||
std::move(split_callback.second)
|
||||
.Run(std::make_optional("Failed to stop tracing"));
|
||||
std::move(split_callback.second).Run("Failed to stop tracing"sv);
|
||||
}
|
||||
} else {
|
||||
std::move(resolve_or_reject)
|
||||
.Run(std::make_optional(
|
||||
"Failed to create temporary file for trace data"));
|
||||
.Run("Failed to create temporary file for trace data"sv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ void FilterCookieWithStatuses(
|
||||
// Parse dictionary property to CanonicalCookie time correctly.
|
||||
base::Time ParseTimeProperty(const std::optional<double>& value) {
|
||||
if (!value) // empty time means ignoring the parameter
|
||||
return base::Time();
|
||||
return {};
|
||||
if (*value == 0) // FromSecondsSinceUnixEpoch would convert 0 to empty Time
|
||||
return base::Time::UnixEpoch();
|
||||
return base::Time::FromSecondsSinceUnixEpoch(*value);
|
||||
@@ -292,8 +292,8 @@ std::string StringToCookieSameSite(const std::string* str_ptr,
|
||||
|
||||
gin::WrapperInfo Cookies::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
Cookies::Cookies(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
|
||||
: browser_context_(browser_context) {
|
||||
Cookies::Cookies(ElectronBrowserContext* browser_context)
|
||||
: browser_context_{browser_context} {
|
||||
cookie_change_subscription_ =
|
||||
browser_context_->cookie_change_notifier()->RegisterCookieChangeCallback(
|
||||
base::BindRepeating(&Cookies::OnCookieChanged,
|
||||
@@ -458,7 +458,7 @@ void Cookies::OnCookieChanged(const net::CookieChangeInfo& change) {
|
||||
// static
|
||||
gin::Handle<Cookies> Cookies::Create(v8::Isolate* isolate,
|
||||
ElectronBrowserContext* browser_context) {
|
||||
return gin::CreateHandle(isolate, new Cookies(isolate, browser_context));
|
||||
return gin::CreateHandle(isolate, new Cookies{browser_context});
|
||||
}
|
||||
|
||||
gin::ObjectTemplateBuilder Cookies::GetObjectTemplateBuilder(
|
||||
|
||||
@@ -50,7 +50,7 @@ class Cookies final : public gin::Wrappable<Cookies>,
|
||||
Cookies& operator=(const Cookies&) = delete;
|
||||
|
||||
protected:
|
||||
Cookies(v8::Isolate* isolate, ElectronBrowserContext* browser_context);
|
||||
explicit Cookies(ElectronBrowserContext* browser_context);
|
||||
~Cookies() override;
|
||||
|
||||
v8::Local<v8::Promise> Get(v8::Isolate*,
|
||||
|
||||
@@ -99,7 +99,7 @@ std::string ReadClientId() {
|
||||
if (GetClientIdPath(&client_id_path) &&
|
||||
(!base::ReadFileToStringWithMaxSize(client_id_path, &client_id, 36) ||
|
||||
client_id.size() != 36))
|
||||
return std::string();
|
||||
return {};
|
||||
return client_id;
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ gin::Handle<DataPipeHolder> DataPipeHolder::From(v8::Isolate* isolate,
|
||||
if (gin::ConvertFromV8(isolate, object.ToLocalChecked(), &handle))
|
||||
return handle;
|
||||
}
|
||||
return gin::Handle<DataPipeHolder>();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace electron::api
|
||||
|
||||
@@ -209,7 +209,7 @@ const GURL& DownloadItem::GetURL() const {
|
||||
|
||||
v8::Local<v8::Value> DownloadItem::GetURLChain() const {
|
||||
if (!CheckAlive())
|
||||
return v8::Local<v8::Value>();
|
||||
return {};
|
||||
return gin::ConvertToV8(isolate_, download_item_->GetUrlChain());
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ Menu::~Menu() {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool InvokeBoolMethod(const Menu* menu,
|
||||
const char* method,
|
||||
int command_id,
|
||||
@@ -84,6 +86,8 @@ bool InvokeBoolMethod(const Menu* menu,
|
||||
return gin::ConvertFromV8(isolate, val, &ret) ? ret : default_value;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Menu::IsCommandIdChecked(int command_id) const {
|
||||
return InvokeBoolMethod(this, "_isCommandIdChecked", command_id);
|
||||
}
|
||||
|
||||
@@ -63,8 +63,7 @@ scoped_refptr<base::SequencedTaskRunner> CreateFileTaskRunner() {
|
||||
}
|
||||
|
||||
base::File OpenFileForWriting(base::FilePath path) {
|
||||
return base::File(path,
|
||||
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
|
||||
return {path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE};
|
||||
}
|
||||
|
||||
void ResolvePromiseWithNetError(gin_helper::Promise<void> promise,
|
||||
@@ -93,7 +92,7 @@ v8::Local<v8::Promise> NetLog::StartLogging(base::FilePath log_path,
|
||||
gin::Arguments* args) {
|
||||
if (log_path.empty()) {
|
||||
args->ThrowTypeError("The first parameter must be a valid string");
|
||||
return v8::Local<v8::Promise>();
|
||||
return {};
|
||||
}
|
||||
|
||||
net::NetLogCaptureMode capture_mode = net::NetLogCaptureMode::kDefault;
|
||||
@@ -106,7 +105,7 @@ v8::Local<v8::Promise> NetLog::StartLogging(base::FilePath log_path,
|
||||
if (!gin::ConvertFromV8(args->isolate(), capture_mode_v8,
|
||||
&capture_mode)) {
|
||||
args->ThrowTypeError("Invalid value for captureMode");
|
||||
return v8::Local<v8::Promise>();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
v8::Local<v8::Value> max_file_size_v8;
|
||||
@@ -114,14 +113,14 @@ v8::Local<v8::Promise> NetLog::StartLogging(base::FilePath log_path,
|
||||
if (!gin::ConvertFromV8(args->isolate(), max_file_size_v8,
|
||||
&max_file_size)) {
|
||||
args->ThrowTypeError("Invalid value for maxFileSize");
|
||||
return v8::Local<v8::Promise>();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (net_log_exporter_) {
|
||||
args->ThrowTypeError("There is already a net log running");
|
||||
return v8::Local<v8::Promise>();
|
||||
return {};
|
||||
}
|
||||
|
||||
pending_start_promise_ =
|
||||
|
||||
@@ -81,7 +81,7 @@ gin::Handle<Notification> Notification::New(gin_helper::ErrorThrower thrower,
|
||||
gin::Arguments* args) {
|
||||
if (!Browser::Get()->is_ready()) {
|
||||
thrower.ThrowError("Cannot create Notification before app is ready");
|
||||
return gin::Handle<Notification>();
|
||||
return {};
|
||||
}
|
||||
return gin::CreateHandle(thrower.isolate(), new Notification(args));
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ gin::Handle<Protocol> Protocol::Create(
|
||||
// static
|
||||
gin::Handle<Protocol> Protocol::New(gin_helper::ErrorThrower thrower) {
|
||||
thrower.ThrowError("Protocol cannot be created from JS");
|
||||
return gin::Handle<Protocol>();
|
||||
return {};
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -53,13 +53,13 @@ v8::Local<v8::Value> EncryptString(v8::Isolate* isolate,
|
||||
if (!electron::Browser::Get()->is_ready()) {
|
||||
gin_helper::ErrorThrower(isolate).ThrowError(
|
||||
"safeStorage cannot be used before app is ready");
|
||||
return v8::Local<v8::Value>();
|
||||
return {};
|
||||
}
|
||||
gin_helper::ErrorThrower(isolate).ThrowError(
|
||||
"Error while encrypting the text provided to "
|
||||
"safeStorage.encryptString. "
|
||||
"Encryption is not available.");
|
||||
return v8::Local<v8::Value>();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string ciphertext;
|
||||
@@ -69,7 +69,7 @@ v8::Local<v8::Value> EncryptString(v8::Isolate* isolate,
|
||||
gin_helper::ErrorThrower(isolate).ThrowError(
|
||||
"Error while encrypting the text provided to "
|
||||
"safeStorage.encryptString.");
|
||||
return v8::Local<v8::Value>();
|
||||
return {};
|
||||
}
|
||||
|
||||
return node::Buffer::Copy(isolate, ciphertext.c_str(), ciphertext.size())
|
||||
|
||||
@@ -82,7 +82,7 @@ gfx::Point Screen::GetCursorScreenPoint(v8::Isolate* isolate) {
|
||||
thrower.ThrowError(
|
||||
"screen.getCursorScreenPoint() cannot be called before a window has "
|
||||
"been created.");
|
||||
return gfx::Point();
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
return screen_->GetCursorScreenPoint();
|
||||
|
||||
@@ -132,7 +132,7 @@ v8::Local<v8::Value> ServiceWorkerContext::GetWorkerInfoFromID(
|
||||
auto iter = info_map.find(version_id);
|
||||
if (iter == info_map.end()) {
|
||||
thrower.ThrowError("Could not find service worker with that version_id");
|
||||
return v8::Local<v8::Value>();
|
||||
return {};
|
||||
}
|
||||
return ServiceWorkerRunningInfoToDict(thrower.isolate(),
|
||||
std::move(iter->second));
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "net/http/http_util.h"
|
||||
#include "services/network/network_service.h"
|
||||
#include "services/network/public/cpp/features.h"
|
||||
#include "services/network/public/cpp/request_destination.h"
|
||||
#include "services/network/public/mojom/clear_data_filter.mojom.h"
|
||||
#include "shell/browser/api/electron_api_app.h"
|
||||
#include "shell/browser/api/electron_api_cookies.h"
|
||||
@@ -79,6 +80,7 @@
|
||||
#include "shell/common/gin_converters/gurl_converter.h"
|
||||
#include "shell/common/gin_converters/media_converter.h"
|
||||
#include "shell/common/gin_converters/net_converter.h"
|
||||
#include "shell/common/gin_converters/time_converter.h"
|
||||
#include "shell/common/gin_converters/usb_protected_classes_converter.h"
|
||||
#include "shell/common/gin_converters/value_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
@@ -158,7 +160,8 @@ uint32_t GetQuotaMask(const std::vector<std::string>& quota_types) {
|
||||
return quota_mask;
|
||||
}
|
||||
|
||||
constexpr BrowsingDataRemover::DataType kClearDataTypeAll = ~0ULL;
|
||||
constexpr BrowsingDataRemover::DataType kClearDataTypeAll =
|
||||
~0ULL & ~BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS;
|
||||
constexpr BrowsingDataRemover::OriginType kClearOriginTypeAll =
|
||||
BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
|
||||
BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
|
||||
@@ -1074,6 +1077,178 @@ std::vector<base::FilePath> Session::GetPreloads() const {
|
||||
return prefs->preloads();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the network service's ClearSharedDictionaryCacheForIsolationKey
|
||||
* method, allowing clearing the Shared Dictionary cache for a given isolation
|
||||
* key. Details about the feature available at
|
||||
* https://developer.chrome.com/blog/shared-dictionary-compression
|
||||
*/
|
||||
v8::Local<v8::Promise> Session::ClearSharedDictionaryCacheForIsolationKey(
|
||||
const gin_helper::Dictionary& options) {
|
||||
gin_helper::Promise<void> promise(isolate_);
|
||||
auto handle = promise.GetHandle();
|
||||
|
||||
GURL frame_origin_url, top_frame_site_url;
|
||||
if (!options.Get("frameOrigin", &frame_origin_url) ||
|
||||
!options.Get("topFrameSite", &top_frame_site_url)) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Must provide frameOrigin and topFrameSite strings to "
|
||||
"`clearSharedDictionaryCacheForIsolationKey`");
|
||||
return handle;
|
||||
}
|
||||
|
||||
if (!frame_origin_url.is_valid() || !top_frame_site_url.is_valid()) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Invalid URLs provided for frameOrigin or topFrameSite");
|
||||
return handle;
|
||||
}
|
||||
|
||||
url::Origin frame_origin = url::Origin::Create(frame_origin_url);
|
||||
net::SchemefulSite top_frame_site(top_frame_site_url);
|
||||
net::SharedDictionaryIsolationKey isolation_key(frame_origin, top_frame_site);
|
||||
|
||||
browser_context_->GetDefaultStoragePartition()
|
||||
->GetNetworkContext()
|
||||
->ClearSharedDictionaryCacheForIsolationKey(
|
||||
isolation_key,
|
||||
base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
|
||||
std::move(promise)));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the network service's ClearSharedDictionaryCache
|
||||
* method, allowing clearing the Shared Dictionary cache.
|
||||
* https://developer.chrome.com/blog/shared-dictionary-compression
|
||||
*/
|
||||
v8::Local<v8::Promise> Session::ClearSharedDictionaryCache() {
|
||||
gin_helper::Promise<void> promise(isolate_);
|
||||
auto handle = promise.GetHandle();
|
||||
|
||||
browser_context_->GetDefaultStoragePartition()
|
||||
->GetNetworkContext()
|
||||
->ClearSharedDictionaryCache(
|
||||
base::Time(), base::Time::Max(),
|
||||
nullptr /*mojom::ClearDataFilterPtr*/,
|
||||
base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
|
||||
std::move(promise)));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the network service's GetSharedDictionaryInfo method, allowing
|
||||
* inspection of Shared Dictionary information. Details about the feature
|
||||
* available at https://developer.chrome.com/blog/shared-dictionary-compression
|
||||
*/
|
||||
v8::Local<v8::Promise> Session::GetSharedDictionaryInfo(
|
||||
const gin_helper::Dictionary& options) {
|
||||
gin_helper::Promise<std::vector<gin_helper::Dictionary>> promise(isolate_);
|
||||
auto handle = promise.GetHandle();
|
||||
|
||||
GURL frame_origin_url, top_frame_site_url;
|
||||
if (!options.Get("frameOrigin", &frame_origin_url) ||
|
||||
!options.Get("topFrameSite", &top_frame_site_url)) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Must provide frameOrigin and topFrameSite strings");
|
||||
return handle;
|
||||
}
|
||||
|
||||
if (!frame_origin_url.is_valid() || !top_frame_site_url.is_valid()) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Invalid URLs provided for frameOrigin or topFrameSite");
|
||||
return handle;
|
||||
}
|
||||
|
||||
url::Origin frame_origin = url::Origin::Create(frame_origin_url);
|
||||
net::SchemefulSite top_frame_site(top_frame_site_url);
|
||||
net::SharedDictionaryIsolationKey isolation_key(frame_origin, top_frame_site);
|
||||
|
||||
browser_context_->GetDefaultStoragePartition()
|
||||
->GetNetworkContext()
|
||||
->GetSharedDictionaryInfo(
|
||||
isolation_key,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<std::vector<gin_helper::Dictionary>>
|
||||
promise,
|
||||
std::vector<network::mojom::SharedDictionaryInfoPtr> info) {
|
||||
v8::Isolate* isolate = promise.isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
std::vector<gin_helper::Dictionary> result;
|
||||
result.reserve(info.size());
|
||||
|
||||
for (const auto& item : info) {
|
||||
gin_helper::Dictionary dict =
|
||||
gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("match", item->match);
|
||||
|
||||
// Convert RequestDestination enum values to strings
|
||||
std::vector<std::string> destinations;
|
||||
for (const auto& dest : item->match_dest) {
|
||||
destinations.push_back(
|
||||
network::RequestDestinationToString(dest));
|
||||
}
|
||||
dict.Set("matchDestinations", destinations);
|
||||
dict.Set("id", item->id);
|
||||
dict.Set("dictionaryUrl", item->dictionary_url.spec());
|
||||
dict.Set("lastFetchTime", item->last_fetch_time);
|
||||
dict.Set("responseTime", item->response_time);
|
||||
dict.Set("expirationDuration",
|
||||
item->expiration.InMillisecondsF());
|
||||
dict.Set("lastUsedTime", item->last_used_time);
|
||||
dict.Set("size", item->size);
|
||||
dict.Set("hash", net::HashValue(item->hash).ToString());
|
||||
|
||||
result.push_back(dict);
|
||||
}
|
||||
|
||||
promise.Resolve(result);
|
||||
},
|
||||
std::move(promise)));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the network service's GetSharedDictionaryUsageInfo method, allowing
|
||||
* inspection of Shared Dictionary information. Details about the feature
|
||||
* available at https://developer.chrome.com/blog/shared-dictionary-compression
|
||||
*/
|
||||
v8::Local<v8::Promise> Session::GetSharedDictionaryUsageInfo() {
|
||||
gin_helper::Promise<std::vector<gin_helper::Dictionary>> promise(isolate_);
|
||||
auto handle = promise.GetHandle();
|
||||
|
||||
browser_context_->GetDefaultStoragePartition()
|
||||
->GetNetworkContext()
|
||||
->GetSharedDictionaryUsageInfo(base::BindOnce(
|
||||
[](gin_helper::Promise<std::vector<gin_helper::Dictionary>> promise,
|
||||
const std::vector<net::SharedDictionaryUsageInfo>& info) {
|
||||
v8::Isolate* isolate = promise.isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
std::vector<gin_helper::Dictionary> result;
|
||||
result.reserve(info.size());
|
||||
|
||||
for (const auto& item : info) {
|
||||
gin_helper::Dictionary dict =
|
||||
gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("frameOrigin",
|
||||
item.isolation_key.frame_origin().Serialize());
|
||||
dict.Set("topFrameSite",
|
||||
item.isolation_key.top_frame_site().Serialize());
|
||||
dict.Set("totalSizeBytes", item.total_size_bytes);
|
||||
result.push_back(dict);
|
||||
}
|
||||
|
||||
promise.Resolve(result);
|
||||
},
|
||||
std::move(promise)));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
v8::Local<v8::Promise> Session::LoadExtension(
|
||||
const base::FilePath& extension_path,
|
||||
@@ -1583,7 +1758,7 @@ std::optional<gin::Handle<Session>> Session::FromPath(
|
||||
gin::Handle<Session> Session::New() {
|
||||
gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
|
||||
.ThrowError("Session objects cannot be created with 'new'");
|
||||
return gin::Handle<Session>();
|
||||
return {};
|
||||
}
|
||||
|
||||
void Session::FillObjectTemplate(v8::Isolate* isolate,
|
||||
@@ -1627,6 +1802,13 @@ void Session::FillObjectTemplate(v8::Isolate* isolate,
|
||||
&Session::CreateInterruptedDownload)
|
||||
.SetMethod("setPreloads", &Session::SetPreloads)
|
||||
.SetMethod("getPreloads", &Session::GetPreloads)
|
||||
.SetMethod("getSharedDictionaryUsageInfo",
|
||||
&Session::GetSharedDictionaryUsageInfo)
|
||||
.SetMethod("getSharedDictionaryInfo", &Session::GetSharedDictionaryInfo)
|
||||
.SetMethod("clearSharedDictionaryCache",
|
||||
&Session::ClearSharedDictionaryCache)
|
||||
.SetMethod("clearSharedDictionaryCacheForIsolationKey",
|
||||
&Session::ClearSharedDictionaryCacheForIsolationKey)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
.SetMethod("loadExtension", &Session::LoadExtension)
|
||||
.SetMethod("removeExtension", &Session::RemoveExtension)
|
||||
|
||||
@@ -140,6 +140,12 @@ class Session final : public gin::Wrappable<Session>,
|
||||
void CreateInterruptedDownload(const gin_helper::Dictionary& options);
|
||||
void SetPreloads(const std::vector<base::FilePath>& preloads);
|
||||
std::vector<base::FilePath> GetPreloads() const;
|
||||
v8::Local<v8::Promise> GetSharedDictionaryInfo(
|
||||
const gin_helper::Dictionary& options);
|
||||
v8::Local<v8::Promise> GetSharedDictionaryUsageInfo();
|
||||
v8::Local<v8::Promise> ClearSharedDictionaryCache();
|
||||
v8::Local<v8::Promise> ClearSharedDictionaryCacheForIsolationKey(
|
||||
const gin_helper::Dictionary& options);
|
||||
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> ServiceWorkerContext(v8::Isolate* isolate);
|
||||
|
||||
@@ -154,7 +154,7 @@ std::string SystemPreferences::GetMediaAccessStatus(
|
||||
DeviceAccessStatus::DeviceAccessStatus_Allowed);
|
||||
} else {
|
||||
thrower.ThrowError("Invalid media type");
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,13 +67,13 @@ gin::Handle<Tray> Tray::New(gin_helper::ErrorThrower thrower,
|
||||
gin::Arguments* args) {
|
||||
if (!Browser::Get()->is_ready()) {
|
||||
thrower.ThrowError("Cannot create Tray before app is ready");
|
||||
return gin::Handle<Tray>();
|
||||
return {};
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
if (!guid.has_value() && args->Length() > 1) {
|
||||
thrower.ThrowError("Invalid GUID format");
|
||||
return gin::Handle<Tray>();
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -85,7 +85,7 @@ gin::Handle<Tray> Tray::New(gin_helper::ErrorThrower thrower,
|
||||
if (try_catch.HasCaught()) {
|
||||
delete tray;
|
||||
try_catch.ReThrow();
|
||||
return gin::Handle<Tray>();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto handle = gin::CreateHandle(args->isolate(), tray);
|
||||
@@ -264,7 +264,7 @@ void Tray::SetTitle(const std::string& title,
|
||||
|
||||
std::string Tray::GetTitle() {
|
||||
if (!CheckAlive())
|
||||
return std::string();
|
||||
return {};
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
return tray_icon_->GetTitle();
|
||||
#else
|
||||
@@ -388,7 +388,7 @@ void Tray::SetContextMenu(gin_helper::ErrorThrower thrower,
|
||||
|
||||
gfx::Rect Tray::GetBounds() {
|
||||
if (!CheckAlive())
|
||||
return gfx::Rect();
|
||||
return {};
|
||||
return tray_icon_->GetBounds();
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
base::IDMap<api::UtilityProcessWrapper*, base::ProcessId>&
|
||||
GetAllUtilityProcessWrappers() {
|
||||
static base::NoDestructor<
|
||||
@@ -52,6 +54,8 @@ GetAllUtilityProcessWrappers() {
|
||||
return *s_all_utility_process_wrappers;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace api {
|
||||
|
||||
gin::WrapperInfo UtilityProcessWrapper::kWrapperInfo = {
|
||||
@@ -411,7 +415,7 @@ gin::Handle<UtilityProcessWrapper> UtilityProcessWrapper::Create(
|
||||
gin_helper::Dictionary dict;
|
||||
if (!args->GetNext(&dict)) {
|
||||
args->ThrowTypeError("Options must be an object.");
|
||||
return gin::Handle<UtilityProcessWrapper>();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::u16string display_name;
|
||||
@@ -425,19 +429,19 @@ gin::Handle<UtilityProcessWrapper> UtilityProcessWrapper::Create(
|
||||
dict.Get("modulePath", ¶ms->script);
|
||||
if (dict.Has("args") && !dict.Get("args", ¶ms->args)) {
|
||||
args->ThrowTypeError("Invalid value for args");
|
||||
return gin::Handle<UtilityProcessWrapper>();
|
||||
return {};
|
||||
}
|
||||
|
||||
gin_helper::Dictionary opts;
|
||||
if (dict.Get("options", &opts)) {
|
||||
if (opts.Has("env") && !opts.Get("env", &env_map)) {
|
||||
args->ThrowTypeError("Invalid value for env");
|
||||
return gin::Handle<UtilityProcessWrapper>();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (opts.Has("execArgv") && !opts.Get("execArgv", ¶ms->exec_args)) {
|
||||
args->ThrowTypeError("Invalid value for execArgv");
|
||||
return gin::Handle<UtilityProcessWrapper>();
|
||||
return {};
|
||||
}
|
||||
|
||||
opts.Get("serviceName", &display_name);
|
||||
|
||||
@@ -282,7 +282,7 @@ void View::SetBounds(const gfx::Rect& bounds) {
|
||||
|
||||
gfx::Rect View::GetBounds() {
|
||||
if (!view_)
|
||||
return gfx::Rect();
|
||||
return {};
|
||||
return view_->bounds();
|
||||
}
|
||||
|
||||
@@ -429,7 +429,7 @@ gin::Handle<View> View::Create(v8::Isolate* isolate) {
|
||||
if (gin::ConvertFromV8(isolate, obj, &view))
|
||||
return view;
|
||||
}
|
||||
return gin::Handle<View>();
|
||||
return {};
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -371,6 +371,9 @@ namespace electron::api {
|
||||
|
||||
namespace {
|
||||
|
||||
// Global toggle for disabling draggable regions checks.
|
||||
bool g_disable_draggable_regions = false;
|
||||
|
||||
constexpr std::string_view CursorTypeToString(
|
||||
ui::mojom::CursorType cursor_type) {
|
||||
switch (cursor_type) {
|
||||
@@ -1984,7 +1987,7 @@ gin::Handle<gin_helper::internal::Event> WebContents::MakeEventWithSender(
|
||||
ReplyChannel::Create(isolate, std::move(callback))
|
||||
->SendError("WebContents was destroyed");
|
||||
}
|
||||
return gin::Handle<gin_helper::internal::Event>();
|
||||
return {};
|
||||
}
|
||||
gin::Handle<gin_helper::internal::Event> event =
|
||||
gin_helper::internal::Event::New(isolate);
|
||||
@@ -2049,6 +2052,10 @@ void WebContents::DraggableRegionsChanged(
|
||||
draggable_region_ = DraggableRegionsToSkRegion(regions);
|
||||
}
|
||||
|
||||
SkRegion* WebContents::draggable_region() {
|
||||
return g_disable_draggable_regions ? nullptr : draggable_region_.get();
|
||||
}
|
||||
|
||||
void WebContents::DidStartNavigation(
|
||||
content::NavigationHandle* navigation_handle) {
|
||||
EmitNavigationEvent("did-start-navigation", navigation_handle);
|
||||
@@ -2542,7 +2549,7 @@ std::vector<content::NavigationEntry*> WebContents::GetHistory() const {
|
||||
// If the history is empty, it contains only one entry and that is
|
||||
// "InitialEntry"
|
||||
if (history_length == 1 && controller.GetEntryAtIndex(0)->IsInitialEntry())
|
||||
return std::vector<content::NavigationEntry*>();
|
||||
return {};
|
||||
|
||||
std::vector<content::NavigationEntry*> history;
|
||||
history.reserve(history_length);
|
||||
@@ -2629,7 +2636,7 @@ std::string WebContents::GetMediaSourceID(
|
||||
content::WebContents* request_web_contents) {
|
||||
auto* frame_host = web_contents()->GetPrimaryMainFrame();
|
||||
if (!frame_host)
|
||||
return std::string();
|
||||
return {};
|
||||
|
||||
content::DesktopMediaID media_id(
|
||||
content::DesktopMediaID::TYPE_WEB_CONTENTS,
|
||||
@@ -2639,7 +2646,7 @@ std::string WebContents::GetMediaSourceID(
|
||||
|
||||
auto* request_frame_host = request_web_contents->GetPrimaryMainFrame();
|
||||
if (!request_frame_host)
|
||||
return std::string();
|
||||
return {};
|
||||
|
||||
std::string id =
|
||||
content::DesktopStreamsRegistry::GetInstance()->RegisterStream(
|
||||
@@ -2761,7 +2768,7 @@ bool WebContents::IsDevToolsOpened() {
|
||||
|
||||
std::u16string WebContents::GetDevToolsTitle() {
|
||||
if (type_ == Type::kRemote)
|
||||
return std::u16string();
|
||||
return {};
|
||||
|
||||
DCHECK(inspectable_web_contents_);
|
||||
return inspectable_web_contents_->GetDevToolsTitle();
|
||||
@@ -3641,7 +3648,7 @@ gfx::Size WebContents::GetSizeForNewRenderView(content::WebContents* wc) {
|
||||
}
|
||||
}
|
||||
|
||||
return gfx::Size();
|
||||
return {};
|
||||
}
|
||||
|
||||
void WebContents::SetZoomLevel(double level) {
|
||||
@@ -4592,6 +4599,11 @@ std::list<WebContents*> WebContents::GetWebContentsList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
// static
|
||||
void WebContents::SetDisableDraggableRegions(bool disable) {
|
||||
g_disable_draggable_regions = disable;
|
||||
}
|
||||
|
||||
// static
|
||||
gin::WrapperInfo WebContents::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
|
||||
@@ -151,6 +151,10 @@ class WebContents final : public ExclusiveAccessContext,
|
||||
static WebContents* FromID(int32_t id);
|
||||
static std::list<WebContents*> GetWebContentsList();
|
||||
|
||||
// Whether to disable draggable regions globally. This can be used to allow
|
||||
// events to skip client region hit tests.
|
||||
static void SetDisableDraggableRegions(bool disable);
|
||||
|
||||
// Get the V8 wrapper of the |web_contents|, or create one if not existed.
|
||||
//
|
||||
// The lifetime of |web_contents| is NOT managed by this class, and the type
|
||||
@@ -485,13 +489,7 @@ class WebContents final : public ExclusiveAccessContext,
|
||||
|
||||
void PDFReadyToPrint();
|
||||
|
||||
SkRegion* draggable_region() {
|
||||
return force_non_draggable_ ? nullptr : draggable_region_.get();
|
||||
}
|
||||
|
||||
void SetForceNonDraggable(bool force_non_draggable) {
|
||||
force_non_draggable_ = force_non_draggable;
|
||||
}
|
||||
SkRegion* draggable_region();
|
||||
|
||||
// disable copy
|
||||
WebContents(const WebContents&) = delete;
|
||||
@@ -888,8 +886,6 @@ class WebContents final : public ExclusiveAccessContext,
|
||||
|
||||
std::unique_ptr<SkRegion> draggable_region_;
|
||||
|
||||
bool force_non_draggable_ = false;
|
||||
|
||||
base::WeakPtrFactory<WebContents> weak_factory_{this};
|
||||
};
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {
|
||||
if (api_web_contents_)
|
||||
return gin::CreateHandle(isolate, api_web_contents_.get());
|
||||
else
|
||||
return gin::Handle<WebContents>();
|
||||
return {};
|
||||
}
|
||||
|
||||
void WebContentsView::SetBackgroundColor(std::optional<WrappedSkColor> color) {
|
||||
@@ -152,7 +152,7 @@ gin::Handle<WebContentsView> WebContentsView::Create(
|
||||
if (gin::ConvertFromV8(isolate, web_contents_view_obj, &web_contents_view))
|
||||
return web_contents_view;
|
||||
}
|
||||
return gin::Handle<WebContentsView>();
|
||||
return {};
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/feature_list.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "content/browser/renderer_host/render_frame_host_impl.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_process_host_impl.h" // nogncheck
|
||||
#include "content/public/browser/frame_tree_node_id.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/common/isolated_world_ids.h"
|
||||
@@ -91,15 +93,16 @@ namespace electron::api {
|
||||
// FrameTreeNodeId -> WebFrameMain*
|
||||
// Using FrameTreeNode allows us to track frame across navigations. This
|
||||
// is most similar to how <iframe> works.
|
||||
typedef std::unordered_map<content::FrameTreeNodeId,
|
||||
WebFrameMain*,
|
||||
content::FrameTreeNodeId::Hasher>
|
||||
FrameTreeNodeIdMap;
|
||||
using FrameTreeNodeIdMap = std::unordered_map<content::FrameTreeNodeId,
|
||||
WebFrameMain*,
|
||||
content::FrameTreeNodeId::Hasher>;
|
||||
|
||||
// Token -> WebFrameMain*
|
||||
// Maps exact RFH to a WebFrameMain instance.
|
||||
typedef std::map<content::GlobalRenderFrameHostToken, WebFrameMain*>
|
||||
FrameTokenMap;
|
||||
using FrameTokenMap =
|
||||
std::map<content::GlobalRenderFrameHostToken, WebFrameMain*>;
|
||||
|
||||
namespace {
|
||||
|
||||
FrameTreeNodeIdMap& GetFrameTreeNodeIdMap() {
|
||||
static base::NoDestructor<FrameTreeNodeIdMap> instance;
|
||||
@@ -110,6 +113,8 @@ FrameTokenMap& GetFrameTokenMap() {
|
||||
return *instance;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
WebFrameMain* WebFrameMain::FromFrameTreeNodeId(
|
||||
content::FrameTreeNodeId frame_tree_node_id) {
|
||||
@@ -345,7 +350,7 @@ content::FrameTreeNodeId WebFrameMain::FrameTreeNodeID() const {
|
||||
|
||||
std::string WebFrameMain::Name() const {
|
||||
if (!CheckRenderFrame())
|
||||
return std::string();
|
||||
return {};
|
||||
return render_frame_->GetFrameName();
|
||||
}
|
||||
|
||||
@@ -377,7 +382,7 @@ GURL WebFrameMain::URL() const {
|
||||
|
||||
std::string WebFrameMain::Origin() const {
|
||||
if (!CheckRenderFrame())
|
||||
return std::string();
|
||||
return {};
|
||||
return render_frame_->GetLastCommittedOrigin().Serialize();
|
||||
}
|
||||
|
||||
@@ -426,20 +431,75 @@ std::vector<content::RenderFrameHost*> WebFrameMain::FramesInSubtree() const {
|
||||
return frame_hosts;
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> WebFrameMain::CollectDocumentJSCallStack(
|
||||
gin::Arguments* args) {
|
||||
gin_helper::Promise<base::Value> promise(args->isolate());
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
if (render_frame_disposed_) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Render frame was disposed before WebFrameMain could be accessed");
|
||||
return handle;
|
||||
}
|
||||
|
||||
if (!base::FeatureList::IsEnabled(
|
||||
blink::features::kDocumentPolicyIncludeJSCallStacksInCrashReports)) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"DocumentPolicyIncludeJSCallStacksInCrashReports is not enabled");
|
||||
return handle;
|
||||
}
|
||||
|
||||
content::RenderProcessHostImpl* rph_impl =
|
||||
static_cast<content::RenderProcessHostImpl*>(render_frame_->GetProcess());
|
||||
|
||||
rph_impl->GetJavaScriptCallStackGeneratorInterface()
|
||||
->CollectJavaScriptCallStack(
|
||||
base::BindOnce(&WebFrameMain::CollectedJavaScriptCallStack,
|
||||
weak_factory_.GetWeakPtr(), std::move(promise)));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void WebFrameMain::CollectedJavaScriptCallStack(
|
||||
gin_helper::Promise<base::Value> promise,
|
||||
const std::string& untrusted_javascript_call_stack,
|
||||
const std::optional<blink::LocalFrameToken>& remote_frame_token) {
|
||||
if (render_frame_disposed_) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Render frame was disposed before call stack was received");
|
||||
return;
|
||||
}
|
||||
|
||||
const blink::LocalFrameToken& frame_token = render_frame_->GetFrameToken();
|
||||
if (remote_frame_token == frame_token) {
|
||||
base::Value base_value(untrusted_javascript_call_stack);
|
||||
promise.Resolve(base_value);
|
||||
} else if (!remote_frame_token) {
|
||||
// Failed to collect call stack. See logic in:
|
||||
// third_party/blink/renderer/controller/javascript_call_stack_collector.cc
|
||||
promise.Resolve(base::Value());
|
||||
} else {
|
||||
// Requests for call stacks can be initiated on an old RenderProcessHost
|
||||
// then be received after a frame swap.
|
||||
LOG(ERROR) << "Received call stack from old RPH";
|
||||
promise.Resolve(base::Value());
|
||||
}
|
||||
}
|
||||
|
||||
void WebFrameMain::DOMContentLoaded() {
|
||||
Emit("dom-ready");
|
||||
}
|
||||
|
||||
// static
|
||||
gin::Handle<WebFrameMain> WebFrameMain::New(v8::Isolate* isolate) {
|
||||
return gin::Handle<WebFrameMain>();
|
||||
return {};
|
||||
}
|
||||
|
||||
// static
|
||||
gin::Handle<WebFrameMain> WebFrameMain::From(v8::Isolate* isolate,
|
||||
content::RenderFrameHost* rfh) {
|
||||
if (!rfh)
|
||||
return gin::Handle<WebFrameMain>();
|
||||
return {};
|
||||
|
||||
auto* web_frame = FromRenderFrameHost(rfh);
|
||||
if (web_frame)
|
||||
@@ -458,6 +518,8 @@ void WebFrameMain::FillObjectTemplate(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> templ) {
|
||||
gin_helper::ObjectTemplateBuilder(isolate, templ)
|
||||
.SetMethod("executeJavaScript", &WebFrameMain::ExecuteJavaScript)
|
||||
.SetMethod("collectJavaScriptCallStack",
|
||||
&WebFrameMain::CollectDocumentJSCallStack)
|
||||
.SetMethod("reload", &WebFrameMain::Reload)
|
||||
.SetMethod("isDestroyed", &WebFrameMain::IsDestroyed)
|
||||
.SetMethod("_send", &WebFrameMain::Send)
|
||||
|
||||
@@ -36,6 +36,11 @@ template <typename T>
|
||||
class Handle;
|
||||
} // namespace gin
|
||||
|
||||
namespace gin_helper {
|
||||
template <typename T>
|
||||
class Promise;
|
||||
} // namespace gin_helper
|
||||
|
||||
namespace electron::api {
|
||||
|
||||
class WebContents;
|
||||
@@ -128,6 +133,12 @@ class WebFrameMain final : public gin::Wrappable<WebFrameMain>,
|
||||
std::vector<content::RenderFrameHost*> Frames() const;
|
||||
std::vector<content::RenderFrameHost*> FramesInSubtree() const;
|
||||
|
||||
v8::Local<v8::Promise> CollectDocumentJSCallStack(gin::Arguments* args);
|
||||
void CollectedJavaScriptCallStack(
|
||||
gin_helper::Promise<base::Value> promise,
|
||||
const std::string& untrusted_javascript_call_stack,
|
||||
const std::optional<blink::LocalFrameToken>& remote_frame_token);
|
||||
|
||||
void DOMContentLoaded();
|
||||
|
||||
mojo::Remote<mojom::ElectronRenderer> renderer_api_;
|
||||
|
||||
@@ -723,11 +723,11 @@ gin::Handle<WebRequest> WebRequest::From(
|
||||
v8::Isolate* isolate,
|
||||
content::BrowserContext* browser_context) {
|
||||
if (!browser_context)
|
||||
return gin::Handle<WebRequest>();
|
||||
return {};
|
||||
auto* user_data =
|
||||
static_cast<UserData*>(browser_context->GetUserData(kUserDataKey));
|
||||
if (!user_data)
|
||||
return gin::Handle<WebRequest>();
|
||||
return {};
|
||||
return gin::CreateHandle(isolate, user_data->data.get());
|
||||
}
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ std::vector<blink::MessagePortChannel> MessagePort::DisentanglePorts(
|
||||
const std::vector<gin::Handle<MessagePort>>& ports,
|
||||
bool* threw_exception) {
|
||||
if (ports.empty())
|
||||
return std::vector<blink::MessagePortChannel>();
|
||||
return {};
|
||||
|
||||
std::unordered_set<MessagePort*> visited;
|
||||
|
||||
@@ -244,7 +244,7 @@ std::vector<blink::MessagePortChannel> MessagePort::DisentanglePorts(
|
||||
gin_helper::ErrorThrower(isolate).ThrowError(
|
||||
"Port at index " + base::NumberToString(i) + " is " + type + ".");
|
||||
*threw_exception = true;
|
||||
return std::vector<blink::MessagePortChannel>();
|
||||
return {};
|
||||
}
|
||||
visited.insert(port);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class Delegate {
|
||||
const std::string& update_url) {}
|
||||
|
||||
protected:
|
||||
virtual ~Delegate() {}
|
||||
virtual ~Delegate() = default;
|
||||
};
|
||||
|
||||
class AutoUpdater {
|
||||
|
||||
@@ -66,9 +66,9 @@ std::optional<std::string> GetXdgAppOutput(
|
||||
&success_code);
|
||||
|
||||
if (!ran_ok || success_code != EXIT_SUCCESS)
|
||||
return std::optional<std::string>();
|
||||
return {};
|
||||
|
||||
return std::make_optional(reply);
|
||||
return reply;
|
||||
}
|
||||
|
||||
bool SetDefaultWebClient(const std::string& protocol) {
|
||||
|
||||
@@ -85,7 +85,7 @@ class BrowserObserver : public base::CheckedObserver {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
~BrowserObserver() override {}
|
||||
~BrowserObserver() override = default;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -97,7 +97,7 @@ bool IsValidCustomProtocol(const std::wstring& scheme) {
|
||||
std::wstring GetAppInfoHelperForProtocol(ASSOCSTR assoc_str, const GURL& url) {
|
||||
const std::wstring url_scheme = base::ASCIIToWide(url.scheme_piece());
|
||||
if (!IsValidCustomProtocol(url_scheme))
|
||||
return std::wstring();
|
||||
return {};
|
||||
|
||||
wchar_t out_buffer[1024];
|
||||
DWORD buffer_size = std::size(out_buffer);
|
||||
@@ -106,7 +106,7 @@ std::wstring GetAppInfoHelperForProtocol(ASSOCSTR assoc_str, const GURL& url) {
|
||||
nullptr, out_buffer, &buffer_size);
|
||||
if (FAILED(hr)) {
|
||||
DLOG(WARNING) << "AssocQueryString failed!";
|
||||
return std::wstring();
|
||||
return {};
|
||||
}
|
||||
return std::wstring(out_buffer);
|
||||
}
|
||||
@@ -251,7 +251,7 @@ std::unique_ptr<FileVersionInfo> FetchFileVersionInfo() {
|
||||
electron::ScopedAllowBlockingForElectron allow_blocking;
|
||||
return FileVersionInfo::CreateFileVersionInfo(path);
|
||||
}
|
||||
return std::unique_ptr<FileVersionInfo>();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -264,9 +264,7 @@ bool AllowFileAccess(const std::string& extension_id,
|
||||
extension_id);
|
||||
}
|
||||
|
||||
RenderProcessHostPrivilege GetPrivilegeRequiredByUrl(
|
||||
const GURL& url,
|
||||
extensions::ExtensionRegistry* registry) {
|
||||
RenderProcessHostPrivilege GetPrivilegeRequiredByUrl(const GURL& url) {
|
||||
// Default to a normal renderer cause it is lower privileged. This should only
|
||||
// occur if the URL on a site instance is either malformed, or uninitialized.
|
||||
// If it is malformed, then there is no need for better privileges anyways.
|
||||
@@ -405,9 +403,9 @@ void ElectronBrowserClient::OverrideWebkitPrefs(
|
||||
prefs->javascript_enabled = true;
|
||||
prefs->web_security_enabled = true;
|
||||
prefs->plugins_enabled = true;
|
||||
prefs->dom_paste_enabled = true;
|
||||
prefs->dom_paste_enabled = false;
|
||||
prefs->javascript_can_access_clipboard = false;
|
||||
prefs->allow_scripts_to_close_windows = true;
|
||||
prefs->javascript_can_access_clipboard = true;
|
||||
prefs->local_storage_enabled = true;
|
||||
prefs->databases_enabled = true;
|
||||
prefs->allow_universal_access_from_file_urls =
|
||||
@@ -594,7 +592,7 @@ ElectronBrowserClient::GetGeneratedCodeCacheSettings(
|
||||
// If we pass 0 for size, disk_cache will pick a default size using the
|
||||
// heuristics based on available disk size. These are implemented in
|
||||
// disk_cache::PreferredCacheSize in net/disk_cache/cache_util.cc.
|
||||
return content::GeneratedCodeCacheSettings(true, 0, cache_path);
|
||||
return {true, 0, cache_path};
|
||||
}
|
||||
|
||||
void ElectronBrowserClient::AllowCertificateError(
|
||||
@@ -627,7 +625,7 @@ base::OnceClosure ElectronBrowserClient::SelectClientCertificate(
|
||||
std::move(client_certs), std::move(delegate));
|
||||
}
|
||||
|
||||
return base::OnceClosure();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ElectronBrowserClient::CanCreateWindow(
|
||||
@@ -731,15 +729,12 @@ bool ElectronBrowserClient::IsSuitableHost(
|
||||
const GURL& site_url) {
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
auto* browser_context = process_host->GetBrowserContext();
|
||||
extensions::ExtensionRegistry* registry =
|
||||
extensions::ExtensionRegistry::Get(browser_context);
|
||||
extensions::ProcessMap* process_map =
|
||||
extensions::ProcessMap::Get(browser_context);
|
||||
|
||||
// Otherwise, just make sure the process privilege matches the privilege
|
||||
// required by the site.
|
||||
RenderProcessHostPrivilege privilege_required =
|
||||
GetPrivilegeRequiredByUrl(site_url, registry);
|
||||
const auto privilege_required = GetPrivilegeRequiredByUrl(site_url);
|
||||
return GetProcessPrivilege(process_host, process_map) == privilege_required;
|
||||
#else
|
||||
return content::ContentBrowserClient::IsSuitableHost(process_host, site_url);
|
||||
@@ -792,7 +787,7 @@ ElectronBrowserClient::CreateClientCertStore(
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
return std::make_unique<net::ClientCertStoreMac>();
|
||||
#elif defined(USE_OPENSSL)
|
||||
return std::unique_ptr<net::ClientCertStore>();
|
||||
return ();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -801,7 +796,7 @@ ElectronBrowserClient::OverrideSystemLocationProvider() {
|
||||
#if BUILDFLAG(OVERRIDE_LOCATION_PROVIDER)
|
||||
return std::make_unique<FakeLocationProvider>();
|
||||
#else
|
||||
return nullptr;
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -983,7 +978,7 @@ base::FilePath ElectronBrowserClient::GetDefaultDownloadDirectory() {
|
||||
base::FilePath download_path;
|
||||
if (base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &download_path))
|
||||
return download_path;
|
||||
return base::FilePath();
|
||||
return {};
|
||||
}
|
||||
|
||||
scoped_refptr<network::SharedURLLoaderFactory>
|
||||
|
||||
@@ -484,7 +484,7 @@ ElectronBrowserContext::CreateZoomLevelDelegate(
|
||||
if (!IsOffTheRecord()) {
|
||||
return std::make_unique<ZoomLevelDelegate>(prefs(), partition_path);
|
||||
}
|
||||
return std::unique_ptr<content::ZoomLevelDelegate>();
|
||||
return {};
|
||||
}
|
||||
|
||||
content::DownloadManagerDelegate*
|
||||
|
||||
@@ -163,7 +163,7 @@ std::u16string MediaStringProvider(media::MessageId id) {
|
||||
return u"Communications";
|
||||
#endif
|
||||
default:
|
||||
return std::u16string();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ class ElectronPermissionManager::PendingRequest {
|
||||
return content::RenderFrameHost::FromID(render_frame_host_id_);
|
||||
}
|
||||
|
||||
bool IsComplete() const { return remaining_results_ == 0; }
|
||||
[[nodiscard]] bool IsComplete() const { return remaining_results_ == 0; }
|
||||
|
||||
void RunCallback() {
|
||||
if (!callback_.is_null()) {
|
||||
@@ -278,8 +278,7 @@ ElectronPermissionManager::GetPermissionResultForOriginWithoutContext(
|
||||
const url::Origin& embedding_origin) {
|
||||
blink::mojom::PermissionStatus status = GetPermissionStatus(
|
||||
permission, requesting_origin.GetURL(), embedding_origin.GetURL());
|
||||
return content::PermissionResult(
|
||||
status, content::PermissionStatusSource::UNSPECIFIED);
|
||||
return {status, content::PermissionStatusSource::UNSPECIFIED};
|
||||
}
|
||||
|
||||
void ElectronPermissionManager::CheckBluetoothDevicePair(
|
||||
|
||||
@@ -54,7 +54,7 @@ ElectronWebUIControllerFactory::CreateWebUIControllerForURL(
|
||||
if (host == chrome::kChromeUIAccessibilityHost)
|
||||
return std::make_unique<ElectronAccessibilityUI>(web_ui);
|
||||
|
||||
return std::unique_ptr<content::WebUIController>();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -26,7 +26,7 @@ class ExtendedWebContentsObserver : public base::CheckedObserver {
|
||||
virtual void OnDevToolsResized() {}
|
||||
|
||||
protected:
|
||||
~ExtendedWebContentsObserver() override {}
|
||||
~ExtendedWebContentsObserver() override = default;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -106,14 +106,14 @@ class ExtensionActionFunction : public ExtensionFunction {
|
||||
// show
|
||||
class ExtensionActionShowFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionShowFunction() override {}
|
||||
~ExtensionActionShowFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// hide
|
||||
class ExtensionActionHideFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionHideFunction() override {}
|
||||
~ExtensionActionHideFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
@@ -123,28 +123,28 @@ class ExtensionActionSetIconFunction : public ExtensionActionFunction {
|
||||
static void SetReportErrorForInvisibleIconForTesting(bool value);
|
||||
|
||||
protected:
|
||||
~ExtensionActionSetIconFunction() override {}
|
||||
~ExtensionActionSetIconFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// setTitle
|
||||
class ExtensionActionSetTitleFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionSetTitleFunction() override {}
|
||||
~ExtensionActionSetTitleFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// setPopup
|
||||
class ExtensionActionSetPopupFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionSetPopupFunction() override {}
|
||||
~ExtensionActionSetPopupFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// setBadgeText
|
||||
class ExtensionActionSetBadgeTextFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionSetBadgeTextFunction() override {}
|
||||
~ExtensionActionSetBadgeTextFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
@@ -152,35 +152,35 @@ class ExtensionActionSetBadgeTextFunction : public ExtensionActionFunction {
|
||||
class ExtensionActionSetBadgeBackgroundColorFunction
|
||||
: public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionSetBadgeBackgroundColorFunction() override {}
|
||||
~ExtensionActionSetBadgeBackgroundColorFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// getTitle
|
||||
class ExtensionActionGetTitleFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionGetTitleFunction() override {}
|
||||
~ExtensionActionGetTitleFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// getPopup
|
||||
class ExtensionActionGetPopupFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionGetPopupFunction() override {}
|
||||
~ExtensionActionGetPopupFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// openPopup
|
||||
class ExtensionActionOpenPopupFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionOpenPopupFunction() override {}
|
||||
~ExtensionActionOpenPopupFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// getBadgeText
|
||||
class ExtensionActionGetBadgeTextFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionGetBadgeTextFunction() override {}
|
||||
~ExtensionActionGetBadgeTextFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
@@ -188,7 +188,7 @@ class ExtensionActionGetBadgeTextFunction : public ExtensionActionFunction {
|
||||
class ExtensionActionGetBadgeBackgroundColorFunction
|
||||
: public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionGetBadgeBackgroundColorFunction() override {}
|
||||
~ExtensionActionGetBadgeBackgroundColorFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
@@ -201,7 +201,7 @@ class ActionSetIconFunction : public ExtensionActionSetIconFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.setIcon", ACTION_SETICON)
|
||||
|
||||
protected:
|
||||
~ActionSetIconFunction() override {}
|
||||
~ActionSetIconFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionGetPopupFunction : public ExtensionActionGetPopupFunction {
|
||||
@@ -209,7 +209,7 @@ class ActionGetPopupFunction : public ExtensionActionGetPopupFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.getPopup", ACTION_GETPOPUP)
|
||||
|
||||
protected:
|
||||
~ActionGetPopupFunction() override {}
|
||||
~ActionGetPopupFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionSetPopupFunction : public ExtensionActionSetPopupFunction {
|
||||
@@ -217,7 +217,7 @@ class ActionSetPopupFunction : public ExtensionActionSetPopupFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.setPopup", ACTION_SETPOPUP)
|
||||
|
||||
protected:
|
||||
~ActionSetPopupFunction() override {}
|
||||
~ActionSetPopupFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionGetTitleFunction : public ExtensionActionGetTitleFunction {
|
||||
@@ -225,7 +225,7 @@ class ActionGetTitleFunction : public ExtensionActionGetTitleFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.getTitle", ACTION_GETTITLE)
|
||||
|
||||
protected:
|
||||
~ActionGetTitleFunction() override {}
|
||||
~ActionGetTitleFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionSetTitleFunction : public ExtensionActionSetTitleFunction {
|
||||
@@ -233,7 +233,7 @@ class ActionSetTitleFunction : public ExtensionActionSetTitleFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.setTitle", ACTION_SETTITLE)
|
||||
|
||||
protected:
|
||||
~ActionSetTitleFunction() override {}
|
||||
~ActionSetTitleFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionGetBadgeTextFunction : public ExtensionActionGetBadgeTextFunction {
|
||||
@@ -241,7 +241,7 @@ class ActionGetBadgeTextFunction : public ExtensionActionGetBadgeTextFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.getBadgeText", ACTION_GETBADGETEXT)
|
||||
|
||||
protected:
|
||||
~ActionGetBadgeTextFunction() override {}
|
||||
~ActionGetBadgeTextFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionSetBadgeTextFunction : public ExtensionActionSetBadgeTextFunction {
|
||||
@@ -249,7 +249,7 @@ class ActionSetBadgeTextFunction : public ExtensionActionSetBadgeTextFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.setBadgeText", ACTION_SETBADGETEXT)
|
||||
|
||||
protected:
|
||||
~ActionSetBadgeTextFunction() override {}
|
||||
~ActionSetBadgeTextFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionGetBadgeBackgroundColorFunction
|
||||
@@ -259,7 +259,7 @@ class ActionGetBadgeBackgroundColorFunction
|
||||
ACTION_GETBADGEBACKGROUNDCOLOR)
|
||||
|
||||
protected:
|
||||
~ActionGetBadgeBackgroundColorFunction() override {}
|
||||
~ActionGetBadgeBackgroundColorFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionSetBadgeBackgroundColorFunction
|
||||
@@ -269,7 +269,7 @@ class ActionSetBadgeBackgroundColorFunction
|
||||
ACTION_SETBADGEBACKGROUNDCOLOR)
|
||||
|
||||
protected:
|
||||
~ActionSetBadgeBackgroundColorFunction() override {}
|
||||
~ActionSetBadgeBackgroundColorFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionGetBadgeTextColorFunction : public ExtensionActionFunction {
|
||||
@@ -297,7 +297,7 @@ class ActionEnableFunction : public ExtensionActionShowFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.enable", ACTION_ENABLE)
|
||||
|
||||
protected:
|
||||
~ActionEnableFunction() override {}
|
||||
~ActionEnableFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionDisableFunction : public ExtensionActionHideFunction {
|
||||
@@ -305,7 +305,7 @@ class ActionDisableFunction : public ExtensionActionHideFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("action.disable", ACTION_DISABLE)
|
||||
|
||||
protected:
|
||||
~ActionDisableFunction() override {}
|
||||
~ActionDisableFunction() override = default;
|
||||
};
|
||||
|
||||
class ActionIsEnabledFunction : public ExtensionActionFunction {
|
||||
@@ -350,7 +350,7 @@ class BrowserActionSetIconFunction : public ExtensionActionSetIconFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.setIcon", BROWSERACTION_SETICON)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetIconFunction() override {}
|
||||
~BrowserActionSetIconFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionSetTitleFunction : public ExtensionActionSetTitleFunction {
|
||||
@@ -358,7 +358,7 @@ class BrowserActionSetTitleFunction : public ExtensionActionSetTitleFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.setTitle", BROWSERACTION_SETTITLE)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetTitleFunction() override {}
|
||||
~BrowserActionSetTitleFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionSetPopupFunction : public ExtensionActionSetPopupFunction {
|
||||
@@ -366,7 +366,7 @@ class BrowserActionSetPopupFunction : public ExtensionActionSetPopupFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.setPopup", BROWSERACTION_SETPOPUP)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetPopupFunction() override {}
|
||||
~BrowserActionSetPopupFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionGetTitleFunction : public ExtensionActionGetTitleFunction {
|
||||
@@ -374,7 +374,7 @@ class BrowserActionGetTitleFunction : public ExtensionActionGetTitleFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.getTitle", BROWSERACTION_GETTITLE)
|
||||
|
||||
protected:
|
||||
~BrowserActionGetTitleFunction() override {}
|
||||
~BrowserActionGetTitleFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionGetPopupFunction : public ExtensionActionGetPopupFunction {
|
||||
@@ -382,7 +382,7 @@ class BrowserActionGetPopupFunction : public ExtensionActionGetPopupFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.getPopup", BROWSERACTION_GETPOPUP)
|
||||
|
||||
protected:
|
||||
~BrowserActionGetPopupFunction() override {}
|
||||
~BrowserActionGetPopupFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionSetBadgeTextFunction
|
||||
@@ -392,7 +392,7 @@ class BrowserActionSetBadgeTextFunction
|
||||
BROWSERACTION_SETBADGETEXT)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetBadgeTextFunction() override {}
|
||||
~BrowserActionSetBadgeTextFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionSetBadgeBackgroundColorFunction
|
||||
@@ -402,7 +402,7 @@ class BrowserActionSetBadgeBackgroundColorFunction
|
||||
BROWSERACTION_SETBADGEBACKGROUNDCOLOR)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetBadgeBackgroundColorFunction() override {}
|
||||
~BrowserActionSetBadgeBackgroundColorFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionGetBadgeTextFunction
|
||||
@@ -412,7 +412,7 @@ class BrowserActionGetBadgeTextFunction
|
||||
BROWSERACTION_GETBADGETEXT)
|
||||
|
||||
protected:
|
||||
~BrowserActionGetBadgeTextFunction() override {}
|
||||
~BrowserActionGetBadgeTextFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionGetBadgeBackgroundColorFunction
|
||||
@@ -422,7 +422,7 @@ class BrowserActionGetBadgeBackgroundColorFunction
|
||||
BROWSERACTION_GETBADGEBACKGROUNDCOLOR)
|
||||
|
||||
protected:
|
||||
~BrowserActionGetBadgeBackgroundColorFunction() override {}
|
||||
~BrowserActionGetBadgeBackgroundColorFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionEnableFunction : public ExtensionActionShowFunction {
|
||||
@@ -430,7 +430,7 @@ class BrowserActionEnableFunction : public ExtensionActionShowFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.enable", BROWSERACTION_ENABLE)
|
||||
|
||||
protected:
|
||||
~BrowserActionEnableFunction() override {}
|
||||
~BrowserActionEnableFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionDisableFunction : public ExtensionActionHideFunction {
|
||||
@@ -438,7 +438,7 @@ class BrowserActionDisableFunction : public ExtensionActionHideFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.disable", BROWSERACTION_DISABLE)
|
||||
|
||||
protected:
|
||||
~BrowserActionDisableFunction() override {}
|
||||
~BrowserActionDisableFunction() override = default;
|
||||
};
|
||||
|
||||
class BrowserActionOpenPopupFunction : public ExtensionActionOpenPopupFunction {
|
||||
@@ -447,7 +447,7 @@ class BrowserActionOpenPopupFunction : public ExtensionActionOpenPopupFunction {
|
||||
BROWSERACTION_OPEN_POPUP)
|
||||
|
||||
protected:
|
||||
~BrowserActionOpenPopupFunction() override {}
|
||||
~BrowserActionOpenPopupFunction() override = default;
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
@@ -461,7 +461,7 @@ class PageActionShowFunction : public extensions::ExtensionActionShowFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.show", PAGEACTION_SHOW)
|
||||
|
||||
protected:
|
||||
~PageActionShowFunction() override {}
|
||||
~PageActionShowFunction() override = default;
|
||||
};
|
||||
|
||||
class PageActionHideFunction : public extensions::ExtensionActionHideFunction {
|
||||
@@ -469,7 +469,7 @@ class PageActionHideFunction : public extensions::ExtensionActionHideFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.hide", PAGEACTION_HIDE)
|
||||
|
||||
protected:
|
||||
~PageActionHideFunction() override {}
|
||||
~PageActionHideFunction() override = default;
|
||||
};
|
||||
|
||||
class PageActionSetIconFunction
|
||||
@@ -478,7 +478,7 @@ class PageActionSetIconFunction
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.setIcon", PAGEACTION_SETICON)
|
||||
|
||||
protected:
|
||||
~PageActionSetIconFunction() override {}
|
||||
~PageActionSetIconFunction() override = default;
|
||||
};
|
||||
|
||||
class PageActionSetTitleFunction
|
||||
@@ -487,7 +487,7 @@ class PageActionSetTitleFunction
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.setTitle", PAGEACTION_SETTITLE)
|
||||
|
||||
protected:
|
||||
~PageActionSetTitleFunction() override {}
|
||||
~PageActionSetTitleFunction() override = default;
|
||||
};
|
||||
|
||||
class PageActionSetPopupFunction
|
||||
@@ -496,7 +496,7 @@ class PageActionSetPopupFunction
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.setPopup", PAGEACTION_SETPOPUP)
|
||||
|
||||
protected:
|
||||
~PageActionSetPopupFunction() override {}
|
||||
~PageActionSetPopupFunction() override = default;
|
||||
};
|
||||
|
||||
class PageActionGetTitleFunction
|
||||
@@ -505,7 +505,7 @@ class PageActionGetTitleFunction
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.getTitle", PAGEACTION_GETTITLE)
|
||||
|
||||
protected:
|
||||
~PageActionGetTitleFunction() override {}
|
||||
~PageActionGetTitleFunction() override = default;
|
||||
};
|
||||
|
||||
class PageActionGetPopupFunction
|
||||
@@ -514,7 +514,7 @@ class PageActionGetPopupFunction
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.getPopup", PAGEACTION_GETPOPUP)
|
||||
|
||||
protected:
|
||||
~PageActionGetPopupFunction() override {}
|
||||
~PageActionGetPopupFunction() override = default;
|
||||
};
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_
|
||||
|
||||
@@ -49,10 +49,10 @@ namespace extensions {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kCouldNotLoadFileError[] = "Could not load file: '*'.";
|
||||
constexpr char kDuplicateFileSpecifiedError[] =
|
||||
constexpr std::string_view kCouldNotLoadFileError = "Could not load file: '*'.";
|
||||
constexpr std::string_view kDuplicateFileSpecifiedError =
|
||||
"Duplicate file specified: '*'.";
|
||||
constexpr char kEmptyMatchesError[] =
|
||||
constexpr std::string_view kEmptyMatchesError =
|
||||
"Script with ID '*' must specify 'matches'.";
|
||||
constexpr char kExactlyOneOfCssAndFilesError[] =
|
||||
"Exactly one of 'css' and 'files' must be specified.";
|
||||
@@ -490,14 +490,8 @@ std::unique_ptr<UserScript> ParseUserScript(
|
||||
ConvertRegisteredContentScriptToSerializedUserScript(
|
||||
std::move(content_script));
|
||||
|
||||
std::unique_ptr<UserScript> user_script =
|
||||
script_serialization::ParseSerializedUserScript(
|
||||
serialized_script, extension, allowed_in_incognito, error);
|
||||
if (!user_script) {
|
||||
return nullptr; // Parsing failed.
|
||||
}
|
||||
|
||||
return user_script;
|
||||
return script_serialization::ParseSerializedUserScript(
|
||||
serialized_script, extension, allowed_in_incognito, error);
|
||||
}
|
||||
|
||||
// Converts a UserScript object to a api::scripting::RegisteredContentScript
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -39,7 +40,7 @@ namespace extensions {
|
||||
|
||||
namespace tabs = api::tabs;
|
||||
|
||||
const char kFrameNotFoundError[] = "No frame with id * in tab *.";
|
||||
constexpr std::string_view kFrameNotFoundError = "No frame with id * in tab *.";
|
||||
const char kPerOriginOnlyInAutomaticError[] =
|
||||
"Can only set scope to "
|
||||
"\"per-origin\" in \"automatic\" mode.";
|
||||
|
||||
@@ -43,13 +43,13 @@ class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction {
|
||||
bool ShouldRemoveCSS() const override;
|
||||
|
||||
private:
|
||||
~TabsExecuteScriptFunction() override {}
|
||||
~TabsExecuteScriptFunction() override = default;
|
||||
|
||||
DECLARE_EXTENSION_FUNCTION("tabs.executeScript", TABS_EXECUTESCRIPT)
|
||||
};
|
||||
|
||||
class TabsReloadFunction : public ExtensionFunction {
|
||||
~TabsReloadFunction() override {}
|
||||
~TabsReloadFunction() override = default;
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
@@ -57,7 +57,7 @@ class TabsReloadFunction : public ExtensionFunction {
|
||||
};
|
||||
|
||||
class TabsQueryFunction : public ExtensionFunction {
|
||||
~TabsQueryFunction() override {}
|
||||
~TabsQueryFunction() override = default;
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
@@ -65,7 +65,7 @@ class TabsQueryFunction : public ExtensionFunction {
|
||||
};
|
||||
|
||||
class TabsGetFunction : public ExtensionFunction {
|
||||
~TabsGetFunction() override {}
|
||||
~TabsGetFunction() override = default;
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
@@ -74,7 +74,7 @@ class TabsGetFunction : public ExtensionFunction {
|
||||
|
||||
class TabsSetZoomFunction : public ExtensionFunction {
|
||||
private:
|
||||
~TabsSetZoomFunction() override {}
|
||||
~TabsSetZoomFunction() override = default;
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
@@ -83,7 +83,7 @@ class TabsSetZoomFunction : public ExtensionFunction {
|
||||
|
||||
class TabsGetZoomFunction : public ExtensionFunction {
|
||||
private:
|
||||
~TabsGetZoomFunction() override {}
|
||||
~TabsGetZoomFunction() override = default;
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
@@ -92,7 +92,7 @@ class TabsGetZoomFunction : public ExtensionFunction {
|
||||
|
||||
class TabsSetZoomSettingsFunction : public ExtensionFunction {
|
||||
private:
|
||||
~TabsSetZoomSettingsFunction() override {}
|
||||
~TabsSetZoomSettingsFunction() override = default;
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
@@ -101,7 +101,7 @@ class TabsSetZoomSettingsFunction : public ExtensionFunction {
|
||||
|
||||
class TabsGetZoomSettingsFunction : public ExtensionFunction {
|
||||
private:
|
||||
~TabsGetZoomSettingsFunction() override {}
|
||||
~TabsGetZoomSettingsFunction() override = default;
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
@@ -113,7 +113,7 @@ class TabsUpdateFunction : public ExtensionFunction {
|
||||
TabsUpdateFunction();
|
||||
|
||||
protected:
|
||||
~TabsUpdateFunction() override {}
|
||||
~TabsUpdateFunction() override = default;
|
||||
bool UpdateURL(const std::string& url, int tab_id, std::string* error);
|
||||
ResponseValue GetResult();
|
||||
|
||||
|
||||
@@ -87,6 +87,9 @@ void ElectronExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
|
||||
management_policy_ = std::make_unique<ManagementPolicy>();
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<base::Value::Dict> ParseManifest(
|
||||
const std::string_view manifest_contents) {
|
||||
JSONStringValueDeserializer deserializer(manifest_contents);
|
||||
@@ -95,14 +98,17 @@ std::unique_ptr<base::Value::Dict> ParseManifest(
|
||||
|
||||
if (!manifest.get() || !manifest->is_dict()) {
|
||||
LOG(ERROR) << "Failed to parse extension manifest.";
|
||||
return std::unique_ptr<base::Value::Dict>();
|
||||
return {};
|
||||
}
|
||||
return std::make_unique<base::Value::Dict>(std::move(*manifest).TakeDict());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif // if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
|
||||
void ElectronExtensionSystem::LoadComponentExtensions() {
|
||||
std::string utf8_error;
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
std::string utf8_error;
|
||||
std::string pdf_manifest_string = pdf_extension_util::GetManifest();
|
||||
std::unique_ptr<base::Value::Dict> pdf_manifest =
|
||||
ParseManifest(pdf_manifest_string);
|
||||
|
||||
@@ -165,14 +165,14 @@ base::FilePath ElectronExtensionsBrowserClient::GetBundleResourcePath(
|
||||
*resource_id = 0;
|
||||
base::FilePath chrome_resources_path;
|
||||
if (!base::PathService::Get(chrome::DIR_RESOURCES, &chrome_resources_path))
|
||||
return base::FilePath();
|
||||
return {};
|
||||
|
||||
// Since component extension resources are included in
|
||||
// component_extension_resources.pak file in |chrome_resources_path|,
|
||||
// calculate the extension |request_relative_path| against
|
||||
// |chrome_resources_path|.
|
||||
if (!chrome_resources_path.IsParent(extension_resources_path))
|
||||
return base::FilePath();
|
||||
return {};
|
||||
|
||||
base::FilePath request_relative_path =
|
||||
extensions::file_util::ExtensionURLToRelativeFilePath(request.url);
|
||||
@@ -180,7 +180,7 @@ base::FilePath ElectronExtensionsBrowserClient::GetBundleResourcePath(
|
||||
->GetComponentExtensionResourceManager()
|
||||
->IsComponentExtensionResource(extension_resources_path,
|
||||
request_relative_path, resource_id)) {
|
||||
return base::FilePath();
|
||||
return {};
|
||||
}
|
||||
DCHECK_NE(0, *resource_id);
|
||||
|
||||
|
||||
@@ -531,7 +531,7 @@ bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
|
||||
base::FilePath FileSelectHelper::GetSanitizedFileName(
|
||||
const base::FilePath& suggested_filename) {
|
||||
if (suggested_filename.empty())
|
||||
return base::FilePath();
|
||||
return {};
|
||||
return net::GenerateFileName(
|
||||
GURL(), std::string(), std::string(), suggested_filename.AsUTF8Unsafe(),
|
||||
std::string(), l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
|
||||
|
||||
@@ -90,10 +90,10 @@ using blink::mojom::PermissionStatus;
|
||||
// }
|
||||
const char kDefaultLastPickedDirectoryKey[] = "default-id";
|
||||
const char kCustomLastPickedDirectoryKey[] = "custom-id";
|
||||
const char kPathKey[] = "path";
|
||||
const char kPathTypeKey[] = "path-type";
|
||||
const char kDisplayNameKey[] = "display-name";
|
||||
const char kTimestampKey[] = "timestamp";
|
||||
constexpr std::string_view kPathKey = "path";
|
||||
constexpr std::string_view kPathTypeKey = "path-type";
|
||||
constexpr std::string_view kDisplayNameKey = "display-name";
|
||||
constexpr std::string_view kTimestampKey = "timestamp";
|
||||
|
||||
constexpr base::TimeDelta kPermissionRevocationTimeout = base::Seconds(5);
|
||||
|
||||
@@ -459,7 +459,7 @@ FileSystemAccessPermissionContext::GetReadPermissionGrant(
|
||||
// but that is exactly what we want.
|
||||
auto& origin_state = active_permissions_map_[origin];
|
||||
auto*& existing_grant = origin_state.read_grants[path_info.path];
|
||||
scoped_refptr<PermissionGrantImpl> new_grant;
|
||||
scoped_refptr<PermissionGrantImpl> grant;
|
||||
|
||||
if (existing_grant && existing_grant->handle_type() != handle_type) {
|
||||
// |path| changed from being a directory to being a file or vice versa,
|
||||
@@ -469,18 +469,21 @@ FileSystemAccessPermissionContext::GetReadPermissionGrant(
|
||||
existing_grant = nullptr;
|
||||
}
|
||||
|
||||
if (!existing_grant) {
|
||||
new_grant = base::MakeRefCounted<PermissionGrantImpl>(
|
||||
bool creating_new_grant = !existing_grant;
|
||||
if (creating_new_grant) {
|
||||
grant = base::MakeRefCounted<PermissionGrantImpl>(
|
||||
weak_factory_.GetWeakPtr(), origin, path_info, handle_type,
|
||||
GrantType::kRead, user_action);
|
||||
existing_grant = new_grant.get();
|
||||
existing_grant = grant.get();
|
||||
} else {
|
||||
grant = existing_grant;
|
||||
}
|
||||
|
||||
// If a parent directory is already readable this new grant should also be
|
||||
// readable.
|
||||
if (new_grant &&
|
||||
if (creating_new_grant &&
|
||||
AncestorHasActivePermission(origin, path_info.path, GrantType::kRead)) {
|
||||
existing_grant->SetStatus(PermissionStatus::GRANTED);
|
||||
grant->SetStatus(PermissionStatus::GRANTED);
|
||||
} else {
|
||||
switch (user_action) {
|
||||
case UserAction::kOpen:
|
||||
@@ -492,7 +495,7 @@ FileSystemAccessPermissionContext::GetReadPermissionGrant(
|
||||
[[fallthrough]];
|
||||
case UserAction::kDragAndDrop:
|
||||
// Drag&drop grants read access for all handles.
|
||||
existing_grant->SetStatus(PermissionStatus::GRANTED);
|
||||
grant->SetStatus(PermissionStatus::GRANTED);
|
||||
break;
|
||||
case UserAction::kLoadFromStorage:
|
||||
case UserAction::kNone:
|
||||
@@ -500,7 +503,7 @@ FileSystemAccessPermissionContext::GetReadPermissionGrant(
|
||||
}
|
||||
}
|
||||
|
||||
return existing_grant;
|
||||
return grant;
|
||||
}
|
||||
|
||||
scoped_refptr<content::FileSystemAccessPermissionGrant>
|
||||
@@ -514,7 +517,7 @@ FileSystemAccessPermissionContext::GetWritePermissionGrant(
|
||||
// but that is exactly what we want.
|
||||
auto& origin_state = active_permissions_map_[origin];
|
||||
auto*& existing_grant = origin_state.write_grants[path_info.path];
|
||||
scoped_refptr<PermissionGrantImpl> new_grant;
|
||||
scoped_refptr<PermissionGrantImpl> grant;
|
||||
|
||||
if (existing_grant && existing_grant->handle_type() != handle_type) {
|
||||
// |path| changed from being a directory to being a file or vice versa,
|
||||
@@ -524,23 +527,26 @@ FileSystemAccessPermissionContext::GetWritePermissionGrant(
|
||||
existing_grant = nullptr;
|
||||
}
|
||||
|
||||
if (!existing_grant) {
|
||||
new_grant = base::MakeRefCounted<PermissionGrantImpl>(
|
||||
bool creating_new_grant = !existing_grant;
|
||||
if (creating_new_grant) {
|
||||
grant = base::MakeRefCounted<PermissionGrantImpl>(
|
||||
weak_factory_.GetWeakPtr(), origin, path_info, handle_type,
|
||||
GrantType::kWrite, user_action);
|
||||
existing_grant = new_grant.get();
|
||||
existing_grant = grant.get();
|
||||
} else {
|
||||
grant = existing_grant;
|
||||
}
|
||||
|
||||
// If a parent directory is already writable this new grant should also be
|
||||
// writable.
|
||||
if (new_grant &&
|
||||
if (creating_new_grant &&
|
||||
AncestorHasActivePermission(origin, path_info.path, GrantType::kWrite)) {
|
||||
existing_grant->SetStatus(PermissionStatus::GRANTED);
|
||||
grant->SetStatus(PermissionStatus::GRANTED);
|
||||
} else {
|
||||
switch (user_action) {
|
||||
case UserAction::kSave:
|
||||
// Only automatically grant write access for save dialogs.
|
||||
existing_grant->SetStatus(PermissionStatus::GRANTED);
|
||||
grant->SetStatus(PermissionStatus::GRANTED);
|
||||
break;
|
||||
case UserAction::kOpen:
|
||||
case UserAction::kDragAndDrop:
|
||||
@@ -550,7 +556,7 @@ FileSystemAccessPermissionContext::GetWritePermissionGrant(
|
||||
}
|
||||
}
|
||||
|
||||
return existing_grant;
|
||||
return grant;
|
||||
}
|
||||
|
||||
bool FileSystemAccessPermissionContext::IsFileTypeDangerous(
|
||||
@@ -586,7 +592,7 @@ void FileSystemAccessPermissionContext::ConfirmSensitiveEntryAccess(
|
||||
content::GlobalRenderFrameHostId frame_id,
|
||||
base::OnceCallback<void(SensitiveEntryResult)> callback) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
callback_ = std::move(callback);
|
||||
callback_map_.try_emplace(path_info.path, std::move(callback));
|
||||
|
||||
auto after_blocklist_check_callback = base::BindOnce(
|
||||
&FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist,
|
||||
@@ -630,16 +636,18 @@ void FileSystemAccessPermissionContext::PerformAfterWriteChecks(
|
||||
}
|
||||
|
||||
void FileSystemAccessPermissionContext::RunRestrictedPathCallback(
|
||||
const base::FilePath& file_path,
|
||||
SensitiveEntryResult result) {
|
||||
if (callback_)
|
||||
std::move(callback_).Run(result);
|
||||
if (auto val = callback_map_.extract(file_path))
|
||||
std::move(val.mapped()).Run(result);
|
||||
}
|
||||
|
||||
void FileSystemAccessPermissionContext::OnRestrictedPathResult(
|
||||
const base::FilePath& file_path,
|
||||
gin::Arguments* args) {
|
||||
SensitiveEntryResult result = SensitiveEntryResult::kAbort;
|
||||
args->GetNext(&result);
|
||||
RunRestrictedPathCallback(result);
|
||||
RunRestrictedPathCallback(file_path, result);
|
||||
}
|
||||
|
||||
void FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist(
|
||||
@@ -652,8 +660,9 @@ void FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist(
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
if (user_action == UserAction::kNone) {
|
||||
RunRestrictedPathCallback(should_block ? SensitiveEntryResult::kAbort
|
||||
: SensitiveEntryResult::kAllowed);
|
||||
auto result = should_block ? SensitiveEntryResult::kAbort
|
||||
: SensitiveEntryResult::kAllowed;
|
||||
RunRestrictedPathCallback(path_info.path, result);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -672,11 +681,11 @@ void FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist(
|
||||
"file-system-access-restricted", details,
|
||||
base::BindRepeating(
|
||||
&FileSystemAccessPermissionContext::OnRestrictedPathResult,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
weak_factory_.GetWeakPtr(), path_info.path));
|
||||
return;
|
||||
}
|
||||
|
||||
RunRestrictedPathCallback(SensitiveEntryResult::kAllowed);
|
||||
RunRestrictedPathCallback(path_info.path, SensitiveEntryResult::kAllowed);
|
||||
}
|
||||
|
||||
void FileSystemAccessPermissionContext::MaybeEvictEntries(
|
||||
@@ -864,12 +873,22 @@ void FileSystemAccessPermissionContext::RevokeActiveGrants(
|
||||
auto origin_it = active_permissions_map_.find(origin);
|
||||
if (origin_it != active_permissions_map_.end()) {
|
||||
OriginState& origin_state = origin_it->second;
|
||||
for (auto& grant : origin_state.read_grants) {
|
||||
for (auto grant_iter = origin_state.read_grants.begin(),
|
||||
grant_end = origin_state.read_grants.end();
|
||||
grant_iter != grant_end;) {
|
||||
// The grant may be removed from `read_grants`, so increase the iterator
|
||||
// before continuing.
|
||||
auto& grant = *(grant_iter++);
|
||||
if (file_path.empty() || grant.first == file_path) {
|
||||
grant.second->SetStatus(PermissionStatus::ASK);
|
||||
}
|
||||
}
|
||||
for (auto& grant : origin_state.write_grants) {
|
||||
for (auto grant_iter = origin_state.write_grants.begin(),
|
||||
grant_end = origin_state.write_grants.end();
|
||||
grant_iter != grant_end;) {
|
||||
// The grant may be removed from `write_grants`, so increase the iterator
|
||||
// before continuing.
|
||||
auto& grant = *(grant_iter++);
|
||||
if (file_path.empty() || grant.first == file_path) {
|
||||
grant.second->SetStatus(PermissionStatus::ASK);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user