mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d18062d0f | ||
|
|
832ffb2330 | ||
|
|
03121eeaef | ||
|
|
8282c07a0f | ||
|
|
f2d1cb21b0 | ||
|
|
ef9b4162af | ||
|
|
6e97bca80d | ||
|
|
c511fc5c3f | ||
|
|
22dfbb0822 | ||
|
|
85913a38da | ||
|
|
a327629ca2 | ||
|
|
7deed2b980 | ||
|
|
65fc06a9f7 | ||
|
|
245e70aedd | ||
|
|
2a8164f499 | ||
|
|
2f7024dbcc | ||
|
|
d53d3bb99e | ||
|
|
c2c1d40294 | ||
|
|
0e9decd459 | ||
|
|
b2e73d28e2 | ||
|
|
aeb5af803f | ||
|
|
53819a8a2a | ||
|
|
14565211f7 | ||
|
|
00646c9db6 | ||
|
|
d9c33a951a | ||
|
|
8b02e33187 | ||
|
|
eecca2cb19 | ||
|
|
08b5ef556c | ||
|
|
ab85f2c2f7 | ||
|
|
1936243ce1 | ||
|
|
e7e052f5b1 | ||
|
|
349a9b6398 | ||
|
|
b5f19ce974 | ||
|
|
bb930b887b | ||
|
|
e962bc3743 | ||
|
|
895cf006e7 | ||
|
|
bc1ca72dc7 | ||
|
|
a9a4c77353 | ||
|
|
0f613246d9 | ||
|
|
a77b92adf2 | ||
|
|
d62c324567 | ||
|
|
108a26a0f9 | ||
|
|
331f8cca47 | ||
|
|
215128715a | ||
|
|
efcab52714 |
2
.github/actions/build-electron/action.yml
vendored
2
.github/actions/build-electron/action.yml
vendored
@@ -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
|
||||
|
||||
2
.github/actions/checkout/action.yml
vendored
2
.github/actions/checkout/action.yml
vendored
@@ -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
|
||||
|
||||
11
.github/actions/free-space-macos/action.yml
vendored
11
.github/actions/free-space-macos/action.yml
vendored
@@ -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
|
||||
|
||||
14
.github/actions/generate-types/action.yml
vendored
14
.github/actions/generate-types/action.yml
vendored
@@ -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
|
||||
|
||||
14
.github/actions/install-dependencies/action.yml
vendored
14
.github/actions/install-dependencies/action.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/archaeologist-dig.yml
vendored
4
.github/workflows/archaeologist-dig.yml
vendored
@@ -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
|
||||
|
||||
8
.github/workflows/build-git-cache.yml
vendored
8
.github/workflows/build-git-cache.yml
vendored
@@ -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:
|
||||
|
||||
27
.github/workflows/build.yml
vendored
27
.github/workflows/build.yml
vendored
@@ -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 }}
|
||||
@@ -63,6 +66,10 @@ jobs:
|
||||
filters: |
|
||||
docs:
|
||||
- 'docs/**'
|
||||
- README.md
|
||||
- SECURITY.md
|
||||
- CONTRIBUTING.md
|
||||
- CODE_OF_CONDUCT.md
|
||||
src:
|
||||
- '!docs/**'
|
||||
- name: Set Outputs for Build Image SHA & Docs Only
|
||||
@@ -80,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
|
||||
@@ -89,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
|
||||
@@ -98,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
|
||||
@@ -126,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
|
||||
@@ -155,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
|
||||
@@ -185,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
|
||||
@@ -195,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:
|
||||
@@ -207,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
|
||||
@@ -310,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
|
||||
@@ -400,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:
|
||||
|
||||
10
.github/workflows/clean-src-cache.yml
vendored
10
.github/workflows/clean-src-cache.yml
vendored
@@ -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
|
||||
|
||||
7
.github/workflows/issue-labeled.yml
vendored
7
.github/workflows/issue-labeled.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/issue-opened.yml
vendored
2
.github/workflows/issue-opened.yml
vendored
@@ -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
|
||||
|
||||
1
.github/workflows/issue-transferred.yml
vendored
1
.github/workflows/issue-transferred.yml
vendored
@@ -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
|
||||
|
||||
5
.github/workflows/issue-unlabeled.yml
vendored
5
.github/workflows/issue-unlabeled.yml
vendored
@@ -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
|
||||
|
||||
10
.github/workflows/linux-publish.yml
vendored
10
.github/workflows/linux-publish.yml
vendored
@@ -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
|
||||
|
||||
12
.github/workflows/macos-publish.yml
vendored
12
.github/workflows/macos-publish.yml
vendored
@@ -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
|
||||
|
||||
@@ -7,6 +7,8 @@ on:
|
||||
- 'spec/yarn.lock'
|
||||
- '.github/workflows/**'
|
||||
- '.github/actions/**'
|
||||
- '.yarn/**'
|
||||
- '.yarnrc.yml'
|
||||
|
||||
permissions: {}
|
||||
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
10
.github/workflows/pipeline-electron-lint.yml
vendored
10
.github/workflows/pipeline-electron-lint.yml
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
2
.github/workflows/pull-request-labeled.yml
vendored
2
.github/workflows/pull-request-labeled.yml
vendored
@@ -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
|
||||
|
||||
3
.github/workflows/semantic.yml
vendored
3
.github/workflows/semantic.yml
vendored
@@ -7,8 +7,7 @@ on:
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
main:
|
||||
|
||||
1
.github/workflows/stable-prep-items.yml
vendored
1
.github/workflows/stable-prep-items.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -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:
|
||||
|
||||
10
.github/workflows/windows-publish.yml
vendored
10
.github/workflows/windows-publish.yml
vendored
@@ -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
2
.gitignore
vendored
@@ -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
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
12
.yarnrc.yml
Normal 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
7
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'142.0.7444.134',
|
||||
'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"]);',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1216,6 +1216,13 @@ Disables hardware acceleration for current app.
|
||||
|
||||
This method can only be called before app is ready.
|
||||
|
||||
### `app.isHardwareAccelerationEnabled()`
|
||||
|
||||
Returns `boolean` - whether hardware acceleration is currently enabled.
|
||||
|
||||
> [!NOTE]
|
||||
> This information is only usable after the `gpu-info-update` event is emitted.
|
||||
|
||||
### `app.disableDomainBlockingFor3DAPIs()`
|
||||
|
||||
By default, Chromium disables 3D APIs (e.g. WebGL) until restart on a per
|
||||
|
||||
@@ -1262,15 +1262,16 @@ Sets the properties for the window's taskbar button.
|
||||
|
||||
#### `win.setAccentColor(accentColor)` _Windows_
|
||||
|
||||
* `accentColor` boolean | string - The accent color for the window. By default, follows user preference in System Settings.
|
||||
* `accentColor` boolean | string | null - The accent color for the window. By default, follows user preference in System Settings. To reset to system default, pass `null`.
|
||||
|
||||
Sets the system accent color and highlighting of active window border.
|
||||
|
||||
The `accentColor` parameter accepts the following values:
|
||||
|
||||
* **Color string** - Sets a custom accent color using standard CSS color formats (Hex, RGB, RGBA, HSL, HSLA, or named colors). Alpha values in RGBA/HSLA formats are ignored and the color is treated as fully opaque.
|
||||
* **`true`** - Uses the system's default accent color from user preferences in System Settings.
|
||||
* **`false`** - Explicitly disables accent color highlighting for the window.
|
||||
* **Color string** - Like `true`, but sets a custom accent color using standard CSS color formats (Hex, RGB, RGBA, HSL, HSLA, or named colors). Alpha values in RGBA/HSLA formats are ignored and the color is treated as fully opaque.
|
||||
* **`true`** - Enable accent color highlighting for the window with the system accent color regardless of whether accent colors are enabled for windows in System `Settings.`
|
||||
* **`false`** - Disable accent color highlighting for the window regardless of whether accent colors are currently enabled for windows in System Settings.
|
||||
* **`null`** - Reset window accent color behavior to follow behavior set in System Settings.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -1283,11 +1284,14 @@ win.setAccentColor('#ff0000')
|
||||
// RGB format (alpha ignored if present).
|
||||
win.setAccentColor('rgba(255,0,0,0.5)')
|
||||
|
||||
// Use system accent color.
|
||||
// Enable accent color, using the color specified in System Settings.
|
||||
win.setAccentColor(true)
|
||||
|
||||
// Disable accent color.
|
||||
win.setAccentColor(false)
|
||||
|
||||
// Reset window accent color behavior to follow behavior set in System Settings.
|
||||
win.setAccentColor(null)
|
||||
```
|
||||
|
||||
#### `win.getAccentColor()` _Windows_
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -1467,15 +1468,16 @@ Sets the properties for the window's taskbar button.
|
||||
|
||||
#### `win.setAccentColor(accentColor)` _Windows_
|
||||
|
||||
* `accentColor` boolean | string - The accent color for the window. By default, follows user preference in System Settings.
|
||||
* `accentColor` boolean | string | null - The accent color for the window. By default, follows user preference in System Settings. To reset to system default, pass `null`.
|
||||
|
||||
Sets the system accent color and highlighting of active window border.
|
||||
|
||||
The `accentColor` parameter accepts the following values:
|
||||
|
||||
* **Color string** - Sets a custom accent color using standard CSS color formats (Hex, RGB, RGBA, HSL, HSLA, or named colors). Alpha values in RGBA/HSLA formats are ignored and the color is treated as fully opaque.
|
||||
* **`true`** - Uses the system's default accent color from user preferences in System Settings.
|
||||
* **`false`** - Explicitly disables accent color highlighting for the window.
|
||||
* **Color string** - Like `true`, but sets a custom accent color using standard CSS color formats (Hex, RGB, RGBA, HSL, HSLA, or named colors). Alpha values in RGBA/HSLA formats are ignored and the color is treated as fully opaque.
|
||||
* **`true`** - Enable accent color highlighting for the window with the system accent color regardless of whether accent colors are enabled for windows in System `Settings.`
|
||||
* **`false`** - Disable accent color highlighting for the window regardless of whether accent colors are currently enabled for windows in System Settings.
|
||||
* **`null`** - Reset window accent color behavior to follow behavior set in System Settings.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -1488,11 +1490,14 @@ win.setAccentColor('#ff0000')
|
||||
// RGB format (alpha ignored if present).
|
||||
win.setAccentColor('rgba(255,0,0,0.5)')
|
||||
|
||||
// Use system accent color.
|
||||
// Enable accent color, using the color specified in System Settings.
|
||||
win.setAccentColor(true)
|
||||
|
||||
// Disable accent color.
|
||||
win.setAccentColor(false)
|
||||
|
||||
// Reset window accent color behavior to follow behavior set in System Settings.
|
||||
win.setAccentColor(null)
|
||||
```
|
||||
|
||||
#### `win.getAccentColor()` _Windows_
|
||||
|
||||
@@ -25,6 +25,11 @@ following properties:
|
||||
with which the request is associated. Defaults to the empty string. The
|
||||
`session` option supersedes `partition`. Thus if a `session` is explicitly
|
||||
specified, `partition` is ignored.
|
||||
* `bypassCustomProtocolHandlers` boolean (optional) - When set to `true`,
|
||||
custom protocol handlers registered for the request's URL scheme will not be
|
||||
called. This allows forwarding an intercepted request to the built-in
|
||||
handler. [webRequest](web-request.md) handlers will still be triggered
|
||||
when bypassing custom protocols. Defaults to `false`.
|
||||
* `credentials` string (optional) - Can be `include`, `omit` or
|
||||
`same-origin`. Whether to send
|
||||
[credentials](https://fetch.spec.whatwg.org/#credentials) with this
|
||||
|
||||
@@ -34,7 +34,8 @@ See [`Menu`](menu.md) for examples.
|
||||
* `sublabel` string (optional) _macOS_ - Available in macOS >= 14.4
|
||||
* `toolTip` string (optional) _macOS_ - Hover text for this menu item.
|
||||
* `accelerator` string (optional) - An [Accelerator](../tutorial/keyboard-shortcuts.md#accelerators) string.
|
||||
* `icon` ([NativeImage](native-image.md) | string) (optional)
|
||||
* `icon` ([NativeImage](native-image.md) | string) (optional) - Can be a
|
||||
[NativeImage](native-image.md) or the file path of an icon.
|
||||
* `enabled` boolean (optional) - If false, the menu item will be greyed out and
|
||||
unclickable.
|
||||
* `acceleratorWorksWhenHidden` boolean (optional) _macOS_ - default is `true`, and when `false` will prevent the accelerator from triggering the item if the item is not visible.
|
||||
|
||||
@@ -202,8 +202,7 @@ Creates a new `NativeImage` instance from `dataUrl`, a base 64 encoded [Data URL
|
||||
Returns `NativeImage`
|
||||
|
||||
Creates a new `NativeImage` instance from the `NSImage` that maps to the
|
||||
given image name. See Apple's [`NSImageName`](https://developer.apple.com/documentation/appkit/nsimagename#2901388)
|
||||
documentation for a list of possible values.
|
||||
given image name. See Apple's [`NSImageName`](https://developer.apple.com/documentation/appkit/nsimagename#2901388) documentation and [SF Symbols](https://developer.apple.com/sf-symbols/) for a list of possible values.
|
||||
|
||||
The `hslShift` is applied to the image with the following rules:
|
||||
|
||||
@@ -231,6 +230,15 @@ echo -e '#import <Cocoa/Cocoa.h>\nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME);
|
||||
|
||||
where `SYSTEM_IMAGE_NAME` should be replaced with any value from [this list](https://developer.apple.com/documentation/appkit/nsimagename?language=objc).
|
||||
|
||||
For SF Symbols, usage looks as follows:
|
||||
|
||||
```js
|
||||
const image = nativeImage.createFromNamedImage('square.and.pencil')
|
||||
```
|
||||
|
||||
where `'square.and.pencil'` is the symbol name from the
|
||||
[SF Symbols app](https://developer.apple.com/sf-symbols/).
|
||||
|
||||
## Class: NativeImage
|
||||
|
||||
> Natively wrap images such as tray, dock, and application icons.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,7 +9,7 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
|
||||
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
|
||||
| 40.0.0 | 2025-Oct-30 | 2025-Dec-03 | 2025-Oct-28 | 2026-Jun-30 | M144 | TBD | ✅ |
|
||||
| 40.0.0 | 2025-Oct-30 | 2025-Dec-03 | 2026-Jan-13 | 2026-Jun-30 | M144 | TBD | ✅ |
|
||||
| 39.0.0 | 2025-Sep-04 | 2025-Oct-01 | 2025-Oct-28 | 2026-May-05 | M142 | v22.20 | ✅ |
|
||||
| 38.0.0 | 2025-Jun-26 | 2025-Aug-06 | 2025-Sep-02 | 2026-Mar-10 | M140 | v22.18 | ✅ |
|
||||
| 37.0.0 | 2025-May-01 | 2025-May-28 | 2025-Jun-24 | 2026-Jan-13 | M138 | v22.16 | ✅ |
|
||||
|
||||
@@ -118,13 +118,6 @@ You should at least follow these steps to improve the security of your applicati
|
||||
19. [Check which fuses you can change](#19-check-which-fuses-you-can-change)
|
||||
20. [Do not expose Electron APIs to untrusted web content](#20-do-not-expose-electron-apis-to-untrusted-web-content)
|
||||
|
||||
To automate the detection of misconfigurations and insecure patterns, it is
|
||||
possible to use
|
||||
[Electronegativity](https://github.com/doyensec/electronegativity). For
|
||||
additional details on potential weaknesses and implementation bugs when
|
||||
developing applications using Electron, please refer to this
|
||||
[guide for developers and auditors](https://doyensec.com/resources/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security-wp.pdf).
|
||||
|
||||
### 1. Only load secure content
|
||||
|
||||
Any resources not included with your application should be loaded using a
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -289,7 +289,8 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
|
||||
referrerPolicy: options.referrerPolicy,
|
||||
cache: options.cache,
|
||||
allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols),
|
||||
priority: options.priority
|
||||
priority: options.priority,
|
||||
bypassCustomProtocolHandlers: options.bypassCustomProtocolHandlers
|
||||
};
|
||||
if ('priorityIncremental' in options) {
|
||||
urlLoaderOptions.priorityIncremental = options.priorityIncremental;
|
||||
|
||||
21
package.json
21
package.json
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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 6f47f1f484ffbc107f454fb2b23a9adda68ff7c0..10a13a986bb5d39323bf25ecba1b3935b1b0a081 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 @@
|
||||
|
||||
@@ -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
|
||||
@@ -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|,
|
||||
@@ -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() ||
|
||||
@@ -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() ||
|
||||
@@ -1 +1,2 @@
|
||||
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
|
||||
fix_element_tree_flickering_with_node_inspection.patch
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -39,7 +39,6 @@ build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch
|
||||
fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch
|
||||
chore_add_createexternalizabletwobytestring_to_globals.patch
|
||||
refactor_attach_cppgc_heap_on_v8_isolate_creation.patch
|
||||
fix_ensure_traverseparent_bails_on_resource_path_exit.patch
|
||||
cli_move_--trace-atomics-wait_to_eol.patch
|
||||
fix_cppgc_initializing_twice.patch
|
||||
fix_task_starvation_in_inspector_context_test.patch
|
||||
@@ -53,3 +52,5 @@ src_switch_from_get_setprototype_to_get_setprototypev2.patch
|
||||
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
|
||||
|
||||
@@ -592,7 +592,7 @@ index 3c5f38ba4f492749c9d7d82179d2a6563787602b..6e83da3ee975dea431e21209bba9227e
|
||||
data_.Reset();
|
||||
return ret;
|
||||
diff --git a/src/node_modules.cc b/src/node_modules.cc
|
||||
index ed22da844a61b14b8580cd3d6bb3a233b8559b38..14f2a35f87e8c2fa17898147d7247cc00c066f35 100644
|
||||
index 62050791174563f88b8629d576eed8959b3c2e20..fa04b4a8cdd02a2cad1eaf2e5a848b951d1b1150 100644
|
||||
--- a/src/node_modules.cc
|
||||
+++ b/src/node_modules.cc
|
||||
@@ -64,7 +64,7 @@ void BindingData::Deserialize(v8::Local<v8::Context> context,
|
||||
@@ -604,7 +604,7 @@ index ed22da844a61b14b8580cd3d6bb3a233b8559b38..14f2a35f87e8c2fa17898147d7247cc0
|
||||
Realm* realm = Realm::GetCurrent(context);
|
||||
BindingData* binding = realm->AddBindingData<BindingData>(holder);
|
||||
CHECK_NOT_NULL(binding);
|
||||
@@ -656,7 +656,7 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
|
||||
@@ -617,7 +617,7 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
|
||||
Realm* realm = Realm::GetCurrent(context);
|
||||
realm->AddBindingData<BindingData>(target);
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ resource path. This commit ensures that the TraverseParent function
|
||||
bails out if the parent path is outside of the resource path.
|
||||
|
||||
diff --git a/src/node_modules.cc b/src/node_modules.cc
|
||||
index 60b03b1563b230f78d8a9bdd8480da4d2ae90a27..35bfada261258407982d9e24cf7b3e820235c941 100644
|
||||
index 947513d5b91e8c13478b25d80a1157edbcd64b64..4e46df6afe6ca57f6df9a64d3fc572450baf7d5c 100644
|
||||
--- a/src/node_modules.cc
|
||||
+++ b/src/node_modules.cc
|
||||
@@ -279,8 +279,41 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
@@ -315,8 +315,41 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
Realm* realm, const std::filesystem::path& check_path) {
|
||||
std::filesystem::path current_path = check_path;
|
||||
auto env = realm->env();
|
||||
@@ -47,22 +47,22 @@ index 60b03b1563b230f78d8a9bdd8480da4d2ae90a27..35bfada261258407982d9e24cf7b3e82
|
||||
+ });
|
||||
+ };
|
||||
+
|
||||
+ bool did_original_path_start_with_resources_path = starts_with(check_path.
|
||||
+ generic_string(), resources_path);
|
||||
+ bool did_original_path_start_with_resources_path = starts_with(
|
||||
+ ConvertGenericPathToUTF8(check_path), resources_path);
|
||||
+
|
||||
do {
|
||||
current_path = current_path.parent_path();
|
||||
|
||||
@@ -299,6 +332,12 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
return nullptr;
|
||||
@@ -336,6 +369,12 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
}
|
||||
}
|
||||
|
||||
+ // If current path is outside the resources path, bail.
|
||||
+ if (did_original_path_start_with_resources_path &&
|
||||
+ !starts_with(current_path.generic_string(), resources_path)) {
|
||||
+ !starts_with(ConvertGenericPathToUTF8(current_path), resources_path)) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
// Check if the path ends with `/node_modules`
|
||||
if (current_path.generic_string().ends_with("/node_modules")) {
|
||||
if (current_path.filename() == "node_modules") {
|
||||
return nullptr;
|
||||
|
||||
@@ -20,7 +20,7 @@ index 82225b0a53dd828750991a4e15a060b736b6ea2b..4b0d31356a2496a7fc67876a22da2453
|
||||
V(performance_entry_callback, v8::Function) \
|
||||
V(prepare_stack_trace_callback, v8::Function) \
|
||||
diff --git a/src/node_modules.cc b/src/node_modules.cc
|
||||
index 35bfada261258407982d9e24cf7b3e820235c941..ed22da844a61b14b8580cd3d6bb3a233b8559b38 100644
|
||||
index 60b03b1563b230f78d8a9bdd8480da4d2ae90a27..62050791174563f88b8629d576eed8959b3c2e20 100644
|
||||
--- a/src/node_modules.cc
|
||||
+++ b/src/node_modules.cc
|
||||
@@ -21,6 +21,7 @@ namespace modules {
|
||||
@@ -90,7 +90,7 @@ index 35bfada261258407982d9e24cf7b3e820235c941..ed22da844a61b14b8580cd3d6bb3a233
|
||||
void BindingData::ReadPackageJSON(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_GE(args.Length(), 1); // path, [is_esm, base, specifier]
|
||||
CHECK(args[0]->IsString()); // path
|
||||
@@ -597,6 +633,8 @@ void InitImportMetaPathHelpers(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -558,6 +594,8 @@ void InitImportMetaPathHelpers(const FunctionCallbackInfo<Value>& args) {
|
||||
void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
Local<ObjectTemplate> target) {
|
||||
Isolate* isolate = isolate_data->isolate();
|
||||
@@ -99,7 +99,7 @@ index 35bfada261258407982d9e24cf7b3e820235c941..ed22da844a61b14b8580cd3d6bb3a233
|
||||
SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON);
|
||||
SetMethod(isolate,
|
||||
target,
|
||||
@@ -635,6 +673,8 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
|
||||
@@ -596,6 +634,8 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
|
||||
|
||||
void BindingData::RegisterExternalReferences(
|
||||
ExternalReferenceRegistry* registry) {
|
||||
|
||||
@@ -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);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,10 +136,10 @@ index 75be21c9e8b413f522240a906da06d26c44d5b71..e94c2b5f2cf7cac413cd5cb782fa1cca
|
||||
std::make_error_code(std::errc::file_exists),
|
||||
"cp",
|
||||
diff --git a/src/node_modules.cc b/src/node_modules.cc
|
||||
index 14f2a35f87e8c2fa17898147d7247cc00c066f35..871282c6f8780ee0bca1e7230c0c2d83fd0c98c0 100644
|
||||
index fa04b4a8cdd02a2cad1eaf2e5a848b951d1b1150..947513d5b91e8c13478b25d80a1157edbcd64b64 100644
|
||||
--- a/src/node_modules.cc
|
||||
+++ b/src/node_modules.cc
|
||||
@@ -360,12 +360,13 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
@@ -327,22 +327,24 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
|
||||
// Stop the search when the process doesn't have permissions
|
||||
// to walk upwards
|
||||
@@ -158,10 +158,6 @@ index 14f2a35f87e8c2fa17898147d7247cc00c066f35..871282c6f8780ee0bca1e7230c0c2d83
|
||||
+ }
|
||||
}
|
||||
|
||||
// If current path is outside the resources path, bail.
|
||||
@@ -375,13 +376,14 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
}
|
||||
|
||||
// Check if the path ends with `/node_modules`
|
||||
- if (current_path.generic_string().ends_with("/node_modules")) {
|
||||
+ if (current_path.filename() == "node_modules") {
|
||||
@@ -176,7 +172,7 @@ index 14f2a35f87e8c2fa17898147d7247cc00c066f35..871282c6f8780ee0bca1e7230c0c2d83
|
||||
if (package_json != nullptr) {
|
||||
return package_json;
|
||||
}
|
||||
@@ -403,20 +405,12 @@ void BindingData::GetNearestParentPackageJSONType(
|
||||
@@ -364,20 +366,12 @@ void BindingData::GetNearestParentPackageJSONType(
|
||||
|
||||
ToNamespacedPath(realm->env(), &path_value);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
18
script/yarn.js
Normal file → Executable 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;
|
||||
|
||||
@@ -1136,6 +1136,10 @@ void App::DisableHardwareAcceleration(gin_helper::ErrorThrower thrower) {
|
||||
"before app is ready");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the GpuDataManager is already initialized, disable hardware
|
||||
// acceleration immediately. Otherwise, set a flag to disable it in
|
||||
// OnPreCreateThreads().
|
||||
if (content::GpuDataManager::Initialized()) {
|
||||
content::GpuDataManager::GetInstance()->DisableHardwareAcceleration();
|
||||
} else {
|
||||
@@ -1143,6 +1147,13 @@ void App::DisableHardwareAcceleration(gin_helper::ErrorThrower thrower) {
|
||||
}
|
||||
}
|
||||
|
||||
bool App::IsHardwareAccelerationEnabled() {
|
||||
if (content::GpuDataManager::Initialized())
|
||||
return content::GpuDataManager::GetInstance()
|
||||
->HardwareAccelerationEnabled();
|
||||
return !disable_hw_acceleration_;
|
||||
}
|
||||
|
||||
void App::DisableDomainBlockingFor3DAPIs(gin_helper::ErrorThrower thrower) {
|
||||
if (Browser::Get()->is_ready()) {
|
||||
thrower.ThrowError(
|
||||
@@ -1923,6 +1934,8 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
|
||||
&App::SetAccessibilitySupportEnabled)
|
||||
.SetMethod("disableHardwareAcceleration",
|
||||
&App::DisableHardwareAcceleration)
|
||||
.SetMethod("isHardwareAccelerationEnabled",
|
||||
&App::IsHardwareAccelerationEnabled)
|
||||
.SetMethod("disableDomainBlockingFor3DAPIs",
|
||||
&App::DisableDomainBlockingFor3DAPIs)
|
||||
.SetMethod("getFileIcon", &App::GetFileIcon)
|
||||
|
||||
@@ -212,6 +212,7 @@ class App final : public gin::Wrappable<App>,
|
||||
void ReleaseSingleInstanceLock();
|
||||
bool Relaunch(gin::Arguments* args);
|
||||
void DisableHardwareAcceleration(gin_helper::ErrorThrower thrower);
|
||||
bool IsHardwareAccelerationEnabled();
|
||||
void DisableDomainBlockingFor3DAPIs(gin_helper::ErrorThrower thrower);
|
||||
bool IsAccessibilitySupportEnabled();
|
||||
v8::Local<v8::Value> GetAccessibilitySupportFeatures();
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include <variant>
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#include "shell/browser/ui/win/taskbar_host.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
@@ -1096,7 +1097,11 @@ bool BaseWindow::IsSnapped() const {
|
||||
void BaseWindow::SetAccentColor(gin_helper::Arguments* args) {
|
||||
bool accent_color = false;
|
||||
std::string accent_color_string;
|
||||
if (args->GetNext(&accent_color_string)) {
|
||||
if (!args->PeekNext().IsEmpty() && args->PeekNext()->IsNull()) {
|
||||
window_->SetAccentColor(std::monostate{});
|
||||
window_->UpdateWindowAccentColor(window_->IsActive());
|
||||
} else if (args->GetNext(&accent_color_string) &&
|
||||
!accent_color_string.empty()) {
|
||||
std::optional<SkColor> maybe_color = ParseCSSColor(accent_color_string);
|
||||
if (maybe_color.has_value()) {
|
||||
window_->SetAccentColor(maybe_color.value());
|
||||
@@ -1107,7 +1112,7 @@ void BaseWindow::SetAccentColor(gin_helper::Arguments* args) {
|
||||
window_->UpdateWindowAccentColor(window_->IsActive());
|
||||
} else {
|
||||
args->ThrowError(
|
||||
"Invalid accent color value - must be a string or boolean");
|
||||
"Invalid accent color value - must be null, hex string, or boolean");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -546,6 +546,9 @@ v8::Local<v8::Promise> Browser::DockShow(v8::Isolate* isolate) {
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
for (auto* const& window : WindowList::GetWindows())
|
||||
[window->GetNativeWindow().GetNativeNSWindow() setCanHide:YES];
|
||||
|
||||
BOOL active = [[NSRunningApplication currentApplication] isActive];
|
||||
ProcessSerialNumber psn = {0, kCurrentProcess};
|
||||
if (active) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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());
|
||||
@@ -1364,7 +1364,8 @@ void NativeWindowViews::SetFocusable(bool focusable) {
|
||||
ex_style |= WS_EX_NOACTIVATE;
|
||||
::SetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE, ex_style);
|
||||
SetSkipTaskbar(!focusable);
|
||||
Focus(false);
|
||||
if (!focusable)
|
||||
Focus(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -43,6 +43,10 @@ struct NotificationOptions {
|
||||
std::u16string toast_xml;
|
||||
|
||||
NotificationOptions();
|
||||
NotificationOptions(const NotificationOptions&);
|
||||
NotificationOptions& operator=(const NotificationOptions&);
|
||||
NotificationOptions(NotificationOptions&&);
|
||||
NotificationOptions& operator=(NotificationOptions&&);
|
||||
~NotificationOptions();
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -520,6 +520,12 @@ void InspectableWebContents::UpdateDevToolsZoomLevel(double level) {
|
||||
}
|
||||
|
||||
void InspectableWebContents::ActivateWindow() {
|
||||
if (embedder_message_dispatcher_) {
|
||||
if (managed_devtools_web_contents_ && view_) {
|
||||
view_->ActivateDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
// Set the zoom level.
|
||||
SetZoomLevelForWebContents(GetDevToolsWebContents(), GetDevToolsZoomLevel());
|
||||
}
|
||||
|
||||
@@ -132,6 +132,23 @@ void InspectableWebContentsView::ShowDevTools(bool activate) {
|
||||
}
|
||||
}
|
||||
|
||||
void InspectableWebContentsView::ActivateDevTools() {
|
||||
if (!devtools_visible_) {
|
||||
return;
|
||||
}
|
||||
if (devtools_window_) {
|
||||
if (!devtools_window_->IsActive()) {
|
||||
devtools_window_->Activate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (devtools_web_view_) {
|
||||
if (!devtools_web_view_->HasFocus()) {
|
||||
devtools_web_view_->RequestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InspectableWebContentsView::CloseDevTools() {
|
||||
if (!devtools_visible_)
|
||||
return;
|
||||
|
||||
@@ -49,6 +49,7 @@ class InspectableWebContentsView : public views::View {
|
||||
void SetCornerRadii(const gfx::RoundedCornersF& corner_radii);
|
||||
|
||||
void ShowDevTools(bool activate);
|
||||
void ActivateDevTools();
|
||||
void CloseDevTools();
|
||||
bool IsDevToolsViewShowing();
|
||||
bool IsDevToolsViewFocused();
|
||||
|
||||
@@ -120,9 +120,18 @@ gin_helper::Handle<NativeImage> NativeImage::CreateFromNamedImage(
|
||||
name.erase(pos, to_remove.length());
|
||||
}
|
||||
|
||||
NSImage* image = [NSImage imageNamed:base::SysUTF8ToNSString(name)];
|
||||
NSImage* image = nil;
|
||||
NSString* ns_name = base::SysUTF8ToNSString(name);
|
||||
|
||||
if (!image.valid) {
|
||||
// Treat non-Cocoa-prefixed names as SF Symbols first.
|
||||
if (!base::StartsWith(name, "NS") && !base::StartsWith(name, "NX")) {
|
||||
image = [NSImage imageWithSystemSymbolName:ns_name
|
||||
accessibilityDescription:nil];
|
||||
} else {
|
||||
image = [NSImage imageNamed:ns_name];
|
||||
}
|
||||
|
||||
if (!image || !image.valid) {
|
||||
return CreateEmpty(args->isolate());
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +149,12 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('app.isHardwareAccelerationEnabled()', () => {
|
||||
it('should be a boolean', () => {
|
||||
expect(app.isHardwareAccelerationEnabled()).to.be.a('boolean');
|
||||
});
|
||||
});
|
||||
|
||||
describe('app.isPackaged', () => {
|
||||
it('should be false during tests', () => {
|
||||
expect(app.isPackaged).to.equal(false);
|
||||
|
||||
@@ -2562,7 +2562,23 @@ describe('BrowserWindow module', () => {
|
||||
expect(() => {
|
||||
// @ts-ignore this is wrong on purpose.
|
||||
w.setAccentColor([1, 2, 3]);
|
||||
}).to.throw('Invalid accent color value - must be a string or boolean');
|
||||
}).to.throw('Invalid accent color value - must be null, hex string, or boolean');
|
||||
});
|
||||
|
||||
it('throws if called with an invalid parameter', () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
expect(() => {
|
||||
// @ts-ignore this is wrong on purpose.
|
||||
w.setAccentColor(new Date());
|
||||
}).to.throw('Invalid accent color value - must be null, hex string, or boolean');
|
||||
});
|
||||
|
||||
it('can be reset with null', () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.setAccentColor('#FF0000');
|
||||
expect(w.getAccentColor()).to.equal('#FF0000');
|
||||
w.setAccentColor(null);
|
||||
expect(w.getAccentColor()).to.not.equal('#FF0000');
|
||||
});
|
||||
|
||||
it('returns the accent color after setting it to a string', () => {
|
||||
|
||||
@@ -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)); }
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}})()`);
|
||||
|
||||
@@ -348,6 +348,11 @@ describe('nativeImage module', () => {
|
||||
expect(image.isEmpty()).to.be.false();
|
||||
});
|
||||
|
||||
ifit(process.platform === 'darwin')('returns a valid named symbol on darwin', function () {
|
||||
const image = nativeImage.createFromNamedImage('atom');
|
||||
expect(image.isEmpty()).to.be.false();
|
||||
});
|
||||
|
||||
ifit(process.platform === 'darwin')('returns allows an HSL shift for a valid image on darwin', function () {
|
||||
const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8]);
|
||||
expect(image.isEmpty()).to.be.false();
|
||||
|
||||
@@ -15,6 +15,7 @@ import * as webStream from 'node:stream/web';
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import * as url from 'node:url';
|
||||
|
||||
import { collectStreamBody, getResponse } from './lib/net-helpers';
|
||||
import { listen, defer, ifit } from './lib/spec-helpers';
|
||||
import { WebmGenerator } from './lib/video-helpers';
|
||||
import { closeAllWindows, closeWindow } from './lib/window-helpers';
|
||||
@@ -1578,6 +1579,22 @@ describe('protocol module', () => {
|
||||
expect(await net.fetch(url, { bypassCustomProtocolHandlers: true }).then(r => r.text())).to.equal('default');
|
||||
});
|
||||
|
||||
it('can bypass intercepted protocol handlers with net.request', async () => {
|
||||
protocol.handle('http', () => new Response('custom'));
|
||||
defer(() => { protocol.unhandle('http'); });
|
||||
const server = http.createServer((req, res) => {
|
||||
res.end('default');
|
||||
});
|
||||
defer(() => server.close());
|
||||
const { url } = await listen(server);
|
||||
// Make a request using net.request with bypassCustomProtocolHandlers: true
|
||||
const request = net.request({ method: 'GET', url, bypassCustomProtocolHandlers: true });
|
||||
const response = await getResponse(request);
|
||||
const body = await collectStreamBody(response);
|
||||
expect(response.statusCode).to.equal(200);
|
||||
expect(body).to.equal('default');
|
||||
});
|
||||
|
||||
it('bypassing custom protocol handlers also bypasses new protocols', async () => {
|
||||
protocol.handle('app', () => new Response('custom'));
|
||||
defer(() => { protocol.unhandle('app'); });
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user