mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
34 Commits
cherry-pic
...
v35.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8562b0beb | ||
|
|
dbf9f04085 | ||
|
|
12be909cc0 | ||
|
|
c5c94822ce | ||
|
|
7efd6ff76e | ||
|
|
382e2740f5 | ||
|
|
9c5562d290 | ||
|
|
7807ac893c | ||
|
|
e6666f811e | ||
|
|
ff5b9a6680 | ||
|
|
eb6c475b5f | ||
|
|
ded238107f | ||
|
|
2d2e62feda | ||
|
|
64dfeb8a4d | ||
|
|
8ca091251f | ||
|
|
6b08d83af7 | ||
|
|
71233a4517 | ||
|
|
f370a19e36 | ||
|
|
a7fd8872bf | ||
|
|
7f5e6c54bc | ||
|
|
fadec9ac65 | ||
|
|
a8cdd60c88 | ||
|
|
6280172ee9 | ||
|
|
94f6e16871 | ||
|
|
5fde5696d0 | ||
|
|
73605f97ee | ||
|
|
72d3d359c3 | ||
|
|
0b026d261e | ||
|
|
0954ac7843 | ||
|
|
feab959781 | ||
|
|
282903e7b8 | ||
|
|
15d6344b6a | ||
|
|
fcb576566a | ||
|
|
64a07ffc3f |
6
.github/actions/build-electron/action.yml
vendored
6
.github/actions/build-electron/action.yml
vendored
@@ -38,6 +38,9 @@ runs:
|
||||
run: |
|
||||
GN_APPENDED_ARGS="$GN_EXTRA_ARGS target_cpu=\"x64\" v8_snapshot_toolchain=\"//build/toolchain/mac:clang_x64\""
|
||||
echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV
|
||||
- name: Add Clang problem matcher
|
||||
shell: bash
|
||||
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
|
||||
- name: Build Electron ${{ inputs.step-suffix }}
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -199,6 +202,9 @@ runs:
|
||||
e build --target electron:libcxx_headers_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
e build --target electron:libcxxabi_headers_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
e build --target electron:libcxx_objects_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
- name: Remove Clang problem matcher
|
||||
shell: bash
|
||||
run: echo "::remove-matcher owner=clang::"
|
||||
- name: Generate TypeScript Definitions ${{ inputs.step-suffix }}
|
||||
if: ${{ inputs.is-release == 'true' }}
|
||||
shell: bash
|
||||
|
||||
83
.github/actions/build-git-cache/action.yml
vendored
Normal file
83
.github/actions/build-git-cache/action.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
name: 'Build Git Cache'
|
||||
description: 'Runs a gclient sync to build the git cache for Electron'
|
||||
inputs:
|
||||
target-platform:
|
||||
description: 'Target platform, should be linux, win, macos'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set GIT_CACHE_PATH to make gclient to use the cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV
|
||||
- name: Set Chromium Git Cookie
|
||||
uses: ./src/electron/.github/actions/set-chromium-cookie
|
||||
- name: Install Build Tools
|
||||
uses: ./src/electron/.github/actions/install-build-tools
|
||||
- name: Set up cache drive
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${{ inputs.target-platform }}" = "win" ]; then
|
||||
echo "CACHE_DRIVE=/mnt/win-cache" >> $GITHUB_ENV
|
||||
else
|
||||
echo "CACHE_DRIVE=/mnt/cross-instance-cache" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Check cross instance cache disk space
|
||||
shell: bash
|
||||
run: |
|
||||
# if there is less than 35 GB free space then creating the cache might fail so exit early
|
||||
freespace=`df -m $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'`
|
||||
freespace_human=`df -h $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'`
|
||||
if [ $freespace -le 35000 ]; then
|
||||
echo "The cross mount cache has $freespace_human free space which is not enough - exiting"
|
||||
exit 1
|
||||
else
|
||||
echo "The cross mount cache has $freespace_human free space - continuing"
|
||||
fi
|
||||
- name: Restore gitcache
|
||||
shell: bash
|
||||
run: |
|
||||
GIT_CACHE_TAR="$CACHE_DRIVE/gitcache.tar"
|
||||
if [ ! -f "$GIT_CACHE_TAR" ]; then
|
||||
echo "Git cache tar file does not exist, skipping restore"
|
||||
exit 0
|
||||
fi
|
||||
echo "Restoring git cache from $GIT_CACHE_TAR to $GIT_CACHE_PATH"
|
||||
mkdir -p $GIT_CACHE_PATH
|
||||
tar -xf $GIT_CACHE_TAR -C $GIT_CACHE_PATH
|
||||
- name: Gclient Sync
|
||||
shell: bash
|
||||
run: |
|
||||
e d gclient config \
|
||||
--name "src/electron" \
|
||||
--unmanaged \
|
||||
${GCLIENT_EXTRA_ARGS} \
|
||||
"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY"
|
||||
|
||||
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 --nohooks -vv
|
||||
- name: Compress Git Cache Directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Uncompressed gitcache size: $(du -sh $GIT_CACHE_PATH | cut -f1 -d' ')"
|
||||
cd $GIT_CACHE_PATH
|
||||
tar -cf ../gitcache.tar .
|
||||
cd ..
|
||||
echo "Compressed gitcache to $(du -sh gitcache.tar | cut -f1 -d' ')"
|
||||
# remove the old cache file if it exists
|
||||
if [ -f $CACHE_DRIVE/gitcache.tar ]; then
|
||||
echo "Removing old gitcache.tar from $CACHE_DRIVE"
|
||||
rm $CACHE_DRIVE/gitcache.tar
|
||||
fi
|
||||
cp ./gitcache.tar $CACHE_DRIVE/
|
||||
- name: Wait for active SSH sessions
|
||||
shell: bash
|
||||
if: always() && !cancelled()
|
||||
run: |
|
||||
while [ -f /var/.ssh-lock ]
|
||||
do
|
||||
sleep 60
|
||||
done
|
||||
35
.github/actions/checkout/action.yml
vendored
35
.github/actions/checkout/action.yml
vendored
@@ -43,7 +43,7 @@ runs:
|
||||
curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}" > sas-token
|
||||
- name: Save SAS Key
|
||||
if: ${{ inputs.generate-sas-token == 'true' }}
|
||||
uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf
|
||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: sas-token
|
||||
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}
|
||||
@@ -80,6 +80,21 @@ runs:
|
||||
else
|
||||
echo "The cross mount cache has $freespace_human free space - continuing"
|
||||
fi
|
||||
- name: Add patch conflict problem matcher
|
||||
shell: bash
|
||||
run: echo "::add-matcher::src/electron/.github/problem-matchers/patch-conflict.json"
|
||||
- name: Restore gitcache
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
GIT_CACHE_TAR="$CACHE_DRIVE/gitcache.tar"
|
||||
if [ ! -f "$GIT_CACHE_TAR" ]; then
|
||||
echo "Git cache tar file does not exist, skipping restore"
|
||||
exit 0
|
||||
fi
|
||||
echo "Restoring git cache from $GIT_CACHE_TAR to $GIT_CACHE_PATH"
|
||||
mkdir -p $GIT_CACHE_PATH
|
||||
tar -xf $GIT_CACHE_TAR -C $GIT_CACHE_PATH
|
||||
- name: Gclient Sync
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
shell: bash
|
||||
@@ -102,12 +117,7 @@ runs:
|
||||
git update-index --refresh || true
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
# There are changes to the patches. Make a git commit with the updated patches
|
||||
git add patches
|
||||
GIT_COMMITTER_NAME="PatchUp" GIT_COMMITTER_EMAIL="73610968+patchup[bot]@users.noreply.github.com" git commit -m "chore: update patches" --author="PatchUp <73610968+patchup[bot]@users.noreply.github.com>"
|
||||
# Export it
|
||||
mkdir -p ../../patches
|
||||
git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch
|
||||
if node ./script/push-patch.js; then
|
||||
if node ./script/patch-up.js; then
|
||||
echo
|
||||
echo "======================================================================"
|
||||
echo "Changes to the patches when applying, we have auto-pushed the diff to the current branch"
|
||||
@@ -115,6 +125,11 @@ runs:
|
||||
echo "======================================================================"
|
||||
exit 1
|
||||
else
|
||||
git add patches
|
||||
GIT_COMMITTER_NAME="PatchUp" GIT_COMMITTER_EMAIL="73610968+patchup[bot]@users.noreply.github.com" git commit -m "chore: update patches" --author="PatchUp <73610968+patchup[bot]@users.noreply.github.com>"
|
||||
# Export it
|
||||
mkdir -p ../../patches
|
||||
git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch
|
||||
echo
|
||||
echo "======================================================================"
|
||||
echo "There were changes to the patches when applying."
|
||||
@@ -128,7 +143,11 @@ runs:
|
||||
echo "No changes to patches detected"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Remove patch conflict problem matcher
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::remove-matcher owner=merge-conflict::"
|
||||
echo "::remove-matcher owner=patch-conflict::"
|
||||
# delete all .git directories under src/ except for
|
||||
# third_party/angle/ and third_party/dawn/ because of build time generation of files
|
||||
# gen/angle/commit.h depends on third_party/angle/.git/HEAD
|
||||
|
||||
@@ -15,11 +15,16 @@ runs:
|
||||
fi
|
||||
export BUILD_TOOLS_SHA=6e8526315ea3b4828882497e532b8340e64e053c
|
||||
npm i -g @electron/build-tools
|
||||
# Update depot_tools to ensure python
|
||||
e d update_depot_tools
|
||||
e auto-update disable
|
||||
# Disable further updates of depot_tools
|
||||
e d auto-update disable
|
||||
if [ "$(expr substr $(uname -s) 1 10)" == "MSYS_NT-10" ]; then
|
||||
e d cipd.bat --version
|
||||
cp "C:\Python311\python.exe" "C:\Python311\python3.exe"
|
||||
echo "C:\Users\ContainerAdministrator\.electron_build_tools\third_party\depot_tools" >> $GITHUB_PATH
|
||||
else
|
||||
echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH
|
||||
echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH
|
||||
fi
|
||||
echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH
|
||||
|
||||
@@ -7,7 +7,7 @@ runs:
|
||||
shell: bash
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(node src/electron/script/yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
||||
@@ -8,14 +8,14 @@ runs:
|
||||
steps:
|
||||
- name: Obtain SAS Key
|
||||
continue-on-error: true
|
||||
uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: sas-token
|
||||
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-1
|
||||
enableCrossOsArchive: true
|
||||
- name: Obtain SAS Key
|
||||
continue-on-error: true
|
||||
uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: sas-token
|
||||
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}
|
||||
@@ -32,7 +32,7 @@ runs:
|
||||
shell: bash
|
||||
command: |
|
||||
sas_token=$(cat sas-token)
|
||||
if [ -z $sas-token ]; then
|
||||
if [ -z "$sas_token" ]; then
|
||||
echo "SAS Token not found; exiting src cache download early..."
|
||||
exit 1
|
||||
else
|
||||
|
||||
18
.github/problem-matchers/clang.json
vendored
Normal file
18
.github/problem-matchers/clang.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "clang",
|
||||
"fromPath": "src/out/Default/args.gn",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.+)[(:](\\d+)[:,](\\d+)\\)?:\\s+(warning|error):\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
22
.github/problem-matchers/eslint-stylish.json
vendored
Normal file
22
.github/problem-matchers/eslint-stylish.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "eslint-stylish",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^\\s*([^\\s].*)$",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
|
||||
"line": 1,
|
||||
"column": 2,
|
||||
"severity": 3,
|
||||
"message": 4,
|
||||
"code": 5,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
24
.github/problem-matchers/patch-conflict.json
vendored
Normal file
24
.github/problem-matchers/patch-conflict.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "merge-conflict",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^CONFLICT\\s\\(\\S+\\): (Merge conflict in \\S+)$",
|
||||
"message": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "patch-conflict",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^error: (patch failed: (\\S+):(\\d+))$",
|
||||
"message": 1,
|
||||
"file": 2,
|
||||
"line": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
2
.github/workflows/archaeologist-dig.yml
vendored
2
.github/workflows/archaeologist-dig.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Setup Node.js/npm
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||
with:
|
||||
node-version: 20.11.x
|
||||
node-version: 20.19.x
|
||||
- name: Setting Up Dig Site
|
||||
run: |
|
||||
echo "remote: ${{ github.event.pull_request.head.repo.clone_url }}"
|
||||
|
||||
74
.github/workflows/build-git-cache.yml
vendored
Normal file
74
.github/workflows/build-git-cache.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
name: Build Git Cache
|
||||
# This workflow updates git cache on the cross-instance cache volumes
|
||||
# It runs daily at midnight.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
build-git-cache-linux:
|
||||
runs-on: electron-arc-linux-amd64-32core
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
options: --user root
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
env:
|
||||
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
steps:
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
- name: Build Git Cache
|
||||
uses: ./src/electron/.github/actions/build-git-cache
|
||||
with:
|
||||
target-platform: linux
|
||||
|
||||
build-git-cache-windows:
|
||||
runs-on: electron-arc-linux-amd64-32core
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
|
||||
volumes:
|
||||
- /mnt/win-cache:/mnt/win-cache
|
||||
env:
|
||||
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True'
|
||||
TARGET_OS: 'win'
|
||||
steps:
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
- name: Build Git Cache
|
||||
uses: ./src/electron/.github/actions/build-git-cache
|
||||
with:
|
||||
target-platform: win
|
||||
|
||||
build-git-cache-macos:
|
||||
runs-on: electron-arc-linux-amd64-32core
|
||||
# This job updates the same git cache as linux, so it needs to run after the linux one.
|
||||
needs: build-git-cache-linux
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
options: --user root
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
env:
|
||||
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
|
||||
steps:
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
- name: Build Git Cache
|
||||
uses: ./src/electron/.github/actions/build-git-cache
|
||||
with:
|
||||
target-platform: macos
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -128,7 +128,7 @@ jobs:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
- /var/run/sas:/var/run/sas
|
||||
env:
|
||||
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
|
||||
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
PATCH_UP_APP_CREDS: ${{ secrets.PATCH_UP_APP_CREDS }}
|
||||
outputs:
|
||||
|
||||
3
.github/workflows/pipeline-electron-lint.yml
vendored
3
.github/workflows/pipeline-electron-lint.yml
vendored
@@ -61,6 +61,9 @@ jobs:
|
||||
curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > src/buildtools/DEPS
|
||||
|
||||
gclient sync --spec="solutions=[{'name':'src/buildtools','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':True},'managed':False}]"
|
||||
- name: Add ESLint problem matcher
|
||||
shell: bash
|
||||
run: echo "::add-matcher::src/electron/.github/problem-matchers/eslint-stylish.json"
|
||||
- name: Run Lint
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||
with:
|
||||
node-version: 20.11.x
|
||||
node-version: 20.19.x
|
||||
cache: yarn
|
||||
cache-dependency-path: src/electron/yarn.lock
|
||||
- name: Install Dependencies
|
||||
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||
with:
|
||||
node-version: 20.11.x
|
||||
node-version: 20.19.x
|
||||
- name: Add TCC permissions on macOS
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
run: |
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"extends": "@electron/lint-roller/configs/markdownlint.json",
|
||||
"descriptive-link-text": false,
|
||||
"link-image-style": {
|
||||
"autolink": false,
|
||||
"shortcut": false
|
||||
@@ -26,6 +27,6 @@
|
||||
"no-newline-in-links": true
|
||||
},
|
||||
"customRules": [
|
||||
"@electron/lint-roller/markdownlint-rules/"
|
||||
"./node_modules/@electron/lint-roller/markdownlint-rules/index.mjs"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -323,6 +323,10 @@ Set the directory to which all Node.js diagnostic output files are written. Defa
|
||||
|
||||
Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https://nodejs.org/docs/latest/api/v8.html#v8setheapsnapshotnearheaplimitlimit).
|
||||
|
||||
### `--no-experimental-global-navigator`
|
||||
|
||||
Disable exposition of [Navigator API][] on the global scope from Node.js.
|
||||
|
||||
[app]: app.md
|
||||
[append-switch]: command-line.md#commandlineappendswitchswitch-value
|
||||
[debugging-main-process]: ../tutorial/debugging-main-process.md
|
||||
@@ -331,3 +335,4 @@ Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https:
|
||||
[play-silent-audio]: https://github.com/atom/atom/pull/9485/files
|
||||
[ready]: app.md#event-ready
|
||||
[severities]: https://source.chromium.org/chromium/chromium/src/+/main:base/logging.h?q=logging::LogSeverity&ss=chromium
|
||||
[Navigator API]: https://github.com/nodejs/node/blob/main/doc/api/globals.md#navigator
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
* `color` String (optional) _Windows_ _Linux_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color.
|
||||
* `symbolColor` String (optional) _Windows_ _Linux_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color.
|
||||
* `height` Integer (optional) - The height of the title bar and Window Controls Overlay in pixels. Default is system height.
|
||||
* `accentColor` boolean | string (optional) _Windows_ - The accent color for the window. By default, follows user preference in System Settings. Set to `false` to explicitly disable, or set the color in Hex, RGB, RGBA, HSL, HSLA or named CSS color format. Alpha values will be ignored.
|
||||
* `trafficLightPosition` [Point](point.md) (optional) _macOS_ -
|
||||
Set a custom position for the traffic light buttons in frameless windows.
|
||||
* `roundedCorners` boolean (optional) _macOS_ _Windows_ - Whether frameless window
|
||||
|
||||
@@ -838,9 +838,10 @@ Emitted when a bluetooth device needs to be selected when a call to
|
||||
the `deviceId` of the device to be selected. Passing an empty string to
|
||||
`callback` will cancel the request.
|
||||
|
||||
If an event listener is not added for this event, or if `event.preventDefault`
|
||||
is not called when handling this event, the first available device will be
|
||||
automatically selected.
|
||||
If no event listener is added for this event, all bluetooth requests will be cancelled.
|
||||
|
||||
If `event.preventDefault` is not called when handling this event, the first available
|
||||
device will be automatically selected.
|
||||
|
||||
Due to the nature of bluetooth, scanning for devices when
|
||||
`navigator.bluetooth.requestDevice` is called may take time and will cause
|
||||
|
||||
@@ -28,7 +28,7 @@ On Linux, the required portal version for file dialogs has been reverted
|
||||
to 3 from 4. Using the `defaultPath` option of the Dialog API is not
|
||||
supported when using portal file chooser dialogs unless the portal
|
||||
backend is version 4 or higher. The `--xdg-portal-required-version`
|
||||
[command-line switch](/api/command-line-switches.md#--xdg-portal-required-versionversion)
|
||||
[command-line switch](api/command-line-switches.md#--xdg-portal-required-versionversion)
|
||||
can be used to force a required version for your application.
|
||||
See [#44426](https://github.com/electron/electron/pull/44426) for more details.
|
||||
|
||||
|
||||
1638
docs/tutorial/native-code-and-electron-cpp-linux.md
Normal file
1638
docs/tutorial/native-code-and-electron-cpp-linux.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -478,6 +478,7 @@ filenames = {
|
||||
"shell/browser/osr/osr_web_contents_view.h",
|
||||
"shell/browser/plugins/plugin_utils.cc",
|
||||
"shell/browser/plugins/plugin_utils.h",
|
||||
"shell/browser/preload_script.cc",
|
||||
"shell/browser/preload_script.h",
|
||||
"shell/browser/protocol_registry.cc",
|
||||
"shell/browser/protocol_registry.h",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@electron/docs-parser": "^2.0.0",
|
||||
"@electron/fiddle-core": "^1.3.4",
|
||||
"@electron/github-app-auth": "^2.2.1",
|
||||
"@electron/lint-roller": "^2.4.0",
|
||||
"@electron/lint-roller": "^3.1.1",
|
||||
"@electron/typescript-definitions": "^9.0.0",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@primer/octicons": "^10.0.0",
|
||||
@@ -40,7 +40,7 @@
|
||||
"got": "^11.8.5",
|
||||
"husky": "^8.0.1",
|
||||
"lint-staged": "^10.2.11",
|
||||
"markdownlint-cli2": "^0.13.0",
|
||||
"markdownlint-cli2": "^0.18.0",
|
||||
"minimist": "^1.2.8",
|
||||
"null-loader": "^4.0.1",
|
||||
"pre-flight": "^2.0.0",
|
||||
|
||||
@@ -140,7 +140,7 @@ feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch
|
||||
feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch
|
||||
cherry-pick-dd8e2822e507.patch
|
||||
fix_osr_stutter_in_both_cpu_and_gpu_capture_when_page_has_animation.patch
|
||||
ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch
|
||||
ignore_parse_errors_for_resolveshortcutproperties.patch
|
||||
fix_win32_synchronous_spellcheck.patch
|
||||
fix_drag_and_drop_icons_on_windows.patch
|
||||
chore_remove_conflicting_allow_unsafe_libc_calls.patch
|
||||
@@ -153,4 +153,4 @@ add_linux_window_controls_menu.patch
|
||||
make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch
|
||||
cherry-pick-295a4a1b14b8.patch
|
||||
cherry-pick-c3568ceda9d8.patch
|
||||
cherry-pick-8d9662f73d73.patch
|
||||
cherry-pick-f1e6422a355c.patch
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
From 8d9662f73d73c9ef20f99eac066ed0fdd6cf8984 Mon Sep 17 00:00:00 2001
|
||||
From: Jayson Adams <shrike@chromium.org>
|
||||
Date: Wed, 07 May 2025 11:16:18 -0700
|
||||
Subject: [PATCH] Revert "[Mac Text Subs] Ignore out-of-order text substitutions" [M137]
|
||||
|
||||
This reverts commit d5bf4ba9cd66ce8bc28939a6807777403f295259.
|
||||
|
||||
Reason for revert: breaks text substitution for all web text fields
|
||||
|
||||
Bug: 409342979
|
||||
Fixed: 415810816
|
||||
|
||||
Original change's description:
|
||||
> [Mac Text Subs] Ignore out-of-order text substitutions
|
||||
>
|
||||
> This cl forces the web contents to ignore any incoming text
|
||||
> correction that appies to an older version of the web contents's
|
||||
> text, avoiding a crash.
|
||||
>
|
||||
> Bug: 372217922
|
||||
> Change-Id: Iaf502a04af58fba395cadc12652ddd6ccaed520c
|
||||
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6299632
|
||||
> Commit-Queue: Jayson Adams <shrike@chromium.org>
|
||||
> Reviewed-by: Leonard Grey <lgrey@chromium.org>
|
||||
> Cr-Commit-Position: refs/heads/main@{#1427904}
|
||||
|
||||
Bug: 372217922
|
||||
Change-Id: I2d7c5ff8a6c5892ff67ddc90e3c69270e9a5a5a5
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6514150
|
||||
Reviewed-by: Leonard Grey <lgrey@chromium.org>
|
||||
Commit-Queue: Jayson Adams <shrike@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/7151@{#517}
|
||||
Cr-Branched-From: 8e0d32ed6e49a2415b16e5ed402957cac2349ce2-refs/heads/main@{#1453031}
|
||||
---
|
||||
|
||||
diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
|
||||
index a6b6917e..1b458cd 100644
|
||||
--- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
|
||||
+++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
|
||||
@@ -289,7 +289,7 @@
|
||||
// full string in the renderer.
|
||||
std::u16string _availableText;
|
||||
size_t _availableTextOffset;
|
||||
- NSUInteger _availableTextChangeNumber;
|
||||
+ NSUInteger _availableTextChangeCounter;
|
||||
gfx::Range _textSelectionRange;
|
||||
|
||||
// The composition range, cached from the RenderWidgetHostView. This is only
|
||||
@@ -479,7 +479,7 @@
|
||||
NSRect textRectInViewCoordinates =
|
||||
[self convertRect:textRectInWindowCoordinates fromView:nil];
|
||||
|
||||
- NSUInteger capturedChangeNumber = _availableTextChangeNumber;
|
||||
+ NSUInteger capturedChangeCounter = _availableTextChangeCounter;
|
||||
|
||||
[self.spellChecker
|
||||
showCorrectionIndicatorOfType:NSCorrectionIndicatorTypeDefault
|
||||
@@ -490,7 +490,7 @@
|
||||
completionHandler:^(NSString* acceptedString) {
|
||||
[self didAcceptReplacementString:acceptedString
|
||||
forTextCheckingResult:candidateResult
|
||||
- withChangeNumber:capturedChangeNumber];
|
||||
+ withChangeNumber:capturedChangeCounter];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -502,11 +502,8 @@
|
||||
// Call it to report whether they initially accepted or rejected the
|
||||
// suggestion, but also if they edit, revert, etc. later.
|
||||
|
||||
- // Exit if there's no replacement string, or if the web contents changed
|
||||
- // in between our spell checker request and this response.
|
||||
- if (acceptedString == nil || _availableTextChangeNumber != changeNumber) {
|
||||
+ if (acceptedString == nil)
|
||||
return;
|
||||
- }
|
||||
|
||||
NSRange availableTextRange =
|
||||
NSMakeRange(_availableTextOffset, _availableText.length());
|
||||
@@ -530,6 +527,18 @@
|
||||
language:nil])
|
||||
return;
|
||||
|
||||
+ // Gather some info in case -doubleClickAtIndex: throws an exception.
|
||||
+ // This change will eventually be reverted.
|
||||
+ NSString* info = [NSString
|
||||
+ stringWithFormat:@"%lu == %lu %lu %@ %@ %@ %@", changeNumber,
|
||||
+ _availableTextChangeCounter, attString.string.length,
|
||||
+ NSStringFromRange(availableTextRange),
|
||||
+ NSStringFromRange(correction.range),
|
||||
+ NSStringFromRange(trailingRange),
|
||||
+ NSStringFromRange(trailingRangeInAvailableText)];
|
||||
+ SCOPED_CRASH_KEY_STRING256("RenderWidgetHostViewCocoa", "didAcceptTR",
|
||||
+ base::SysNSStringToUTF8(info));
|
||||
+
|
||||
if ([attString doubleClickAtIndex:trailingRangeInAvailableText.location]
|
||||
.location < trailingRangeInAvailableText.location)
|
||||
return;
|
||||
@@ -650,7 +659,7 @@
|
||||
range:(gfx::Range)range {
|
||||
_availableText = text;
|
||||
_availableTextOffset = offset;
|
||||
- _availableTextChangeNumber++;
|
||||
+ _availableTextChangeCounter++;
|
||||
_textSelectionRange = range;
|
||||
_substitutionWasApplied = NO;
|
||||
[NSSpellChecker.sharedSpellChecker dismissCorrectionIndicatorForView:self];
|
||||
273
patches/chromium/cherry-pick-f1e6422a355c.patch
Normal file
273
patches/chromium/cherry-pick-f1e6422a355c.patch
Normal file
@@ -0,0 +1,273 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
|
||||
Date: Wed, 21 May 2025 23:25:12 -0700
|
||||
Subject: Enforce SharedWorker::Terminate() procedure order
|
||||
|
||||
During the investigation of crbug.com/409059706, we observed that
|
||||
PerformShutdownOnWorkerThread() is called during the status is
|
||||
running.
|
||||
|
||||
I suppose the root cause is race condition between `Terminate()`
|
||||
procedure and a child process termination procedure in different
|
||||
thread. WorkerThread can be terminated if two conditions are met;
|
||||
`Terminate()` is called and all child worker threads have been
|
||||
terminated. Both `Terminate()` and the child process termination
|
||||
procedure may call `PerformShutdownOnWorkerThread()`, and former
|
||||
is executed regardless of two conditions are met. The latter
|
||||
is called if `Terminate()` is called and no child processes.
|
||||
To be clear, "`Terminate()` is called" does not mean
|
||||
`PrepareForShutdownOnWorkerThread()` is executed. `Terminate()`
|
||||
queues it after the flag to tell `Terminate()` call. And, when
|
||||
the issue happen, I am quite sure the flag is set but,
|
||||
`PrepareForShutdownOnWorkerThread()` won't be executed yet.
|
||||
|
||||
The fix is that:
|
||||
1. The "Terminate() is called" flag to be multi staged.
|
||||
The flag is used for two purpose; a. avoid re-enter of
|
||||
`Terminate()`, and b. `PrepareForShutdownOnWorkerThread()` is
|
||||
in flight. The CL changed the flag to enum to represent
|
||||
the stage properly.
|
||||
2. `PerformShutdownOnWorkerThread()` is queued even if it is
|
||||
called within the child process termination procedure.
|
||||
It avoid the execution order flip between
|
||||
`PrepareForShutdownOnWorkerThread()` and
|
||||
`PerformShutdownOnWorkerThread()`.
|
||||
|
||||
In addition, this change ensures `PerformShutdownOnWorkerThread()`
|
||||
is called once. While `PerformShutdownOnWorkerThread()` touches
|
||||
fields inside, the fields must not be touched at some point within
|
||||
the function, the function is actually not re-entrant when it reaches
|
||||
to the end. Upon mikt@ suggestion, I made
|
||||
`PerformShutdownOnWorkerThread()` is called only when two conditions
|
||||
are fulfilled. i.e. `Terminate()` is called and the number of child
|
||||
threads is 0. Also, the CL uses the enum to show
|
||||
`PerformShutdownOnWorkerThread()` is in-flight to avoid re-entrance
|
||||
in this level.
|
||||
|
||||
Bug: 409059706
|
||||
Change-Id: I81a1c3b1a34e827fa75ec2d1a9b37023965dbe27
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6543412
|
||||
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
|
||||
Commit-Queue: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1463892}
|
||||
|
||||
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
|
||||
index 30b8bf535393a015e670acb64816c0e784dd92b0..1f53af0024ccf442053ed0605c684441ab240244 100644
|
||||
--- a/third_party/blink/common/features.cc
|
||||
+++ b/third_party/blink/common/features.cc
|
||||
@@ -2830,6 +2830,13 @@ BASE_FEATURE(kWebviewAccelerateSmallCanvases,
|
||||
"WebviewAccelerateSmallCanvases",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
+// WorkerThread termination procedure (prepare and shutdown) runs sequentially
|
||||
+// in the same task without calling another cross thread post task.
|
||||
+// Kill switch for crbug.com/409059706.
|
||||
+BASE_FEATURE(kWorkerThreadSequentialShutdown,
|
||||
+ "WorkerThreadSequentialShutdown",
|
||||
+ base::FEATURE_ENABLED_BY_DEFAULT);
|
||||
+
|
||||
BASE_FEATURE(kNoReferrerForPreloadFromSubresource,
|
||||
"NoReferrerForPreloadFromSubresource",
|
||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
|
||||
index 360512748cbcc0321cb1c54d4c7f87753e182bd4..f9dee34cb9f7b9f4c43f8cad9548ca3a8fe39a4f 100644
|
||||
--- a/third_party/blink/public/common/features.h
|
||||
+++ b/third_party/blink/public/common/features.h
|
||||
@@ -1834,6 +1834,8 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebUSBTransferSizeLimit);
|
||||
|
||||
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebviewAccelerateSmallCanvases);
|
||||
|
||||
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWorkerThreadSequentialShutdown);
|
||||
+
|
||||
// Kill switch for https://crbug.com/415810136.
|
||||
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kNoReferrerForPreloadFromSubresource);
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
|
||||
index 2449348f956f81845bf314558fa5b7268500adeb..618e21302aa1ab61247a5803c830c117e81fc897 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
|
||||
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
|
||||
@@ -279,9 +279,10 @@ void WorkerThread::Terminate() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(parent_thread_checker_);
|
||||
{
|
||||
base::AutoLock locker(lock_);
|
||||
- if (requested_to_terminate_)
|
||||
+ if (termination_progress_ != TerminationProgress::kNotRequested) {
|
||||
return;
|
||||
- requested_to_terminate_ = true;
|
||||
+ }
|
||||
+ termination_progress_ = TerminationProgress::kRequested;
|
||||
}
|
||||
|
||||
// Schedule a task to forcibly terminate the script execution in case that the
|
||||
@@ -297,10 +298,33 @@ void WorkerThread::Terminate() {
|
||||
*task_runner, FROM_HERE,
|
||||
CrossThreadBindOnce(&WorkerThread::PrepareForShutdownOnWorkerThread,
|
||||
CrossThreadUnretained(this)));
|
||||
- PostCrossThreadTask(
|
||||
- *task_runner, FROM_HERE,
|
||||
- CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread,
|
||||
- CrossThreadUnretained(this)));
|
||||
+
|
||||
+ if (!base::FeatureList::IsEnabled(
|
||||
+ blink::features::kWorkerThreadSequentialShutdown)) {
|
||||
+ PostCrossThreadTask(
|
||||
+ *task_runner, FROM_HERE,
|
||||
+ CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread,
|
||||
+ CrossThreadUnretained(this)));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ bool perform_shutdown = false;
|
||||
+ {
|
||||
+ base::AutoLock locker(lock_);
|
||||
+ CHECK_EQ(TerminationProgress::kRequested, termination_progress_);
|
||||
+ termination_progress_ = TerminationProgress::kPrepared;
|
||||
+ if (num_child_threads_ == 0) {
|
||||
+ termination_progress_ = TerminationProgress::kPerforming;
|
||||
+ perform_shutdown = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (perform_shutdown) {
|
||||
+ PostCrossThreadTask(
|
||||
+ *task_runner, FROM_HERE,
|
||||
+ CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread,
|
||||
+ CrossThreadUnretained(this)));
|
||||
+ }
|
||||
}
|
||||
|
||||
void WorkerThread::TerminateForTesting() {
|
||||
@@ -438,20 +462,48 @@ scoped_refptr<base::SingleThreadTaskRunner> WorkerThread::GetTaskRunner(
|
||||
|
||||
void WorkerThread::ChildThreadStartedOnWorkerThread(WorkerThread* child) {
|
||||
DCHECK(IsCurrentThread());
|
||||
-#if DCHECK_IS_ON()
|
||||
+ child_threads_.insert(child);
|
||||
{
|
||||
base::AutoLock locker(lock_);
|
||||
DCHECK_EQ(ThreadState::kRunning, thread_state_);
|
||||
+ CHECK_EQ(TerminationProgress::kNotRequested, termination_progress_);
|
||||
+ if (base::FeatureList::IsEnabled(
|
||||
+ blink::features::kWorkerThreadSequentialShutdown)) {
|
||||
+ ++num_child_threads_;
|
||||
+ CHECK_EQ(child_threads_.size(), num_child_threads_);
|
||||
+ }
|
||||
}
|
||||
-#endif
|
||||
- child_threads_.insert(child);
|
||||
}
|
||||
|
||||
void WorkerThread::ChildThreadTerminatedOnWorkerThread(WorkerThread* child) {
|
||||
DCHECK(IsCurrentThread());
|
||||
child_threads_.erase(child);
|
||||
- if (child_threads_.empty() && CheckRequestedToTerminate())
|
||||
- PerformShutdownOnWorkerThread();
|
||||
+ if (!base::FeatureList::IsEnabled(
|
||||
+ blink::features::kWorkerThreadSequentialShutdown)) {
|
||||
+ if (child_threads_.empty() && CheckRequestedToTerminate()) {
|
||||
+ PerformShutdownOnWorkerThread();
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ bool perform_shutdown = false;
|
||||
+ {
|
||||
+ base::AutoLock locker(lock_);
|
||||
+ --num_child_threads_;
|
||||
+ CHECK_EQ(child_threads_.size(), num_child_threads_);
|
||||
+ if (num_child_threads_ == 0 &&
|
||||
+ termination_progress_ == TerminationProgress::kPrepared) {
|
||||
+ termination_progress_ = TerminationProgress::kPerforming;
|
||||
+ perform_shutdown = true;
|
||||
+ }
|
||||
+ }
|
||||
+ if (perform_shutdown) {
|
||||
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner =
|
||||
+ GetWorkerBackingThread().BackingThread().GetTaskRunner();
|
||||
+ GetWorkerBackingThread().BackingThread().GetTaskRunner()->PostTask(
|
||||
+ FROM_HERE, WTF::BindOnce(&WorkerThread::PerformShutdownOnWorkerThread,
|
||||
+ WTF::Unretained(this)));
|
||||
+ }
|
||||
}
|
||||
|
||||
WorkerThread::WorkerThread(WorkerReportingProxy& worker_reporting_proxy)
|
||||
@@ -792,18 +844,32 @@ void WorkerThread::PerformShutdownOnWorkerThread() {
|
||||
DCHECK(IsCurrentThread());
|
||||
{
|
||||
base::AutoLock locker(lock_);
|
||||
- DCHECK(requested_to_terminate_);
|
||||
+ if (!base::FeatureList::IsEnabled(
|
||||
+ blink::features::kWorkerThreadSequentialShutdown)) {
|
||||
+ DCHECK_NE(TerminationProgress::kNotRequested, termination_progress_);
|
||||
+ } else {
|
||||
+ DCHECK_EQ(TerminationProgress::kPerforming, termination_progress_);
|
||||
+ }
|
||||
DCHECK_EQ(ThreadState::kReadyToShutdown, thread_state_);
|
||||
if (exit_code_ == ExitCode::kNotTerminated)
|
||||
SetExitCode(ExitCode::kGracefullyTerminated);
|
||||
}
|
||||
|
||||
- // When child workers are present, wait for them to shutdown before shutting
|
||||
- // down this thread. ChildThreadTerminatedOnWorkerThread() is responsible
|
||||
- // for completing shutdown on the worker thread after the last child shuts
|
||||
- // down.
|
||||
- if (!child_threads_.empty())
|
||||
- return;
|
||||
+ if (!base::FeatureList::IsEnabled(
|
||||
+ blink::features::kWorkerThreadSequentialShutdown)) {
|
||||
+ // When child workers are present, wait for them to shutdown before shutting
|
||||
+ // down this thread. ChildThreadTerminatedOnWorkerThread() is responsible
|
||||
+ // for completing shutdown on the worker thread after the last child shuts
|
||||
+ // down.
|
||||
+ if (!child_threads_.empty()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ } else {
|
||||
+ // Child workers must not exist when `PerformShutdownOnWorkerThread()`
|
||||
+ // is called because it has already been checked before calling the
|
||||
+ // function.
|
||||
+ CHECK(child_threads_.empty());
|
||||
+ }
|
||||
|
||||
inspector_task_runner_->Dispose();
|
||||
if (worker_inspector_controller_) {
|
||||
@@ -859,7 +925,7 @@ void WorkerThread::SetExitCode(ExitCode exit_code) {
|
||||
|
||||
bool WorkerThread::CheckRequestedToTerminate() {
|
||||
base::AutoLock locker(lock_);
|
||||
- return requested_to_terminate_;
|
||||
+ return termination_progress_ != TerminationProgress::kNotRequested;
|
||||
}
|
||||
|
||||
void WorkerThread::PauseOrFreeze(mojom::blink::FrameLifecycleState state,
|
||||
diff --git a/third_party/blink/renderer/core/workers/worker_thread.h b/third_party/blink/renderer/core/workers/worker_thread.h
|
||||
index 23895d9ab78b4abfcf0cd70af4bfc0096975da36..cdf6796325cf2b8150c8d5cc875e71c3c7ef1f36 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worker_thread.h
|
||||
+++ b/third_party/blink/renderer/core/workers/worker_thread.h
|
||||
@@ -321,6 +321,13 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver {
|
||||
kTerminationUnnecessary,
|
||||
};
|
||||
|
||||
+ enum class TerminationProgress {
|
||||
+ kNotRequested,
|
||||
+ kRequested,
|
||||
+ kPrepared,
|
||||
+ kPerforming,
|
||||
+ };
|
||||
+
|
||||
// Returns true if we should synchronously terminate the script execution so
|
||||
// that a shutdown task can be handled by the thread event loop.
|
||||
TerminationState ShouldTerminateScriptExecution()
|
||||
@@ -417,8 +424,10 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver {
|
||||
// A unique identifier among all WorkerThreads.
|
||||
const int worker_thread_id_;
|
||||
|
||||
- // Set on the parent thread.
|
||||
- bool requested_to_terminate_ GUARDED_BY(lock_) = false;
|
||||
+ // Represents progress after the Terminate() call.
|
||||
+ TerminationProgress termination_progress_ GUARDED_BY(lock_) =
|
||||
+ TerminationProgress::kNotRequested;
|
||||
+ size_t num_child_threads_ GUARDED_BY(lock_) = 0;
|
||||
|
||||
ThreadState thread_state_ GUARDED_BY(lock_) = ThreadState::kNotStarted;
|
||||
ExitCode exit_code_ GUARDED_BY(lock_) = ExitCode::kNotTerminated;
|
||||
@@ -61,7 +61,7 @@ index 932351e288f37fd09ae1a43f44e8b51fb0caa4b8..4a0616bc210d234e51e564daabdd2ebd
|
||||
// Overridden from WidgetObserver.
|
||||
void OnWidgetThemeChanged(Widget* widget) override;
|
||||
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
|
||||
index c4d1d483454591031bd76dffced0c1d821800e13..e3bb491f218b2e5d96c424e40a071a618b345039 100644
|
||||
index 193e7c1bd76ce18abe6ac47848fc0fb02f2151dd..e985cd1017a54cf8d93875d642d846efb76b60e0 100644
|
||||
--- a/ui/views/win/hwnd_message_handler.cc
|
||||
+++ b/ui/views/win/hwnd_message_handler.cc
|
||||
@@ -3165,15 +3165,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message,
|
||||
|
||||
@@ -14,7 +14,7 @@ This patch likely can't be upstreamed as-is, as Chromium doesn't have
|
||||
this use case in mind currently.
|
||||
|
||||
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
|
||||
index 1bbc783c8606f28c93b6e3bf2c60fe8ea4e1fc01..1ed6a143f76c21e0cfd5c7b319171c6f38a5df19 100644
|
||||
index 1bbc783c8606f28c93b6e3bf2c60fe8ea4e1fc01..79807442326d2e57b62cf889267507ebd4c484a9 100644
|
||||
--- a/ui/views/win/hwnd_message_handler.cc
|
||||
+++ b/ui/views/win/hwnd_message_handler.cc
|
||||
@@ -932,13 +932,13 @@ void HWNDMessageHandler::FrameTypeChanged() {
|
||||
@@ -33,6 +33,15 @@ index 1bbc783c8606f28c93b6e3bf2c60fe8ea4e1fc01..1ed6a143f76c21e0cfd5c7b319171c6f
|
||||
}
|
||||
|
||||
void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon,
|
||||
@@ -1725,7 +1725,7 @@ void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) {
|
||||
if (delegate_->HasNonClientView() && !active &&
|
||||
thread_id != GetCurrentThreadId()) {
|
||||
// Update the native frame if it is rendering the non-client area.
|
||||
- if (HasSystemFrame()) {
|
||||
+ if (is_translucent_ || HasSystemFrame()) {
|
||||
DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0);
|
||||
}
|
||||
}
|
||||
@@ -2327,17 +2327,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message,
|
||||
delegate_->SchedulePaint();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ would be removed from its snapped state when re-shown. This fixes that.
|
||||
Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/6330848.
|
||||
|
||||
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
|
||||
index 1ed6a143f76c21e0cfd5c7b319171c6f38a5df19..c4d1d483454591031bd76dffced0c1d821800e13 100644
|
||||
index 79807442326d2e57b62cf889267507ebd4c484a9..193e7c1bd76ce18abe6ac47848fc0fb02f2151dd 100644
|
||||
--- a/ui/views/win/hwnd_message_handler.cc
|
||||
+++ b/ui/views/win/hwnd_message_handler.cc
|
||||
@@ -674,7 +674,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state,
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20L=C3=B6nnhager?= <dv.lnh.d@gmail.com>
|
||||
Date: Fri, 17 Jan 2025 14:30:48 +0100
|
||||
Subject: Ignore parse errors for PKEY_AppUserModel_ToastActivatorCLSID
|
||||
|
||||
Some shortcuts store this as a string UUID as opposed to VT_CLSID,
|
||||
hitting NOTREACHED() and sometimes breaking parsing in Electron.
|
||||
Ignore this error instead.
|
||||
|
||||
Bug: N/A
|
||||
Change-Id: I9fc472212b2d3afac2c8e18a2159bc2d50bbdf98
|
||||
|
||||
diff --git a/AUTHORS b/AUTHORS
|
||||
index e96a3afdabc731afe355cda83eec4923ea780fec..0b1dc07aad197eab7b79344bc5aee702a2d580ab 100644
|
||||
--- a/AUTHORS
|
||||
+++ b/AUTHORS
|
||||
@@ -340,6 +340,7 @@ David Futcher <david.mike.futcher@gmail.com>
|
||||
David Jin <davidjin@amazon.com>
|
||||
David Lechner <david@pybricks.com>
|
||||
David Leen <davileen@amazon.com>
|
||||
+David Lönnhager <dv.lnh.d@gmail.com>
|
||||
David Manouchehri <david@davidmanouchehri.com>
|
||||
David McAllister <mcdavid@amazon.com>
|
||||
David Michael Barr <david.barr@samsung.com>
|
||||
diff --git a/base/win/shortcut.cc b/base/win/shortcut.cc
|
||||
index 967e130e823f41c402411dfadb53b805e8a8c92b..3a9df7f31861ca69168fd24513ee554d0984798d 100644
|
||||
--- a/base/win/shortcut.cc
|
||||
+++ b/base/win/shortcut.cc
|
||||
@@ -356,8 +356,9 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path,
|
||||
*(pv_toast_activator_clsid.get().puuid));
|
||||
break;
|
||||
default:
|
||||
- NOTREACHED() << "Unexpected variant type: "
|
||||
- << pv_toast_activator_clsid.get().vt;
|
||||
+ // Shortcuts may use strings to represent the CLSID. This case is
|
||||
+ // ignored.
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?David=20L=C3=B6nnhager?= <dv.lnh.d@gmail.com>
|
||||
Date: Fri, 17 Jan 2025 14:30:48 +0100
|
||||
Subject: Ignore parse errors in ResolveShortcutProperties
|
||||
|
||||
Some shortcuts store this as a string UUID as opposed to VT_CLSID,
|
||||
hitting NOTREACHED() and sometimes breaking parsing in Electron.
|
||||
Ignore this error instead.
|
||||
|
||||
diff --git a/base/win/shortcut.cc b/base/win/shortcut.cc
|
||||
index 967e130e823f41c402411dfadb53b805e8a8c92b..c1cc95fa8993cc5bdab422710934fb6217272b96 100644
|
||||
--- a/base/win/shortcut.cc
|
||||
+++ b/base/win/shortcut.cc
|
||||
@@ -317,7 +317,8 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path,
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
- NOTREACHED() << "Unexpected variant type: " << pv_app_id.get().vt;
|
||||
+ LOG(WARNING) << "Unexpected variant type: " << pv_app_id.get().vt;
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,7 +337,8 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path,
|
||||
properties->set_dual_mode(pv_dual_mode.get().boolVal == VARIANT_TRUE);
|
||||
break;
|
||||
default:
|
||||
- NOTREACHED() << "Unexpected variant type: " << pv_dual_mode.get().vt;
|
||||
+ LOG(WARNING) << "Unexpected variant type: " << pv_dual_mode.get().vt;
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,8 +358,9 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path,
|
||||
*(pv_toast_activator_clsid.get().puuid));
|
||||
break;
|
||||
default:
|
||||
- NOTREACHED() << "Unexpected variant type: "
|
||||
- << pv_toast_activator_clsid.get().vt;
|
||||
+ LOG(INFO) << "Unexpected variant type: "
|
||||
+ << pv_toast_activator_clsid.get().vt;
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,19 +5,6 @@ Subject: fix: do not resolve electron entrypoints
|
||||
|
||||
This wastes fs cycles and can result in strange behavior if this path actually exists on disk
|
||||
|
||||
diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js
|
||||
index c9d4a3536d0f60375ae623b48ca2fa7095c88d42..d818320fbbc430d06a0c2852e4723981d6e1a844 100644
|
||||
--- a/lib/internal/modules/esm/load.js
|
||||
+++ b/lib/internal/modules/esm/load.js
|
||||
@@ -109,7 +109,7 @@ async function defaultLoad(url, context = kEmptyObject) {
|
||||
source = null;
|
||||
format ??= 'builtin';
|
||||
} else if (format !== 'commonjs' || defaultType === 'module') {
|
||||
- if (source == null) {
|
||||
+ if (format !== 'electron' && source == null) {
|
||||
({ responseURL, source } = await getSource(urlInstance, context));
|
||||
context = { __proto__: context, source };
|
||||
}
|
||||
diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js
|
||||
index 49aacb6262502ced54e817f99dd596db85b9659c..f9f065bb743275e9b2ce71375e6a9f06e00c0f36 100644
|
||||
--- a/lib/internal/modules/esm/translators.js
|
||||
|
||||
@@ -18,9 +18,18 @@ index 9519f947b8dfdc69808839948c9cb8434a0acf0e..23ce72d479f638c33edffcea7c35f5da
|
||||
|
||||
/**
|
||||
diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js
|
||||
index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..a00b5979e3b5deb4ba315b4635c7e5d2801c376e 100644
|
||||
index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..09a332c0999086b30fd952d9456f788925240e27 100644
|
||||
--- a/lib/internal/modules/esm/load.js
|
||||
+++ b/lib/internal/modules/esm/load.js
|
||||
@@ -106,7 +106,7 @@ async function defaultLoad(url, context = kEmptyObject) {
|
||||
|
||||
throwIfUnsupportedURLScheme(urlInstance);
|
||||
|
||||
- if (urlInstance.protocol === 'node:') {
|
||||
+ if (urlInstance.protocol === 'node:' || format === 'electron') {
|
||||
source = null;
|
||||
format ??= 'builtin';
|
||||
} else if (format !== 'commonjs' || defaultType === 'module') {
|
||||
@@ -119,7 +119,7 @@ async function defaultLoad(url, context = kEmptyObject) {
|
||||
// Now that we have the source for the module, run `defaultGetFormat` to detect its format.
|
||||
format = await defaultGetFormat(urlInstance, context);
|
||||
@@ -30,6 +39,15 @@ index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..a00b5979e3b5deb4ba315b4635c7e5d2
|
||||
// For backward compatibility reasons, we need to discard the source in
|
||||
// order for the CJS loader to re-fetch it.
|
||||
source = null;
|
||||
@@ -167,7 +167,7 @@ function defaultLoadSync(url, context = kEmptyObject) {
|
||||
|
||||
throwIfUnsupportedURLScheme(urlInstance, false);
|
||||
|
||||
- if (urlInstance.protocol === 'node:') {
|
||||
+ if (urlInstance.protocol === 'node:' || format === 'electron') {
|
||||
source = null;
|
||||
} else if (source == null) {
|
||||
({ responseURL, source } = getSourceSync(urlInstance, context));
|
||||
@@ -200,12 +200,13 @@ function throwIfUnsupportedURLScheme(parsed) {
|
||||
protocol !== 'file:' &&
|
||||
protocol !== 'data:' &&
|
||||
@@ -45,6 +63,19 @@ index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..a00b5979e3b5deb4ba315b4635c7e5d2
|
||||
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed, schemes);
|
||||
}
|
||||
}
|
||||
diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js
|
||||
index ae03073aff8140b11c63b6c05d831ba573568dba..b70c7cfe40e2eaaeea7b5ad6fcf0aaee87276aa1 100644
|
||||
--- a/lib/internal/modules/esm/loader.js
|
||||
+++ b/lib/internal/modules/esm/loader.js
|
||||
@@ -492,7 +492,7 @@ class ModuleLoader {
|
||||
}
|
||||
|
||||
const cjsModule = wrap[imported_cjs_symbol];
|
||||
- if (cjsModule) {
|
||||
+ if (cjsModule && finalFormat !== 'electron') {
|
||||
assert(finalFormat === 'commonjs-sync');
|
||||
// Check if the ESM initiating import CJS is being required by the same CJS module.
|
||||
if (cjsModule?.[kIsExecuting]) {
|
||||
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
|
||||
index bfd9bd3d127404de1cbb6f30c43ab0342590759d..9e7d8ef0adef3b68a3ec186e4b218f591aa69266 100644
|
||||
--- a/lib/internal/modules/esm/resolve.js
|
||||
|
||||
@@ -6,7 +6,7 @@ Subject: fix: lazyload fs in esm loaders to apply asar patches
|
||||
Changes { foo } from fs to just "fs.foo" so that our patching of fs is applied to esm loaders
|
||||
|
||||
diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js
|
||||
index a00b5979e3b5deb4ba315b4635c7e5d2801c376e..c9d4a3536d0f60375ae623b48ca2fa7095c88d42 100644
|
||||
index 09a332c0999086b30fd952d9456f788925240e27..910ac85cdc86e7fb3f850f7edf0b95ea09d464dc 100644
|
||||
--- a/lib/internal/modules/esm/load.js
|
||||
+++ b/lib/internal/modules/esm/load.js
|
||||
@@ -10,7 +10,7 @@ const {
|
||||
|
||||
@@ -7,3 +7,4 @@ fix_abort_installation_attempt_at_the_final_mile_if_the_app_is.patch
|
||||
feat_add_ability_to_prevent_version_downgrades.patch
|
||||
refactor_use_non-deprecated_nskeyedarchiver_apis.patch
|
||||
chore_turn_off_launchapplicationaturl_deprecation_errors_in_squirrel.patch
|
||||
fix_crash_when_process_to_extract_zip_cannot_be_launched.patch
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Niklas Wenzel <dev@nikwen.de>
|
||||
Date: Tue, 27 May 2025 02:03:54 +0200
|
||||
Subject: fix: crash when process to extract zip cannot be launched
|
||||
|
||||
Fixes https://github.com/electron/electron/issues/47270
|
||||
|
||||
diff --git a/Squirrel/SQRLZipArchiver.m b/Squirrel/SQRLZipArchiver.m
|
||||
index 68f5dac8e553638f41306956df9d38eeda18f8f2..a9cd676df63e19edf9e20473d27b85591c7cb49e 100644
|
||||
--- a/Squirrel/SQRLZipArchiver.m
|
||||
+++ b/Squirrel/SQRLZipArchiver.m
|
||||
@@ -153,7 +153,17 @@ - (RACSignal *)launchWithArguments:(NSArray *)arguments {
|
||||
setNameWithFormat:@"-launchWithArguments: %@", arguments];
|
||||
|
||||
self.dittoTask.arguments = arguments;
|
||||
- [self.dittoTask launch];
|
||||
+
|
||||
+ NSError *launchError = nil;
|
||||
+
|
||||
+ if (![self.dittoTask launchAndReturnError:&launchError]) {
|
||||
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
+ userInfo[NSLocalizedDescriptionKey] = launchError.localizedDescription;
|
||||
+
|
||||
+ NSLog(@"Starting ditto task failed with error: %@", launchError.localizedDescription);
|
||||
+
|
||||
+ return [RACSignal error:[NSError errorWithDomain:SQRLZipArchiverErrorDomain code:SQRLZipArchiverShellTaskFailed userInfo:userInfo]];
|
||||
+ }
|
||||
|
||||
return signal;
|
||||
}
|
||||
@@ -2,3 +2,7 @@ chore_allow_customizing_microtask_policy_per_context.patch
|
||||
deps_add_v8_object_setinternalfieldfornodecore.patch
|
||||
enable_--perf-prof_flag_on_macos.patch
|
||||
cherry-pick-22ac8acf3508.patch
|
||||
debug_fix_crash_when_pausing_in_for-of_loop_header.patch
|
||||
parser_set_start_end_on_materialized_scopes.patch
|
||||
cherry-pick-7bc0a67ebfbf.patch
|
||||
cherry-pick-45eb42cd398e.patch
|
||||
|
||||
32
patches/v8/cherry-pick-45eb42cd398e.patch
Normal file
32
patches/v8/cherry-pick-45eb42cd398e.patch
Normal file
@@ -0,0 +1,32 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Igor Sheludko <ishell@chromium.org>
|
||||
Date: Tue, 27 May 2025 21:34:45 +0200
|
||||
Subject: Convert Smi to Word64 using zero extension
|
||||
|
||||
... when a known type range contains only positive values.
|
||||
|
||||
Bug: 420637585
|
||||
Change-Id: I8d9bb3f2fe2e5268e1659bb4ea7bbf97bfb52288
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6594731
|
||||
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
|
||||
Commit-Queue: Igor Sheludko <ishell@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#100538}
|
||||
|
||||
diff --git a/src/compiler/representation-change.cc b/src/compiler/representation-change.cc
|
||||
index 5b760963501b96aec21e4d971076b2026be145eb..48c670464aecbd69bf993f3dc6a0467613333259 100644
|
||||
--- a/src/compiler/representation-change.cc
|
||||
+++ b/src/compiler/representation-change.cc
|
||||
@@ -1255,7 +1255,12 @@ Node* RepresentationChanger::GetWord64RepresentationFor(
|
||||
}
|
||||
} else if (output_rep == MachineRepresentation::kTaggedSigned) {
|
||||
if (output_type.Is(Type::SignedSmall())) {
|
||||
- op = simplified()->ChangeTaggedSignedToInt64();
|
||||
+ if (output_type.IsRange() && output_type.AsRange()->Min() >= 0) {
|
||||
+ node = InsertChangeTaggedSignedToInt32(node);
|
||||
+ op = machine()->ChangeUint32ToUint64();
|
||||
+ } else {
|
||||
+ op = simplified()->ChangeTaggedSignedToInt64();
|
||||
+ }
|
||||
} else {
|
||||
return TypeError(node, output_rep, output_type,
|
||||
MachineRepresentation::kWord64);
|
||||
53
patches/v8/cherry-pick-7bc0a67ebfbf.patch
Normal file
53
patches/v8/cherry-pick-7bc0a67ebfbf.patch
Normal file
@@ -0,0 +1,53 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Leszek Swirski <leszeks@chromium.org>
|
||||
Date: Tue, 27 May 2025 20:33:19 +0200
|
||||
Subject: Weaken alias analysis in store-store elimination
|
||||
|
||||
Bug: 420636529
|
||||
Change-Id: I7c5a8f47960708cecbb27d811eedc7f754933deb
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6594051
|
||||
Reviewed-by: Shu-yu Guo <syg@chromium.org>
|
||||
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
|
||||
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#100530}
|
||||
|
||||
diff --git a/src/compiler/turboshaft/store-store-elimination-reducer-inl.h b/src/compiler/turboshaft/store-store-elimination-reducer-inl.h
|
||||
index 45654a022fbaa67634d68d7d6e9dab7a5a50cb28..e058a41f23e29bbe4f5098608b340ccfef50bf98 100644
|
||||
--- a/src/compiler/turboshaft/store-store-elimination-reducer-inl.h
|
||||
+++ b/src/compiler/turboshaft/store-store-elimination-reducer-inl.h
|
||||
@@ -325,10 +325,11 @@ class RedundantStoreAnalysis {
|
||||
// TODO(nicohartmann@): Use the new effect flags to distinguish heap
|
||||
// access once available.
|
||||
const bool is_on_heap_store = store.kind.tagged_base;
|
||||
- const bool is_field_store = !store.index().valid();
|
||||
+ const bool is_fixed_offset_store = !store.index().valid();
|
||||
const uint8_t size = store.stored_rep.SizeInBytes();
|
||||
- // For now we consider only stores of fields of objects on the heap.
|
||||
- if (is_on_heap_store && is_field_store) {
|
||||
+ // For now we consider only stores of fixed offsets of objects on the
|
||||
+ // heap.
|
||||
+ if (is_on_heap_store && is_fixed_offset_store) {
|
||||
bool is_eliminable_store = false;
|
||||
switch (table_.GetObservability(store.base(), store.offset, size)) {
|
||||
case StoreObservability::kUnobservable:
|
||||
@@ -415,11 +416,16 @@ class RedundantStoreAnalysis {
|
||||
// TODO(nicohartmann@): Use the new effect flags to distinguish heap
|
||||
// access once available.
|
||||
const bool is_on_heap_load = load.kind.tagged_base;
|
||||
- const bool is_field_load = !load.index().valid();
|
||||
+ const bool is_fixed_offset_load = !load.index().valid();
|
||||
// For now we consider only loads of fields of objects on the heap.
|
||||
- if (is_on_heap_load && is_field_load) {
|
||||
- table_.MarkPotentiallyAliasingStoresAsObservable(load.base(),
|
||||
- load.offset);
|
||||
+ if (is_on_heap_load) {
|
||||
+ if (is_fixed_offset_load) {
|
||||
+ table_.MarkPotentiallyAliasingStoresAsObservable(load.base(),
|
||||
+ load.offset);
|
||||
+ } else {
|
||||
+ // A dynamically indexed load might alias any fixed offset.
|
||||
+ table_.MarkAllStoresAsObservable();
|
||||
+ }
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Simon=20Z=C3=BCnd?= <szuend@chromium.org>
|
||||
Date: Fri, 2 May 2025 10:44:42 +0000
|
||||
Subject: Fix crash when pausing in for-of loop header
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Given a for-of loop:
|
||||
|
||||
for (const each of subject) {
|
||||
|
||||
The bytecode generator emits the iterator.next call + done check +
|
||||
assigning to `each` all into the source position of `const each`.
|
||||
|
||||
The pseudo-desugared code looks something like:
|
||||
|
||||
var tmp;
|
||||
loop {
|
||||
var result = iterator.next();
|
||||
if (result.done) break;
|
||||
tmp = result.value;
|
||||
|
||||
PushBlockContext;
|
||||
const each = tmp;
|
||||
|
||||
// rest of the loop.
|
||||
}
|
||||
|
||||
This is a problem, as the parser starts the block scope already on
|
||||
the `const each`. If the scope requires a context we can pause on
|
||||
bytecode that has or has not pushed the block context yet, while
|
||||
the source position looks the same.
|
||||
|
||||
The recent addition of per-script unique scope IDs lets us fix
|
||||
this problem in the debugger: We can check if the scope ID of
|
||||
the runtime scope matches the parser scope. If not, the context
|
||||
was not pushed yet.
|
||||
|
||||
The debugger already has a `HasContext` helper. We extend it to
|
||||
also check for matching scope IDs and then use `HasContext` where
|
||||
we would read variable values off the context. If the context was
|
||||
not pushed yet, we report them as 'unavailable'.
|
||||
|
||||
R=leszeks@chromium.org
|
||||
|
||||
Fixed: 384413079,399002824
|
||||
Change-Id: Ia2d0008d574e7eaf6c06b640053df696014d37f8
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6507402
|
||||
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
|
||||
Commit-Queue: Simon Zünd <szuend@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#100029}
|
||||
|
||||
diff --git a/src/debug/debug-scopes.cc b/src/debug/debug-scopes.cc
|
||||
index 001cf25be3abf4c164610007d657236f23038f4d..f15943e2cd530c875b0784f5a3b20b747c96cac4 100644
|
||||
--- a/src/debug/debug-scopes.cc
|
||||
+++ b/src/debug/debug-scopes.cc
|
||||
@@ -431,6 +431,22 @@ bool ScopeIterator::DeclaresLocals(Mode mode) const {
|
||||
}
|
||||
|
||||
bool ScopeIterator::HasContext() const {
|
||||
+ // In rare cases we pause in a scope that doesn't have its context pushed yet.
|
||||
+ // E.g. when pausing in for-of loop headers (see https://crbug.com/399002824).
|
||||
+ //
|
||||
+ // We can detect this by comparing the scope ID of the parsed scope and the
|
||||
+ // runtime scope.
|
||||
+ // We can skip this check for function scopes, those will have their context
|
||||
+ // always pushed. Also, there is an oddity where parsing::ParseFunction
|
||||
+ // produces function scopes with (-1, -1) as the start/end position,
|
||||
+ // which messes up the unique ID.
|
||||
+ if (current_scope_ && !current_scope_->is_function_scope() &&
|
||||
+ NeedsContext() &&
|
||||
+ current_scope_->UniqueIdInScript() !=
|
||||
+ context_->scope_info()->UniqueIdInScript()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
return !InInnerScope() || NeedsContext();
|
||||
}
|
||||
|
||||
@@ -475,7 +491,7 @@ void ScopeIterator::AdvanceScope() {
|
||||
DCHECK(InInnerScope());
|
||||
|
||||
do {
|
||||
- if (NeedsContext()) {
|
||||
+ if (NeedsAndHasContext()) {
|
||||
// current_scope_ needs a context so moving one scope up requires us to
|
||||
// also move up one context.
|
||||
AdvanceOneContext();
|
||||
@@ -538,6 +554,11 @@ void ScopeIterator::Next() {
|
||||
MaybeCollectAndStoreLocalBlocklists();
|
||||
UnwrapEvaluationContext();
|
||||
|
||||
+ DCHECK_IMPLIES(current_scope_ && !current_scope_->is_function_scope() &&
|
||||
+ NeedsAndHasContext(),
|
||||
+ current_scope_->UniqueIdInScript() ==
|
||||
+ context_->scope_info()->UniqueIdInScript());
|
||||
+
|
||||
if (leaving_closure) function_ = Handle<JSFunction>();
|
||||
}
|
||||
|
||||
@@ -547,32 +568,33 @@ ScopeIterator::ScopeType ScopeIterator::Type() const {
|
||||
if (InInnerScope()) {
|
||||
switch (current_scope_->scope_type()) {
|
||||
case FUNCTION_SCOPE:
|
||||
- DCHECK_IMPLIES(NeedsContext(), context_->IsFunctionContext() ||
|
||||
- context_->IsDebugEvaluateContext());
|
||||
+ DCHECK_IMPLIES(NeedsAndHasContext(),
|
||||
+ context_->IsFunctionContext() ||
|
||||
+ context_->IsDebugEvaluateContext());
|
||||
return ScopeTypeLocal;
|
||||
case MODULE_SCOPE:
|
||||
- DCHECK_IMPLIES(NeedsContext(), context_->IsModuleContext());
|
||||
+ DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsModuleContext());
|
||||
return ScopeTypeModule;
|
||||
case SCRIPT_SCOPE:
|
||||
case REPL_MODE_SCOPE:
|
||||
- DCHECK_IMPLIES(NeedsContext(), context_->IsScriptContext() ||
|
||||
- IsNativeContext(*context_));
|
||||
+ DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsScriptContext() ||
|
||||
+ IsNativeContext(*context_));
|
||||
return ScopeTypeScript;
|
||||
case WITH_SCOPE:
|
||||
- DCHECK_IMPLIES(NeedsContext(), context_->IsWithContext());
|
||||
+ DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsWithContext());
|
||||
return ScopeTypeWith;
|
||||
case CATCH_SCOPE:
|
||||
DCHECK(context_->IsCatchContext());
|
||||
return ScopeTypeCatch;
|
||||
case BLOCK_SCOPE:
|
||||
case CLASS_SCOPE:
|
||||
- DCHECK_IMPLIES(NeedsContext(), context_->IsBlockContext());
|
||||
+ DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsBlockContext());
|
||||
return ScopeTypeBlock;
|
||||
case EVAL_SCOPE:
|
||||
- DCHECK_IMPLIES(NeedsContext(), context_->IsEvalContext());
|
||||
+ DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsEvalContext());
|
||||
return ScopeTypeEval;
|
||||
case SHADOW_REALM_SCOPE:
|
||||
- DCHECK_IMPLIES(NeedsContext(), IsNativeContext(*context_));
|
||||
+ DCHECK_IMPLIES(NeedsAndHasContext(), IsNativeContext(*context_));
|
||||
// TODO(v8:11989): New ScopeType for ShadowRealms?
|
||||
return ScopeTypeScript;
|
||||
}
|
||||
@@ -956,6 +978,12 @@ bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode,
|
||||
|
||||
case VariableLocation::CONTEXT:
|
||||
if (mode == Mode::STACK) continue;
|
||||
+ if (!HasContext()) {
|
||||
+ // If the context was not yet pushed we report the variable as
|
||||
+ // unavailable.
|
||||
+ value = isolate_->factory()->the_hole_value();
|
||||
+ break;
|
||||
+ }
|
||||
DCHECK(var->IsContextSlot());
|
||||
|
||||
// We know of at least one open bug where the context and scope chain
|
||||
diff --git a/src/debug/debug-scopes.h b/src/debug/debug-scopes.h
|
||||
index e02f1b73702e2b115765d3067d9d44f367b4d5e4..f5736220f6bc0e6c129664dac3f48c63daee4c47 100644
|
||||
--- a/src/debug/debug-scopes.h
|
||||
+++ b/src/debug/debug-scopes.h
|
||||
@@ -106,6 +106,7 @@ class V8_EXPORT_PRIVATE ScopeIterator {
|
||||
bool InInnerScope() const { return !function_.is_null(); }
|
||||
bool HasContext() const;
|
||||
bool NeedsContext() const;
|
||||
+ bool NeedsAndHasContext() const { return NeedsContext() && HasContext(); }
|
||||
Handle<Context> CurrentContext() const {
|
||||
DCHECK(HasContext());
|
||||
return context_;
|
||||
diff --git a/test/inspector/regress/regress-crbug-399002824-expected.txt b/test/inspector/regress/regress-crbug-399002824-expected.txt
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..f94e3c7abebffcddf28d99830e04d93b27500db6
|
||||
--- /dev/null
|
||||
+++ b/test/inspector/regress/regress-crbug-399002824-expected.txt
|
||||
@@ -0,0 +1,5 @@
|
||||
+Don't crash when pausing on the iterator ".next" call
|
||||
+ function crashMe() {
|
||||
+ for (const #e of iter()) {
|
||||
+ () => e; // Context allocate e.
|
||||
+
|
||||
diff --git a/test/inspector/regress/regress-crbug-399002824.js b/test/inspector/regress/regress-crbug-399002824.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..45c06e7d5d81740a28eca67c8e4e52b6df985248
|
||||
--- /dev/null
|
||||
+++ b/test/inspector/regress/regress-crbug-399002824.js
|
||||
@@ -0,0 +1,35 @@
|
||||
+// Copyright 2025 the V8 project authors. All rights reserved.
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+const { session, Protocol, contextGroup } =
|
||||
+ InspectorTest.start('Don\'t crash when pausing on the iterator ".next" call');
|
||||
+
|
||||
+session.setupScriptMap();
|
||||
+contextGroup.addScript(`
|
||||
+ function *iter() {
|
||||
+ yield 1;
|
||||
+ debugger;
|
||||
+ yield 2;
|
||||
+ }
|
||||
+
|
||||
+ function crashMe() {
|
||||
+ for (const e of iter()) {
|
||||
+ () => e; // Context allocate e.
|
||||
+ }
|
||||
+ }
|
||||
+`);
|
||||
+
|
||||
+Protocol.Debugger.enable();
|
||||
+
|
||||
+(async () => {
|
||||
+ let pausedPromise = Protocol.Debugger.oncePaused();
|
||||
+ Protocol.Runtime.evaluate({ expression: 'crashMe()' });
|
||||
+
|
||||
+ let { params: { callFrames } } = await pausedPromise;
|
||||
+ await session.logSourceLocation(callFrames[1].location);
|
||||
+
|
||||
+ Protocol.Debugger.resume();
|
||||
+
|
||||
+ InspectorTest.completeTest();
|
||||
+})();
|
||||
80
patches/v8/parser_set_start_end_on_materialized_scopes.patch
Normal file
80
patches/v8/parser_set_start_end_on_materialized_scopes.patch
Normal file
@@ -0,0 +1,80 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Simon=20Z=C3=BCnd?= <szuend@chromium.org>
|
||||
Date: Mon, 5 May 2025 07:59:53 +0000
|
||||
Subject: Set start/end on materialized scopes
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
debug-evaluate requires the 'UniqueIdInScript' of scopes to compare
|
||||
re-parsed with runtime scopes. parsing::ParseFunction materializes
|
||||
outer scopes from runtime scopes before (re-)parsing a function.
|
||||
Currently, this materialization step does not re-materialize the
|
||||
start/end position, so debug-evaluate is not able to properly compare
|
||||
parsed with runtime scopes.
|
||||
|
||||
This CL fixes this by also re-materializing the position info when
|
||||
parsing functions.
|
||||
|
||||
For the script scope we don't have to set the start/end
|
||||
position as the script scope always has a UniqueIdInScript of -2.
|
||||
|
||||
Note: The current implementation leads `HasContext()` to return
|
||||
`false` even though we have a context and the parsed and runtime
|
||||
scope. One could think that we might show variables as unavailable
|
||||
in this case. But we got lucky: For the scope view, we re-parse the
|
||||
full script, which results in full position information for all
|
||||
scopes. We use `parsing::ParseFunction` only for debug-evaluate
|
||||
where `HasContext()` for outer scopes is unused and does not matter.
|
||||
|
||||
R=leszeks@chromium.org
|
||||
|
||||
Bug: 399002824
|
||||
Change-Id: If8c4e73693b112dbced945f2094730cd92b535c3
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6507407
|
||||
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
|
||||
Commit-Queue: Simon Zünd <szuend@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#100041}
|
||||
|
||||
diff --git a/src/ast/scopes.cc b/src/ast/scopes.cc
|
||||
index 53651e867d244314ffb35e3aa66415cf01d55d99..6c0c0b534a31641971d028ef2aa8f8e38e3eb033 100644
|
||||
--- a/src/ast/scopes.cc
|
||||
+++ b/src/ast/scopes.cc
|
||||
@@ -496,6 +496,9 @@ Scope* Scope::DeserializeScopeChain(IsolateT* isolate, Zone* zone,
|
||||
if (current_scope != nullptr) {
|
||||
outer_scope->AddInnerScope(current_scope);
|
||||
}
|
||||
+ outer_scope->set_start_position(scope_info->StartPosition());
|
||||
+ outer_scope->set_end_position(scope_info->EndPosition());
|
||||
+
|
||||
current_scope = outer_scope;
|
||||
if (innermost_scope == nullptr) innermost_scope = current_scope;
|
||||
scope_info = scope_info->HasOuterScopeInfo() ? scope_info->OuterScopeInfo()
|
||||
diff --git a/src/debug/debug-scopes.cc b/src/debug/debug-scopes.cc
|
||||
index f15943e2cd530c875b0784f5a3b20b747c96cac4..4c770f8572ac5051391dd4c4e33d4ba4b6a37a36 100644
|
||||
--- a/src/debug/debug-scopes.cc
|
||||
+++ b/src/debug/debug-scopes.cc
|
||||
@@ -436,12 +436,7 @@ bool ScopeIterator::HasContext() const {
|
||||
//
|
||||
// We can detect this by comparing the scope ID of the parsed scope and the
|
||||
// runtime scope.
|
||||
- // We can skip this check for function scopes, those will have their context
|
||||
- // always pushed. Also, there is an oddity where parsing::ParseFunction
|
||||
- // produces function scopes with (-1, -1) as the start/end position,
|
||||
- // which messes up the unique ID.
|
||||
- if (current_scope_ && !current_scope_->is_function_scope() &&
|
||||
- NeedsContext() &&
|
||||
+ if (current_scope_ && NeedsContext() &&
|
||||
current_scope_->UniqueIdInScript() !=
|
||||
context_->scope_info()->UniqueIdInScript()) {
|
||||
return false;
|
||||
@@ -554,8 +549,7 @@ void ScopeIterator::Next() {
|
||||
MaybeCollectAndStoreLocalBlocklists();
|
||||
UnwrapEvaluationContext();
|
||||
|
||||
- DCHECK_IMPLIES(current_scope_ && !current_scope_->is_function_scope() &&
|
||||
- NeedsAndHasContext(),
|
||||
+ DCHECK_IMPLIES(current_scope_ && NeedsAndHasContext(),
|
||||
current_scope_->UniqueIdInScript() ==
|
||||
context_->scope_info()->UniqueIdInScript());
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { getCodeBlocks } = require('@electron/lint-roller/dist/lib/markdown');
|
||||
|
||||
const { GitProcess } = require('dugite');
|
||||
const { ESLint } = require('eslint');
|
||||
const minimist = require('minimist');
|
||||
@@ -281,6 +279,7 @@ const LINTERS = [{
|
||||
ignoreRoots: ['.git', 'node_modules', 'spec/node_modules'],
|
||||
test: filename => filename.endsWith('.md'),
|
||||
run: async (opts, filenames) => {
|
||||
const { getCodeBlocks } = await import('@electron/lint-roller/dist/lib/markdown.js');
|
||||
let errors = false;
|
||||
|
||||
// Run markdownlint on all Markdown files
|
||||
|
||||
128
script/patch-up.js
Normal file
128
script/patch-up.js
Normal file
@@ -0,0 +1,128 @@
|
||||
const { appCredentialsFromString, getAuthOptionsForRepo } = require('@electron/github-app-auth');
|
||||
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const { PATCH_UP_APP_CREDS } = process.env;
|
||||
|
||||
const REPO_OWNER = 'electron';
|
||||
const REPO_NAME = 'electron';
|
||||
|
||||
async function getAllPatchFiles (dir) {
|
||||
const files = [];
|
||||
|
||||
async function walkDir (currentDir) {
|
||||
const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(currentDir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
await walkDir(fullPath);
|
||||
} else if (entry.isFile() && (entry.name.endsWith('.patch') || entry.name === 'README.md' || entry.name === 'config.json')) {
|
||||
const relativePath = path.relative(process.cwd(), fullPath);
|
||||
const content = await fs.promises.readFile(fullPath, 'utf8');
|
||||
files.push({
|
||||
path: relativePath,
|
||||
content
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await walkDir(dir);
|
||||
return files;
|
||||
}
|
||||
|
||||
function getCurrentCommitSha () {
|
||||
return process.env.GITHUB_SHA;
|
||||
}
|
||||
|
||||
function getCurrentBranch () {
|
||||
return process.env.GITHUB_HEAD_REF;
|
||||
}
|
||||
|
||||
async function main () {
|
||||
if (!PATCH_UP_APP_CREDS) {
|
||||
throw new Error('PATCH_UP_APP_CREDS environment variable not set');
|
||||
}
|
||||
|
||||
const currentBranch = getCurrentBranch();
|
||||
|
||||
if (!currentBranch) {
|
||||
throw new Error('GITHUB_HEAD_REF environment variable not set. Patch Up only works in PR workflows currently.');
|
||||
}
|
||||
|
||||
const octokit = new Octokit({
|
||||
...await getAuthOptionsForRepo({
|
||||
name: REPO_OWNER,
|
||||
owner: REPO_NAME
|
||||
}, appCredentialsFromString(PATCH_UP_APP_CREDS))
|
||||
});
|
||||
|
||||
const patchesDir = path.join(process.cwd(), 'patches');
|
||||
|
||||
// Get current git state
|
||||
const currentCommitSha = getCurrentCommitSha();
|
||||
|
||||
// Get the tree SHA from the current commit
|
||||
const currentCommit = await octokit.git.getCommit({
|
||||
owner: REPO_OWNER,
|
||||
repo: REPO_NAME,
|
||||
commit_sha: currentCommitSha
|
||||
});
|
||||
const baseTreeSha = currentCommit.data.tree.sha;
|
||||
|
||||
// Find all patch files
|
||||
const patchFiles = await getAllPatchFiles(patchesDir);
|
||||
|
||||
if (patchFiles.length === 0) {
|
||||
throw new Error('No patch files found');
|
||||
}
|
||||
|
||||
console.log(`Found ${patchFiles.length} patch files`);
|
||||
|
||||
// Create a new tree with the patch files
|
||||
const tree = patchFiles.map(file => ({
|
||||
path: file.path,
|
||||
mode: '100644',
|
||||
type: 'blob',
|
||||
content: file.content
|
||||
}));
|
||||
|
||||
const treeResponse = await octokit.git.createTree({
|
||||
owner: REPO_OWNER,
|
||||
repo: REPO_NAME,
|
||||
base_tree: baseTreeSha,
|
||||
tree
|
||||
});
|
||||
|
||||
// Create a new commit
|
||||
const commitMessage = 'chore: update patches';
|
||||
const commitResponse = await octokit.git.createCommit({
|
||||
owner: REPO_OWNER,
|
||||
repo: REPO_NAME,
|
||||
message: commitMessage,
|
||||
tree: treeResponse.data.sha,
|
||||
parents: [currentCommitSha]
|
||||
});
|
||||
|
||||
// Update the branch reference
|
||||
await octokit.git.updateRef({
|
||||
owner: REPO_OWNER,
|
||||
repo: REPO_NAME,
|
||||
ref: `heads/${currentBranch}`,
|
||||
sha: commitResponse.data.sha
|
||||
});
|
||||
|
||||
console.log(`Successfully pushed commit ${commitResponse.data.sha} to ${currentBranch}`);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
const { appCredentialsFromString, getTokenForRepo } = require('@electron/github-app-auth');
|
||||
|
||||
const cp = require('node:child_process');
|
||||
|
||||
const { PATCH_UP_APP_CREDS } = process.env;
|
||||
|
||||
async function main () {
|
||||
if (!PATCH_UP_APP_CREDS) {
|
||||
throw new Error('PATCH_UP_APP_CREDS environment variable not set');
|
||||
}
|
||||
|
||||
const token = await getTokenForRepo(
|
||||
{
|
||||
name: 'electron',
|
||||
owner: 'electron'
|
||||
},
|
||||
appCredentialsFromString(PATCH_UP_APP_CREDS)
|
||||
);
|
||||
|
||||
const remoteURL = `https://x-access-token:${token}@github.com/electron/electron.git`;
|
||||
|
||||
// NEVER LOG THE OUTPUT OF THIS COMMAND
|
||||
// GIT LEAKS THE ACCESS CREDENTIALS IN CONSOLE LOGS
|
||||
const { status } = cp.spawnSync('git', ['push', '--set-upstream', remoteURL], {
|
||||
stdio: 'ignore'
|
||||
});
|
||||
|
||||
if (status !== 0) {
|
||||
throw new Error('Failed to push to target branch');
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -81,7 +81,11 @@ void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host,
|
||||
|
||||
void Debugger::RenderFrameHostChanged(content::RenderFrameHost* old_rfh,
|
||||
content::RenderFrameHost* new_rfh) {
|
||||
if (agent_host_) {
|
||||
// ConnectWebContents uses the primary main frame of the webContents,
|
||||
// so if the new_rfh is not the primary main frame, we don't want to
|
||||
// reconnect otherwise we'll end up trying to reconnect to a RenderFrameHost
|
||||
// that already has a DevToolsAgentHost associated with it.
|
||||
if (agent_host_ && new_rfh->IsInPrimaryMainFrame()) {
|
||||
agent_host_->DisconnectWebContents();
|
||||
auto* web_contents = content::WebContents::FromRenderFrameHost(new_rfh);
|
||||
agent_host_->ConnectWebContents(web_contents);
|
||||
|
||||
@@ -71,9 +71,8 @@ v8::Local<v8::Promise> ShowOpenDialog(
|
||||
|
||||
void ShowSaveDialogSync(const file_dialog::DialogSettings& settings,
|
||||
gin::Arguments* args) {
|
||||
base::FilePath path;
|
||||
if (file_dialog::ShowSaveDialogSync(settings, &path))
|
||||
args->Return(path);
|
||||
if (const auto path = file_dialog::ShowSaveDialogSync(settings))
|
||||
args->Return(*path);
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> ShowSaveDialog(
|
||||
|
||||
@@ -34,17 +34,6 @@
|
||||
selector:@selector(onScreenUnlocked:)
|
||||
name:@"com.apple.screenIsUnlocked"
|
||||
object:nil];
|
||||
// A notification that the workspace posts before the machine goes to sleep.
|
||||
[distributed_center addObserver:self
|
||||
selector:@selector(isSuspending:)
|
||||
name:NSWorkspaceWillSleepNotification
|
||||
object:nil];
|
||||
// A notification that the workspace posts when the machine wakes from
|
||||
// sleep.
|
||||
[distributed_center addObserver:self
|
||||
selector:@selector(isResuming:)
|
||||
name:NSWorkspaceDidWakeNotification
|
||||
object:nil];
|
||||
|
||||
NSNotificationCenter* shared_center =
|
||||
[[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
@@ -73,18 +62,6 @@
|
||||
self->emitters.push_back(monitor_);
|
||||
}
|
||||
|
||||
- (void)isSuspending:(NSNotification*)notify {
|
||||
for (auto* emitter : self->emitters) {
|
||||
emitter->Emit("suspend");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)isResuming:(NSNotification*)notify {
|
||||
for (auto* emitter : self->emitters) {
|
||||
emitter->Emit("resume");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onScreenLocked:(NSNotification*)notification {
|
||||
for (auto* emitter : self->emitters) {
|
||||
emitter->Emit("lock-screen");
|
||||
|
||||
@@ -88,18 +88,6 @@ LRESULT CALLBACK PowerMonitor::WndProc(HWND hwnd,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
}
|
||||
} else if (message == WM_POWERBROADCAST) {
|
||||
if (wparam == PBT_APMRESUMEAUTOMATIC) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce([](PowerMonitor* pm) { pm->Emit("resume"); },
|
||||
base::Unretained(this)));
|
||||
} else if (wparam == PBT_APMSUSPEND) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce([](PowerMonitor* pm) { pm->Emit("suspend"); },
|
||||
base::Unretained(this)));
|
||||
}
|
||||
}
|
||||
return ::DefWindowProc(hwnd, message, wparam, lparam);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
#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/cleaned_up_at_exit.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
@@ -202,9 +203,11 @@ std::vector<std::string> GetDataTypesFromMask(
|
||||
|
||||
// Represents a task to clear browsing data for the `clearData` API method.
|
||||
//
|
||||
// This type manages its own lifetime, deleting itself once the task finishes
|
||||
// completely.
|
||||
class ClearDataTask {
|
||||
// This type manages its own lifetime,
|
||||
// 1) deleting itself once all the operations created by this task are
|
||||
// completed. 2) through gin_helper::CleanedUpAtExit, ensuring it's destroyed
|
||||
// before the node environment shuts down.
|
||||
class ClearDataTask : public gin_helper::CleanedUpAtExit {
|
||||
public:
|
||||
// Starts running a task. This function will return before the task is
|
||||
// finished, but will resolve or reject the |promise| when it finishes.
|
||||
@@ -215,7 +218,7 @@ class ClearDataTask {
|
||||
std::vector<url::Origin> origins,
|
||||
BrowsingDataFilterBuilder::Mode filter_mode,
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode origin_matching_mode) {
|
||||
std::shared_ptr<ClearDataTask> task(new ClearDataTask(std::move(promise)));
|
||||
auto* task = new ClearDataTask(std::move(promise));
|
||||
|
||||
// This method counts as an operation. This is important so we can call
|
||||
// `OnOperationFinished` at the end of this method as a fallback if all the
|
||||
@@ -259,42 +262,36 @@ class ClearDataTask {
|
||||
}
|
||||
|
||||
// This static method counts as an operation.
|
||||
task->OnOperationFinished(std::nullopt);
|
||||
task->OnOperationFinished(nullptr, std::nullopt);
|
||||
}
|
||||
|
||||
private:
|
||||
// An individual |content::BrowsingDataRemover::Remove...| operation as part
|
||||
// of a full |ClearDataTask|. This class manages its own lifetime, cleaning
|
||||
// itself up after the operation completes and notifies the task of the
|
||||
// result.
|
||||
// of a full |ClearDataTask|. This class is owned by ClearDataTask and cleaned
|
||||
// up either when the operation completes or when ClearDataTask is destroyed.
|
||||
class ClearDataOperation : private BrowsingDataRemover::Observer {
|
||||
public:
|
||||
static void Run(std::shared_ptr<ClearDataTask> task,
|
||||
BrowsingDataRemover* remover,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
|
||||
auto* operation = new ClearDataOperation(task, remover);
|
||||
ClearDataOperation(ClearDataTask* task, BrowsingDataRemover* remover)
|
||||
: task_(task) {
|
||||
observation_.Observe(remover);
|
||||
}
|
||||
|
||||
void Start(BrowsingDataRemover* remover,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
|
||||
remover->RemoveWithFilterAndReply(base::Time::Min(), base::Time::Max(),
|
||||
data_type_mask, kClearOriginTypeAll,
|
||||
std::move(filter_builder), operation);
|
||||
std::move(filter_builder), this);
|
||||
}
|
||||
|
||||
// BrowsingDataRemover::Observer:
|
||||
void OnBrowsingDataRemoverDone(
|
||||
BrowsingDataRemover::DataType failed_data_types) override {
|
||||
task_->OnOperationFinished(failed_data_types);
|
||||
delete this;
|
||||
task_->OnOperationFinished(this, failed_data_types);
|
||||
}
|
||||
|
||||
private:
|
||||
ClearDataOperation(std::shared_ptr<ClearDataTask> task,
|
||||
BrowsingDataRemover* remover)
|
||||
: task_(task) {
|
||||
observation_.Observe(remover);
|
||||
}
|
||||
|
||||
std::shared_ptr<ClearDataTask> task_;
|
||||
raw_ptr<ClearDataTask> task_;
|
||||
base::ScopedObservation<BrowsingDataRemover, BrowsingDataRemover::Observer>
|
||||
observation_{this};
|
||||
};
|
||||
@@ -303,18 +300,20 @@ class ClearDataTask {
|
||||
: promise_(std::move(promise)) {}
|
||||
|
||||
static void StartOperation(
|
||||
std::shared_ptr<ClearDataTask> task,
|
||||
ClearDataTask* task,
|
||||
BrowsingDataRemover* remover,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
|
||||
// Track this operation
|
||||
task->operations_running_ += 1;
|
||||
|
||||
ClearDataOperation::Run(task, remover, data_type_mask,
|
||||
std::move(filter_builder));
|
||||
auto& operation = task->operations_.emplace_back(
|
||||
std::make_unique<ClearDataOperation>(task, remover));
|
||||
operation->Start(remover, data_type_mask, std::move(filter_builder));
|
||||
}
|
||||
|
||||
void OnOperationFinished(
|
||||
ClearDataOperation* operation,
|
||||
std::optional<BrowsingDataRemover::DataType> failed_data_types) {
|
||||
DCHECK_GT(operations_running_, 0);
|
||||
operations_running_ -= 1;
|
||||
@@ -323,6 +322,16 @@ class ClearDataTask {
|
||||
failed_data_types_ |= failed_data_types.value();
|
||||
}
|
||||
|
||||
if (operation) {
|
||||
operations_.erase(
|
||||
std::remove_if(
|
||||
operations_.begin(), operations_.end(),
|
||||
[operation](const std::unique_ptr<ClearDataOperation>& op) {
|
||||
return op.get() == operation;
|
||||
}),
|
||||
operations_.end());
|
||||
}
|
||||
|
||||
// If this is the last operation, then the task is finished
|
||||
if (operations_running_ == 0) {
|
||||
OnTaskFinished();
|
||||
@@ -350,11 +359,14 @@ class ClearDataTask {
|
||||
|
||||
promise_.Reject(error);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
int operations_running_ = 0;
|
||||
BrowsingDataRemover::DataType failed_data_types_ = 0ULL;
|
||||
gin_helper::Promise<void> promise_;
|
||||
std::vector<std::unique_ptr<ClearDataOperation>> operations_;
|
||||
};
|
||||
|
||||
base::Value::Dict createProxyConfig(ProxyPrefs::ProxyMode proxy_mode,
|
||||
|
||||
@@ -78,6 +78,9 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
||||
base::FileHandleMappingVector fds_to_remap;
|
||||
#endif
|
||||
for (const auto& [io_handle, io_type] : stdio) {
|
||||
if (io_handle == IOHandle::STDIN)
|
||||
continue;
|
||||
|
||||
if (io_type == IOType::IO_PIPE) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
HANDLE read = nullptr;
|
||||
|
||||
@@ -216,6 +216,12 @@ void View::AddChildViewAt(gin::Handle<View> child,
|
||||
if (!view_)
|
||||
return;
|
||||
|
||||
if (!child->view()) {
|
||||
gin_helper::ErrorThrower(isolate()).ThrowError(
|
||||
"Can't add a destroyed child view to a parent view");
|
||||
return;
|
||||
}
|
||||
|
||||
// This will CHECK and crash in View::AddChildViewAtImpl if not handled here.
|
||||
if (view_ == child->view()) {
|
||||
gin_helper::ErrorThrower(isolate()).ThrowError(
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "base/containers/fixed_flat_map.h"
|
||||
#include "base/containers/flat_set.h"
|
||||
#include "base/containers/id_map.h"
|
||||
#include "base/containers/map_util.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/no_destructor.h"
|
||||
@@ -2245,8 +2246,11 @@ void WebContents::DevToolsOpened() {
|
||||
// Inherit owner window in devtools when it doesn't have one.
|
||||
auto* devtools = inspectable_web_contents_->GetDevToolsWebContents();
|
||||
bool has_window = devtools->GetUserData(NativeWindowRelay::UserDataKey());
|
||||
if (owner_window() && !has_window)
|
||||
if (owner_window_ && !has_window) {
|
||||
DCHECK(!owner_window_.WasInvalidated());
|
||||
DCHECK_EQ(handle->owner_window(), nullptr);
|
||||
handle->SetOwnerWindow(devtools, owner_window());
|
||||
}
|
||||
|
||||
Emit("devtools-opened");
|
||||
}
|
||||
@@ -3043,12 +3047,15 @@ void OnGetDeviceNameToUse(base::WeakPtr<content::WebContents> web_contents,
|
||||
print_settings.Set(printing::kSettingDpiVertical, dpi.height());
|
||||
}
|
||||
|
||||
auto* print_view_manager =
|
||||
PrintViewManagerElectron::FromWebContents(web_contents.get());
|
||||
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents.get());
|
||||
if (!rfh)
|
||||
return;
|
||||
|
||||
auto* print_view_manager = PrintViewManagerElectron::FromWebContents(
|
||||
content::WebContents::FromRenderFrameHost(rfh));
|
||||
if (!print_view_manager)
|
||||
return;
|
||||
|
||||
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents.get());
|
||||
print_view_manager->PrintNow(rfh, std::move(print_settings),
|
||||
std::move(print_callback));
|
||||
}
|
||||
@@ -3096,12 +3103,15 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
}
|
||||
|
||||
if (options.IsEmptyObject()) {
|
||||
auto* print_view_manager =
|
||||
PrintViewManagerElectron::FromWebContents(web_contents());
|
||||
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents());
|
||||
if (!rfh)
|
||||
return;
|
||||
|
||||
auto* print_view_manager = PrintViewManagerElectron::FromWebContents(
|
||||
content::WebContents::FromRenderFrameHost(rfh));
|
||||
if (!print_view_manager)
|
||||
return;
|
||||
|
||||
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents());
|
||||
print_view_manager->PrintNow(rfh, std::move(settings), std::move(callback));
|
||||
return;
|
||||
}
|
||||
@@ -4113,30 +4123,35 @@ void WebContents::DevToolsSaveToFile(const std::string& url,
|
||||
const std::string& content,
|
||||
bool save_as,
|
||||
bool is_base64) {
|
||||
base::FilePath path;
|
||||
auto it = saved_files_.find(url);
|
||||
if (it != saved_files_.end() && !save_as) {
|
||||
path = it->second;
|
||||
} else {
|
||||
const base::FilePath* path = nullptr;
|
||||
|
||||
if (!save_as)
|
||||
base::FindOrNull(saved_files_, url);
|
||||
|
||||
if (path == nullptr) {
|
||||
file_dialog::DialogSettings settings;
|
||||
settings.parent_window = owner_window();
|
||||
settings.force_detached = offscreen_;
|
||||
settings.title = url;
|
||||
settings.default_path = base::FilePath::FromUTF8Unsafe(url);
|
||||
if (!file_dialog::ShowSaveDialogSync(settings, &path)) {
|
||||
inspectable_web_contents_->CallClientFunction(
|
||||
"DevToolsAPI", "canceledSaveURL", base::Value(url));
|
||||
return;
|
||||
if (auto new_path = file_dialog::ShowSaveDialogSync(settings)) {
|
||||
auto [iter, _] = saved_files_.try_emplace(url, std::move(*new_path));
|
||||
path = &iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
saved_files_[url] = path;
|
||||
if (path == nullptr) {
|
||||
inspectable_web_contents_->CallClientFunction(
|
||||
"DevToolsAPI", "canceledSaveURL", base::Value{url});
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify DevTools.
|
||||
inspectable_web_contents_->CallClientFunction(
|
||||
"DevToolsAPI", "savedURL", base::Value(url),
|
||||
base::Value(path.AsUTF8Unsafe()));
|
||||
"DevToolsAPI", "savedURL", base::Value{url},
|
||||
base::Value{path->AsUTF8Unsafe()});
|
||||
file_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&WriteToFile, path, content, is_base64));
|
||||
FROM_HERE, base::BindOnce(&WriteToFile, *path, content, is_base64));
|
||||
}
|
||||
|
||||
void WebContents::DevToolsAppendToFile(const std::string& url,
|
||||
|
||||
@@ -105,20 +105,12 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
|
||||
options.Get(options::kVibrancyType, &vibrancy_);
|
||||
#endif
|
||||
|
||||
v8::Local<v8::Value> titlebar_overlay;
|
||||
if (options.Get(options::ktitleBarOverlay, &titlebar_overlay)) {
|
||||
if (titlebar_overlay->IsBoolean()) {
|
||||
options.Get(options::ktitleBarOverlay, &titlebar_overlay_);
|
||||
} else if (titlebar_overlay->IsObject()) {
|
||||
titlebar_overlay_ = true;
|
||||
|
||||
auto titlebar_overlay_dict =
|
||||
gin_helper::Dictionary::CreateEmpty(options.isolate());
|
||||
options.Get(options::ktitleBarOverlay, &titlebar_overlay_dict);
|
||||
int height;
|
||||
if (titlebar_overlay_dict.Get(options::kOverlayHeight, &height))
|
||||
titlebar_overlay_height_ = height;
|
||||
}
|
||||
if (gin_helper::Dictionary dict;
|
||||
options.Get(options::ktitleBarOverlay, &dict)) {
|
||||
titlebar_overlay_ = true;
|
||||
titlebar_overlay_height_ = dict.ValueOrDefault(options::kOverlayHeight, 0);
|
||||
} else if (bool flag; options.Get(options::ktitleBarOverlay, &flag)) {
|
||||
titlebar_overlay_ = flag;
|
||||
}
|
||||
|
||||
if (parent)
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/queue.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/observer_list.h"
|
||||
@@ -463,9 +463,10 @@ class NativeWindow : public base::SupportsUserData,
|
||||
// on HiDPI displays on some environments.
|
||||
std::optional<extensions::SizeConstraints> content_size_constraints_;
|
||||
|
||||
std::queue<bool> pending_transitions_;
|
||||
base::queue<bool> pending_transitions_;
|
||||
FullScreenTransitionState fullscreen_transition_state_ =
|
||||
FullScreenTransitionState::kNone;
|
||||
|
||||
FullScreenTransitionType fullscreen_transition_type_ =
|
||||
FullScreenTransitionType::kNone;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/containers/fixed_flat_set.h"
|
||||
#include "base/memory/raw_ref.h"
|
||||
#include "base/numerics/ranges.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
@@ -79,6 +79,7 @@
|
||||
#include "base/win/windows_version.h"
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#include "shell/browser/ui/win/electron_desktop_native_widget_aura.h"
|
||||
#include "shell/common/color_util.h"
|
||||
#include "skia/ext/skia_utils_win.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/gfx/color_utils.h"
|
||||
@@ -213,6 +214,14 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||
|
||||
overlay_button_color_ = color_utils::GetSysSkColor(COLOR_BTNFACE);
|
||||
overlay_symbol_color_ = color_utils::GetSysSkColor(COLOR_BTNTEXT);
|
||||
|
||||
bool accent_color = true;
|
||||
std::string accent_color_string;
|
||||
if (options.Get(options::kAccentColor, &accent_color_string)) {
|
||||
accent_color_ = ParseCSSColor(accent_color_string);
|
||||
} else if (options.Get(options::kAccentColor, &accent_color)) {
|
||||
accent_color_ = accent_color;
|
||||
}
|
||||
#endif
|
||||
|
||||
v8::Local<v8::Value> titlebar_overlay;
|
||||
@@ -429,6 +438,8 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||
last_window_state_ = ui::mojom::WindowShowState::kFullscreen;
|
||||
else
|
||||
last_window_state_ = ui::mojom::WindowShowState::kNormal;
|
||||
|
||||
UpdateWindowAccentColor();
|
||||
#endif
|
||||
|
||||
// Listen to mouse events.
|
||||
@@ -1125,9 +1136,9 @@ void NativeWindowViews::SetAlwaysOnTop(ui::ZOrderLevel z_order,
|
||||
if (z_order != ui::ZOrderLevel::kNormal) {
|
||||
// On macOS the window is placed behind the Dock for the following levels.
|
||||
// Re-use the same names on Windows to make it easier for the user.
|
||||
static const std::vector<std::string> levels = {
|
||||
"floating", "torn-off-menu", "modal-panel", "main-menu", "status"};
|
||||
behind_task_bar_ = base::Contains(levels, level);
|
||||
static constexpr auto levels = base::MakeFixedFlatSet<std::string_view>(
|
||||
{"floating", "torn-off-menu", "modal-panel", "main-menu", "status"});
|
||||
behind_task_bar_ = levels.contains(level);
|
||||
}
|
||||
#endif
|
||||
MoveBehindTaskBarIfNeeded();
|
||||
|
||||
@@ -207,6 +207,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
void ResetWindowControls();
|
||||
void SetRoundedCorners(bool rounded);
|
||||
void SetForwardMouseMessages(bool forward);
|
||||
void UpdateWindowAccentColor();
|
||||
static LRESULT CALLBACK SubclassProc(HWND hwnd,
|
||||
UINT msg,
|
||||
WPARAM w_param,
|
||||
@@ -303,6 +304,8 @@ class NativeWindowViews : public NativeWindow,
|
||||
// Whether the window is currently being moved.
|
||||
bool is_moving_ = false;
|
||||
|
||||
std::variant<bool, SkColor> accent_color_ = true;
|
||||
|
||||
std::optional<gfx::Rect> pending_bounds_change_;
|
||||
|
||||
// The message ID of the "TaskbarCreated" message, sent to us when we need to
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "base/win/atl.h" // Must be before UIAutomationCore.h
|
||||
#include "base/win/registry.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "content/public/browser/browser_accessibility_state.h"
|
||||
@@ -29,6 +30,53 @@ namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
void SetWindowBorderAndCaptionColor(HWND hwnd, COLORREF color) {
|
||||
if (base::win::GetVersion() < base::win::Version::WIN11)
|
||||
return;
|
||||
|
||||
HRESULT result =
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &color, sizeof(color));
|
||||
|
||||
if (FAILED(result))
|
||||
LOG(WARNING) << "Failed to set caption color";
|
||||
|
||||
result =
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &color, sizeof(color));
|
||||
|
||||
if (FAILED(result))
|
||||
LOG(WARNING) << "Failed to set border color";
|
||||
}
|
||||
|
||||
std::optional<DWORD> GetAccentColor() {
|
||||
base::win::RegKey key;
|
||||
if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\DWM",
|
||||
KEY_READ) != ERROR_SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DWORD accent_color = 0;
|
||||
if (key.ReadValueDW(L"AccentColor", &accent_color) != ERROR_SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return accent_color;
|
||||
}
|
||||
|
||||
bool IsAccentColorOnTitleBarsEnabled() {
|
||||
base::win::RegKey key;
|
||||
if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\DWM",
|
||||
KEY_READ) != ERROR_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD enabled = 0;
|
||||
if (key.ReadValueDW(L"ColorPrevalence", &enabled) != ERROR_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return enabled != 0;
|
||||
}
|
||||
|
||||
// Convert Win32 WM_QUERYENDSESSIONS to strings.
|
||||
const std::vector<std::string> EndSessionToStringVec(LPARAM end_session_id) {
|
||||
std::vector<std::string> params;
|
||||
@@ -449,6 +497,19 @@ bool NativeWindowViews::PreHandleMSG(UINT message,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case WM_DWMCOLORIZATIONCOLORCHANGED: {
|
||||
UpdateWindowAccentColor();
|
||||
return false;
|
||||
}
|
||||
case WM_SETTINGCHANGE: {
|
||||
if (l_param) {
|
||||
const wchar_t* setting_name = reinterpret_cast<const wchar_t*>(l_param);
|
||||
std::wstring setting_str(setting_name);
|
||||
if (setting_str == L"ImmersiveColorSet")
|
||||
UpdateWindowAccentColor();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
@@ -508,6 +569,35 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) {
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowViews::UpdateWindowAccentColor() {
|
||||
if (base::win::GetVersion() < base::win::Version::WIN11)
|
||||
return;
|
||||
|
||||
if (!IsAccentColorOnTitleBarsEnabled())
|
||||
return;
|
||||
|
||||
COLORREF border_color;
|
||||
if (std::holds_alternative<bool>(accent_color_)) {
|
||||
// Don't set accent color if the user has disabled it.
|
||||
if (!std::get<bool>(accent_color_))
|
||||
return;
|
||||
|
||||
std::optional<DWORD> accent_color = GetAccentColor();
|
||||
if (!accent_color.has_value())
|
||||
return;
|
||||
|
||||
border_color =
|
||||
RGB(GetRValue(accent_color.value()), GetGValue(accent_color.value()),
|
||||
GetBValue(accent_color.value()));
|
||||
} else {
|
||||
SkColor color = std::get<SkColor>(accent_color_);
|
||||
border_color =
|
||||
RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
|
||||
}
|
||||
|
||||
SetWindowBorderAndCaptionColor(GetAcceleratedWidget(), border_color);
|
||||
}
|
||||
|
||||
void NativeWindowViews::ResetWindowControls() {
|
||||
// If a given window was minimized and has since been
|
||||
// unminimized (restored/maximized), ensure the WCO buttons
|
||||
|
||||
82
shell/browser/preload_script.cc
Normal file
82
shell/browser/preload_script.cc
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2025 Salesforce, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/preload_script.h"
|
||||
|
||||
#include "base/containers/fixed_flat_map.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/uuid.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
|
||||
namespace gin {
|
||||
|
||||
using electron::PreloadScript;
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value> Converter<PreloadScript::ScriptType>::ToV8(
|
||||
v8::Isolate* isolate,
|
||||
const PreloadScript::ScriptType& in) {
|
||||
using Val = PreloadScript::ScriptType;
|
||||
static constexpr auto Lookup = base::MakeFixedFlatMap<Val, std::string_view>({
|
||||
{Val::kWebFrame, "frame"},
|
||||
{Val::kServiceWorker, "service-worker"},
|
||||
});
|
||||
return StringToV8(isolate, Lookup.at(in));
|
||||
}
|
||||
|
||||
// static
|
||||
bool Converter<PreloadScript::ScriptType>::FromV8(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
PreloadScript::ScriptType* out) {
|
||||
using Val = PreloadScript::ScriptType;
|
||||
static constexpr auto Lookup = base::MakeFixedFlatMap<std::string_view, Val>({
|
||||
{"frame", Val::kWebFrame},
|
||||
{"service-worker", Val::kServiceWorker},
|
||||
});
|
||||
return FromV8WithLookup(isolate, val, Lookup, out);
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value> Converter<PreloadScript>::ToV8(
|
||||
v8::Isolate* isolate,
|
||||
const PreloadScript& script) {
|
||||
gin::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
dict.Set("filePath", script.file_path.AsUTF8Unsafe());
|
||||
dict.Set("id", script.id);
|
||||
dict.Set("type", script.script_type);
|
||||
return ConvertToV8(isolate, dict).As<v8::Object>();
|
||||
}
|
||||
|
||||
// static
|
||||
bool Converter<PreloadScript>::FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
PreloadScript* out) {
|
||||
gin_helper::Dictionary options;
|
||||
if (!ConvertFromV8(isolate, val, &options))
|
||||
return false;
|
||||
if (PreloadScript::ScriptType script_type;
|
||||
options.Get("type", &script_type)) {
|
||||
out->script_type = script_type;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (base::FilePath file_path; options.Get("filePath", &file_path)) {
|
||||
out->file_path = file_path;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (std::string id; options.Get("id", &id)) {
|
||||
out->id = id;
|
||||
} else {
|
||||
out->id = base::Uuid::GenerateRandomV4().AsLowercaseString();
|
||||
}
|
||||
if (bool deprecated; options.Get("_deprecated", &deprecated)) {
|
||||
out->deprecated = deprecated;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gin
|
||||
@@ -5,14 +5,11 @@
|
||||
#ifndef ELECTRON_SHELL_BROWSER_PRELOAD_SCRIPT_H_
|
||||
#define ELECTRON_SHELL_BROWSER_PRELOAD_SCRIPT_H_
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
#include "base/containers/fixed_flat_map.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/uuid.h"
|
||||
#include "gin/converter.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "v8/include/v8-forward.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -36,67 +33,19 @@ using electron::PreloadScript;
|
||||
template <>
|
||||
struct Converter<PreloadScript::ScriptType> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const PreloadScript::ScriptType& in) {
|
||||
using Val = PreloadScript::ScriptType;
|
||||
static constexpr auto Lookup =
|
||||
base::MakeFixedFlatMap<Val, std::string_view>({
|
||||
{Val::kWebFrame, "frame"},
|
||||
{Val::kServiceWorker, "service-worker"},
|
||||
});
|
||||
return StringToV8(isolate, Lookup.at(in));
|
||||
}
|
||||
|
||||
const PreloadScript::ScriptType& in);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
PreloadScript::ScriptType* out) {
|
||||
using Val = PreloadScript::ScriptType;
|
||||
static constexpr auto Lookup =
|
||||
base::MakeFixedFlatMap<std::string_view, Val>({
|
||||
{"frame", Val::kWebFrame},
|
||||
{"service-worker", Val::kServiceWorker},
|
||||
});
|
||||
return FromV8WithLookup(isolate, val, Lookup, out);
|
||||
}
|
||||
PreloadScript::ScriptType* out);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<PreloadScript> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const PreloadScript& script) {
|
||||
gin::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
dict.Set("filePath", script.file_path.AsUTF8Unsafe());
|
||||
dict.Set("id", script.id);
|
||||
dict.Set("type", script.script_type);
|
||||
return ConvertToV8(isolate, dict).As<v8::Object>();
|
||||
}
|
||||
|
||||
const PreloadScript& script);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
PreloadScript* out) {
|
||||
gin_helper::Dictionary options;
|
||||
if (!ConvertFromV8(isolate, val, &options))
|
||||
return false;
|
||||
if (PreloadScript::ScriptType script_type;
|
||||
options.Get("type", &script_type)) {
|
||||
out->script_type = script_type;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (base::FilePath file_path; options.Get("filePath", &file_path)) {
|
||||
out->file_path = file_path;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (std::string id; options.Get("id", &id)) {
|
||||
out->id = id;
|
||||
} else {
|
||||
out->id = base::Uuid::GenerateRandomV4().AsLowercaseString();
|
||||
}
|
||||
if (bool deprecated; options.Get("_deprecated", &deprecated)) {
|
||||
out->deprecated = deprecated;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
PreloadScript* out);
|
||||
};
|
||||
|
||||
} // namespace gin
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef ELECTRON_SHELL_BROWSER_UI_FILE_DIALOG_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_FILE_DIALOG_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -72,7 +73,8 @@ bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||
void ShowOpenDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise);
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path);
|
||||
std::optional<base::FilePath> ShowSaveDialogSync(
|
||||
const DialogSettings& settings);
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise);
|
||||
|
||||
@@ -233,20 +233,25 @@ void ShowOpenDialog(const DialogSettings& settings,
|
||||
dialog->RunOpenDialog(std::move(promise), settings);
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
|
||||
auto cb = base::BindOnce(
|
||||
[](base::RepeatingClosure cb, base::FilePath* file_path,
|
||||
gin_helper::Dictionary result) {
|
||||
result.Get("filePath", file_path);
|
||||
std::move(cb).Run();
|
||||
},
|
||||
run_loop.QuitClosure(), path);
|
||||
std::optional<base::FilePath> ShowSaveDialogSync(
|
||||
const DialogSettings& settings) {
|
||||
std::optional<base::FilePath> path;
|
||||
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunSaveDialog(std::move(cb), settings);
|
||||
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
|
||||
auto on_chooser_dialog_done = base::BindOnce(
|
||||
[](base::RepeatingClosure run_loop_closure,
|
||||
std::optional<base::FilePath>* path, gin_helper::Dictionary result) {
|
||||
if (base::FilePath val; result.Get("filePath", &val))
|
||||
*path = std::move(val);
|
||||
std::move(run_loop_closure).Run();
|
||||
},
|
||||
run_loop.QuitClosure(), &path);
|
||||
|
||||
auto* const dialog = new FileChooserDialog{};
|
||||
dialog->RunSaveDialog(std::move(on_chooser_dialog_done), settings);
|
||||
run_loop.Run();
|
||||
return !path->empty();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
|
||||
@@ -306,7 +306,7 @@ void ReadDialogPathsWithBookmarks(NSOpenPanel* dialog,
|
||||
BOOL is_package_as_directory =
|
||||
[[NSWorkspace sharedWorkspace] isFilePackageAtPath:path] &&
|
||||
[dialog treatsFilePackagesAsDirectories];
|
||||
if (!exists || !is_directory || !is_package_as_directory)
|
||||
if (!exists || !(is_directory || is_package_as_directory))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -421,19 +421,18 @@ void ShowOpenDialog(const DialogSettings& settings,
|
||||
}
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
DCHECK(path);
|
||||
std::optional<base::FilePath> ShowSaveDialogSync(
|
||||
const DialogSettings& settings) {
|
||||
NSSavePanel* dialog = [NSSavePanel savePanel];
|
||||
|
||||
SetupDialog(dialog, settings);
|
||||
SetupSaveDialogForProperties(dialog, settings.properties);
|
||||
|
||||
int chosen = RunModalDialog(dialog, settings);
|
||||
const int chosen = RunModalDialog(dialog, settings);
|
||||
if (chosen == NSModalResponseCancel || ![[dialog URL] isFileURL])
|
||||
return false;
|
||||
return {};
|
||||
|
||||
*path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path]));
|
||||
return true;
|
||||
return base::FilePath{base::SysNSStringToUTF8([[dialog URL] path])};
|
||||
}
|
||||
|
||||
void SaveDialogCompletion(int chosen,
|
||||
|
||||
@@ -233,11 +233,12 @@ void ShowOpenDialog(const DialogSettings& settings,
|
||||
base::BindOnce(done, std::move(promise)));
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
std::optional<base::FilePath> ShowSaveDialogSync(
|
||||
const DialogSettings& settings) {
|
||||
ATL::CComPtr<IFileSaveDialog> file_save_dialog;
|
||||
HRESULT hr = file_save_dialog.CoCreateInstance(CLSID_FileSaveDialog);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
return {};
|
||||
|
||||
DWORD options = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT;
|
||||
if (settings.properties & SAVE_DIALOG_SHOW_HIDDEN_FILES)
|
||||
@@ -250,32 +251,31 @@ bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
hr = ShowFileDialog(file_save_dialog, settings);
|
||||
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
return {};
|
||||
|
||||
CComPtr<IShellItem> pItem;
|
||||
hr = file_save_dialog->GetResult(&pItem);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
return {};
|
||||
|
||||
PWSTR result_path = nullptr;
|
||||
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &result_path);
|
||||
if (!SUCCEEDED(hr))
|
||||
return false;
|
||||
return {};
|
||||
|
||||
*path = base::FilePath(result_path);
|
||||
auto path = base::FilePath{result_path};
|
||||
CoTaskMemFree(result_path);
|
||||
|
||||
return true;
|
||||
return path;
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
auto done = [](gin_helper::Promise<gin_helper::Dictionary> promise,
|
||||
bool success, base::FilePath result) {
|
||||
std::optional<base::FilePath> result) {
|
||||
v8::HandleScope handle_scope(promise.isolate());
|
||||
auto dict = gin::Dictionary::CreateEmpty(promise.isolate());
|
||||
dict.Set("canceled", !success);
|
||||
dict.Set("filePath", result);
|
||||
dict.Set("canceled", !result.has_value());
|
||||
dict.Set("filePath", result.value_or(base::FilePath{}));
|
||||
promise.Resolve(dict);
|
||||
};
|
||||
dialog_thread::Run(base::BindOnce(ShowSaveDialogSync, settings),
|
||||
|
||||
@@ -529,6 +529,8 @@ void InspectableWebContents::CloseWindow() {
|
||||
}
|
||||
|
||||
void InspectableWebContents::LoadCompleted() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
frontend_loaded_ = true;
|
||||
if (managed_devtools_web_contents_)
|
||||
view_->ShowDevTools(activate_);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "base/containers/unique_ptr_adapters.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "chrome/browser/devtools/devtools_embedder_message_dispatcher.h"
|
||||
#include "content/public/browser/devtools_agent_host.h"
|
||||
#include "content/public/browser/devtools_frontend_host.h"
|
||||
@@ -265,6 +266,8 @@ class InspectableWebContents
|
||||
// use, which guarantees that this set must not be persisted.
|
||||
base::flat_set<std::string> synced_setting_names_;
|
||||
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
|
||||
base::WeakPtrFactory<InspectableWebContents> weak_factory_{this};
|
||||
};
|
||||
|
||||
|
||||
@@ -143,11 +143,33 @@ bool ElectronDesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) {
|
||||
void ElectronDesktopWindowTreeHostWin::HandleVisibilityChanged(bool visible) {
|
||||
if (native_window_view_->widget())
|
||||
native_window_view_->widget()->OnNativeWidgetVisibilityChanged(visible);
|
||||
|
||||
if (visible)
|
||||
UpdateAllowScreenshots();
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostWin::SetAllowScreenshots(bool allow) {
|
||||
::SetWindowDisplayAffinity(GetAcceleratedWidget(),
|
||||
allow ? WDA_NONE : WDA_EXCLUDEFROMCAPTURE);
|
||||
if (allow_screenshots_ == allow)
|
||||
return;
|
||||
|
||||
allow_screenshots_ = allow;
|
||||
|
||||
// If the window is not visible, do not set the window display affinity
|
||||
// because `SetWindowDisplayAffinity` will attempt to compose the window,
|
||||
if (!IsVisible())
|
||||
return;
|
||||
|
||||
UpdateAllowScreenshots();
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostWin::UpdateAllowScreenshots() {
|
||||
bool allowed = views::DesktopWindowTreeHostWin::AreScreenshotsAllowed();
|
||||
if (allowed == allow_screenshots_)
|
||||
return;
|
||||
|
||||
::SetWindowDisplayAffinity(
|
||||
GetAcceleratedWidget(),
|
||||
allow_screenshots_ ? WDA_NONE : WDA_EXCLUDEFROMCAPTURE);
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostWin::OnNativeThemeUpdated(
|
||||
|
||||
@@ -51,8 +51,11 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin,
|
||||
bool ShouldWindowContentsBeTransparent() const override;
|
||||
|
||||
private:
|
||||
void UpdateAllowScreenshots();
|
||||
|
||||
raw_ptr<NativeWindowViews> native_window_view_; // weak ref
|
||||
std::optional<bool> force_should_paint_as_active_;
|
||||
bool allow_screenshots_ = true;
|
||||
bool widget_init_done_ = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -359,6 +359,7 @@ bool IsAllowedOption(const std::string_view option) {
|
||||
"--throw-deprecation",
|
||||
"--trace-deprecation",
|
||||
"--trace-warnings",
|
||||
"--no-experimental-global-navigator",
|
||||
});
|
||||
|
||||
if (debug_options.contains(option))
|
||||
|
||||
@@ -123,6 +123,8 @@ inline constexpr std::string_view kRoundedCorners = "roundedCorners";
|
||||
|
||||
inline constexpr std::string_view ktitleBarOverlay = "titleBarOverlay";
|
||||
|
||||
inline constexpr std::string_view kAccentColor = "accentColor";
|
||||
|
||||
// The color to use as the theme and symbol colors respectively for Window
|
||||
// Controls Overlay if enabled on Windows.
|
||||
inline constexpr std::string_view kOverlayButtonColor = "color";
|
||||
|
||||
@@ -42,6 +42,16 @@ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () {
|
||||
return cp.spawn(path.resolve(appPath, 'Contents/MacOS/Electron'), args);
|
||||
};
|
||||
|
||||
const launchAppSandboxed = (appPath: string, profilePath: string, args: string[] = []) => {
|
||||
return spawn('/usr/bin/sandbox-exec', [
|
||||
'-f',
|
||||
profilePath,
|
||||
path.resolve(appPath, 'Contents/MacOS/Electron'),
|
||||
...args,
|
||||
'--no-sandbox'
|
||||
]);
|
||||
};
|
||||
|
||||
const getRunningShipIts = async (appPath: string) => {
|
||||
const processes = await psList();
|
||||
const activeShipIts = processes.filter(p => p.cmd?.includes('Squirrel.framework/Resources/ShipIt com.github.Electron.ShipIt') && p.cmd!.startsWith(appPath));
|
||||
@@ -740,6 +750,41 @@ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should hit the download endpoint when an update is available and fail when the zip extraction process fails to launch', async () => {
|
||||
await withUpdatableApp({
|
||||
nextVersion: '2.0.0',
|
||||
startFixture: 'update',
|
||||
endFixture: 'update'
|
||||
}, async (appPath, updateZipPath) => {
|
||||
server.get('/update-file', (req, res) => {
|
||||
res.download(updateZipPath);
|
||||
});
|
||||
server.get('/update-check', (req, res) => {
|
||||
res.json({
|
||||
url: `http://localhost:${port}/update-file`,
|
||||
name: 'My Release Name',
|
||||
notes: 'Theses are some release notes innit',
|
||||
pub_date: (new Date()).toString()
|
||||
});
|
||||
});
|
||||
const launchResult = await launchAppSandboxed(
|
||||
appPath,
|
||||
path.resolve(__dirname, 'fixtures/auto-update/sandbox/block-ditto.sb'),
|
||||
[`http://localhost:${port}/update-check`]
|
||||
);
|
||||
logOnError(launchResult, () => {
|
||||
expect(launchResult).to.have.property('code', 1);
|
||||
expect(launchResult.out).to.include('Starting ditto task failed with error:');
|
||||
expect(launchResult.out).to.include('SQRLZipArchiverErrorDomain');
|
||||
expect(requests).to.have.lengthOf(2);
|
||||
expect(requests[0]).to.have.property('url', '/update-check');
|
||||
expect(requests[1]).to.have.property('url', '/update-file');
|
||||
expect(requests[0].header('user-agent')).to.include('Electron/');
|
||||
expect(requests[1].header('user-agent')).to.include('Electron/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should hit the download endpoint when an update is available and update successfully when the zip is provided with JSON update mode', async () => {
|
||||
await withUpdatableApp({
|
||||
nextVersion: '2.0.0',
|
||||
|
||||
@@ -780,5 +780,33 @@ describe('utilityProcess module', () => {
|
||||
expect(stat.size).to.be.greaterThan(0);
|
||||
await fs.rm(tmpDir, { recursive: true });
|
||||
});
|
||||
|
||||
it('supports --no-experimental-global-navigator flag', async () => {
|
||||
{
|
||||
const child = utilityProcess.fork(path.join(fixturesPath, 'navigator.js'), [], {
|
||||
stdio: 'ignore'
|
||||
});
|
||||
await once(child, 'spawn');
|
||||
const [data] = await once(child, 'message');
|
||||
expect(data).to.be.true();
|
||||
const exit = once(child, 'exit');
|
||||
expect(child.kill()).to.be.true();
|
||||
await exit;
|
||||
}
|
||||
{
|
||||
const child = utilityProcess.fork(path.join(fixturesPath, 'navigator.js'), [], {
|
||||
stdio: 'ignore',
|
||||
execArgv: [
|
||||
'--no-experimental-global-navigator'
|
||||
]
|
||||
});
|
||||
await once(child, 'spawn');
|
||||
const [data] = await once(child, 'message');
|
||||
expect(data).to.be.false();
|
||||
const exit = once(child, 'exit');
|
||||
expect(child.kill()).to.be.true();
|
||||
await exit;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,6 +55,20 @@ describe('WebContentsView', () => {
|
||||
})).to.throw('options.webContents is already attached to a window');
|
||||
});
|
||||
|
||||
it('should throw an error when adding a destroyed child view to the parent view', async () => {
|
||||
const browserWindow = new BrowserWindow();
|
||||
|
||||
const webContentsView = new WebContentsView();
|
||||
webContentsView.webContents.loadURL('about:blank');
|
||||
webContentsView.webContents.destroy();
|
||||
|
||||
const destroyed = once(webContentsView.webContents, 'destroyed');
|
||||
await destroyed;
|
||||
expect(() => browserWindow.contentView.addChildView(webContentsView)).to.throw(
|
||||
'Can\'t add a destroyed child view to a parent view'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when created with already attached webContents to other WebContentsView', () => {
|
||||
const browserWindow = new BrowserWindow();
|
||||
|
||||
|
||||
1
spec/fixtures/api/utility-process/navigator.js
vendored
Normal file
1
spec/fixtures/api/utility-process/navigator.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
process.parentPort.postMessage(typeof navigator === 'object');
|
||||
5
spec/fixtures/auto-update/sandbox/block-ditto.sb
vendored
Normal file
5
spec/fixtures/auto-update/sandbox/block-ditto.sb
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
(version 1)
|
||||
(allow default)
|
||||
(deny process-exec
|
||||
(literal "/usr/bin/ditto")
|
||||
)
|
||||
Reference in New Issue
Block a user