Compare commits

..

28 Commits

Author SHA1 Message Date
trop[bot]
4d18062d0f ci: disallow non-maintainer changes to Yarn files (#49192)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2025-12-11 17:14:14 -05:00
electron-roller[bot]
832ffb2330 chore: bump chromium to 142.0.7444.235 (39-x-y) (#49190)
chore: bump chromium in DEPS to 142.0.7444.235

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2025-12-11 15:25:12 -05:00
trop[bot]
03121eeaef build: upgrade yarn to 4.12.0 (#49179)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2025-12-10 19:20:43 -08:00
trop[bot]
8282c07a0f build: upgrade github-app-auth to 3.2.0 (#49177)
build: upgrade github-app-auth to 3.2.0 (#49152)

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-12-10 14:20:43 -05:00
Niklas Wenzel
f2d1cb21b0 fix: visual artifacts while resizing on Windows (#49138)
Manual backports of:

- crrev.com/c/7129658
- crrev.com/c/7210913
- crrev.com/c/7115438
2025-12-04 16:42:24 -08:00
electron-roller[bot]
ef9b4162af chore: bump chromium to 142.0.7444.226 (39-x-y) (#49137)
* chore: bump chromium in DEPS to 142.0.7444.226

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Niklas Wenzel <dev@nikwen.de>
2025-12-03 15:50:16 -05:00
trop[bot]
6e97bca80d fix: run toast creation on background thread (#49130)
* fix: run toast creation on background thread

notes: attempts to fix app freeze when triggering notifications and the COM server in WindowsShellExperienceHost hangs

Co-authored-by: Jan Hannemann <jan.hannemann@outlook.com>

* fix: comments

Co-authored-by: Jan Hannemann <jan.hannemann@outlook.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jan Hannemann <jan.hannemann@outlook.com>
2025-12-02 20:41:24 -08:00
trop[bot]
c511fc5c3f chore: reclaim macOS disk space (#49121)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-12-01 15:30:07 -08:00
trop[bot]
22dfbb0822 ci: use clang problem matcher with nan spec runner (#49099)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2025-12-01 09:45:35 -05:00
trop[bot]
85913a38da fix: ensure menu-did-close is emitted for application menus (#49093)
fix: ensure menu-did-close is emitted for application menus

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-11-26 11:47:13 -06:00
trop[bot]
a327629ca2 ci: don't build yarn modules for linux arm (#49088)
This should fix the oom errors

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-11-26 10:32:30 -05:00
Niklas Wenzel
7deed2b980 fix: reduce visual artifacts while resizing on Windows (#49076) 2025-11-26 10:23:52 +01:00
trop[bot]
65fc06a9f7 chore: backport 744f40f from devtools-frontend (#49044)
* chore: backport 744f40f from devtools-frontend

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

* chore: update patch

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-11-25 12:07:04 -06:00
John Kleinschmidt
245e70aedd test: fixup test failures on linux (#49059)
* test: fixup spec runner to properly fail on linux when tests fail

* test: fixup dbus tests

* test: disable context menu spellcheck tests on linux

https://github.com/electron/electron/pull/48657 broke those tests
(cherry picked from commit cc3c999148)

* test:rebuild native modules

(cherry picked from commit bb8e2a924b)

* fix: wait for devtools blur event in focus test to avoid race condition

(cherry picked from commit 6fd2575cbc)

* fix: wait for devtools blur event in focus test to avoid race condition

(cherry picked from commit ea830139af)

---------

Co-authored-by: Alice Zhao <alicelovescake@anthropic.com>
2025-11-24 17:02:14 -05:00
trop[bot]
2a8164f499 fix: exception when reading system certificates via nodejs (#49042)
* fix: exception when reading system certificates via nodejs

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

* fixup! fix: exception when reading system certificates via nodejs

chore: fix trop patch shear

* chore: update patches

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2025-11-24 14:47:53 -06:00
trop[bot]
2f7024dbcc docs: update linux build instructions (#49060)
* docs: update linux build instructions

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

* Update docs/development/build-instructions-linux.md

Co-authored-by: Erick Zhao <ezhao@slack-corp.com>

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

* Update docs/development/build-instructions-linux.md

Co-authored-by: Erick Zhao <ezhao@slack-corp.com>

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-11-24 13:50:33 -06:00
trop[bot]
d53d3bb99e docs: explain how to create transparent window using BaseWindow (#49051)
docs: clarify how to create transparent BaseWindow

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: teoyuqi <83915879+teoyuqi@users.noreply.github.com>
2025-11-24 09:41:48 +01:00
trop[bot]
c2c1d40294 fix: only call popup closecallback for top-level menu (#49045)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-11-23 21:35:47 +01:00
electron-roller[bot]
0e9decd459 chore: bump chromium to 142.0.7444.177 (39-x-y) (#49037)
chore: bump chromium in DEPS to 142.0.7444.177

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2025-11-21 10:21:05 -05:00
John Kleinschmidt
b2e73d28e2 build: update to yarn v4 (#48994)
* build: update to yarn v4

(cherry picked from commit 6adec744f3)

* chore: fixup types after yarn v4 migration

* chore: update nan yarn.lock patch

* build: automatically install git for dugite
2025-11-19 17:32:30 -05:00
trop[bot]
aeb5af803f fix: abort more descriptively for beforeunload (#49011)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-11-19 17:00:51 -05:00
trop[bot]
53819a8a2a fix: revert the parent window remained interactive after the modal window was opened (#49019)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: bill.shen <15865969+cucbin@users.noreply.github.com>
2025-11-19 16:56:33 -05:00
electron-roller[bot]
14565211f7 chore: bump chromium to 142.0.7444.175 (39-x-y) (#49003)
* chore: bump chromium in DEPS to 142.0.7444.175

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-11-18 11:25:22 -08:00
trop[bot]
00646c9db6 fix: handle empty event scenario in ipc callbacks (#48992)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2025-11-18 09:17:20 -05:00
trop[bot]
d9c33a951a build: add header for SetStackDumpFirstChanceCallback in renderer client (#48980)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
2025-11-15 10:04:28 -08:00
trop[bot]
8b02e33187 build: limit workflow gh token permissions (#48969)
* build: limit workflow gh token permissions

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

* feedback

Co-authored-by: Samuel Attard <sattard@anthropic.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 <sattard@anthropic.com>
2025-11-15 11:32:21 +01:00
trop[bot]
eecca2cb19 fix: revert enabling WASM trap handlers in all Node.js processes (#48975)
Revert "fix: enable wasm trap handlers in all Node.js processes (#48788)"

This reverts commit ca0b46b413.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2025-11-14 18:56:50 -08:00
trop[bot]
08b5ef556c test: add view.getBounds|setBounds tests (#48961)
test: add view.getBounds|setBounds tests

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-11-14 12:12:22 -05:00
103 changed files with 19552 additions and 11866 deletions

View File

@@ -184,7 +184,7 @@ runs:
shell: bash
run: |
cd src/electron
node script/yarn create-typescript-definitions
node script/yarn.js create-typescript-definitions
- name: Publish Electron Dist ${{ inputs.step-suffix }}
if: ${{ inputs.is-release == 'true' }}
shell: bash

View File

@@ -152,7 +152,7 @@ runs:
if: ${{ inputs.target-platform == 'linux' && github.ref == 'refs/heads/main' }}
shell: bash
run: |
npx node src/electron/script/patches-stats.mjs --upload-stats || true
node src/electron/script/patches-stats.mjs --upload-stats || true
# 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

@@ -64,15 +64,24 @@ runs:
sudo rm -rf /Applications/Xcode_16.1.app
sudo rm -rf /Applications/Xcode_16.2.app
sudo rm -rf /Applications/Xcode_16.3.app
sudo rm -rf /Applications/Xcode_26*
sudo rm -rf /Applications/Google Chrome.app
sudo rm -rf /Applications/Google Chrome for Testing.app
sudo rm -rf /Applications/Firefox.app
sudo rm -rf /Applications/Firefox.app
sudo rm -rf /Applications/Microsoft Edge.app
sudo rm -rf ~/project/src/third_party/catapult/tracing/test_data
sudo rm -rf ~/project/src/third_party/angle/third_party/VK-GL-CTS
sudo rm -rf /Users/runner/Library/Android
sudo rm -rf $JAVA_HOME_11_arm64
sudo rm -rf $JAVA_HOME_17_arm64
sudo rm -rf $JAVA_HOME_21_arm64
sudo rm -rf $JAVA_HOME_25_arm64
sudo rm -rf /Users/runner/.dotnet/
sudo rm -rf /Users/runner/.rustup
# remove homebrew packages we don't need
brew uninstall -f --zap aws-sam-cli session-manager-plugin gcc gcc@13 gcc@14 llvm@18 gradle maven ant azure-cli
brew autoremove
# lipo off some huge binaries arm64 versions to save space
strip_universal_deep $(xcode-select -p)/../SharedFrameworks

View File

@@ -13,12 +13,16 @@ runs:
- name: Generating Types for SHA in ${{ inputs.sha-file }}
shell: bash
run: |
git checkout $(cat ${{ inputs.sha-file }})
rm -rf node_modules
yarn install --frozen-lockfile --ignore-scripts
export ELECTRON_DIR=$(pwd)
if [ "${{ inputs.sha-file }}" == ".dig-old" ]; then
cd /tmp
git clone https://github.com/electron/electron.git
cd electron
fi
git checkout $(cat $ELECTRON_DIR/${{ inputs.sha-file }})
node script/yarn.js install --immutable
echo "#!/usr/bin/env node\nglobal.x=1" > node_modules/typescript/bin/tsc
node node_modules/.bin/electron-docs-parser --dir=./ --outDir=./ --moduleVersion=0.0.0-development
node node_modules/.bin/electron-typescript-definitions --api=electron-api.json --outDir=artifacts
mv artifacts/electron.d.ts artifacts/${{ inputs.filename }}
git checkout .
mv artifacts/electron.d.ts $ELECTRON_DIR/artifacts/${{ inputs.filename }}
working-directory: ./electron

View File

@@ -6,7 +6,7 @@ runs:
- name: Get yarn cache directory path
shell: bash
id: yarn-cache-dir-path
run: echo "dir=$(node src/electron/script/yarn cache dir)" >> $GITHUB_OUTPUT
run: echo "dir=$(node src/electron/script/yarn.js config get cacheFolder)" >> $GITHUB_OUTPUT
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
id: yarn-cache
with:
@@ -18,4 +18,14 @@ runs:
shell: bash
run: |
cd src/electron
node script/yarn install --frozen-lockfile --prefer-offline
if [ "$TARGET_ARCH" = "x86" ]; then
export npm_config_arch="ia32"
fi
# if running on linux arm skip yarn Builds
ARCH=$(uname -m)
if [ "$ARCH" = "armv7l" ]; then
echo "Skipping yarn build on linux arm"
node script/yarn.js install --immutable --mode=skip-build
else
node script/yarn.js install --immutable
fi

View File

@@ -3,10 +3,14 @@ name: Archaeologist
on:
pull_request:
permissions: {}
jobs:
archaeologist-dig:
name: Archaeologist Dig
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Electron
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.0.2

View File

@@ -6,9 +6,13 @@ on:
schedule:
- cron: "0 0 * * *"
permissions: {}
jobs:
build-git-cache-linux:
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
options: --user root
@@ -30,6 +34,8 @@ jobs:
build-git-cache-windows:
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
@@ -52,6 +58,8 @@ jobs:
build-git-cache-macos:
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
# This job updates the same git cache as linux, so it needs to run after the linux one.
needs: build-git-cache-linux
container:

View File

@@ -43,10 +43,13 @@ defaults:
run:
shell: bash
permissions: {}
jobs:
setup:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
docs: ${{ steps.filter.outputs.docs }}
@@ -84,6 +87,8 @@ jobs:
needs: setup
if: ${{ !inputs.skip-lint }}
uses: ./.github/workflows/pipeline-electron-lint.yml
permissions:
contents: read
with:
container: '{"image":"ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}","options":"--user root"}'
secrets: inherit
@@ -93,6 +98,8 @@ jobs:
needs: [setup, checkout-linux]
if: ${{ needs.setup.outputs.docs-only == 'true' }}
uses: ./.github/workflows/pipeline-electron-docs-only.yml
permissions:
contents: read
with:
container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
secrets: inherit
@@ -102,6 +109,8 @@ jobs:
needs: setup
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-macos}}
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
options: --user root
@@ -130,6 +139,8 @@ jobs:
needs: setup
if: ${{ !inputs.skip-linux}}
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
options: --user root
@@ -159,6 +170,8 @@ jobs:
needs: setup
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
@@ -189,6 +202,8 @@ jobs:
# GN Check Jobs
macos-gn-check:
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
permissions:
contents: read
needs: checkout-macos
with:
target-platform: macos
@@ -199,6 +214,8 @@ jobs:
linux-gn-check:
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
permissions:
contents: read
needs: checkout-linux
if: ${{ needs.setup.outputs.src == 'true' }}
with:
@@ -211,6 +228,8 @@ jobs:
windows-gn-check:
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
permissions:
contents: read
needs: checkout-windows
with:
target-platform: win
@@ -314,7 +333,7 @@ jobs:
build-runs-on: electron-arc-centralus-linux-amd64-32core
test-runs-on: electron-arc-centralus-linux-arm64-4core
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init","volumes":["/home/runner/externals:/mnt/runner-externals"]}'
test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init --memory=12g","volumes":["/home/runner/externals:/mnt/runner-externals"]}'
target-platform: linux
target-arch: arm
is-release: false
@@ -404,6 +423,8 @@ jobs:
gha-done:
name: GitHub Actions Completed
runs-on: ubuntu-latest
permissions:
contents: read
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, windows-x64, windows-x86, windows-arm64]
if: always() && !contains(needs.*.result, 'failure')
steps:

View File

@@ -1,16 +1,20 @@
name: Clean Source Cache
description: |
This workflow cleans up the source cache on the cross-instance cache volume
to free up space. It runs daily at midnight and clears files older than 15 days.
# Description:
# This workflow cleans up the source cache on the cross-instance cache volume
# to free up space. It runs daily at midnight and clears files older than 15 days.
on:
schedule:
- cron: "0 0 * * *"
permissions: {}
jobs:
clean-src-cache:
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
options: --user root

View File

@@ -4,14 +4,15 @@ on:
issues:
types: [labeled]
permissions: # added using https://github.com/step-security/secure-workflows
contents: read
permissions: {}
jobs:
issue-labeled-with-status:
name: status/{confirmed,reviewed} label added
if: github.event.label.name == 'status/confirmed' || github.event.label.name == 'status/reviewed'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
@@ -31,6 +32,8 @@ jobs:
name: blocked/* label added
if: startsWith(github.event.label.name, 'blocked/')
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1

View File

@@ -11,6 +11,7 @@ jobs:
add-to-issue-triage:
if: ${{ contains(github.event.issue.labels.*.name, 'bug :beetle:') }}
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
@@ -28,6 +29,7 @@ jobs:
set-labels:
if: ${{ contains(github.event.issue.labels.*.name, 'bug :beetle:') }}
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1

View File

@@ -10,6 +10,7 @@ jobs:
issue-transferred:
name: Issue Transferred
runs-on: ubuntu-latest
permissions: {}
if: ${{ !github.event.changes.new_repository.private }}
steps:
- name: Generate GitHub App token

View File

@@ -4,14 +4,15 @@ on:
issues:
types: [unlabeled]
permissions:
contents: read
permissions: {}
jobs:
issue-unlabeled-blocked:
name: All blocked/* labels removed
if: startsWith(github.event.label.name, 'blocked/') && github.event.issue.state == 'open'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Check for any blocked labels
id: check-for-blocked-labels

View File

@@ -17,9 +17,13 @@ on:
type: boolean
default: false
permissions: {}
jobs:
checkout-linux:
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ inputs.build-image-sha }}
options: --user root
@@ -40,6 +44,8 @@ jobs:
publish-x64:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-linux
with:
environment: production-release
@@ -55,6 +61,8 @@ jobs:
publish-arm:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-linux
with:
environment: production-release
@@ -70,6 +78,8 @@ jobs:
publish-arm64:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-linux
with:
environment: production-release

View File

@@ -18,9 +18,13 @@ on:
type: boolean
default: false
permissions: {}
jobs:
checkout-macos:
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ inputs.build-image-sha }}
options: --user root
@@ -44,6 +48,8 @@ jobs:
publish-x64-darwin:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-macos
with:
environment: production-release
@@ -59,6 +65,8 @@ jobs:
publish-x64-mas:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-macos
with:
environment: production-release
@@ -74,6 +82,8 @@ jobs:
publish-arm64-darwin:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-macos
with:
environment: production-release
@@ -89,6 +99,8 @@ jobs:
publish-arm64-mas:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-macos
with:
environment: production-release

View File

@@ -7,6 +7,8 @@ on:
- 'spec/yarn.lock'
- '.github/workflows/**'
- '.github/actions/**'
- '.yarn/**'
- '.yarnrc.yml'
permissions: {}

View File

@@ -55,6 +55,8 @@ on:
type: boolean
default: false
permissions: {}
concurrency:
group: electron-build-and-test-and-nan-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ github.ref_protected == true && github.run_id || github.ref }}
cancel-in-progress: ${{ github.ref_protected != true }}
@@ -62,6 +64,8 @@ concurrency:
jobs:
build:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
with:
build-runs-on: ${{ inputs.build-runs-on }}
build-container: ${{ inputs.build-container }}
@@ -74,6 +78,10 @@ jobs:
secrets: inherit
test:
uses: ./.github/workflows/pipeline-segment-electron-test.yml
permissions:
contents: read
issues: read
pull-requests: read
needs: build
with:
target-arch: ${{ inputs.target-arch }}
@@ -83,6 +91,8 @@ jobs:
secrets: inherit
nn-test:
uses: ./.github/workflows/pipeline-segment-node-nan-test.yml
permissions:
contents: read
needs: build
with:
target-arch: ${{ inputs.target-arch }}

View File

@@ -64,14 +64,13 @@ concurrency:
group: electron-build-and-test-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ github.ref_protected == true && github.run_id || github.ref }}
cancel-in-progress: ${{ github.ref_protected != true }}
permissions:
contents: read
issues: read
pull-requests: read
permissions: {}
jobs:
build:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
with:
build-runs-on: ${{ inputs.build-runs-on }}
build-container: ${{ inputs.build-container }}
@@ -86,6 +85,10 @@ jobs:
secrets: inherit
test:
uses: ./.github/workflows/pipeline-segment-electron-test.yml
permissions:
contents: read
issues: read
pull-requests: read
needs: build
with:
target-arch: ${{ inputs.target-arch }}

View File

@@ -8,6 +8,8 @@ on:
description: 'Container to run the docs-only ts compile in'
type: string
permissions: {}
concurrency:
group: electron-docs-only-${{ github.ref }}
cancel-in-progress: true
@@ -19,6 +21,8 @@ jobs:
docs-only:
name: Docs Only Compile
runs-on: electron-arc-centralus-linux-amd64-4core
permissions:
contents: read
timeout-minutes: 20
container: ${{ fromJSON(inputs.container) }}
steps:
@@ -50,12 +54,12 @@ jobs:
shell: bash
run: |
cd src/electron
node script/yarn create-typescript-definitions
node script/yarn tsc -p tsconfig.default_app.json --noEmit
node script/yarn.js create-typescript-definitions
node script/yarn.js tsc -p tsconfig.default_app.json --noEmit
for f in build/webpack/*.js
do
out="${f:29}"
if [ "$out" != "base.js" ]; then
node script/yarn webpack --config $f --output-filename=$out --output-path=./.tmp --env mode=development
node script/yarn.js webpack --config $f --output-filename=$out --output-path=./.tmp --env mode=development
fi
done

View File

@@ -8,6 +8,8 @@ on:
description: 'Container to run lint in'
type: string
permissions: {}
concurrency:
group: electron-lint-${{ github.ref_protected == true && github.run_id || github.ref }}
cancel-in-progress: ${{ github.ref_protected != true }}
@@ -19,6 +21,8 @@ jobs:
lint:
name: Lint
runs-on: electron-arc-centralus-linux-amd64-4core
permissions:
contents: read
timeout-minutes: 20
container: ${{ fromJSON(inputs.container) }}
steps:
@@ -74,11 +78,11 @@ jobs:
# but then we would lint its contents (at least gn format), and it doesn't pass it.
cd src/electron
node script/yarn install --frozen-lockfile
node script/yarn lint
node script/yarn.js install --immutable
node script/yarn.js lint
- name: Run Script Typechecker
shell: bash
run: |
cd src/electron
node script/yarn tsc -p tsconfig.script.json
node script/yarn.js tsc -p tsconfig.script.json

View File

@@ -59,6 +59,8 @@ on:
type: boolean
default: false
permissions: {}
concurrency:
group: electron-build-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ inputs.target-variant }}-${{ inputs.is-asan }}-${{ github.ref_protected == true && github.run_id || github.ref }}
cancel-in-progress: ${{ github.ref_protected != true }}
@@ -81,6 +83,8 @@ jobs:
run:
shell: bash
runs-on: ${{ inputs.build-runs-on }}
permissions:
contents: read
container: ${{ fromJSON(inputs.build-container) }}
environment: ${{ inputs.environment }}
env:

View File

@@ -26,6 +26,8 @@ on:
type: string
default: testing
permissions: {}
concurrency:
group: electron-gn-check-${{ inputs.target-platform }}-${{ github.ref }}
cancel-in-progress: true
@@ -41,6 +43,8 @@ jobs:
run:
shell: bash
runs-on: ${{ inputs.check-runs-on }}
permissions:
contents: read
container: ${{ fromJSON(inputs.check-container) }}
steps:
- name: Checkout Electron

View File

@@ -35,10 +35,7 @@ concurrency:
group: electron-test-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ inputs.is-asan }}-${{ github.ref_protected == true && github.run_id || github.ref }}
cancel-in-progress: ${{ github.ref_protected != true }}
permissions:
contents: read
issues: read
pull-requests: read
permissions: {}
env:
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
@@ -53,6 +50,10 @@ jobs:
run:
shell: bash
runs-on: ${{ inputs.test-runs-on }}
permissions:
contents: read
issues: read
pull-requests: read
container: ${{ fromJSON(inputs.test-container) }}
strategy:
fail-fast: false
@@ -195,10 +196,7 @@ jobs:
# sudo security authorizationdb write com.apple.trust-settings.admin allow
# cd src/electron
# ./script/codesign/generate-identity.sh
- name: Install Datadog CLI
run: |
cd src/electron
node script/yarn global add @datadog/datadog-ci
- name: Run Electron Tests
shell: bash
env:
@@ -224,7 +222,7 @@ jobs:
export ELECTRON_FORCE_TEST_SUITE_EXIT="true"
fi
fi
node script/yarn test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
node script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
else
chown :builduser .. && chmod g+w ..
chown -R :builduser . && chmod -R g+w .
@@ -241,9 +239,14 @@ jobs:
export MOCHA_TIMEOUT=180000
echo "Piping output to ASAN_SYMBOLIZE ($ASAN_SYMBOLIZE)"
cd electron
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn test --runners=main --trace-uncaught --enable-logging --files $tests_files | $ASAN_SYMBOLIZE
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --trace-uncaught --enable-logging --files $tests_files | $ASAN_SYMBOLIZE
else
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn test --runners=main --trace-uncaught --enable-logging --files $tests_files
if [ "${{ inputs.target-arch }}" = "arm" ]; then
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --skipYarnInstall --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
else
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
fi
fi
fi
- name: Upload Test results to Datadog
@@ -255,9 +258,10 @@ jobs:
DD_TAGS: "os.architecture:${{ inputs.target-arch }},os.family:${{ inputs.target-platform }},os.platform:${{ inputs.target-platform }},asan:${{ inputs.is-asan }}"
run: |
if ! [ -z $DD_API_KEY ] && [ -f src/electron/junit/test-results-main.xml ]; then
export DATADOG_PATH=`node src/electron/script/yarn global bin`
$DATADOG_PATH/datadog-ci junit upload src/electron/junit/test-results-main.xml
fi
cd src/electron
export DATADOG_PATH=`node script/yarn.js bin datadog-ci`
$DATADOG_PATH junit upload junit/test-results-main.xml
fi
if: always() && !cancelled()
- name: Upload Test Artifacts
if: always() && !cancelled()

View File

@@ -26,6 +26,8 @@ on:
type: string
default: testing
permissions: {}
concurrency:
group: electron-node-nan-test-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ github.ref_protected == true && github.run_id || github.ref }}
cancel-in-progress: ${{ github.ref_protected != true }}
@@ -39,6 +41,8 @@ jobs:
node-tests:
name: Run Node.js Tests
runs-on: electron-arc-centralus-linux-amd64-8core
permissions:
contents: read
timeout-minutes: 30
env:
TARGET_ARCH: ${{ inputs.target-arch }}
@@ -93,6 +97,8 @@ jobs:
nan-tests:
name: Run Nan Tests
runs-on: electron-arc-centralus-linux-amd64-4core
permissions:
contents: read
timeout-minutes: 30
env:
TARGET_ARCH: ${{ inputs.target-arch }}
@@ -132,10 +138,16 @@ jobs:
unzip -:o dist.zip
- name: Setup Linux for Headless Testing
run: sh -e /etc/init.d/xvfb start
- name: Add Clang problem matcher
shell: bash
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
- name: Run Nan Tests
run: |
cd src
node electron/script/nan-spec-runner.js
- name: Remove Clang problem matcher
shell: bash
run: echo "::remove-matcher owner=clang::"
- name: Wait for active SSH sessions
shell: bash
if: always() && !cancelled()

View File

@@ -11,6 +11,7 @@ jobs:
name: backport/requested label added
if: github.event.label.name == 'backport/requested 🗳'
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Trigger Slack workflow
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
@@ -28,6 +29,7 @@ jobs:
name: deprecation-review/complete label added
if: github.event.label.name == 'deprecation-review/complete ✅'
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1

View File

@@ -7,8 +7,7 @@ on:
- edited
- synchronize
permissions:
contents: read
permissions: {}
jobs:
main:

View File

@@ -11,6 +11,7 @@ jobs:
check-stable-prep-items:
name: Check Stable Prep Items
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1

View File

@@ -10,6 +10,7 @@ permissions: {}
jobs:
stale:
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
@@ -31,6 +32,7 @@ jobs:
only-pr-labels: not-a-real-label
pending-repro:
runs-on: ubuntu-latest
permissions: {}
if: ${{ always() }}
needs: stale
steps:

View File

@@ -18,9 +18,13 @@ on:
type: boolean
default: false
permissions: {}
jobs:
checkout-windows:
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ inputs.build-image-sha }}
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
@@ -48,6 +52,8 @@ jobs:
publish-x64-win:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-windows
with:
environment: production-release
@@ -62,6 +68,8 @@ jobs:
publish-arm64-win:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-windows
with:
environment: production-release
@@ -76,6 +84,8 @@ jobs:
publish-x86-win:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
permissions:
contents: read
needs: checkout-windows
with:
environment: production-release

2
.gitignore vendored
View File

@@ -53,3 +53,5 @@ ts-gen
patches/mtime-cache.json
spec/fixtures/logo.png
.yarn/install-state.gz

942
.yarn/releases/yarn-4.12.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

12
.yarnrc.yml Normal file
View File

@@ -0,0 +1,12 @@
enableScripts: false
nmHoistingLimits: workspaces
nodeLinker: node-modules
npmMinimalAgeGate: 10080
npmPreapprovedPackages:
- "@electron/*"
yarnPath: .yarn/releases/yarn-4.12.0.cjs

7
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'142.0.7444.162',
'142.0.7444.235',
'node_version':
'v22.21.1',
'nan_version':
@@ -30,9 +30,6 @@ vars = {
# The path of the sysroots.json file.
'sysroots_json_path': 'electron/script/sysroots.json',
# KEEP IN SYNC WITH utils.js FILE
'yarn_version': '1.22.22',
# To be able to build clean Chromium from sources.
'apply_patches': True,
@@ -155,7 +152,7 @@ hooks = [
'action': [
'python3',
'-c',
'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["python3", "script/lib/npx.py", "yarn@' + (Var("yarn_version")) + '", "install", "--frozen-lockfile"]);',
'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["node", ".yarn/releases/yarn-4.12.0.cjs", "install", "--immutable"]);',
],
},
{

View File

@@ -1252,7 +1252,8 @@ Captures a snapshot of the page within `rect`. Omitting `rect` will capture the
Returns `Promise<void>` - the promise will resolve when the page has finished loading
(see [`did-finish-load`](web-contents.md#event-did-finish-load)), and rejects
if the page fails to load (see [`did-fail-load`](web-contents.md#event-did-fail-load)).
if the page fails to load (see
[`did-fail-load`](web-contents.md#event-did-fail-load)). A noop rejection handler is already attached, which avoids unhandled rejection errors. If the existing page has a beforeUnload handler, [`did-fail-load`](web-contents.md#event-did-fail-load) will be called unless [`will-prevent-unload`](web-contents.md#event-did-fail-load) is handled.
Same as [`webContents.loadURL(url[, options])`](web-contents.md#contentsloadurlurl-options).

View File

@@ -72,6 +72,9 @@
some GTK+3 desktop environments. Default is `false`.
* `transparent` boolean (optional) - Makes the window [transparent](../../tutorial/custom-window-styles.md#transparent-windows).
Default is `false`. On Windows, does not work unless the window is frameless.
When you add a [`View`](../view.md) to a `BaseWindow`, you'll need to call
[`view.setBackgroundColor`](../view.md#viewsetbackgroundcolorcolor) with a transparent
background color on that view to make its background transparent as well.
* `type` string (optional) - The type of window, default is normal window. See more about
this below.
* `visualEffectState` string (optional) _macOS_ - Specify how the material

View File

@@ -1079,7 +1079,7 @@ Emitted when the [mainFrame](web-contents.md#contentsmainframe-readonly), an `<i
Returns `Promise<void>` - the promise will resolve when the page has finished loading
(see [`did-finish-load`](web-contents.md#event-did-finish-load)), and rejects
if the page fails to load (see
[`did-fail-load`](web-contents.md#event-did-fail-load)). A noop rejection handler is already attached, which avoids unhandled rejection errors.
[`did-fail-load`](web-contents.md#event-did-fail-load)). A noop rejection handler is already attached, which avoids unhandled rejection errors. If the existing page has a beforeUnload handler, [`did-fail-load`](web-contents.md#event-did-fail-load) will be called unless [`will-prevent-unload`](web-contents.md#event-did-fail-load) is handled.
Loads the `url` in the window. The `url` must contain the protocol prefix,
e.g. the `http://` or `file://`. If the load should bypass http cache then

View File

@@ -6,77 +6,17 @@ Follow the guidelines below for building **Electron itself** on Linux, for the p
## Prerequisites
* At least 25GB disk space and 8GB RAM.
* Python >= 3.9.
* [Node.js](https://nodejs.org/download/) >= 22.12.0
* [clang](https://clang.llvm.org/get_started.html) 3.4 or later.
* Development headers of GTK 3 and libnotify.
Due to Electron's dependency on Chromium, prerequisites and dependencies for Electron change over time. [Chromium's documentation on building on Linux](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux/build_instructions.md) has up to date information for building Chromium on Linux. This documentation can generally
be followed for building Electron on Linux as well.
On Ubuntu >= 20.04, install the following libraries:
```sh
$ sudo apt-get install build-essential clang libdbus-1-dev libgtk-3-dev \
libnotify-dev libasound2-dev libcap-dev \
libcups2-dev libxtst-dev \
libxss1 libnss3-dev gcc-multilib g++-multilib curl \
gperf bison python3-dbusmock openjdk-8-jre
```
On Ubuntu < 20.04, install the following libraries:
```sh
$ sudo apt-get install build-essential clang libdbus-1-dev libgtk-3-dev \
libnotify-dev libgnome-keyring-dev \
libasound2-dev libcap-dev libcups2-dev libxtst-dev \
libxss1 libnss3-dev gcc-multilib g++-multilib curl \
gperf bison python-dbusmock openjdk-8-jre
```
On RHEL / CentOS, install the following libraries:
```sh
$ sudo yum install clang dbus-devel gtk3-devel libnotify-devel \
libgnome-keyring-devel xorg-x11-server-utils libcap-devel \
cups-devel libXtst-devel alsa-lib-devel libXrandr-devel \
nss-devel python-dbusmock openjdk-8-jre
```
On Fedora, install the following libraries:
```sh
$ sudo dnf install clang dbus-devel gperf gtk3-devel \
libnotify-devel libgnome-keyring-devel libcap-devel \
cups-devel libXtst-devel alsa-lib-devel libXrandr-devel \
nss-devel python-dbusmock
```
On Arch Linux / Manjaro, install the following libraries:
```sh
$ sudo pacman -Syu base-devel clang libdbus gtk2 libnotify \
libgnome-keyring alsa-lib libcap libcups libxtst \
libxss nss gcc-multilib curl gperf bison \
python2 python-dbusmock jdk8-openjdk
```
Other distributions may offer similar packages for installation via package
managers such as pacman. Or one can compile from source code.
Additionally, Electron's [Linux dependency installer](https://github.com/electron/build-images/blob/main/tools/install-deps.sh) can be referenced to get the current dependencies that Electron requires in addition to what Chromium installs via [build/install-deps.sh](https://chromium.googlesource.com/chromium/src/+/HEAD/build/install-build-deps.sh).
### Cross compilation
If you want to build for an `arm` target you should also install the following
dependencies:
If you want to build for an `arm` target, you can use Electron's [Linux dependency installer](https://github.com/electron/build-images/blob/main/tools/install-deps.sh) to install the additional dependencies by passing the `--arm argument`:
```sh
$ sudo apt-get install libc6-dev-armhf-cross linux-libc-dev-armhf-cross \
g++-arm-linux-gnueabihf
```
Similarly for `arm64`, install the following:
```sh
$ sudo apt-get install libc6-dev-arm64-cross linux-libc-dev-arm64-cross \
g++-aarch64-linux-gnu
$ sudo install-deps.sh --arm
```
And to cross-compile for `arm` or targets, you should pass the

View File

@@ -119,7 +119,10 @@ export function fetchWithSession (input: RequestInfo, init: (RequestInit & {bypa
p.reject(err);
});
if (!req.body?.pipeTo(Writable.toWeb(r as unknown as Writable)).then(() => r.end())) { r.end(); }
// pipeTo expects a WritableStream<Uint8Array>. Node.js' Writable.toWeb returns WritableStream<any>,
// which causes a TS structural mismatch.
const writable = Writable.toWeb(r as unknown as Writable) as unknown as WritableStream<Uint8Array>;
if (!req.body?.pipeTo(writable).then(() => r.end())) { r.end(); }
return p.promise;
}

View File

@@ -4,6 +4,8 @@ import { createReadStream } from 'fs';
import { Readable } from 'stream';
import { ReadableStream } from 'stream/web';
import type { ReadableStreamDefaultReader } from 'stream/web';
// Global protocol APIs.
const { registerSchemesAsPrivileged, getStandardSchemes, Protocol } = process._linkedBinding('electron_browser_protocol');
@@ -12,7 +14,7 @@ const ERR_UNEXPECTED = -9;
const isBuiltInScheme = (scheme: string) => ['http', 'https', 'file'].includes(scheme);
function makeStreamFromPipe (pipe: any): ReadableStream {
function makeStreamFromPipe (pipe: any): ReadableStream<Uint8Array> {
const buf = new Uint8Array(1024 * 1024 /* 1 MB */);
return new ReadableStream({
async pull (controller) {
@@ -38,21 +40,26 @@ function makeStreamFromFileInfo ({
filePath: string;
offset?: number;
length?: number;
}): ReadableStream {
}): ReadableStream<Uint8Array> {
// Node's Readable.toWeb produces a WHATWG ReadableStream whose chunks are Uint8Array.
return Readable.toWeb(createReadStream(filePath, {
start: offset,
end: length >= 0 ? offset + length : undefined
}));
})) as ReadableStream<Uint8Array>;
}
function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): RequestInit['body'] {
if (!uploadData) return null;
// Optimization: skip creating a stream if the request is just a single buffer.
if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') return uploadData[0].bytes;
if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') {
return uploadData[0].bytes as any;
}
const chunks = [...uploadData] as any[]; // TODO: types are wrong
let current: ReadableStreamDefaultReader | null = null;
return new ReadableStream({
const chunks = [...uploadData] as any[]; // TODO: refine ProtocolRequest types
// Use Node's web stream types explicitly to avoid DOM lib vs Node lib structural mismatches.
// Generic <Uint8Array> ensures reader.read() returns value?: Uint8Array consistent with enqueue.
let current: ReadableStreamDefaultReader<Uint8Array> | null = null;
return new ReadableStream<Uint8Array>({
async pull (controller) {
if (current) {
const { done, value } = await current.read();
@@ -67,7 +74,7 @@ function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): Reque
if (!chunks.length) { return controller.close(); }
const chunk = chunks.shift()!;
if (chunk.type === 'rawData') {
controller.enqueue(chunk.bytes);
controller.enqueue(chunk.bytes as Uint8Array);
} else if (chunk.type === 'file') {
current = makeStreamFromFileInfo(chunk).getReader();
return this.pull!(controller);

View File

@@ -40,7 +40,7 @@ process.on('uncaughtException', function (error) {
// Emit 'exit' event on quit.
const { app } = require('electron');
app.on('quit', (_event, exitCode) => {
app.on('quit', (_event: any, exitCode: number) => {
process.emit('exit', exitCode);
});

View File

@@ -5,10 +5,11 @@
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {
"@azure/storage-blob": "^12.28.0",
"@datadog/datadog-ci": "^4.1.2",
"@electron/asar": "^3.2.13",
"@electron/docs-parser": "^2.0.0",
"@electron/fiddle-core": "^1.3.4",
"@electron/github-app-auth": "^2.2.1",
"@electron/github-app-auth": "^3.2.0",
"@electron/lint-roller": "^3.1.2",
"@electron/typescript-definitions": "^9.1.2",
"@octokit/rest": "^20.1.2",
@@ -40,6 +41,7 @@
"lint-staged": "^16.1.0",
"markdownlint-cli2": "^0.18.0",
"minimist": "^1.2.8",
"node-gyp": "^11.4.2",
"null-loader": "^4.0.1",
"pre-flight": "^2.0.0",
"process": "^0.11.10",
@@ -134,6 +136,21 @@
]
},
"resolutions": {
"nan": "nodejs/nan#e14bdcd1f72d62bca1d541b66da43130384ec213"
"dbus-native/xml2js": "0.5.0",
"abstract-socket": "github:deepak1556/node-abstractsocket#928cc591decd12aff7dad96449da8afc29832c19",
"minimist@npm:~0.0.1": "0.2.4"
},
"packageManager": "yarn@4.12.0",
"workspaces": [
"spec",
"spec/fixtures/native-addon/*"
],
"dependenciesMeta": {
"abstract-socket": {
"built": true
},
"dugite": {
"built": true
}
}
}

View File

@@ -143,3 +143,7 @@ allow_electron_to_depend_on_components_os_crypt_sync.patch
expose_referrerscriptinfo_hostdefinedoptionsindex.patch
chore_disable_protocol_handler_dcheck.patch
fix_release_mouse_buttons_on_focus_loss_on_wayland.patch
viz_fix_visual_artifacts_due_to_resizing_root_render_pass_with_dcomp.patch
viz_do_not_overallocate_surface_on_initial_render.patch
viz_create_isbufferqueuesupportedandenabled.patch
viz_fix_visual_artifacts_while_resizing_window_with_dcomp.patch

View File

@@ -46,7 +46,7 @@ index 7280ef29b85c1b16b11dd9e4d628a9eb579bb4dd..ab56e29d402a400a8c91aa97b6e82ad7
# than here in :chrome_dll.
deps += [ "//chrome:packed_resources_integrity_header" ]
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 15cfa64348e80a3507fc83c3819e9abdb238bffb..4650080f45da4e321d4b0f0dabd98f2bf097c7c2 100644
index d8978eb678048850cc0e629d0c978f23417b5be9..219c9ae3dde4f2239b5c1331b99c8f2e9a170628 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7571,9 +7571,12 @@ test("unit_tests") {

View File

@@ -312,7 +312,7 @@ index 7e3d46902fbf736b4240eb3fcb89975a7b222197..57fdc89fc265ad70cb0bff8443cc1026
auto DrawAsSinglePath = [&]() {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a25e99fac5f5a171becff833464cdb3dfe7522c2..123a7c1fc980b1b3bcbe0e3e00afa594632d720b 100644
index 2e464ef7bbfd7e0d84df348d012e8020d4758726..b59eceacbd4143ef183bb67c39f9ccccc7582adb 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -214,6 +214,10 @@

View File

@@ -0,0 +1,93 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Wed, 3 Dec 2025 17:10:02 +0100
Subject: viz: Create IsBufferQueueSupportedAndEnabled()
Manual backport of crrev.com/c/7210913
Bug: 457463689
Change-Id: I31bbaa6b5d79697c6bb5e1fc6738f6ea5a937b4f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7210913
Reviewed-by: Michael Tang <tangm@microsoft.com>
Commit-Queue: David Sanders <dsanders11@ucsbalum.com>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1553190}
diff --git a/components/viz/service/display/output_surface.cc b/components/viz/service/display/output_surface.cc
index ff795eb057ac64f40aa842fec8e053d12f57f538..71287614c627d39f8d019889498205ab3eff2a69 100644
--- a/components/viz/service/display/output_surface.cc
+++ b/components/viz/service/display/output_surface.cc
@@ -21,6 +21,16 @@
namespace viz {
+namespace {
+
+#if BUILDFLAG(IS_WIN)
+// Use BufferQueue for the primary plane instead of a DXGI swap chain or DComp
+// surface.
+BASE_FEATURE(kBufferQueue, base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
+
+} // namespace
+
OutputSurface::Capabilities::Capabilities() = default;
OutputSurface::Capabilities::~Capabilities() = default;
OutputSurface::Capabilities::Capabilities(const Capabilities& capabilities) =
@@ -94,6 +104,12 @@ bool IsDelegatedCompositingSupportedAndEnabled(
// Ensure we check the feature flag iff the feature is supported.
return features::IsDelegatedCompositingEnabled();
}
+
+bool IsBufferQueueSupportedAndEnabled(
+ OutputSurface::DCSupportLevel support_level) {
+ return support_level >= OutputSurface::DCSupportLevel::kDCompDynamicTexture &&
+ base::FeatureList::IsEnabled(kBufferQueue);
+}
#endif
} // namespace viz
diff --git a/components/viz/service/display/output_surface.h b/components/viz/service/display/output_surface.h
index 25306ab6e18a266efdc329e4ddd81f5303033f4c..589f4c10dad9c807c9e3ce7baba63795b629435b 100644
--- a/components/viz/service/display/output_surface.h
+++ b/components/viz/service/display/output_surface.h
@@ -307,6 +307,9 @@ class VIZ_SERVICE_EXPORT OutputSurface {
// `features::IsDelegatedCompositingEnabled()`.
bool IsDelegatedCompositingSupportedAndEnabled(
OutputSurface::DCSupportLevel support_level);
+
+bool IsBufferQueueSupportedAndEnabled(
+ OutputSurface::DCSupportLevel support_level);
#endif
} // namespace viz
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index e34e15dda13a183568fc3e186d3b89da1e828ad4..9b9a4c02c975799fe42b04f0d5b680274d28b09e 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -121,12 +121,6 @@ namespace {
BASE_FEATURE(kDumpWithoutCrashingOnMissingRenderPassBacking,
base::FEATURE_ENABLED_BY_DEFAULT);
-#if BUILDFLAG(IS_WIN)
-// Use BufferQueue for the primary plane instead of a DXGI swap chain or DComp
-// surface.
-BASE_FEATURE(kBufferQueue, base::FEATURE_DISABLED_BY_DEFAULT);
-#endif
-
// Smallest unit that impacts anti-aliasing output. We use this to determine
// when an exterior edge (with AA) has been clipped (no AA). The specific value
// was chosen to match that used by gl_renderer.
@@ -992,10 +986,8 @@ SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
// It's possible to use BufferQueue with DComp textures, so we can optionally
// enable it behind a feature flag.
- const bool want_buffer_queue =
- output_surface_->capabilities().dc_support_level >=
- OutputSurface::DCSupportLevel::kDCompDynamicTexture &&
- base::FeatureList::IsEnabled(kBufferQueue);
+ const bool want_buffer_queue = IsBufferQueueSupportedAndEnabled(
+ output_surface_->capabilities().dc_support_level);
#else
const bool want_buffer_queue = true;
#endif

View File

@@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Wed, 3 Dec 2025 17:08:55 +0100
Subject: viz: Do not overallocate surface on initial render
Manual backport of crrev.com/c/7129658
Change-Id: I0baa5865dbe66efc7b0f3f793c8e89cdc6aaa0b6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7129658
Commit-Queue: Vasiliy Telezhnikov <vasilyt@chromium.org>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Reviewed-by: Michael Tang <tangm@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1544293}
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index c03a4b3db61371021a94128f698126b5fef2f577..01abf5d36e2e117acf6f9cdc91307c9ac5616453 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -1081,10 +1081,13 @@ gfx::Size DirectRenderer::CalculateTextureSizeForRenderPass(
// buffer area and number of reallocations to quantify the trade-off.
gfx::Size DirectRenderer::CalculateSizeForOutputSurface(
const gfx::Size& requested_viewport_size) {
+ const gfx::Size surface_size = surface_size_for_swap_buffers();
+
// We're not able to clip back buffers if output surface does not support
- // clipping.
- if (requested_viewport_size == surface_size_for_swap_buffers() ||
+ // clipping. We don't round on the initial frame when a window is first shown.
+ if (requested_viewport_size == surface_size ||
!output_surface_->capabilities().supports_viewporter ||
+ surface_size.IsZero() ||
settings_->dont_round_texture_sizes_for_pixel_tests) {
device_viewport_size_ = requested_viewport_size;
return requested_viewport_size;
@@ -1102,8 +1105,8 @@ gfx::Size DirectRenderer::CalculateSizeForOutputSurface(
// allows backings to be more easily reused during a resize operation.
const int request_width = requested_viewport_size.width();
const int request_height = requested_viewport_size.height();
- int surface_width = surface_size_for_swap_buffers().width();
- int surface_height = surface_size_for_swap_buffers().height();
+ int surface_width = surface_size.width();
+ int surface_height = surface_size.height();
constexpr int multiple = 256;
// If |request_width| or |request_height| is already a multiple of |multiple|,

View File

@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Thu, 20 Nov 2025 11:51:44 -0800
Subject: viz: Fix visual artifacts due to resizing root render pass with DComp
Backport of crrev.com/c/7156576
Refs https://github.com/electron/electron/issues/36280#issuecomment-3560964534
Bug: 457463689
Change-Id: I7e00eadb2a1c3b44d64939d1a870d89befee158b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7156576
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Commit-Queue: Jonathan Ross <jonross@chromium.org>
Reviewed-by: Michael Tang <tangm@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1547987}
diff --git a/components/viz/service/display_embedder/skia_output_device_dcomp.cc b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
index 81649eb59eb71ac950779af0f521a1f2a02e3add..7ac9ea1cdc4416a7af8dc2a75404cbc15be6cfad 100644
--- a/components/viz/service/display_embedder/skia_output_device_dcomp.cc
+++ b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
@@ -39,6 +39,11 @@
namespace viz {
namespace {
+// With DirectComposition, resize surface based on root render pass size to
+// avoid gutter which shows stale pixels.
+BASE_FEATURE(kDirectCompositionResizeBasedOnRootSurface,
+ base::FEATURE_ENABLED_BY_DEFAULT);
+
base::TimeTicks g_last_reshape_failure = base::TimeTicks();
NOINLINE void CheckForLoopFailures() {
@@ -155,6 +160,8 @@ SkiaOutputDeviceDComp::SkiaOutputDeviceDComp(
capabilities_.renderer_allocates_images = true;
capabilities_.supports_viewporter = presenter_->SupportsViewporter();
capabilities_.supports_non_backed_solid_color_overlays = true;
+ capabilities_.resize_based_on_root_surface =
+ base::FeatureList::IsEnabled(kDirectCompositionResizeBasedOnRootSurface);
DCHECK(context_state_);
DCHECK(context_state_->gr_context() ||

View File

@@ -0,0 +1,122 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Wed, 3 Dec 2025 17:42:58 +0100
Subject: viz: Fix visual artifacts while resizing window with DComp
Manual backport of crrev.com/c/7115438
Bug: 457463689
Change-Id: I9c684effe15e0b112ae533faa243e5a035e9c875
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7115438
Commit-Queue: David Sanders <dsanders11@ucsbalum.com>
Reviewed-by: Michael Tang <tangm@microsoft.com>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1553192}
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 01abf5d36e2e117acf6f9cdc91307c9ac5616453..7e45d9ea46d0d4095169daf748e20c98db21969d 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -243,6 +243,31 @@ void DirectRenderer::DrawFrame(
current_frame()->device_viewport_size = device_viewport_size;
current_frame()->display_color_spaces = display_color_spaces;
+ gfx::Size surface_resource_size =
+ CalculateSizeForOutputSurface(device_viewport_size);
+
+#if BUILDFLAG(IS_WIN)
+ if (output_surface_->capabilities().clear_drawn_areas_outside_viewport &&
+ device_viewport_size != surface_resource_size) {
+ // On Windows with DirectComposition, we cannot synchronize the swap chain
+ // |Present| and the DComp |Commit| calls to take effect at the same time.
+ // (Both take effect asynchronously.) Hence, presenting a frame and changing
+ // the DComp layer clip rect can happen at different times. This can lead to
+ // ugly visual artifacts while resizing the window because it can reveal
+ // areas of the surface that are outside the viewport (crbug.com/457463689).
+ // To prevent those artifacts, we clear areas outside of the viewport with a
+ // transparent color. Transparency is expensive, so we use it only while
+ // resizing.
+ // This line gives us a transparent image format and triggers the background
+ // to be cleared in |SkiaRenderer::ClearFramebuffer|.
+ root_render_pass->has_transparent_background = true;
+ // Redraw and swap the whole surface.
+ root_render_pass->output_rect = gfx::Rect(surface_resource_size);
+ current_frame()->root_damage_rect = gfx::Rect(surface_resource_size);
+ current_frame()->device_viewport_size = surface_resource_size;
+ }
+#endif
+
output_surface_->SetNeedsMeasureNextDrawLatency();
BeginDrawingFrame();
@@ -274,8 +299,6 @@ void DirectRenderer::DrawFrame(
current_frame()->display_color_spaces.GetOutputBufferFormat(
current_frame()->root_render_pass->content_color_usage,
frame_has_alpha));
- gfx::Size surface_resource_size =
- CalculateSizeForOutputSurface(device_viewport_size);
if (overlay_processor_) {
// Display transform and viewport size are needed for overlay validator on
// Android SurfaceControl, and viewport size is need on Windows. These need
@@ -397,8 +420,10 @@ void DirectRenderer::DrawFrame(
// If we need to redraw the frame, the whole output should be considered
// damaged.
- if (needs_full_frame_redraw)
- current_frame()->root_damage_rect = gfx::Rect(device_viewport_size);
+ if (needs_full_frame_redraw) {
+ current_frame()->root_damage_rect =
+ gfx::Rect(current_frame()->device_viewport_size);
+ }
if (!skip_drawing_root_render_pass) {
DrawRenderPassAndExecuteCopyRequests(root_render_pass);
diff --git a/components/viz/service/display/output_surface.h b/components/viz/service/display/output_surface.h
index 589f4c10dad9c807c9e3ce7baba63795b629435b..641bbfc732c88141ddd929a4c334360462259ee4 100644
--- a/components/viz/service/display/output_surface.h
+++ b/components/viz/service/display/output_surface.h
@@ -106,6 +106,11 @@ class VIZ_SERVICE_EXPORT OutputSurface {
#if BUILDFLAG(IS_WIN)
// Whether this OutputSurface supports direct composition layers.
DCSupportLevel dc_support_level = DCSupportLevel::kNone;
+ // Whether to 1) clear all drawn areas outside the viewport with a
+ // transparent background color when drawing a frame and 2) swap them. This
+ // is necessary if the surface clip rect can get out of sync with the
+ // viewport size (e.g., due to a race condition).
+ bool clear_drawn_areas_outside_viewport = false;
#endif
// Whether this OutputSurface should skip DrawAndSwap(). This is true for
// the unified display on Chrome OS. All drawing is handled by the physical
diff --git a/components/viz/service/display_embedder/skia_output_device_dcomp.cc b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
index 7ac9ea1cdc4416a7af8dc2a75404cbc15be6cfad..8e027d1382c5d639c1e114b8e25b6cea3af3445d 100644
--- a/components/viz/service/display_embedder/skia_output_device_dcomp.cc
+++ b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
@@ -39,10 +39,8 @@
namespace viz {
namespace {
-// With DirectComposition, resize surface based on root render pass size to
-// avoid gutter which shows stale pixels.
-BASE_FEATURE(kDirectCompositionResizeBasedOnRootSurface,
- base::FEATURE_ENABLED_BY_DEFAULT);
+// Apply fixes for crbug.com/457463689.
+BASE_FEATURE(kDirectCompositionResizeFixes, base::FEATURE_ENABLED_BY_DEFAULT);
base::TimeTicks g_last_reshape_failure = base::TimeTicks();
@@ -161,7 +159,14 @@ SkiaOutputDeviceDComp::SkiaOutputDeviceDComp(
capabilities_.supports_viewporter = presenter_->SupportsViewporter();
capabilities_.supports_non_backed_solid_color_overlays = true;
capabilities_.resize_based_on_root_surface =
- base::FeatureList::IsEnabled(kDirectCompositionResizeBasedOnRootSurface);
+ base::FeatureList::IsEnabled(kDirectCompositionResizeFixes);
+ // With delegated compositing or a buffer queue, |Present| and |Commit| are
+ // synchronized and the clear is not needed.
+ capabilities_.clear_drawn_areas_outside_viewport =
+ base::FeatureList::IsEnabled(kDirectCompositionResizeFixes) &&
+ !IsDelegatedCompositingSupportedAndEnabled(
+ capabilities_.dc_support_level) &&
+ !IsBufferQueueSupportedAndEnabled(capabilities_.dc_support_level);
DCHECK(context_state_);
DCHECK(context_state_->gr_context() ||

View File

@@ -1 +1,2 @@
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
fix_element_tree_flickering_with_node_inspection.patch

View File

@@ -0,0 +1,107 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alex Rudenko <alexrudenko@chromium.org>
Date: Thu, 20 Nov 2025 11:18:30 +0100
Subject: Fix element tree flickering with node inspection
Regressed in https://crrev.com/c/6888910. The highlighting in the tree
outline emits the INSPECT_MODE_WILL_BE_TOGGLED on the overlay model
causing the tree to clear the highlight immediately. This CL
recovers the logic missed in the https://crrev.com/c/6888910
to prevent re-entrancy during highlighting.
Fixed: 462120622
Change-Id: I08af098f7a142085c2fb16511031855623e07c4b
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7170551
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Commit-Queue: Philip Pfaffe <pfaffe@chromium.org>
Auto-Submit: Alex Rudenko <alexrudenko@chromium.org>
diff --git a/front_end/panels/elements/DOMTreeWidget.test.ts b/front_end/panels/elements/DOMTreeWidget.test.ts
index 43ae133087a61c47c84349c7550485bd52b23847..d83fddd7afc1871b8064631d63c1b6b1e5357b70 100644
--- a/front_end/panels/elements/DOMTreeWidget.test.ts
+++ b/front_end/panels/elements/DOMTreeWidget.test.ts
@@ -24,6 +24,7 @@ describeWithMockConnection('DOMTreeWidget', () => {
elementsTreeOutline,
alreadyExpandedParentTreeElement: null,
highlightedTreeElement: null,
+ isUpdatingHighlights: false,
});
const domTree = new Elements.ElementsTreeOutline.DOMTreeWidget(undefined, view);
domTree.performUpdate();
diff --git a/front_end/panels/elements/ElementsTreeOutline.ts b/front_end/panels/elements/ElementsTreeOutline.ts
index bd8093e1c0961648e782234ed5826273d6ace6d9..c6c8d9d29a84f872126ca0b7c847dffe08c285ad 100644
--- a/front_end/panels/elements/ElementsTreeOutline.ts
+++ b/front_end/panels/elements/ElementsTreeOutline.ts
@@ -105,6 +105,7 @@ interface ViewInput {
interface ViewOutput {
elementsTreeOutline?: ElementsTreeOutline;
highlightedTreeElement: ElementsTreeElement|null;
+ isUpdatingHighlights: boolean;
alreadyExpandedParentTreeElement: ElementsTreeElement|null;
}
@@ -136,6 +137,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
// Node highlighting logic. FIXME: express as a lit template.
const previousHighlightedNode = output.highlightedTreeElement?.node() ?? null;
if (previousHighlightedNode !== input.currentHighlightedNode) {
+ output.isUpdatingHighlights = true;
let treeElement: ElementsTreeElement|null = null;
if (output.highlightedTreeElement) {
@@ -173,6 +175,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
output.highlightedTreeElement = treeElement;
output.elementsTreeOutline.setHoverEffect(treeElement);
treeElement?.reveal(true);
+ output.isUpdatingHighlights = false;
}
};
@@ -225,6 +228,7 @@ export class DOMTreeWidget extends UI.Widget.Widget {
#viewOutput: ViewOutput = {
highlightedTreeElement: null,
alreadyExpandedParentTreeElement: null,
+ isUpdatingHighlights: false,
};
#highlightThrottler = new Common.Throttler.Throttler(100);
@@ -239,8 +243,8 @@ export class DOMTreeWidget extends UI.Widget.Widget {
SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.HIGHLIGHT_NODE_REQUESTED, this.#highlightNode, this,
{scoped: true});
SDK.TargetManager.TargetManager.instance().addModelListener(
- SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.INSPECT_MODE_WILL_BE_TOGGLED, this.#clearState, this,
- {scoped: true});
+ SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.INSPECT_MODE_WILL_BE_TOGGLED,
+ this.#clearHighlightedNode, this, {scoped: true});
}
}
@@ -251,7 +255,13 @@ export class DOMTreeWidget extends UI.Widget.Widget {
});
}
- #clearState(): void {
+ #clearHighlightedNode(): void {
+ // Highlighting an element via tree outline will emit the
+ // INSPECT_MODE_WILL_BE_TOGGLED event, therefore, we skip it if the view
+ // informed us that it is updating the element.
+ if (this.#viewOutput.isUpdatingHighlights) {
+ return;
+ }
this.#currentHighlightedNode = null;
this.requestUpdate();
}
@@ -305,11 +315,11 @@ export class DOMTreeWidget extends UI.Widget.Widget {
currentHighlightedNode: this.#currentHighlightedNode,
onElementsTreeUpdated: this.onElementsTreeUpdated.bind(this),
onSelectedNodeChanged: event => {
- this.#clearState();
+ this.#clearHighlightedNode();
this.onSelectedNodeChanged(event);
},
- onElementCollapsed: this.#clearState.bind(this),
- onElementExpanded: this.#clearState.bind(this),
+ onElementCollapsed: this.#clearHighlightedNode.bind(this),
+ onElementExpanded: this.#clearHighlightedNode.bind(this),
},
this.#viewOutput, this.contentElement);
}

View File

@@ -7,3 +7,4 @@ fix_support_new_variant_of_namedpropertyhandlerconfiguration_and.patch
fix_correct_usages_of_v8_returnvalue_void_set_nonempty_for_new.patch
chore_remove_deprecated_functioncallbackinfo_holder.patch
fix_replace_deprecated_get_setprototype.patch
chore_add_yarnrc_yml_and_yarn_lock_file_to_use_yarn_v4.patch

File diff suppressed because it is too large Load Diff

View File

@@ -53,3 +53,4 @@ fix_replace_deprecated_setprototype.patch
fix_redefined_macos_sdk_header_symbols.patch
src_use_cp_utf8_for_wide_file_names_on_win32.patch
fix_ensure_traverseparent_bails_on_resource_path_exit.patch
src_handle_der_decoding_errors_from_system_certificates.patch

View File

@@ -0,0 +1,74 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Joyee Cheung <joyeec9h3@gmail.com>
Date: Thu, 20 Nov 2025 13:50:28 +0900
Subject: src: handle DER decoding errors from system certificates
When decoding certificates from the system store, it's not actually
guaranteed to succeed. In case the system returns a certificate
that cannot be decoded (might be related to SSL implementation issues),
skip them.
diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc
index 0c6b12f8e17b4a7e86ebc836a4e1cc77333f211a..dacf10c3c2e663b03a251c86d69276d0be0dff9d 100644
--- a/src/crypto/crypto_context.cc
+++ b/src/crypto/crypto_context.cc
@@ -505,7 +505,11 @@ void ReadMacOSKeychainCertificates(
CFRelease(search);
if (ortn) {
- fprintf(stderr, "ERROR: SecItemCopyMatching failed %d\n", ortn);
+ per_process::Debug(DebugCategory::CRYPTO,
+ "Cannot read certificates from system because "
+ "SecItemCopyMatching failed %d\n",
+ ortn);
+ return;
}
CFIndex count = CFArrayGetCount(curr_anchors);
@@ -516,7 +520,9 @@ void ReadMacOSKeychainCertificates(
CFDataRef der_data = SecCertificateCopyData(cert_ref);
if (!der_data) {
- fprintf(stderr, "ERROR: SecCertificateCopyData failed\n");
+ per_process::Debug(DebugCategory::CRYPTO,
+ "Skipping read of a system certificate "
+ "because SecCertificateCopyData failed\n");
continue;
}
auto data_buffer_pointer = CFDataGetBytePtr(der_data);
@@ -524,9 +530,19 @@ void ReadMacOSKeychainCertificates(
X509* cert =
d2i_X509(nullptr, &data_buffer_pointer, CFDataGetLength(der_data));
CFRelease(der_data);
+
+ if (cert == nullptr) {
+ per_process::Debug(DebugCategory::CRYPTO,
+ "Skipping read of a system certificate "
+ "because decoding failed\n");
+ continue;
+ }
+
bool is_valid = IsCertificateTrustedForPolicy(cert, cert_ref);
if (is_valid) {
system_root_certificates_X509->emplace_back(cert);
+ } else {
+ X509_free(cert);
}
}
CFRelease(curr_anchors);
@@ -636,7 +652,14 @@ void GatherCertsForLocation(std::vector<X509*>* vector,
reinterpret_cast<const unsigned char*>(cert_from_store->pbCertEncoded);
const size_t cert_size = cert_from_store->cbCertEncoded;
- vector->emplace_back(d2i_X509(nullptr, &cert_data, cert_size));
+ X509* x509 = d2i_X509(nullptr, &cert_data, cert_size);
+ if (x509 == nullptr) {
+ per_process::Debug(DebugCategory::CRYPTO,
+ "Skipping read of a system certificate "
+ "because decoding failed\n");
+ } else {
+ vector->emplace_back(x509);
+ }
}
}

View File

@@ -1,3 +1,2 @@
chore_allow_customizing_microtask_policy_per_context.patch
turboshaft_avoid_introducing_too_many_variables.patch
preserve_field_repr_in_property_array_extension.patch

View File

@@ -1,302 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Leszek Swirski <leszeks@chromium.org>
Date: Wed, 12 Nov 2025 16:46:01 +0100
Subject: Preserve field repr in property array extension
Walk the descriptor array in lockstep with the property array when
extending the latter.
Fixed: 460017370
Change-Id: If0b4fc3c5f62fc0cc373588cbddc3c0a95c7225c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7146166
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#103674}
diff --git a/src/compiler/access-builder.cc b/src/compiler/access-builder.cc
index c0bf470ada9443c63805b9e331bfdceb230929a8..8517527ecc2df89289cde05d7bc124f34133a838 100644
--- a/src/compiler/access-builder.cc
+++ b/src/compiler/access-builder.cc
@@ -4,6 +4,8 @@
#include "src/compiler/access-builder.h"
+#include "src/codegen/machine-type.h"
+#include "src/compiler/property-access-builder.h"
#include "src/compiler/type-cache.h"
#include "src/handles/handles-inl.h"
#include "src/objects/arguments.h"
@@ -1097,12 +1099,16 @@ FieldAccess AccessBuilder::ForFeedbackVectorSlot(int index) {
}
// static
-FieldAccess AccessBuilder::ForPropertyArraySlot(int index) {
+FieldAccess AccessBuilder::ForPropertyArraySlot(int index,
+ Representation representation) {
int offset = PropertyArray::OffsetOfElementAt(index);
- FieldAccess access = {kTaggedBase, offset,
- Handle<Name>(), OptionalMapRef(),
- Type::Any(), MachineType::AnyTagged(),
- kFullWriteBarrier, "PropertyArraySlot"};
+ MachineType machine_type =
+ representation.IsHeapObject() || representation.IsDouble()
+ ? MachineType::TaggedPointer()
+ : MachineType::AnyTagged();
+ FieldAccess access = {
+ kTaggedBase, offset, Handle<Name>(), OptionalMapRef(),
+ Type::Any(), machine_type, kFullWriteBarrier, "PropertyArraySlot"};
return access;
}
diff --git a/src/compiler/access-builder.h b/src/compiler/access-builder.h
index 0363f0fc34593c8cb5dfda4c3b59a59664d689e3..b946fc58690cbe5b12903a832d0178c83a87416c 100644
--- a/src/compiler/access-builder.h
+++ b/src/compiler/access-builder.h
@@ -11,6 +11,7 @@
#include "src/compiler/write-barrier-kind.h"
#include "src/objects/elements-kind.h"
#include "src/objects/js-objects.h"
+#include "src/objects/property-details.h"
namespace v8 {
namespace internal {
@@ -323,7 +324,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
static FieldAccess ForFeedbackVectorSlot(int index);
// Provides access to PropertyArray slots.
- static FieldAccess ForPropertyArraySlot(int index);
+ static FieldAccess ForPropertyArraySlot(int index,
+ Representation representation);
// Provides access to ScopeInfo flags.
static FieldAccess ForScopeInfoFlags();
diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc
index 60b4df5fbb6b762cee6609c5f0b3452e68e0fe64..697bb0526d465df96378ea3f81ad725f49fd3dec 100644
--- a/src/compiler/js-native-context-specialization.cc
+++ b/src/compiler/js-native-context-specialization.cc
@@ -38,6 +38,7 @@
#include "src/objects/elements-kind.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/heap-number.h"
+#include "src/objects/property-details.h"
#include "src/objects/string.h"
namespace v8 {
@@ -4231,25 +4232,59 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
// for intermediate states of chains of property additions. That makes
// it unclear what the best approach is here.
DCHECK_EQ(map.UnusedPropertyFields(), 0);
- int length = map.NextFreePropertyIndex() - map.GetInObjectProperties();
+ int in_object_length = map.GetInObjectProperties();
+ int length = map.NextFreePropertyIndex() - in_object_length;
// Under normal circumstances, NextFreePropertyIndex() will always be larger
// than GetInObjectProperties(). However, an attacker able to corrupt heap
// memory can break this invariant, in which case we'll get confused here,
// potentially causing a sandbox violation. This CHECK defends against that.
SBXCHECK_GE(length, 0);
int new_length = length + JSObject::kFieldsAdded;
+
+ // Find the descriptor index corresponding to the first out-of-object
+ // property.
+ DescriptorArrayRef descs = map.instance_descriptors(broker());
+ InternalIndex first_out_of_object_descriptor(in_object_length);
+ InternalIndex number_of_descriptors(descs.object()->number_of_descriptors());
+ for (InternalIndex i(in_object_length); i < number_of_descriptors; ++i) {
+ PropertyDetails details = descs.GetPropertyDetails(i);
+ // Skip over non-field properties.
+ if (details.location() != PropertyLocation::kField) {
+ continue;
+ }
+ // Skip over in-object fields.
+ // TODO(leszeks): We could make this smarter, like a binary search.
+ if (details.field_index() < in_object_length) {
+ continue;
+ }
+ first_out_of_object_descriptor = i;
+ break;
+ }
+
// Collect the field values from the {properties}.
- ZoneVector<Node*> values(zone());
+ ZoneVector<std::pair<Node*, Representation>> values(zone());
values.reserve(new_length);
- for (int i = 0; i < length; ++i) {
+
+ // Walk the property descriptors alongside the property values, to make
+ // sure to get and store them with the right machine type.
+ InternalIndex descriptor = first_out_of_object_descriptor;
+ for (int i = 0; i < length; ++i, ++descriptor) {
+ PropertyDetails details = descs.GetPropertyDetails(descriptor);
+ while (details.location() != PropertyLocation::kField) {
+ ++descriptor;
+ details = descs.GetPropertyDetails(descriptor);
+ }
+ DCHECK_EQ(i, details.field_index() - in_object_length);
Node* value = effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForFixedArraySlot(i)),
+ simplified()->LoadField(
+ AccessBuilder::ForPropertyArraySlot(i, details.representation())),
properties, effect, control);
- values.push_back(value);
+ values.push_back({value, details.representation()});
}
// Initialize the new fields to undefined.
for (int i = 0; i < JSObject::kFieldsAdded; ++i) {
- values.push_back(jsgraph()->UndefinedConstant());
+ values.push_back(
+ {jsgraph()->UndefinedConstant(), Representation::Tagged()});
}
// Compute new length and hash.
@@ -4287,7 +4322,8 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
a.Store(AccessBuilder::ForMap(), jsgraph()->PropertyArrayMapConstant());
a.Store(AccessBuilder::ForPropertyArrayLengthAndHash(), new_length_and_hash);
for (int i = 0; i < new_length; ++i) {
- a.Store(AccessBuilder::ForFixedArraySlot(i), values[i]);
+ a.Store(AccessBuilder::ForPropertyArraySlot(i, values[i].second),
+ values[i].first);
}
return a.Finish();
}
diff --git a/src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h b/src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h
index bf0832ecc67bec1a6da711b9bff7266ba3df7d51..a6452e7cfb9e7d7d6d222bd625d13dabe4f97cb7 100644
--- a/src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h
+++ b/src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h
@@ -14,6 +14,7 @@
#include "src/compiler/turboshaft/representations.h"
#include "src/deoptimizer/deoptimize-reason.h"
#include "src/objects/contexts.h"
+#include "src/objects/descriptor-array-inl.h"
#include "src/objects/instance-type-inl.h"
namespace v8::internal::compiler::turboshaft {
@@ -323,8 +324,32 @@ class TurbolevEarlyLoweringReducer : public Next {
}
V<PropertyArray> ExtendPropertiesBackingStore(
- V<PropertyArray> old_property_array, V<JSObject> object, int old_length,
+ V<PropertyArray> old_property_array, V<JSObject> object,
+ const compiler::MapRef& old_map, int old_length,
V<FrameState> frame_state, const FeedbackSource& feedback) {
+ int in_object_length = old_map.GetInObjectProperties();
+
+ // Find the descriptor index corresponding to the first out-of-object
+ // property.
+ DescriptorArrayRef descs = old_map.instance_descriptors(broker_);
+ InternalIndex first_out_of_object_descriptor(in_object_length);
+ InternalIndex number_of_descriptors(
+ descs.object()->number_of_descriptors());
+ for (InternalIndex i(in_object_length); i < number_of_descriptors; ++i) {
+ PropertyDetails details = descs.GetPropertyDetails(i);
+ // Skip over non-field properties.
+ if (details.location() != PropertyLocation::kField) {
+ continue;
+ }
+ // Skip over in-object fields.
+ // TODO(leszeks): We could make this smarter, like a binary search.
+ if (details.field_index() < in_object_length) {
+ continue;
+ }
+ first_out_of_object_descriptor = i;
+ break;
+ }
+
// Allocate new PropertyArray.
int new_length = old_length + JSObject::kFieldsAdded;
Uninitialized<PropertyArray> new_property_array =
@@ -335,18 +360,28 @@ class TurbolevEarlyLoweringReducer : public Next {
__ HeapConstant(factory_->property_array_map()));
// Copy existing properties over.
- for (int i = 0; i < old_length; i++) {
+ InternalIndex descriptor = first_out_of_object_descriptor;
+ for (int i = 0; i < old_length; ++i, ++descriptor) {
+ PropertyDetails details = descs.GetPropertyDetails(descriptor);
+ while (details.location() != PropertyLocation::kField) {
+ ++descriptor;
+ details = descs.GetPropertyDetails(descriptor);
+ }
+ DCHECK_EQ(i, details.field_index() - in_object_length);
+ Representation r = details.representation();
+
V<Object> old_value = __ template LoadField<Object>(
- old_property_array, AccessBuilder::ForPropertyArraySlot(i));
+ old_property_array, AccessBuilder::ForPropertyArraySlot(i, r));
__ InitializeField(new_property_array,
- AccessBuilder::ForPropertyArraySlot(i), old_value);
+ AccessBuilder::ForPropertyArraySlot(i, r), old_value);
}
// Initialize new properties to undefined.
V<Undefined> undefined = __ HeapConstant(factory_->undefined_value());
for (int i = 0; i < JSObject::kFieldsAdded; ++i) {
__ InitializeField(new_property_array,
- AccessBuilder::ForPropertyArraySlot(old_length + i),
+ AccessBuilder::ForPropertyArraySlot(
+ old_length + i, Representation::Tagged()),
undefined);
}
diff --git a/src/compiler/turboshaft/turbolev-graph-builder.cc b/src/compiler/turboshaft/turbolev-graph-builder.cc
index bcb461488899bcbf3a130845f39f0005bd34131e..d80362036da4c80e192ed489e3c66e8bfed271ba 100644
--- a/src/compiler/turboshaft/turbolev-graph-builder.cc
+++ b/src/compiler/turboshaft/turbolev-graph-builder.cc
@@ -2760,10 +2760,11 @@ class GraphBuildingNodeProcessor {
maglev::ProcessResult Process(maglev::ExtendPropertiesBackingStore* node,
const maglev::ProcessingState& state) {
GET_FRAME_STATE_MAYBE_ABORT(frame_state, node->eager_deopt_info());
- SetMap(node, __ ExtendPropertiesBackingStore(
- Map(node->property_array_input()),
- Map(node->object_input()), node->old_length(), frame_state,
- node->eager_deopt_info()->feedback_to_update()));
+ SetMap(node,
+ __ ExtendPropertiesBackingStore(
+ Map(node->property_array_input()), Map(node->object_input()),
+ node->old_map(), node->old_length(), frame_state,
+ node->eager_deopt_info()->feedback_to_update()));
return maglev::ProcessResult::kContinue;
}
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
index a2cac0c5ded6806292d98218dcb6da0f7430f16b..4664ca78b4413da6a3d9d6cafa705d33f5c02ee2 100644
--- a/src/maglev/maglev-graph-builder.cc
+++ b/src/maglev/maglev-graph-builder.cc
@@ -5253,7 +5253,7 @@ ReduceResult MaglevGraphBuilder::BuildExtendPropertiesBackingStore(
// potentially causing a sandbox violation. This CHECK defends against that.
SBXCHECK_GE(length, 0);
return AddNewNode<ExtendPropertiesBackingStore>({property_array, receiver},
- length);
+ map, length);
}
MaybeReduceResult MaglevGraphBuilder::TryBuildStoreField(
diff --git a/src/maglev/maglev-ir.h b/src/maglev/maglev-ir.h
index 9d5cf70429ed5513aa7cd23baf1a044acfc25feb..707d8fd56a367856a648dbe152e3d945416c70af 100644
--- a/src/maglev/maglev-ir.h
+++ b/src/maglev/maglev-ir.h
@@ -9084,8 +9084,10 @@ class ExtendPropertiesBackingStore
using Base = FixedInputValueNodeT<2, ExtendPropertiesBackingStore>;
public:
- explicit ExtendPropertiesBackingStore(uint64_t bitfield, int old_length)
- : Base(bitfield), old_length_(old_length) {}
+ explicit ExtendPropertiesBackingStore(uint64_t bitfield,
+ const compiler::MapRef& old_map,
+ int old_length)
+ : Base(bitfield), old_map_(old_map), old_length_(old_length) {}
static constexpr OpProperties kProperties =
OpProperties::CanAllocate() | OpProperties::CanRead() |
@@ -9105,9 +9107,11 @@ class ExtendPropertiesBackingStore
void GenerateCode(MaglevAssembler*, const ProcessingState&);
void PrintParams(std::ostream&) const;
+ const compiler::MapRef& old_map() const { return old_map_; }
int old_length() const { return old_length_; }
private:
+ const compiler::MapRef old_map_;
const int old_length_;
};

View File

@@ -325,7 +325,7 @@ index 181d298bfa27d21f013016b34a586078d12f8a58..92d6f7b36d4c5c0a64723f7d18427a62
#undef TRACE
diff --git a/src/compiler/turboshaft/turbolev-graph-builder.cc b/src/compiler/turboshaft/turbolev-graph-builder.cc
index 9d1dafe2b0b733c88283b21eddb36c9827912eca..bcb461488899bcbf3a130845f39f0005bd34131e 100644
index 3db187b8c48cc0c7168be039e7d90078c4df7bda..d80362036da4c80e192ed489e3c66e8bfed271ba 100644
--- a/src/compiler/turboshaft/turbolev-graph-builder.cc
+++ b/src/compiler/turboshaft/turbolev-graph-builder.cc
@@ -118,12 +118,7 @@ class BlockOriginTrackingReducer : public Next {

View File

@@ -40,6 +40,7 @@ DevToolsSecurity -enable
# security import "$dir"/public.key -k $KEY_CHAIN
# Generate Trust Settings
# TODO: Remove NPX
npm_config_yes=true npx ts-node "$(dirname $0)"/gen-trust.ts "$dir"/certificate.cer "$dir"/trust.xml
# Import Trust Settings

View File

@@ -153,7 +153,7 @@ const LINTERS = [{
}, {
key: 'javascript',
roots: ['build', 'default_app', 'lib', 'npm', 'script', 'spec'],
ignoreRoots: ['spec/node_modules'],
ignoreRoots: ['spec/node_modules', 'spec/fixtures/native-addon'],
test: filename => filename.endsWith('.js') || filename.endsWith('.ts') || filename.endsWith('.mjs'),
run: async (opts, filenames) => {
const eslint = new ESLint({
@@ -282,7 +282,7 @@ const LINTERS = [{
}, {
key: 'md',
roots: ['.'],
ignoreRoots: ['.git', 'node_modules', 'spec/node_modules'],
ignoreRoots: ['.git', 'node_modules', 'spec/node_modules', 'spec/fixtures/native-addon'],
test: filename => filename.endsWith('.md'),
run: async (opts, filenames) => {
const { getCodeBlocks } = await import('@electron/lint-roller/dist/lib/markdown.js');

View File

@@ -9,7 +9,7 @@ const NAN_DIR = path.resolve(BASE, 'third_party', 'nan');
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
const utils = require('./lib/utils');
const { YARN_VERSION } = require('./yarn');
const { YARN_SCRIPT_PATH } = require('./yarn');
if (!require.main) {
throw new Error('Must call the nan spec runner directly');
@@ -106,13 +106,12 @@ async function main () {
stdio: 'inherit',
shell: process.platform === 'win32'
});
if (buildStatus !== 0 || signal != null) {
console.error('Failed to build nan test modules');
return process.exit(buildStatus !== 0 ? buildStatus : signal);
}
const { status: installStatus, signal: installSignal } = cp.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install'], {
const { status: installStatus, signal: installSignal } = cp.spawnSync(process.execPath, [YARN_SCRIPT_PATH, 'install'], {
env,
cwd: NAN_DIR,
stdio: 'inherit',

View File

@@ -211,6 +211,7 @@ new Promise<string>((resolve, reject) => {
});
})
.then((tarballPath) => {
// TODO: Remove NPX
const existingVersionJSON = childProcess.execSync(`npx npm@7 view ${rootPackageJson.name}@${currentElectronVersion} --json`).toString('utf-8');
// It's possible this is a re-run and we already have published the package, if not we just publish like normal
if (!existingVersionJSON) {

View File

@@ -21,6 +21,7 @@ const fail = chalk.red('✗');
const FAILURE_STATUS_KEY = 'Electron_Spec_Runner_Failures';
const args = minimist(process.argv, {
boolean: ['skipYarnInstall'],
string: ['runners', 'target', 'electronVersion'],
number: ['enableRerun'],
unknown: arg => unknownFlags.push(arg)
@@ -36,10 +37,9 @@ for (const flag of unknownFlags) {
}
const utils = require('./lib/utils');
const { YARN_VERSION } = require('./yarn');
const { YARN_SCRIPT_PATH } = require('./yarn');
const BASE = path.resolve(__dirname, '../..');
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
const runners = new Map([
['main', { description: 'Main process specs', run: runMainProcessElectronTests }]
@@ -96,7 +96,7 @@ async function main () {
const somethingChanged = (currentSpecHash !== lastSpecHash) ||
(lastSpecInstallHash !== currentSpecInstallHash);
if (somethingChanged) {
if (somethingChanged && !args.skipYarnInstall) {
await installSpecModules(path.resolve(__dirname, '..', 'spec'));
await getSpecHash().then(saveSpecHash);
}
@@ -375,7 +375,7 @@ async function runTestUsingElectron (specDir, testName, shouldRerun, additionalA
if (shouldRerun) {
await rerunFailedTests(specDir, testName);
} else {
return false;
process.exit(1);
}
}
console.log(`${pass} Electron ${testName} process tests passed.`);
@@ -419,7 +419,8 @@ async function installSpecModules (dir) {
if (fs.existsSync(path.resolve(dir, 'node_modules'))) {
await fs.promises.rm(path.resolve(dir, 'node_modules'), { force: true, recursive: true });
}
const { status } = childProcess.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install', '--frozen-lockfile'], {
const yarnArgs = [YARN_SCRIPT_PATH, 'install', '--immutable'];
const { status } = childProcess.spawnSync(process.execPath, yarnArgs, {
env,
cwd: dir,
stdio: 'inherit',
@@ -429,14 +430,27 @@ async function installSpecModules (dir) {
console.log(`${fail} Failed to yarn install in '${dir}'`);
process.exit(1);
}
if (process.platform === 'linux') {
const { status: rebuildStatus } = childProcess.spawnSync('npm', ['rebuild', 'abstract-socket'], {
env,
cwd: dir,
stdio: 'inherit',
shell: process.platform === 'win32'
});
if (rebuildStatus !== 0) {
console.log(`${fail} Failed to rebuild abstract-socket native module`);
process.exit(1);
}
}
}
function getSpecHash () {
return Promise.all([
(async () => {
const hasher = crypto.createHash('SHA256');
hasher.update(fs.readFileSync(path.resolve(__dirname, '../yarn.lock')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/yarn.lock')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../script/spec-runner.js')));
return hasher.digest('hex');
})(),

18
script/yarn.js Normal file → Executable file
View File

@@ -1,21 +1,7 @@
const cp = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const YARN_VERSION = /'yarn_version': '(.+?)'/.exec(fs.readFileSync(path.resolve(__dirname, '../DEPS'), 'utf8'))[1];
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
exports.YARN_SCRIPT_PATH = path.resolve(__dirname, '..', '.yarn/releases/yarn-4.12.0.cjs');
if (require.main === module) {
const child = cp.spawn(NPX_CMD, [`yarn@${YARN_VERSION}`, ...process.argv.slice(2)], {
stdio: 'inherit',
env: {
...process.env,
npm_config_yes: 'true'
},
shell: process.platform === 'win32'
});
child.on('exit', code => process.exit(code));
require(exports.YARN_SCRIPT_PATH);
}
exports.YARN_VERSION = YARN_VERSION;

View File

@@ -155,7 +155,8 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
if (rightmostMenuPoint > screenRight)
position.x = position.x - [menu size].width;
[popup_controllers_[window_id] setCloseCallback:std::move(close_callback)];
[popup_controllers_[window_id]
setPopupCloseCallback:std::move(close_callback)];
if (frame && frame->render_frame_host()) {
auto* rfh = frame->render_frame_host()->GetOutermostMainFrameOrEmbedder();

View File

@@ -2395,6 +2395,9 @@ void WebContents::LoadURL(const GURL& url,
return;
}
if (web_contents()->NeedToFireBeforeUnloadOrUnloadEvents())
pending_unload_url_ = url;
// Discard non-committed entries to ensure we don't re-use a pending entry.
web_contents()->GetController().DiscardNonCommittedEntries();
web_contents()->GetController().LoadURLWithParams(params);
@@ -3897,8 +3900,15 @@ void WebContents::RunBeforeUnloadDialog(content::WebContents* web_contents,
content::RenderFrameHost* rfh,
bool is_reload,
DialogClosedCallback callback) {
// TODO: asyncify?
bool default_prevented = Emit("will-prevent-unload");
if (pending_unload_url_.has_value() && !default_prevented) {
Emit("did-fail-load", static_cast<int>(net::ERR_ABORTED),
net::ErrorToShortString(net::ERR_ABORTED),
pending_unload_url_.value().possibly_invalid_spec(), true);
pending_unload_url_.reset();
}
std::move(callback).Run(default_prevented, std::u16string());
}

View File

@@ -844,6 +844,8 @@ class WebContents final : public ExclusiveAccessContext,
// that field to ensure the dtor destroys them in the right order.
raw_ptr<WebContentsZoomController> zoom_controller_ = nullptr;
std::optional<GURL> pending_unload_url_ = std::nullopt;
// Maps url to file path, used by the file requests sent from devtools.
typedef std::map<std::string, base::FilePath> PathsMap;
PathsMap saved_files_;

View File

@@ -49,6 +49,8 @@ void ElectronApiIPCHandlerImpl::Message(bool internal,
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
auto* event = MakeIPCEvent(isolate, session->Get(), internal);
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->Message(event_object, channel, std::move(arguments));
@@ -64,6 +66,8 @@ void ElectronApiIPCHandlerImpl::Invoke(bool internal,
v8::HandleScope handle_scope(isolate);
auto* event =
MakeIPCEvent(isolate, session->Get(), internal, std::move(callback));
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->Invoke(event_object, channel, std::move(arguments));
@@ -78,6 +82,8 @@ void ElectronApiIPCHandlerImpl::ReceivePostMessage(
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
auto* event = MakeIPCEvent(isolate, session->Get(), false);
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->ReceivePostMessage(event_object, channel,
@@ -95,6 +101,8 @@ void ElectronApiIPCHandlerImpl::MessageSync(bool internal,
v8::HandleScope handle_scope(isolate);
auto* event =
MakeIPCEvent(isolate, session->Get(), internal, std::move(callback));
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->MessageSync(event_object, channel, std::move(arguments));
@@ -108,6 +116,8 @@ void ElectronApiIPCHandlerImpl::MessageHost(const std::string& channel,
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
auto* event = MakeIPCEvent(isolate, session->Get(), false);
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->MessageHost(event_object, channel, std::move(arguments));

View File

@@ -76,6 +76,8 @@ void ElectronApiSWIPCHandlerImpl::Message(bool internal,
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
auto* event = MakeIPCEvent(isolate, session->Get(), internal);
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->Message(event_object, channel, std::move(arguments));
@@ -92,6 +94,8 @@ void ElectronApiSWIPCHandlerImpl::Invoke(bool internal,
v8::HandleScope handle_scope(isolate);
auto* event =
MakeIPCEvent(isolate, session->Get(), internal, std::move(callback));
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->Invoke(event_object, channel, std::move(arguments));
@@ -106,6 +110,8 @@ void ElectronApiSWIPCHandlerImpl::ReceivePostMessage(
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
auto* event = MakeIPCEvent(isolate, session->Get(), false);
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->ReceivePostMessage(event_object, channel,
@@ -123,6 +129,8 @@ void ElectronApiSWIPCHandlerImpl::MessageSync(bool internal,
v8::HandleScope handle_scope(isolate);
auto* event =
MakeIPCEvent(isolate, session->Get(), internal, std::move(callback));
if (!event)
return;
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
session->Get()->MessageSync(event_object, channel, std::move(arguments));

View File

@@ -62,7 +62,6 @@
#include "shell/common/logging.h"
#include "shell/common/node_bindings.h"
#include "shell/common/node_includes.h"
#include "shell/common/v8_util.h"
#include "ui/base/idle/idle.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_switches.h"
@@ -275,10 +274,6 @@ void ElectronBrowserMainParts::PostEarlyInitialization() {
// Initialize field trials.
InitializeFieldTrials();
if (base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) {
electron::SetUpWebAssemblyTrapHandler();
}
// Reinitialize logging now that the app has had a chance to set the app name
// and/or user data directory.
logging::InitElectronLogging(*base::CommandLine::ForCurrentProcess(),

View File

@@ -554,7 +554,7 @@ bool NativeWindowViews::IsFocused() const {
}
void NativeWindowViews::Show() {
if (is_modal() && NativeWindow::parent())
if (is_modal() && NativeWindow::parent() && !widget()->IsVisible())
static_cast<NativeWindowViews*>(parent())->IncrementChildModals();
widget()->native_widget_private()->Show(GetRestoredState(), gfx::Rect());

View File

@@ -14,6 +14,12 @@ const bool debug_notifications =
base::Environment::Create()->HasVar("ELECTRON_DEBUG_NOTIFICATIONS");
NotificationOptions::NotificationOptions() = default;
NotificationOptions::NotificationOptions(const NotificationOptions&) = default;
NotificationOptions& NotificationOptions::operator=(
const NotificationOptions&) = default;
NotificationOptions::NotificationOptions(NotificationOptions&&) = default;
NotificationOptions& NotificationOptions::operator=(NotificationOptions&&) =
default;
NotificationOptions::~NotificationOptions() = default;
Notification::Notification(NotificationDelegate* delegate,

View File

@@ -43,6 +43,10 @@ struct NotificationOptions {
std::u16string toast_xml;
NotificationOptions();
NotificationOptions(const NotificationOptions&);
NotificationOptions& operator=(const NotificationOptions&);
NotificationOptions(NotificationOptions&&);
NotificationOptions& operator=(NotificationOptions&&);
~NotificationOptions();
};

View File

@@ -16,11 +16,13 @@
#include "base/containers/fixed_flat_map.h"
#include "base/hash/hash.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/string_util_win.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "shell/browser/notifications/notification_delegate.h"
@@ -97,14 +99,16 @@ const std::string FailureResultToString(HRESULT failure_reason) {
"The notification platform does not have the proper privileges to "
"complete the request."}});
std::string hresult_str = base::StrCat(
{" (HRESULT: ", base::NumberToString(static_cast<long>(failure_reason)),
")"});
if (const auto it = kFailureMessages.find(failure_reason);
it != kFailureMessages.end()) {
return base::StrCat({"Notification failed - ", it->second});
return base::StrCat({"Notification failed - ", it->second, hresult_str});
}
return base::StrCat({"Notification failed - Unknown failure reason (HRESULT ",
base::NumberToString(static_cast<long>(failure_reason)),
")"});
return hresult_str;
}
constexpr char kToast[] = "toast";
@@ -155,6 +159,17 @@ ComPtr<winui::Notifications::IToastNotifier>*
WindowsToastNotification::toast_notifier_ = nullptr;
// static
scoped_refptr<base::SequencedTaskRunner>
WindowsToastNotification::GetToastTaskRunner() {
// Use function-local static to avoid exit-time destructor warning
static base::NoDestructor<scoped_refptr<base::SequencedTaskRunner>>
task_runner(base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
base::MayBlock()}));
return *task_runner;
}
bool WindowsToastNotification::Initialize() {
// Just initialize, don't care if it fails or already initialized.
Windows::Foundation::Initialize(RO_INIT_MULTITHREADED);
@@ -207,13 +222,277 @@ WindowsToastNotification::~WindowsToastNotification() {
}
}
// This method posts a request onto the toast background thread, which
// creates the toast xml then posts notification creation to the UI thread. This
// avoids blocking the UI for expensive XML parsing and COM initialization or
// the COM server becoming unavailable. All needed fields are captured before
// posting the task.
// The method will eventually result in a display or failure signal being posted
// back to the UI thread, where further callbacks (clicked, dismissed, failed)
// are handled by ToastEventHandler.
void WindowsToastNotification::Show(const NotificationOptions& options) {
if (SUCCEEDED(ShowInternal(options))) {
DebugLog("Notification created");
if (delegate())
delegate()->NotificationDisplayed();
DebugLog("WindowsToastNotification::Show called");
DebugLog(base::StrCat(
{"toast_xml empty: ", options.toast_xml.empty() ? "yes" : "no"}));
if (!options.toast_xml.empty()) {
DebugLog(base::StrCat({"toast_xml length: ",
base::NumberToString(options.toast_xml.length())}));
}
// Capture all needed data on UI thread before posting to background thread
std::string notif_id = notification_id();
NotificationPresenter* presenter_ptr = presenter();
base::WeakPtr<Notification> weak_this = GetWeakPtr();
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner =
content::GetUIThreadTaskRunner({});
DebugLog("Posting task to background thread");
auto task_runner = GetToastTaskRunner();
DebugLog(base::StrCat({"Task runner valid: ", task_runner ? "yes" : "no"}));
// Post Show operation to background thread to prevent blocking
// This is the main entry point for the notification creation process
bool posted = task_runner->PostTask(
FROM_HERE,
base::BindOnce(
&WindowsToastNotification::CreateToastNotificationOnBackgroundThread,
options, presenter_ptr, notif_id, weak_this, ui_task_runner));
DebugLog(base::StrCat(
{"Task posted to background thread: ", posted ? "yes" : "no"}));
}
// Creates the toast XML on the background thread. If the XML is invalid, posts
// a failure event back to the UI thread. Otherwise, continues to create the
// toast notification on the background thread.
void WindowsToastNotification::CreateToastNotificationOnBackgroundThread(
const NotificationOptions& options,
NotificationPresenter* presenter,
const std::string& notification_id,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
DebugLog("CreateToastXmlOnBackgroundThread called");
ComPtr<IXmlDocument> toast_xml;
if (!CreateToastXmlDocument(options, presenter, weak_notification,
ui_task_runner, &toast_xml)) {
return; // Error already posted to UI thread
}
// Continue to create the toast notification
ComPtr<ABI::Windows::UI::Notifications::IToastNotification>
toast_notification;
if (!CreateToastNotification(toast_xml, notification_id, weak_notification,
ui_task_runner, &toast_notification)) {
return; // Error already posted to UI thread
}
// Setup callbacks and show on UI thread (Show must be called on UI thread)
scoped_refptr<base::SingleThreadTaskRunner> ui_runner =
content::GetUIThreadTaskRunner({});
ui_runner->PostTask(
FROM_HERE,
base::BindOnce(&WindowsToastNotification::SetupAndShowOnUIThread,
weak_notification, toast_notification));
}
// Creates the toast XML document on the background thread. Returns true on
// success, false on failure. On failure, posts error to UI thread. static
bool WindowsToastNotification::CreateToastXmlDocument(
const NotificationOptions& options,
NotificationPresenter* presenter,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
ComPtr<IXmlDocument>* toast_xml) {
// The custom xml takes priority over the preset template.
if (!options.toast_xml.empty()) {
DebugLog(base::StrCat({"Processing custom toast_xml, length: ",
base::NumberToString(options.toast_xml.length())}));
HRESULT hr = XmlDocumentFromString(base::as_wcstr(options.toast_xml),
toast_xml->GetAddressOf());
DebugLog(base::StrCat({"XmlDocumentFromString returned HRESULT: ",
base::NumberToString(hr)}));
if (FAILED(hr)) {
std::string err =
base::StrCat({"XML: Invalid XML, ERROR ", FailureResultToString(hr)});
DebugLog(base::StrCat({"XML parsing failed, posting error: ", err}));
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
DebugLog("PostNotificationFailedToUIThread called");
return false;
}
DebugLog("XML parsing succeeded");
} else {
auto* presenter_win = static_cast<NotificationPresenterWin*>(presenter);
std::wstring icon_path =
presenter_win->SaveIconToFilesystem(options.icon, options.icon_url);
std::u16string toast_xml_str =
GetToastXml(options.title, options.msg, icon_path, options.timeout_type,
options.silent);
HRESULT hr = XmlDocumentFromString(base::as_wcstr(toast_xml_str),
toast_xml->GetAddressOf());
if (FAILED(hr)) {
std::string err =
base::StrCat({"XML: Invalid XML, ERROR ", FailureResultToString(hr)});
DebugLog(err);
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
return false;
}
}
return true;
}
// Creates the toast notification on the background thread. Returns true on
// success, false on failure. On failure, posts error to UI thread. On success,
// returns the created notification via out parameter.
bool WindowsToastNotification::CreateToastNotification(
ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> toast_xml,
const std::string& notification_id,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
ComPtr<ABI::Windows::UI::Notifications::IToastNotification>*
toast_notification) {
ScopedHString toast_str(
RuntimeClass_Windows_UI_Notifications_ToastNotification);
if (!toast_str.success()) {
PostNotificationFailedToUIThread(
weak_notification, "Creating ScopedHString failed", ui_task_runner);
return false;
}
ComPtr<winui::Notifications::IToastNotificationFactory> toast_factory;
HRESULT hr =
Windows::Foundation::GetActivationFactory(toast_str, &toast_factory);
if (FAILED(hr)) {
std::string err =
base::StrCat({"WinAPI: GetActivationFactory failed, ERROR ",
FailureResultToString(hr)});
DebugLog(err);
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
return false;
}
hr = toast_factory->CreateToastNotification(
toast_xml.Get(), toast_notification->GetAddressOf());
if (FAILED(hr)) {
std::string err =
base::StrCat({"WinAPI: CreateToastNotification failed, ERROR ",
FailureResultToString(hr)});
DebugLog(err);
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
return false;
}
ComPtr<winui::Notifications::IToastNotification2> toast2;
hr = (*toast_notification)->QueryInterface(IID_PPV_ARGS(&toast2));
if (FAILED(hr)) {
std::string err =
base::StrCat({"WinAPI: Getting Notification interface failed, ERROR ",
FailureResultToString(hr)});
DebugLog(err);
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
return false;
}
ScopedHString group(kGroup);
hr = toast2->put_Group(group);
if (FAILED(hr)) {
std::string err = base::StrCat(
{"WinAPI: Setting group failed, ERROR ", FailureResultToString(hr)});
DebugLog(err);
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
return false;
}
ScopedHString tag(GetTag(notification_id));
hr = toast2->put_Tag(tag);
if (FAILED(hr)) {
std::string err = base::StrCat(
{"WinAPI: Setting tag failed, ERROR ", FailureResultToString(hr)});
DebugLog(err);
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
return false;
}
return true;
}
// Sets up callbacks and shows the notification on the UI thread.
// This part has to be called on the UI thread. This WinRT API
// does not allow calls from background threads.
void WindowsToastNotification::SetupAndShowOnUIThread(
base::WeakPtr<Notification> weak_notification,
ComPtr<ABI::Windows::UI::Notifications::IToastNotification> notification) {
auto* notif = static_cast<WindowsToastNotification*>(weak_notification.get());
if (!notif)
return;
// Setup callbacks and store notification on UI thread
HRESULT hr = notif->SetupCallbacks(notification.Get());
if (FAILED(hr)) {
std::string err = base::StrCat(
{"WinAPI: SetupCallbacks failed, ERROR ", FailureResultToString(hr)});
DebugLog(err);
notif->NotificationFailed(err);
return;
}
notif->toast_notification_ = notification;
// Show notification on UI thread (must be called on UI thread)
hr = (*toast_notifier_)->Show(notification.Get());
if (FAILED(hr)) {
std::string err = base::StrCat(
{"WinAPI: Show failed, ERROR ", FailureResultToString(hr)});
DebugLog(err);
notif->NotificationFailed(err);
return;
}
DebugLog("Notification created");
if (notif->delegate())
notif->delegate()->NotificationDisplayed();
}
// Posts a notification failure event back to the UI thread. If the UI thread's
// task runner is not provided, it fetches it. On the UI thread, calls
// NotificationFailed on the Notification instance (if it is still valid), which
// will invoke the delegate (if set) and clean up.
void WindowsToastNotification::PostNotificationFailedToUIThread(
base::WeakPtr<Notification> weak_notification,
const std::string& error,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
DebugLog(base::StrCat(
{"PostNotificationFailedToUIThread called with error: ", error}));
if (!ui_task_runner) {
ui_task_runner = content::GetUIThreadTaskRunner({});
}
ui_task_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](base::WeakPtr<Notification> weak_notification,
const std::string& error) {
DebugLog(
"PostNotificationFailedToUIThread lambda executing on UI "
"thread");
if (!weak_notification) {
DebugLog(base::StrCat(
{"Notification failed but object destroyed: ", error}));
return;
}
// Call NotificationFailed - it will check for delegate internally
// and emit the event if delegate is set
DebugLog(base::StrCat(
{"Calling NotificationFailed with error: ", error}));
auto* delegate = weak_notification->delegate();
DebugLog(
base::StrCat({"Delegate is set: ", delegate ? "yes" : "no"}));
// Call NotificationFailed which will call delegate if set, then
// cleanup
weak_notification->NotificationFailed(error);
DebugLog("NotificationFailed call completed");
},
weak_notification, error));
}
void WindowsToastNotification::Remove() {
@@ -243,64 +522,6 @@ void WindowsToastNotification::Dismiss() {
(*toast_notifier_)->Hide(toast_notification_.Get());
}
HRESULT WindowsToastNotification::ShowInternal(
const NotificationOptions& options) {
ComPtr<IXmlDocument> toast_xml;
// The custom xml takes priority over the preset template.
if (!options.toast_xml.empty()) {
REPORT_AND_RETURN_IF_FAILED(
XmlDocumentFromString(base::as_wcstr(options.toast_xml), &toast_xml),
"XML: Invalid XML");
} else {
auto* presenter_win = static_cast<NotificationPresenterWin*>(presenter());
std::wstring icon_path =
presenter_win->SaveIconToFilesystem(options.icon, options.icon_url);
std::u16string toast_xml_str =
GetToastXml(options.title, options.msg, icon_path, options.timeout_type,
options.silent);
REPORT_AND_RETURN_IF_FAILED(
XmlDocumentFromString(base::as_wcstr(toast_xml_str), &toast_xml),
"XML: Invalid XML");
}
ScopedHString toast_str(
RuntimeClass_Windows_UI_Notifications_ToastNotification);
if (!toast_str.success()) {
NotificationFailed("Creating ScopedHString failed");
return E_FAIL;
}
ComPtr<winui::Notifications::IToastNotificationFactory> toast_factory;
REPORT_AND_RETURN_IF_FAILED(
Windows::Foundation::GetActivationFactory(toast_str, &toast_factory),
"WinAPI: GetActivationFactory failed");
REPORT_AND_RETURN_IF_FAILED(toast_factory->CreateToastNotification(
toast_xml.Get(), &toast_notification_),
"WinAPI: CreateToastNotification failed");
ComPtr<winui::Notifications::IToastNotification2> toast2;
REPORT_AND_RETURN_IF_FAILED(
toast_notification_->QueryInterface(IID_PPV_ARGS(&toast2)),
"WinAPI: Getting Notification interface failed");
ScopedHString group(kGroup);
REPORT_AND_RETURN_IF_FAILED(toast2->put_Group(group),
"WinAPI: Setting group failed");
ScopedHString tag(GetTag(notification_id()));
REPORT_AND_RETURN_IF_FAILED(toast2->put_Tag(tag),
"WinAPI: Setting tag failed");
REPORT_AND_RETURN_IF_FAILED(SetupCallbacks(toast_notification_.Get()),
"WinAPI: SetupCallbacks failed");
REPORT_AND_RETURN_IF_FAILED(
(*toast_notifier_)->Show(toast_notification_.Get()),
"WinAPI: Show failed");
return S_OK;
}
std::u16string WindowsToastNotification::GetToastXml(
const std::u16string& title,
const std::u16string& msg,

View File

@@ -14,6 +14,8 @@
#include <wrl/implements.h>
#include <string>
#include "base/memory/scoped_refptr.h"
#include "base/task/single_thread_task_runner.h"
#include "shell/browser/notifications/notification.h"
using Microsoft::WRL::ClassicCom;
@@ -58,12 +60,12 @@ class WindowsToastNotification : public Notification {
friend class ToastEventHandler;
HRESULT ShowInternal(const NotificationOptions& options);
std::u16string GetToastXml(const std::u16string& title,
const std::u16string& msg,
const std::wstring& icon_path,
const std::u16string& timeout_type,
const bool silent);
HRESULT XmlDocumentFromString(
static std::u16string GetToastXml(const std::u16string& title,
const std::u16string& msg,
const std::wstring& icon_path,
const std::u16string& timeout_type,
const bool silent);
static HRESULT XmlDocumentFromString(
const wchar_t* xmlString,
ABI::Windows::Data::Xml::Dom::IXmlDocument** doc);
HRESULT SetupCallbacks(
@@ -71,12 +73,43 @@ class WindowsToastNotification : public Notification {
bool RemoveCallbacks(
ABI::Windows::UI::Notifications::IToastNotification* toast);
// Helper methods for async Show() implementation
static bool CreateToastXmlDocument(
const NotificationOptions& options,
NotificationPresenter* presenter,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument>* toast_xml);
static void CreateToastNotificationOnBackgroundThread(
const NotificationOptions& options,
NotificationPresenter* presenter,
const std::string& notification_id,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
static bool CreateToastNotification(
ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> toast_xml,
const std::string& notification_id,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
ComPtr<ABI::Windows::UI::Notifications::IToastNotification>*
toast_notification);
static void SetupAndShowOnUIThread(
base::WeakPtr<Notification> weak_notification,
ComPtr<ABI::Windows::UI::Notifications::IToastNotification> notification);
static void PostNotificationFailedToUIThread(
base::WeakPtr<Notification> weak_notification,
const std::string& error,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
static ComPtr<
ABI::Windows::UI::Notifications::IToastNotificationManagerStatics>*
toast_manager_;
static ComPtr<ABI::Windows::UI::Notifications::IToastNotifier>*
toast_notifier_;
// Returns the task runner for toast operations, creating it if necessary.
static scoped_refptr<base::SequencedTaskRunner> GetToastTaskRunner();
EventRegistrationToken activated_token_;
EventRegistrationToken dismissed_token_;
EventRegistrationToken failed_token_;

View File

@@ -28,7 +28,7 @@ class ElectronMenuModel;
NSMenu* __strong menu_;
BOOL isMenuOpen_;
BOOL useDefaultAccelerator_;
base::OnceClosure closeCallback;
base::OnceClosure popupCloseCallback;
}
// Builds a NSMenu from the pre-built model (must not be nil). Changes made
@@ -36,7 +36,7 @@ class ElectronMenuModel;
- (id)initWithModel:(electron::ElectronMenuModel*)model
useDefaultAccelerator:(BOOL)use;
- (void)setCloseCallback:(base::OnceClosure)callback;
- (void)setPopupCloseCallback:(base::OnceClosure)callback;
// Populate current NSMenu with |model|.
- (void)populateWithModel:(electron::ElectronMenuModel*)model;

View File

@@ -186,8 +186,8 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
model_ = nullptr;
}
- (void)setCloseCallback:(base::OnceClosure)callback {
closeCallback = std::move(callback);
- (void)setPopupCloseCallback:(base::OnceClosure)callback {
popupCloseCallback = std::move(callback);
}
- (void)populateWithModel:(electron::ElectronMenuModel*)model {
@@ -221,9 +221,9 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
isMenuOpen_ = NO;
if (model_)
model_->MenuWillClose();
if (!closeCallback.is_null()) {
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
std::move(closeCallback));
if (!popupCloseCallback.is_null()) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, std::move(popupCloseCallback));
}
}
}
@@ -563,16 +563,29 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
}
- (void)menuDidClose:(NSMenu*)menu {
if (isMenuOpen_) {
isMenuOpen_ = NO;
if (model_)
model_->MenuWillClose();
// Post async task so that itemSelected runs before the close callback
// deletes the controller from the map which deallocates it
if (!closeCallback.is_null()) {
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
std::move(closeCallback));
}
// If the menu is already closed, do nothing.
if (!isMenuOpen_)
return;
bool has_close_cb = !popupCloseCallback.is_null();
// There are two scenarios where we should emit menu-did-close:
// 1. It's a popup and the top level menu is closed.
// 2. It's an application menu, and the current menu's supermenu
// is the top-level menu.
if (menu != menu_) {
if (has_close_cb || menu.supermenu != menu_)
return;
}
isMenuOpen_ = NO;
if (model_)
model_->MenuWillClose();
// Post async task so that itemSelected runs before the close callback
// deletes the controller from the map which deallocates it.
if (has_close_cb) {
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
std::move(popupCloseCallback));
}
}

View File

@@ -8,7 +8,6 @@
#include <utility>
#include <vector>
#include "base/base_switches.h"
#include "base/memory/raw_ptr.h"
#include "gin/converter.h"
#include "shell/common/api/electron_api_native_image.h"
@@ -18,15 +17,6 @@
#include "ui/gfx/image/image_skia.h"
#include "v8/include/v8.h"
#if BUILDFLAG(IS_LINUX) && (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64))
#define ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX
#include "base/command_line.h"
#include "base/debug/stack_trace.h"
#include "components/crash/core/app/crashpad.h" // nogncheck
#include "content/public/common/content_switches.h"
#include "v8/include/v8-wasm-trap-handler-posix.h"
#endif
namespace electron {
namespace {
@@ -250,51 +240,6 @@ v8::Local<v8::Value> DeserializeV8Value(v8::Isolate* isolate,
return V8Deserializer(isolate, data).Deserialize();
}
void SetUpWebAssemblyTrapHandler() {
#if BUILDFLAG(IS_WIN)
// On Windows we use the default trap handler provided by V8.
v8::V8::EnableWebAssemblyTrapHandler(true);
#elif BUILDFLAG(IS_MAC)
// On macOS, Crashpad uses exception ports to handle signals in a
// different process. As we cannot just pass a callback to this other
// process, we ask V8 to install its own signal handler to deal with
// WebAssembly traps.
v8::V8::EnableWebAssemblyTrapHandler(true);
#elif defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
const bool crash_reporter_enabled =
crash_reporter::GetHandlerSocket(nullptr, nullptr);
if (crash_reporter_enabled) {
// If either --enable-crash-reporter or --enable-crash-reporter-for-testing
// is enabled it should take care of signal handling for us, use the default
// implementation which doesn't register an additional handler.
v8::V8::EnableWebAssemblyTrapHandler(false);
return;
}
const bool use_v8_default_handler =
base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kDisableInProcessStackTraces);
if (use_v8_default_handler) {
// There is no signal handler yet, but it's okay if v8 registers one.
v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/true);
return;
}
if (base::debug::SetStackDumpFirstChanceCallback(
v8::TryHandleWebAssemblyTrapPosix)) {
// Crashpad and Breakpad are disabled, but the in-process stack dump
// handlers are enabled, so set the callback on the stack dump handlers.
v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/false);
return;
}
// As the registration of the callback failed, we don't enable trap
// handlers.
#endif
}
namespace util {
/**

View File

@@ -30,8 +30,6 @@ v8::Local<v8::Value> DeserializeV8Value(v8::Isolate* isolate,
v8::Local<v8::Value> DeserializeV8Value(v8::Isolate* isolate,
base::span<const uint8_t> data);
void SetUpWebAssemblyTrapHandler();
namespace util {
[[nodiscard]] base::span<uint8_t> as_byte_span(

View File

@@ -6,8 +6,10 @@
#include <algorithm>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/debug/stack_trace.h"
#include "content/public/renderer/render_frame.h"
#include "net/http/http_request_headers.h"
#include "shell/common/api/electron_bindings.h"
@@ -17,7 +19,6 @@
#include "shell/common/node_includes.h"
#include "shell/common/node_util.h"
#include "shell/common/options_switches.h"
#include "shell/common/v8_util.h"
#include "shell/renderer/electron_render_frame_observer.h"
#include "shell/renderer/web_worker_observer.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
@@ -26,6 +27,13 @@
#include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" // nogncheck
#if BUILDFLAG(IS_LINUX) && (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64))
#define ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX
#include "components/crash/core/app/crashpad.h" // nogncheck
#include "content/public/common/content_switches.h"
#include "v8/include/v8-wasm-trap-handler-posix.h"
#endif
namespace electron {
ElectronRendererClient::ElectronRendererClient()
@@ -240,9 +248,45 @@ void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread(
}
void ElectronRendererClient::SetUpWebAssemblyTrapHandler() {
// content/renderer layer already takes care of the feature flag detection
// so no need to check for features::kWebAssemblyTrapHandler here.
electron::SetUpWebAssemblyTrapHandler();
// See CL:5372409 - copied from ShellContentRendererClient.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
// Mac and Windows use the default implementation (where the default v8 trap
// handler gets set up).
ContentRendererClient::SetUpWebAssemblyTrapHandler();
return;
#elif defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
const bool crash_reporter_enabled =
crash_reporter::GetHandlerSocket(nullptr, nullptr);
if (crash_reporter_enabled) {
// If either --enable-crash-reporter or --enable-crash-reporter-for-testing
// is enabled it should take care of signal handling for us, use the default
// implementation which doesn't register an additional handler.
ContentRendererClient::SetUpWebAssemblyTrapHandler();
return;
}
const bool use_v8_default_handler =
base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kDisableInProcessStackTraces);
if (use_v8_default_handler) {
// There is no signal handler yet, but it's okay if v8 registers one.
v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/true);
return;
}
if (base::debug::SetStackDumpFirstChanceCallback(
v8::TryHandleWebAssemblyTrapPosix)) {
// Crashpad and Breakpad are disabled, but the in-process stack dump
// handlers are enabled, so set the callback on the stack dump handlers.
v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/false);
return;
}
// As the registration of the callback failed, we don't enable trap
// handlers.
#endif // defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
}
node::Environment* ElectronRendererClient::GetEnvironment(

View File

@@ -11,7 +11,6 @@
#include "base/no_destructor.h"
#include "base/process/process.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/common/content_features.h"
#include "electron/mas.h"
#include "net/base/network_change_notifier.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
@@ -23,7 +22,6 @@
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/node_bindings.h"
#include "shell/common/node_includes.h"
#include "shell/common/v8_util.h"
#include "shell/services/node/parent_port.h"
#if !IS_MAS_BUILD()
@@ -132,11 +130,6 @@ void NodeService::Initialize(
v8::Isolate* const isolate = js_env_->isolate();
v8::HandleScope scope{isolate};
// Initialize after setting up the V8 isolate.
if (base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) {
electron::SetUpWebAssemblyTrapHandler();
}
node_bindings_->Initialize(isolate, isolate->GetCurrentContext());
network_change_notifier_ = net::NetworkChangeNotifier::CreateIfNeeded(

View File

@@ -671,7 +671,7 @@ describe('contextBridge', () => {
it('should release the global hold on methods sent across contexts', async () => {
await makeBindingWindow(() => {
const trackedValues: WeakRef<object>[] = [];
require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
require('electron').ipcRenderer.on('get-gc-info', (e: any) => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
contextBridge.exposeInMainWorld('example', {
getFunction: () => () => 123,
track: (value: object) => { trackedValues.push(new WeakRef(value)); }
@@ -699,7 +699,7 @@ describe('contextBridge', () => {
it('should not leak the global hold on methods sent across contexts when reloading a sandboxed renderer', async () => {
await makeBindingWindow(() => {
const trackedValues: WeakRef<object>[] = [];
require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
require('electron').ipcRenderer.on('get-gc-info', (e: any) => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
contextBridge.exposeInMainWorld('example', {
getFunction: () => () => 123,
track: (value: object) => { trackedValues.push(new WeakRef(value)); }

View File

@@ -243,7 +243,6 @@ describe('ipc module', () => {
await w.webContents.executeJavaScript(`(${function () {
try {
const buffer = new ArrayBuffer(10);
// @ts-expect-error
require('electron').ipcRenderer.postMessage('port', '', [buffer]);
} catch (e) {
require('electron').ipcRenderer.postMessage('port', { error: (e as Error).message });
@@ -323,7 +322,7 @@ describe('ipc module', () => {
w.loadURL('about:blank');
await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron');
ipcRenderer.on('port', e => {
ipcRenderer.on('port', (e: any) => {
const [port] = e.ports;
port.start();
port.onclose = () => {
@@ -480,8 +479,8 @@ describe('ipc module', () => {
w.loadURL('about:blank');
await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron');
ipcRenderer.on('port', ev => {
const [port] = ev.ports;
ipcRenderer.on('port', (e: any) => {
const [port] = e.ports;
port.onmessage = () => {
ipcRenderer.send('done');
};
@@ -498,9 +497,9 @@ describe('ipc module', () => {
w.loadURL('about:blank');
await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron');
ipcRenderer.on('port', e1 => {
e1.ports[0].onmessage = e2 => {
e2.ports[0].onmessage = e3 => {
ipcRenderer.on('port', (e1: any) => {
e1.ports[0].onmessage = (e2: any) => {
e2.ports[0].onmessage = (e3: any) => {
ipcRenderer.send('done', e3.data);
};
};
@@ -587,7 +586,7 @@ describe('ipc module', () => {
w.loadURL('about:blank');
await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron');
ipcRenderer.on('foo', (_e, msg) => {
ipcRenderer.on('foo', (_e: Event, msg: string) => {
ipcRenderer.send('bar', msg);
});
}})()`);

View File

@@ -92,4 +92,46 @@ describe('View', () => {
expect(v.getVisible()).to.be.false();
});
});
describe('view.getBounds|setBounds', () => {
it('defaults to 0,0,0,0', () => {
const v = new View();
expect(v.getBounds()).to.deep.equal({ x: 0, y: 0, width: 0, height: 0 });
});
it('can be set and retrieved', () => {
const v = new View();
v.setBounds({ x: 10, y: 20, width: 300, height: 400 });
expect(v.getBounds()).to.deep.equal({ x: 10, y: 20, width: 300, height: 400 });
});
it('emits bounds-changed when bounds mutate', () => {
const v = new View();
let called = 0;
v.once('bounds-changed', () => { called++; });
v.setBounds({ x: 5, y: 6, width: 7, height: 8 });
expect(called).to.equal(1);
});
it('allows zero-size bounds', () => {
const v = new View();
v.setBounds({ x: 1, y: 2, width: 0, height: 0 });
expect(v.getBounds()).to.deep.equal({ x: 1, y: 2, width: 0, height: 0 });
});
it('allows negative coordinates', () => {
const v = new View();
v.setBounds({ x: -10, y: -20, width: 100, height: 50 });
expect(v.getBounds()).to.deep.equal({ x: -10, y: -20, width: 100, height: 50 });
});
it('child bounds remain relative after parent moves', () => {
const parent = new View();
const child = new View();
parent.addChildView(child);
child.setBounds({ x: 10, y: 15, width: 25, height: 30 });
parent.setBounds({ x: 50, y: 60, width: 500, height: 600 });
expect(child.getBounds()).to.deep.equal({ x: 10, y: 15, width: 25, height: 30 });
});
});
});

View File

@@ -109,6 +109,7 @@ describe('webContents module', () => {
await closeAllWindows();
await cleanupWebContents();
});
it('does not emit if beforeunload returns undefined in a BrowserWindow', async () => {
const w = new BrowserWindow({ show: false });
w.webContents.once('will-prevent-unload', () => {
@@ -162,6 +163,35 @@ describe('webContents module', () => {
w.close();
await wait;
});
it('fails loading a subsequent page after beforeunload is not prevented', async () => {
const w = new BrowserWindow({ show: false });
const didFailLoad = once(w.webContents, 'did-fail-load');
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html'));
await w.webContents.executeJavaScript('console.log(\'gesture\')', true);
w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'a.html'));
const [, code, , validatedURL] = await didFailLoad;
expect(code).to.equal(-3); // ERR_ABORTED
const { href: expectedURL } = url.pathToFileURL(path.join(__dirname, 'fixtures', 'pages', 'a.html'));
expect(validatedURL).to.equal(expectedURL);
});
it('allows loading a subsequent page after beforeunload is prevented', async () => {
const w = new BrowserWindow({ show: false });
w.webContents.once('will-prevent-unload', event => event.preventDefault());
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html'));
await w.webContents.executeJavaScript('console.log(\'gesture\')', true);
await w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'a.html'));
const pageTitle = await w.webContents.executeJavaScript('document.title');
expect(pageTitle).to.equal('test');
const wait = once(w, 'closed');
w.close();
await wait;
});
});
describe('webContents.send(channel, args...)', () => {
@@ -1037,13 +1067,15 @@ describe('webContents module', () => {
assert(devToolsWebContents !== null);
const windowFocused = once(window, 'focus');
const devToolsBlurred = once(devToolsWebContents, 'blur');
window.focus();
await windowFocused;
await Promise.all([windowFocused, devToolsBlurred]);
expect(devToolsWebContents.isFocused()).to.be.false();
const devToolsWebContentsFocused = once(devToolsWebContents, 'focus');
const windowBlurred = once(window, 'blur');
window.webContents.inspectElement(100, 100);
await devToolsWebContentsFocused;
await Promise.all([devToolsWebContentsFocused, windowBlurred]);
expect(devToolsWebContents.isFocused()).to.be.true();
expect(window.isFocused()).to.be.false();
@@ -2657,7 +2689,13 @@ describe('webContents module', () => {
const errMsg = Buffer.concat(stderr).toString().trim();
console.error(`Error parsing PDF file, exit code was ${code}; signal was ${signal}, error: ${errMsg}`);
}
return JSON.parse(Buffer.concat(stdout).toString().trim());
try {
return JSON.parse(Buffer.concat(stdout).toString().trim());
} catch (err) {
console.error('Error parsing PDF file:', err);
console.error('Raw output:', Buffer.concat(stdout).toString().trim());
throw err;
}
};
let w: BrowserWindow;

View File

@@ -1,5 +1,8 @@
{
"main": "./lib/echo.js",
"name": "@electron-ci/echo",
"version": "0.0.1"
"version": "0.0.1",
"main": "./lib/echo.js",
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
}

View File

@@ -1,5 +1,8 @@
{
"main": "./lib/test-array-buffer.js",
"name": "@electron-ci/external-ab",
"version": "0.0.1"
"version": "0.0.1",
"main": "./lib/test-array-buffer.js",
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
}

View File

@@ -0,0 +1,13 @@
{
"name": "@electron-ci/is-valid-window",
"version": "0.0.5",
"main": "./lib/is-valid-window.js",
"private": true,
"licenses": "MIT",
"dependencies": {
"nan": "2.x"
},
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
}

View File

@@ -1,5 +1,9 @@
{
"main": "./lib/osr-gpu.js",
"name": "@electron-ci/osr-gpu",
"version": "0.0.1"
"version": "0.0.1",
"main": "./lib/osr-gpu.js",
"private": true,
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
}

View File

@@ -1,5 +1,9 @@
{
"name": "@electron-ci/uv-dlopen",
"version": "0.0.1",
"main": "index.js"
"main": "index.js",
"private": true,
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
}

View File

@@ -1,9 +0,0 @@
{
"main": "./lib/is-valid-window.js",
"name": "is-valid-window",
"version": "0.0.5",
"licenses": "Public Domain",
"dependencies": {
"nan": "2.x"
}
}

View File

@@ -7,6 +7,13 @@
"node-gyp-install": "node-gyp install"
},
"devDependencies": {
"@electron-ci/echo": "*",
"@electron-ci/external-ab": "*",
"@electron-ci/is-valid-window": "*",
"@electron-ci/osr-gpu": "*",
"@electron-ci/uv-dlopen": "*",
"@electron/fuses": "^1.8.0",
"@electron/packager": "^18.3.2",
"@types/basic-auth": "^1.1.8",
"@types/busboy": "^1.5.4",
"@types/chai": "^4.3.19",
@@ -15,19 +22,12 @@
"@types/express": "^4.17.13",
"@types/mocha": "^7.0.2",
"@types/send": "^0.14.5",
"@types/sinon": "^9.0.4",
"@types/split": "^1.0.5",
"@types/uuid": "^3.4.6",
"@types/w3c-web-serial": "^1.0.7",
"express": "^4.20.0",
"@electron-ci/echo": "file:./fixtures/native-addon/echo",
"@electron-ci/is-valid-window": "file:./is-valid-window",
"@electron-ci/uv-dlopen": "file:./fixtures/native-addon/uv-dlopen/",
"@electron-ci/osr-gpu": "file:./fixtures/native-addon/osr-gpu/",
"@electron-ci/external-ab": "file:./fixtures/native-addon/external-ab/",
"@electron/fuses": "^1.8.0",
"@electron/packager": "^18.3.2",
"@types/sinon": "^9.0.4",
"@types/ws": "^7.2.0",
"abstract-socket": "github:deepak1556/node-abstractsocket#928cc591decd12aff7dad96449da8afc29832c19",
"basic-auth": "^2.0.1",
"busboy": "^1.6.0",
"chai": "^4.2.0",
@@ -35,12 +35,13 @@
"coffeescript": "^2.4.1",
"dbus-native": "github:nornagon/dbus-native#master",
"dirty-chai": "^2.0.1",
"express": "^4.20.0",
"graceful-fs": "^4.1.15",
"mkdirp": "^0.5.1",
"mocha": "^10.0.0",
"mocha-junit-reporter": "^1.18.0",
"mocha-multi-reporters": "^1.1.7",
"pdfjs-dist": "^4.2.67",
"pdfjs-dist": "4.2.67",
"ps-list": "^7.0.0",
"q": "^1.5.1",
"send": "^0.19.0",
@@ -50,11 +51,5 @@
"winreg": "1.2.4",
"ws": "^7.5.10",
"yargs": "^16.0.3"
},
"resolutions": {
"nan": "file:../../third_party/nan",
"dbus-native/optimist/minimist": "1.2.7",
"dbus-native/xml2js": "0.5.0",
"abstract-socket": "github:deepak1556/node-abstractsocket#928cc591decd12aff7dad96449da8afc29832c19"
}
}

Some files were not shown because too many files have changed in this diff Show More