Compare commits

..

34 Commits

Author SHA1 Message Date
trop[bot]
a8562b0beb feat: support customizing window accent color on Windows (#47539)
* fix: support window accent color in frameless windows

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* refactor: allow customization

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* Update docs/api/structures/base-window-options.md

Co-authored-by: Will Anderson <andersonw@dropbox.com>

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-06-25 22:51:20 +02:00
trop[bot]
dbf9f04085 docs: Add C++/Linux tutorial (#47551)
* docs: Add C++/Linux tutorial

Co-authored-by: Felix Rieseberg <fr@makenotion.com>

* Update docs/tutorial/native-code-and-electron-cpp-linux.md

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

Co-authored-by: Felix Rieseberg <fr@makenotion.com>

* Apply suggestions from code review

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>

Co-authored-by: Felix Rieseberg <felix@felixrieseberg.com>

* Apply suggestions from code review

Co-authored-by: Erick Zhao <erick@hotmail.ca>

Co-authored-by: Felix Rieseberg <felix@felixrieseberg.com>

* Apply suggestions from code review

Co-authored-by: Erick Zhao <erick@hotmail.ca>

Co-authored-by: Felix Rieseberg <felix@felixrieseberg.com>

* Implement more feedback, lint

Co-authored-by: Felix Rieseberg <felix@felixrieseberg.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Felix Rieseberg <fr@makenotion.com>
Co-authored-by: Felix Rieseberg <felix@felixrieseberg.com>
2025-06-25 10:04:33 -04:00
trop[bot]
12be909cc0 fix: ensure /dev/null fd is closed on failure (#47542)
* fix: ensure /dev/null fd is closed on failure

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* chore: ignore closehandle for windows

Co-authored-by: Robo <hop2deep@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: Robo <hop2deep@gmail.com>
2025-06-24 21:12:25 +02:00
trop[bot]
c5c94822ce build: rewrite push-patch to use the github API instead of local git commits to ensure commits are signed (#47533)
* build: rewrite push-patch to use the github API instead of local git commits to ensure commits are signed

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>

* again

(cherry picked from commit a21afc3e45)

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>

* use pr head ref

(cherry picked from commit 0edcc985fa)

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
2025-06-24 11:55:08 +02:00
trop[bot]
7efd6ff76e refactor: simplify titlebar overlay initialization (#47522)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2025-06-23 21:56:55 +02:00
trop[bot]
382e2740f5 build: update cache action to latest (#47519)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-06-23 14:38:01 +02:00
trop[bot]
9c5562d290 feat: add support for --no-experimental-global-navigator (#47416)
chore: add support for --no-experimental-global-navigator

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2025-06-23 12:21:42 +02:00
trop[bot]
7807ac893c refactor: move gin::Converter<PreloadScript> impl to a .cc file (#47465)
refactor: move gin::Converter<PreloadScript> impl to a .cc file

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2025-06-16 10:21:39 -04:00
trop[bot]
e6666f811e refactor: have ShowSaveDialogSync() return a std::optional<base::FilePath> (#47451)
* refactor: have ShowSaveDialogSync() return a std::optional<base::FilePath>

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* fixup! refactor: have ShowSaveDialogSync() return a std::optional<base::FilePath>

Co-authored-by: Charles Kerr <charles@charleskerr.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2025-06-12 10:34:04 -05:00
trop[bot]
ff5b9a6680 fix: crash calling Fetch.continueResponse with WebContentsView (#47443)
fix: crash calling Fetch.continueResponse with WebContentsView

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-06-11 20:19:39 +02:00
trop[bot]
eb6c475b5f build: cache gitcache dir (#47407)
build: cache gitcache dir (#47328)

* revert build: migrate to new chromium git auth

* build: cache gitcache dir

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-06-09 19:13:27 -04:00
trop[bot]
ded238107f fix: printing PDF via webContents.print() (#47399)
fix: printing PDF via webContents.print()

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-06-08 15:19:09 +02:00
trop[bot]
2d2e62feda fix: rework lifetime mgmt of ClearDataTask/ClearDataOperation (#47410)
* fix: rework lifetime mgmt of ClearDataTask/ClearDataOperation

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* Update shell/browser/api/electron_api_session.cc

Co-authored-by: Robo <hop2deep@gmail.com>

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* Update shell/browser/api/electron_api_session.cc

Co-authored-by: Robo <hop2deep@gmail.com>

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* Update shell/browser/api/electron_api_session.cc

Co-authored-by: Robo <hop2deep@gmail.com>

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* Update shell/browser/api/electron_api_session.cc

Co-authored-by: Robo <hop2deep@gmail.com>

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-06-08 12:46:35 +02:00
trop[bot]
64dfeb8a4d fix: do not load source for electron module in ESM loader synchronous flow (#47344)
* fix: do not load electron from ESM loader's CJS compatibility

Co-authored-by: clavin <clavin@electronjs.org>

* chore: update patches

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: clavin <clavin@electronjs.org>
2025-06-05 10:04:15 +02:00
Keeley Hammond
8ca091251f chore: cherry-pick 2 changes from 1-M137 (#47369)
* chore: cherry-pick 45eb42cd398e from v8

* chore: cherry-pick f1e6422a355c from chromium

* chore: update patches
2025-06-05 10:00:38 +02:00
Keeley Hammond
6b08d83af7 chore: cherry-pick 1 changes from 1-M137 (#47354)
* chore: [35-x-y] cherry-pick 1 changes from 1-M137

* 7bc0a67ebfbf from v8

* chore: run `e patches all` (#47360)

`e patches all`

---------

Co-authored-by: Erick Zhao <ezhao@slack-corp.com>
2025-06-04 02:21:30 +02:00
trop[bot]
71233a4517 fix: addChildView() crashes when adding a closed WebContentsView (#47339)
fix: addChildView() crashes when add a closed WebContentsView

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sida Zhu <zhusida@bytedance.com>
2025-06-03 15:29:36 +02:00
trop[bot]
f370a19e36 docs: correct 'select-bluetooth-device' requirement (#47335)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-06-03 10:54:06 +02:00
trop[bot]
a7fd8872bf fix: Squirrel.Mac crash when zip extraction fails (#47299)
* fix: Squirrel.Mac crash when zip extraction process fails to launch

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

* chore: add end-to-end test

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Niklas Wenzel <dev@nikwen.de>
2025-05-30 10:47:11 -04:00
trop[bot]
7f5e6c54bc ci: add a problem matcher for ESLint output (#47306)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2025-05-30 11:02:04 +02:00
Robo
fadec9ac65 fix: crash due to incorrect debug scope information (#47286)
* fix: crash due to incorrect debug scope information

* chore: update patches

---------

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-05-29 16:52:43 +09:00
David Sanders
a8cdd60c88 chore: update @electron/lint-roller to 3.1.1 (#47273) 2025-05-27 16:59:13 -04:00
trop[bot]
6280172ee9 build: migrate to new chromium git auth (#47253)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-05-27 12:50:32 -04:00
trop[bot]
94f6e16871 fix: regression with directory selection in macOS dialogs (#47276)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2025-05-27 11:18:36 -04:00
trop[bot]
5fde5696d0 fix: titlebar showing in content protected window (#47265)
Closes https://github.com/electron/electron/issues/47152.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-05-27 14:24:45 +02:00
trop[bot]
73605f97ee chore: debug crash on DevTools SetOwnerWindow (#47261)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-05-27 14:24:09 +02:00
Charles Kerr
72d3d359c3 fix: backgroundMaterial on initial activate (35-x-y) (#47236)
fix: backgroundMaterial on initial activate

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-05-23 21:37:54 -05:00
trop[bot]
0b026d261e refactor: use base::fixed_flat_set in NativeWindowViews::SetAlwaysOnTop() (#47238)
refactor: use base::fixed_flat_set in NativeWindowViews::SetAlwaysOnTop()

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2025-05-23 12:58:32 -05:00
Charles Kerr
0954ac7843 fix: possible crash in shell.readShortcutLink (35-x-y) (#47226)
fix: possible crash in shell.readShortcutLink

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-05-23 12:45:56 +02:00
Charles Kerr
feab959781 refactor: make NativeWindow::pending_transitions_ a base::queue (35-x-y) (#47235)
refactor: make `NativeWindow::pending_transitions_` a `base::queue` (#47157)

refactor: make NativeWindow::pending_transitions a base::queue

Follow the base/containers/README.md advice that "Chromium code should
always use `base::circular_deque` or `base::queue` in preference to
`std::deque` or `std::queue` due to memory usage and platform variation."
2025-05-23 09:53:19 +02:00
John Kleinschmidt
282903e7b8 build: fix depot tool pathing on Windows (#47194) (#47227)
build: properly set depot_tools pathing for Windows
(cherry picked from commit b2d0074cc6)
2025-05-22 11:57:23 -07:00
trop[bot]
15d6344b6a ci: add problem matcher for patch conflict output (#47223)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2025-05-22 11:54:35 -07:00
trop[bot]
fcb576566a ci: add problem matcher for clang output (#47218)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2025-05-22 11:53:12 -07:00
trop[bot]
64a07ffc3f fix: remove extra 'suspend'/'resume' handling from powerMonitor (#47190)
fix: remove extra 'suspend'/'resume' handling from powerMonitor

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-05-22 13:31:19 -05:00
75 changed files with 3548 additions and 945 deletions

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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 }}

View File

@@ -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
View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View File

@@ -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
View 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

View File

@@ -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:

View File

@@ -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: |

View File

@@ -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

View File

@@ -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: |

View File

@@ -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"
]
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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",

View File

@@ -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

View File

@@ -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];

View 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;

View File

@@ -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,

View File

@@ -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();
}

View File

@@ -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,

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View 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);

View 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;
}

View File

@@ -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();
+})();

View 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());

View File

@@ -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
View 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);
});
}

View File

@@ -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);
});
}

View File

@@ -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);

View File

@@ -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(

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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(

View File

@@ -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,

View File

@@ -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)

View File

@@ -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;

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,

View File

@@ -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),

View File

@@ -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_);

View File

@@ -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};
};

View File

@@ -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(

View File

@@ -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;
};

View File

@@ -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))

View File

@@ -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";

View File

@@ -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',

View File

@@ -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;
}
});
});
});

View File

@@ -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();

View File

@@ -0,0 +1 @@
process.parentPort.postMessage(typeof navigator === 'object');

View File

@@ -0,0 +1,5 @@
(version 1)
(allow default)
(deny process-exec
(literal "/usr/bin/ditto")
)

768
yarn.lock

File diff suppressed because it is too large Load Diff