mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9c33a951a | ||
|
|
8b02e33187 | ||
|
|
eecca2cb19 | ||
|
|
08b5ef556c | ||
|
|
ab85f2c2f7 | ||
|
|
1936243ce1 | ||
|
|
e7e052f5b1 | ||
|
|
349a9b6398 | ||
|
|
b5f19ce974 | ||
|
|
bb930b887b | ||
|
|
e962bc3743 | ||
|
|
895cf006e7 | ||
|
|
bc1ca72dc7 | ||
|
|
a9a4c77353 | ||
|
|
0f613246d9 | ||
|
|
a77b92adf2 | ||
|
|
d62c324567 | ||
|
|
108a26a0f9 | ||
|
|
331f8cca47 | ||
|
|
215128715a | ||
|
|
efcab52714 | ||
|
|
3495a3da69 | ||
|
|
364f3ed265 | ||
|
|
52f0b08bbb | ||
|
|
8453434b7e | ||
|
|
8d2ad379a6 | ||
|
|
b847900ad2 | ||
|
|
97a339250a | ||
|
|
3fb81955bb | ||
|
|
862129506f | ||
|
|
6972fbfea3 | ||
|
|
81332eaf65 | ||
|
|
a06d00df6c |
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:
|
||||
|
||||
25
.github/workflows/build.yml
vendored
25
.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
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
4
.github/workflows/pipeline-electron-lint.yml
vendored
4
.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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
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
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'142.0.7444.59',
|
||||
'142.0.7444.162',
|
||||
'node_version':
|
||||
'v22.21.1',
|
||||
'nan_version':
|
||||
|
||||
@@ -37,7 +37,7 @@ For more installation options and troubleshooting tips, see
|
||||
|
||||
Each Electron release provides binaries for macOS, Windows, and Linux.
|
||||
|
||||
* macOS (Big Sur and up): Electron provides 64-bit Intel and Apple Silicon / ARM binaries for macOS.
|
||||
* macOS (Monterey and up): Electron provides 64-bit Intel and Apple Silicon / ARM binaries for macOS.
|
||||
* Windows (Windows 10 and up): Electron provides `ia32` (`x86`), `x64` (`amd64`), and `arm64` binaries for Windows. Windows on ARM support was added in Electron 5.0.8. Support for Windows 7, 8 and 8.1 was [removed in Electron 23, in line with Chromium's Windows deprecation policy](https://www.electronjs.org/blog/windows-7-to-8-1-deprecation-notice).
|
||||
* Linux: The prebuilt binaries of Electron are built on Ubuntu 22.04. They have also been verified to work on:
|
||||
* Ubuntu 18.04 and newer
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -1467,15 +1467,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 +1489,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.
|
||||
|
||||
@@ -94,7 +94,7 @@ If the extension works on Chrome but not on Electron, file a bug in Electron's
|
||||
[issue tracker][issue-tracker] and describe which part
|
||||
of the extension is not working as expected.
|
||||
|
||||
[devtools-extension]: https://developer.chrome.com/extensions/devtools
|
||||
[devtools-extension]: https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools
|
||||
[session]: ../api/session.md
|
||||
[react-devtools]: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
|
||||
[load-extension]: ../api/extensions-api.md#extensionsloadextensionpath-options
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 4308450d0a0ac99561f8d1b1b110d0b29eeb1201..6af6ff976da4b3df2a2377babef1bf84da1db917 100644
|
||||
index 15cfa64348e80a3507fc83c3819e9abdb238bffb..4650080f45da4e321d4b0f0dabd98f2bf097c7c2 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 b10b1fe3795149b2a4e74255b7f17ef7f5de52c3..1ab2b3a420df30cddd4a7a9f1d4fabdca967691c 100644
|
||||
index a25e99fac5f5a171becff833464cdb3dfe7522c2..123a7c1fc980b1b3bcbe0e3e00afa594632d720b 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 @@
|
||||
|
||||
@@ -10,10 +10,10 @@ on Windows. We should refactor our code so that this patch isn't
|
||||
necessary.
|
||||
|
||||
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
|
||||
index 0fa02e6a98c795795c67a17f0b6be16f3a066d91..4cc81c0eed1b797c2ec940718c43571990463e4d 100644
|
||||
index 64fc07ab5ae7112be4226ef8f5bc13124bc13e78..da3237026408f71b5fb144801ba0be2390b5d65c 100644
|
||||
--- a/testing/variations/fieldtrial_testing_config.json
|
||||
+++ b/testing/variations/fieldtrial_testing_config.json
|
||||
@@ -25445,6 +25445,21 @@
|
||||
@@ -25439,6 +25439,21 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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
|
||||
@@ -52,3 +51,5 @@ api_remove_deprecated_getisolate.patch
|
||||
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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
308
patches/node/src_use_cp_utf8_for_wide_file_names_on_win32.patch
Normal file
308
patches/node/src_use_cp_utf8_for_wide_file_names_on_win32.patch
Normal file
@@ -0,0 +1,308 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Fedor Indutny <238531+indutny@users.noreply.github.com>
|
||||
Date: Fri, 7 Nov 2025 19:41:44 -0800
|
||||
Subject: src: use CP_UTF8 for wide file names on win32
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
`src/node_modules.cc` needs to be consistent with `src/node_file.cc` in
|
||||
how it translates the utf8 strings to `std::wstring` otherwise we might
|
||||
end up in situation where we can read the source code of imported
|
||||
package from disk, but fail to recognize that it is an ESM (or CJS) and
|
||||
cause runtime errors. This type of error is possible on Windows when the
|
||||
path contains unicode characters and "Language for non-Unicode programs"
|
||||
is set to "Chinese (Traditional, Taiwan)".
|
||||
|
||||
See: #58768
|
||||
PR-URL: https://github.com/nodejs/node/pull/60575
|
||||
Reviewed-By: Anna Henningsen <anna@addaleax.net>
|
||||
Reviewed-By: Darshan Sen <raisinten@gmail.com>
|
||||
Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
|
||||
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
|
||||
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
|
||||
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
|
||||
|
||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||
index 75be21c9e8b413f522240a906da06d26c44d5b71..e94c2b5f2cf7cac413cd5cb782fa1cca6d764960 100644
|
||||
--- a/src/node_file.cc
|
||||
+++ b/src/node_file.cc
|
||||
@@ -3056,42 +3056,6 @@ static void GetFormatOfExtensionlessFile(
|
||||
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
|
||||
}
|
||||
|
||||
-#ifdef _WIN32
|
||||
-#define BufferValueToPath(str) \
|
||||
- std::filesystem::path(ConvertToWideString(str.ToString(), CP_UTF8))
|
||||
-
|
||||
-std::string ConvertWideToUTF8(const std::wstring& wstr) {
|
||||
- if (wstr.empty()) return std::string();
|
||||
-
|
||||
- int size_needed = WideCharToMultiByte(CP_UTF8,
|
||||
- 0,
|
||||
- &wstr[0],
|
||||
- static_cast<int>(wstr.size()),
|
||||
- nullptr,
|
||||
- 0,
|
||||
- nullptr,
|
||||
- nullptr);
|
||||
- std::string strTo(size_needed, 0);
|
||||
- WideCharToMultiByte(CP_UTF8,
|
||||
- 0,
|
||||
- &wstr[0],
|
||||
- static_cast<int>(wstr.size()),
|
||||
- &strTo[0],
|
||||
- size_needed,
|
||||
- nullptr,
|
||||
- nullptr);
|
||||
- return strTo;
|
||||
-}
|
||||
-
|
||||
-#define PathToString(path) ConvertWideToUTF8(path.wstring());
|
||||
-
|
||||
-#else // _WIN32
|
||||
-
|
||||
-#define BufferValueToPath(str) std::filesystem::path(str.ToStringView());
|
||||
-#define PathToString(path) path.native();
|
||||
-
|
||||
-#endif // _WIN32
|
||||
-
|
||||
static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* isolate = env->isolate();
|
||||
@@ -3104,7 +3068,7 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
||||
env, permission::PermissionScope::kFileSystemRead, src.ToStringView());
|
||||
|
||||
- auto src_path = BufferValueToPath(src);
|
||||
+ auto src_path = src.ToPath();
|
||||
|
||||
BufferValue dest(isolate, args[1]);
|
||||
CHECK_NOT_NULL(*dest);
|
||||
@@ -3112,7 +3076,7 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
||||
env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView());
|
||||
|
||||
- auto dest_path = BufferValueToPath(dest);
|
||||
+ auto dest_path = dest.ToPath();
|
||||
bool dereference = args[2]->IsTrue();
|
||||
bool recursive = args[3]->IsTrue();
|
||||
|
||||
@@ -3141,8 +3105,8 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||
(src_status.type() == std::filesystem::file_type::directory) ||
|
||||
(dereference && src_status.type() == std::filesystem::file_type::symlink);
|
||||
|
||||
- auto src_path_str = PathToString(src_path);
|
||||
- auto dest_path_str = PathToString(dest_path);
|
||||
+ auto src_path_str = ConvertPathToUTF8(src_path);
|
||||
+ auto dest_path_str = ConvertPathToUTF8(dest_path);
|
||||
|
||||
if (!error_code) {
|
||||
// Check if src and dest are identical.
|
||||
@@ -3237,7 +3201,7 @@ static bool CopyUtimes(const std::filesystem::path& src,
|
||||
uv_fs_t req;
|
||||
auto cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
|
||||
|
||||
- auto src_path_str = PathToString(src);
|
||||
+ auto src_path_str = ConvertPathToUTF8(src);
|
||||
int result = uv_fs_stat(nullptr, &req, src_path_str.c_str(), nullptr);
|
||||
if (is_uv_error(result)) {
|
||||
env->ThrowUVException(result, "stat", nullptr, src_path_str.c_str());
|
||||
@@ -3248,7 +3212,7 @@ static bool CopyUtimes(const std::filesystem::path& src,
|
||||
const double source_atime = s->st_atim.tv_sec + s->st_atim.tv_nsec / 1e9;
|
||||
const double source_mtime = s->st_mtim.tv_sec + s->st_mtim.tv_nsec / 1e9;
|
||||
|
||||
- auto dest_file_path_str = PathToString(dest);
|
||||
+ auto dest_file_path_str = ConvertPathToUTF8(dest);
|
||||
int utime_result = uv_fs_utime(nullptr,
|
||||
&req,
|
||||
dest_file_path_str.c_str(),
|
||||
@@ -3383,7 +3347,7 @@ static void CpSyncCopyDir(const FunctionCallbackInfo<Value>& args) {
|
||||
std::error_code error;
|
||||
for (auto dir_entry : std::filesystem::directory_iterator(src)) {
|
||||
auto dest_file_path = dest / dir_entry.path().filename();
|
||||
- auto dest_str = PathToString(dest);
|
||||
+ auto dest_str = ConvertPathToUTF8(dest);
|
||||
|
||||
if (dir_entry.is_symlink()) {
|
||||
if (verbatim_symlinks) {
|
||||
@@ -3446,7 +3410,7 @@ static void CpSyncCopyDir(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
} else if (std::filesystem::is_regular_file(dest_file_path)) {
|
||||
if (!dereference || (!force && error_on_exist)) {
|
||||
- auto dest_file_path_str = PathToString(dest_file_path);
|
||||
+ auto dest_file_path_str = ConvertPathToUTF8(dest_file_path);
|
||||
env->ThrowStdErrException(
|
||||
std::make_error_code(std::errc::file_exists),
|
||||
"cp",
|
||||
diff --git a/src/node_modules.cc b/src/node_modules.cc
|
||||
index fa04b4a8cdd02a2cad1eaf2e5a848b951d1b1150..947513d5b91e8c13478b25d80a1157edbcd64b64 100644
|
||||
--- a/src/node_modules.cc
|
||||
+++ b/src/node_modules.cc
|
||||
@@ -327,22 +327,24 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
|
||||
// Stop the search when the process doesn't have permissions
|
||||
// to walk upwards
|
||||
- if (is_permissions_enabled &&
|
||||
- !env->permission()->is_granted(
|
||||
- env,
|
||||
- permission::PermissionScope::kFileSystemRead,
|
||||
- current_path.generic_string())) [[unlikely]] {
|
||||
- return nullptr;
|
||||
+ if (is_permissions_enabled) {
|
||||
+ if (!env->permission()->is_granted(
|
||||
+ env,
|
||||
+ permission::PermissionScope::kFileSystemRead,
|
||||
+ ConvertGenericPathToUTF8(current_path))) [[unlikely]] {
|
||||
+ 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;
|
||||
}
|
||||
|
||||
auto package_json_path = current_path / "package.json";
|
||||
+
|
||||
auto package_json =
|
||||
- GetPackageJSON(realm, package_json_path.string(), nullptr);
|
||||
+ GetPackageJSON(realm, ConvertPathToUTF8(package_json_path), nullptr);
|
||||
if (package_json != nullptr) {
|
||||
return package_json;
|
||||
}
|
||||
@@ -364,20 +366,12 @@ void BindingData::GetNearestParentPackageJSONType(
|
||||
|
||||
ToNamespacedPath(realm->env(), &path_value);
|
||||
|
||||
- std::string path_value_str = path_value.ToString();
|
||||
+ auto path = path_value.ToPath();
|
||||
+
|
||||
if (slashCheck) {
|
||||
- path_value_str.push_back(kPathSeparator);
|
||||
+ path /= "";
|
||||
}
|
||||
|
||||
- std::filesystem::path path;
|
||||
-
|
||||
-#ifdef _WIN32
|
||||
- std::wstring wide_path = ConvertToWideString(path_value_str, GetACP());
|
||||
- path = std::filesystem::path(wide_path);
|
||||
-#else
|
||||
- path = std::filesystem::path(path_value_str);
|
||||
-#endif
|
||||
-
|
||||
auto package_json = TraverseParent(realm, path);
|
||||
|
||||
if (package_json == nullptr) {
|
||||
diff --git a/src/util-inl.h b/src/util-inl.h
|
||||
index 816156282790383e896b28eb46a3b4703bbe17f0..7274e5da8cc9eb164ec46ec2f7932691ed6ba9dc 100644
|
||||
--- a/src/util-inl.h
|
||||
+++ b/src/util-inl.h
|
||||
@@ -678,12 +678,11 @@ inline bool IsWindowsBatchFile(const char* filename) {
|
||||
return !extension.empty() && (extension == "cmd" || extension == "bat");
|
||||
}
|
||||
|
||||
-inline std::wstring ConvertToWideString(const std::string& str,
|
||||
- UINT code_page) {
|
||||
+inline std::wstring ConvertUTF8ToWideString(const std::string& str) {
|
||||
int size_needed = MultiByteToWideChar(
|
||||
- code_page, 0, &str[0], static_cast<int>(str.size()), nullptr, 0);
|
||||
+ CP_UTF8, 0, &str[0], static_cast<int>(str.size()), nullptr, 0);
|
||||
std::wstring wstrTo(size_needed, 0);
|
||||
- MultiByteToWideChar(code_page,
|
||||
+ MultiByteToWideChar(CP_UTF8,
|
||||
0,
|
||||
&str[0],
|
||||
static_cast<int>(str.size()),
|
||||
@@ -691,6 +690,59 @@ inline std::wstring ConvertToWideString(const std::string& str,
|
||||
size_needed);
|
||||
return wstrTo;
|
||||
}
|
||||
+
|
||||
+std::string ConvertWideStringToUTF8(const std::wstring& wstr) {
|
||||
+ if (wstr.empty()) return std::string();
|
||||
+
|
||||
+ int size_needed = WideCharToMultiByte(CP_UTF8,
|
||||
+ 0,
|
||||
+ &wstr[0],
|
||||
+ static_cast<int>(wstr.size()),
|
||||
+ nullptr,
|
||||
+ 0,
|
||||
+ nullptr,
|
||||
+ nullptr);
|
||||
+ std::string strTo(size_needed, 0);
|
||||
+ WideCharToMultiByte(CP_UTF8,
|
||||
+ 0,
|
||||
+ &wstr[0],
|
||||
+ static_cast<int>(wstr.size()),
|
||||
+ &strTo[0],
|
||||
+ size_needed,
|
||||
+ nullptr,
|
||||
+ nullptr);
|
||||
+ return strTo;
|
||||
+}
|
||||
+
|
||||
+template <typename T, size_t kStackStorageSize>
|
||||
+std::filesystem::path MaybeStackBuffer<T, kStackStorageSize>::ToPath() const {
|
||||
+ std::wstring wide_path = ConvertUTF8ToWideString(ToString());
|
||||
+ return std::filesystem::path(wide_path);
|
||||
+}
|
||||
+
|
||||
+std::string ConvertPathToUTF8(const std::filesystem::path& path) {
|
||||
+ return ConvertWideStringToUTF8(path.wstring());
|
||||
+}
|
||||
+
|
||||
+std::string ConvertGenericPathToUTF8(const std::filesystem::path& path) {
|
||||
+ return ConvertWideStringToUTF8(path.generic_wstring());
|
||||
+}
|
||||
+
|
||||
+#else // _WIN32
|
||||
+
|
||||
+template <typename T, size_t kStackStorageSize>
|
||||
+std::filesystem::path MaybeStackBuffer<T, kStackStorageSize>::ToPath() const {
|
||||
+ return std::filesystem::path(ToStringView());
|
||||
+}
|
||||
+
|
||||
+std::string ConvertPathToUTF8(const std::filesystem::path& path) {
|
||||
+ return path.native();
|
||||
+}
|
||||
+
|
||||
+std::string ConvertGenericPathToUTF8(const std::filesystem::path& path) {
|
||||
+ return path.generic_string();
|
||||
+}
|
||||
+
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace node
|
||||
diff --git a/src/util.h b/src/util.h
|
||||
index dab48c59e1cd947a32cf08e5ab23cd60fe32303e..91bfb8d94b1c053b59c20e25306ef0f08e977f49 100644
|
||||
--- a/src/util.h
|
||||
+++ b/src/util.h
|
||||
@@ -507,6 +507,8 @@ class MaybeStackBuffer {
|
||||
inline std::basic_string_view<T> ToStringView() const {
|
||||
return {out(), length()};
|
||||
}
|
||||
+ // This can only be used if the buffer contains path data in UTF8
|
||||
+ inline std::filesystem::path ToPath() const;
|
||||
|
||||
private:
|
||||
size_t length_;
|
||||
@@ -1022,9 +1024,15 @@ class JSONOutputStream final : public v8::OutputStream {
|
||||
// Returns true if OS==Windows and filename ends in .bat or .cmd,
|
||||
// case insensitive.
|
||||
inline bool IsWindowsBatchFile(const char* filename);
|
||||
-inline std::wstring ConvertToWideString(const std::string& str, UINT code_page);
|
||||
+inline std::wstring ConvertUTF8ToWideString(const std::string& str);
|
||||
+inline std::string ConvertWideStringToUTF8(const std::wstring& wstr);
|
||||
+
|
||||
#endif // _WIN32
|
||||
|
||||
+inline std::filesystem::path ConvertUTF8ToPath(const std::string& str);
|
||||
+inline std::string ConvertPathToUTF8(const std::filesystem::path& path);
|
||||
+inline std::string ConvertGenericPathToUTF8(const std::filesystem::path& path);
|
||||
+
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
@@ -1 +1,3 @@
|
||||
chore_allow_customizing_microtask_policy_per_context.patch
|
||||
turboshaft_avoid_introducing_too_many_variables.patch
|
||||
preserve_field_repr_in_property_array_extension.patch
|
||||
|
||||
302
patches/v8/preserve_field_repr_in_property_array_extension.patch
Normal file
302
patches/v8/preserve_field_repr_in_property_array_extension.patch
Normal file
@@ -0,0 +1,302 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Leszek Swirski <leszeks@chromium.org>
|
||||
Date: Wed, 12 Nov 2025 16:46:01 +0100
|
||||
Subject: Preserve field repr in property array extension
|
||||
|
||||
Walk the descriptor array in lockstep with the property array when
|
||||
extending the latter.
|
||||
|
||||
Fixed: 460017370
|
||||
Change-Id: If0b4fc3c5f62fc0cc373588cbddc3c0a95c7225c
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7146166
|
||||
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
|
||||
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
|
||||
Reviewed-by: Igor Sheludko <ishell@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#103674}
|
||||
|
||||
diff --git a/src/compiler/access-builder.cc b/src/compiler/access-builder.cc
|
||||
index c0bf470ada9443c63805b9e331bfdceb230929a8..8517527ecc2df89289cde05d7bc124f34133a838 100644
|
||||
--- a/src/compiler/access-builder.cc
|
||||
+++ b/src/compiler/access-builder.cc
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "src/compiler/access-builder.h"
|
||||
|
||||
+#include "src/codegen/machine-type.h"
|
||||
+#include "src/compiler/property-access-builder.h"
|
||||
#include "src/compiler/type-cache.h"
|
||||
#include "src/handles/handles-inl.h"
|
||||
#include "src/objects/arguments.h"
|
||||
@@ -1097,12 +1099,16 @@ FieldAccess AccessBuilder::ForFeedbackVectorSlot(int index) {
|
||||
}
|
||||
|
||||
// static
|
||||
-FieldAccess AccessBuilder::ForPropertyArraySlot(int index) {
|
||||
+FieldAccess AccessBuilder::ForPropertyArraySlot(int index,
|
||||
+ Representation representation) {
|
||||
int offset = PropertyArray::OffsetOfElementAt(index);
|
||||
- FieldAccess access = {kTaggedBase, offset,
|
||||
- Handle<Name>(), OptionalMapRef(),
|
||||
- Type::Any(), MachineType::AnyTagged(),
|
||||
- kFullWriteBarrier, "PropertyArraySlot"};
|
||||
+ MachineType machine_type =
|
||||
+ representation.IsHeapObject() || representation.IsDouble()
|
||||
+ ? MachineType::TaggedPointer()
|
||||
+ : MachineType::AnyTagged();
|
||||
+ FieldAccess access = {
|
||||
+ kTaggedBase, offset, Handle<Name>(), OptionalMapRef(),
|
||||
+ Type::Any(), machine_type, kFullWriteBarrier, "PropertyArraySlot"};
|
||||
return access;
|
||||
}
|
||||
|
||||
diff --git a/src/compiler/access-builder.h b/src/compiler/access-builder.h
|
||||
index 0363f0fc34593c8cb5dfda4c3b59a59664d689e3..b946fc58690cbe5b12903a832d0178c83a87416c 100644
|
||||
--- a/src/compiler/access-builder.h
|
||||
+++ b/src/compiler/access-builder.h
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "src/compiler/write-barrier-kind.h"
|
||||
#include "src/objects/elements-kind.h"
|
||||
#include "src/objects/js-objects.h"
|
||||
+#include "src/objects/property-details.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@@ -323,7 +324,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
|
||||
static FieldAccess ForFeedbackVectorSlot(int index);
|
||||
|
||||
// Provides access to PropertyArray slots.
|
||||
- static FieldAccess ForPropertyArraySlot(int index);
|
||||
+ static FieldAccess ForPropertyArraySlot(int index,
|
||||
+ Representation representation);
|
||||
|
||||
// Provides access to ScopeInfo flags.
|
||||
static FieldAccess ForScopeInfoFlags();
|
||||
diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc
|
||||
index 60b4df5fbb6b762cee6609c5f0b3452e68e0fe64..697bb0526d465df96378ea3f81ad725f49fd3dec 100644
|
||||
--- a/src/compiler/js-native-context-specialization.cc
|
||||
+++ b/src/compiler/js-native-context-specialization.cc
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "src/objects/elements-kind.h"
|
||||
#include "src/objects/feedback-vector.h"
|
||||
#include "src/objects/heap-number.h"
|
||||
+#include "src/objects/property-details.h"
|
||||
#include "src/objects/string.h"
|
||||
|
||||
namespace v8 {
|
||||
@@ -4231,25 +4232,59 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
|
||||
// for intermediate states of chains of property additions. That makes
|
||||
// it unclear what the best approach is here.
|
||||
DCHECK_EQ(map.UnusedPropertyFields(), 0);
|
||||
- int length = map.NextFreePropertyIndex() - map.GetInObjectProperties();
|
||||
+ int in_object_length = map.GetInObjectProperties();
|
||||
+ int length = map.NextFreePropertyIndex() - in_object_length;
|
||||
// Under normal circumstances, NextFreePropertyIndex() will always be larger
|
||||
// than GetInObjectProperties(). However, an attacker able to corrupt heap
|
||||
// memory can break this invariant, in which case we'll get confused here,
|
||||
// potentially causing a sandbox violation. This CHECK defends against that.
|
||||
SBXCHECK_GE(length, 0);
|
||||
int new_length = length + JSObject::kFieldsAdded;
|
||||
+
|
||||
+ // Find the descriptor index corresponding to the first out-of-object
|
||||
+ // property.
|
||||
+ DescriptorArrayRef descs = map.instance_descriptors(broker());
|
||||
+ InternalIndex first_out_of_object_descriptor(in_object_length);
|
||||
+ InternalIndex number_of_descriptors(descs.object()->number_of_descriptors());
|
||||
+ for (InternalIndex i(in_object_length); i < number_of_descriptors; ++i) {
|
||||
+ PropertyDetails details = descs.GetPropertyDetails(i);
|
||||
+ // Skip over non-field properties.
|
||||
+ if (details.location() != PropertyLocation::kField) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Skip over in-object fields.
|
||||
+ // TODO(leszeks): We could make this smarter, like a binary search.
|
||||
+ if (details.field_index() < in_object_length) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ first_out_of_object_descriptor = i;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
// Collect the field values from the {properties}.
|
||||
- ZoneVector<Node*> values(zone());
|
||||
+ ZoneVector<std::pair<Node*, Representation>> values(zone());
|
||||
values.reserve(new_length);
|
||||
- for (int i = 0; i < length; ++i) {
|
||||
+
|
||||
+ // Walk the property descriptors alongside the property values, to make
|
||||
+ // sure to get and store them with the right machine type.
|
||||
+ InternalIndex descriptor = first_out_of_object_descriptor;
|
||||
+ for (int i = 0; i < length; ++i, ++descriptor) {
|
||||
+ PropertyDetails details = descs.GetPropertyDetails(descriptor);
|
||||
+ while (details.location() != PropertyLocation::kField) {
|
||||
+ ++descriptor;
|
||||
+ details = descs.GetPropertyDetails(descriptor);
|
||||
+ }
|
||||
+ DCHECK_EQ(i, details.field_index() - in_object_length);
|
||||
Node* value = effect = graph()->NewNode(
|
||||
- simplified()->LoadField(AccessBuilder::ForFixedArraySlot(i)),
|
||||
+ simplified()->LoadField(
|
||||
+ AccessBuilder::ForPropertyArraySlot(i, details.representation())),
|
||||
properties, effect, control);
|
||||
- values.push_back(value);
|
||||
+ values.push_back({value, details.representation()});
|
||||
}
|
||||
// Initialize the new fields to undefined.
|
||||
for (int i = 0; i < JSObject::kFieldsAdded; ++i) {
|
||||
- values.push_back(jsgraph()->UndefinedConstant());
|
||||
+ values.push_back(
|
||||
+ {jsgraph()->UndefinedConstant(), Representation::Tagged()});
|
||||
}
|
||||
|
||||
// Compute new length and hash.
|
||||
@@ -4287,7 +4322,8 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
|
||||
a.Store(AccessBuilder::ForMap(), jsgraph()->PropertyArrayMapConstant());
|
||||
a.Store(AccessBuilder::ForPropertyArrayLengthAndHash(), new_length_and_hash);
|
||||
for (int i = 0; i < new_length; ++i) {
|
||||
- a.Store(AccessBuilder::ForFixedArraySlot(i), values[i]);
|
||||
+ a.Store(AccessBuilder::ForPropertyArraySlot(i, values[i].second),
|
||||
+ values[i].first);
|
||||
}
|
||||
return a.Finish();
|
||||
}
|
||||
diff --git a/src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h b/src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h
|
||||
index bf0832ecc67bec1a6da711b9bff7266ba3df7d51..a6452e7cfb9e7d7d6d222bd625d13dabe4f97cb7 100644
|
||||
--- a/src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h
|
||||
+++ b/src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "src/compiler/turboshaft/representations.h"
|
||||
#include "src/deoptimizer/deoptimize-reason.h"
|
||||
#include "src/objects/contexts.h"
|
||||
+#include "src/objects/descriptor-array-inl.h"
|
||||
#include "src/objects/instance-type-inl.h"
|
||||
|
||||
namespace v8::internal::compiler::turboshaft {
|
||||
@@ -323,8 +324,32 @@ class TurbolevEarlyLoweringReducer : public Next {
|
||||
}
|
||||
|
||||
V<PropertyArray> ExtendPropertiesBackingStore(
|
||||
- V<PropertyArray> old_property_array, V<JSObject> object, int old_length,
|
||||
+ V<PropertyArray> old_property_array, V<JSObject> object,
|
||||
+ const compiler::MapRef& old_map, int old_length,
|
||||
V<FrameState> frame_state, const FeedbackSource& feedback) {
|
||||
+ int in_object_length = old_map.GetInObjectProperties();
|
||||
+
|
||||
+ // Find the descriptor index corresponding to the first out-of-object
|
||||
+ // property.
|
||||
+ DescriptorArrayRef descs = old_map.instance_descriptors(broker_);
|
||||
+ InternalIndex first_out_of_object_descriptor(in_object_length);
|
||||
+ InternalIndex number_of_descriptors(
|
||||
+ descs.object()->number_of_descriptors());
|
||||
+ for (InternalIndex i(in_object_length); i < number_of_descriptors; ++i) {
|
||||
+ PropertyDetails details = descs.GetPropertyDetails(i);
|
||||
+ // Skip over non-field properties.
|
||||
+ if (details.location() != PropertyLocation::kField) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Skip over in-object fields.
|
||||
+ // TODO(leszeks): We could make this smarter, like a binary search.
|
||||
+ if (details.field_index() < in_object_length) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ first_out_of_object_descriptor = i;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
// Allocate new PropertyArray.
|
||||
int new_length = old_length + JSObject::kFieldsAdded;
|
||||
Uninitialized<PropertyArray> new_property_array =
|
||||
@@ -335,18 +360,28 @@ class TurbolevEarlyLoweringReducer : public Next {
|
||||
__ HeapConstant(factory_->property_array_map()));
|
||||
|
||||
// Copy existing properties over.
|
||||
- for (int i = 0; i < old_length; i++) {
|
||||
+ InternalIndex descriptor = first_out_of_object_descriptor;
|
||||
+ for (int i = 0; i < old_length; ++i, ++descriptor) {
|
||||
+ PropertyDetails details = descs.GetPropertyDetails(descriptor);
|
||||
+ while (details.location() != PropertyLocation::kField) {
|
||||
+ ++descriptor;
|
||||
+ details = descs.GetPropertyDetails(descriptor);
|
||||
+ }
|
||||
+ DCHECK_EQ(i, details.field_index() - in_object_length);
|
||||
+ Representation r = details.representation();
|
||||
+
|
||||
V<Object> old_value = __ template LoadField<Object>(
|
||||
- old_property_array, AccessBuilder::ForPropertyArraySlot(i));
|
||||
+ old_property_array, AccessBuilder::ForPropertyArraySlot(i, r));
|
||||
__ InitializeField(new_property_array,
|
||||
- AccessBuilder::ForPropertyArraySlot(i), old_value);
|
||||
+ AccessBuilder::ForPropertyArraySlot(i, r), old_value);
|
||||
}
|
||||
|
||||
// Initialize new properties to undefined.
|
||||
V<Undefined> undefined = __ HeapConstant(factory_->undefined_value());
|
||||
for (int i = 0; i < JSObject::kFieldsAdded; ++i) {
|
||||
__ InitializeField(new_property_array,
|
||||
- AccessBuilder::ForPropertyArraySlot(old_length + i),
|
||||
+ AccessBuilder::ForPropertyArraySlot(
|
||||
+ old_length + i, Representation::Tagged()),
|
||||
undefined);
|
||||
}
|
||||
|
||||
diff --git a/src/compiler/turboshaft/turbolev-graph-builder.cc b/src/compiler/turboshaft/turbolev-graph-builder.cc
|
||||
index bcb461488899bcbf3a130845f39f0005bd34131e..d80362036da4c80e192ed489e3c66e8bfed271ba 100644
|
||||
--- a/src/compiler/turboshaft/turbolev-graph-builder.cc
|
||||
+++ b/src/compiler/turboshaft/turbolev-graph-builder.cc
|
||||
@@ -2760,10 +2760,11 @@ class GraphBuildingNodeProcessor {
|
||||
maglev::ProcessResult Process(maglev::ExtendPropertiesBackingStore* node,
|
||||
const maglev::ProcessingState& state) {
|
||||
GET_FRAME_STATE_MAYBE_ABORT(frame_state, node->eager_deopt_info());
|
||||
- SetMap(node, __ ExtendPropertiesBackingStore(
|
||||
- Map(node->property_array_input()),
|
||||
- Map(node->object_input()), node->old_length(), frame_state,
|
||||
- node->eager_deopt_info()->feedback_to_update()));
|
||||
+ SetMap(node,
|
||||
+ __ ExtendPropertiesBackingStore(
|
||||
+ Map(node->property_array_input()), Map(node->object_input()),
|
||||
+ node->old_map(), node->old_length(), frame_state,
|
||||
+ node->eager_deopt_info()->feedback_to_update()));
|
||||
return maglev::ProcessResult::kContinue;
|
||||
}
|
||||
|
||||
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
|
||||
index a2cac0c5ded6806292d98218dcb6da0f7430f16b..4664ca78b4413da6a3d9d6cafa705d33f5c02ee2 100644
|
||||
--- a/src/maglev/maglev-graph-builder.cc
|
||||
+++ b/src/maglev/maglev-graph-builder.cc
|
||||
@@ -5253,7 +5253,7 @@ ReduceResult MaglevGraphBuilder::BuildExtendPropertiesBackingStore(
|
||||
// potentially causing a sandbox violation. This CHECK defends against that.
|
||||
SBXCHECK_GE(length, 0);
|
||||
return AddNewNode<ExtendPropertiesBackingStore>({property_array, receiver},
|
||||
- length);
|
||||
+ map, length);
|
||||
}
|
||||
|
||||
MaybeReduceResult MaglevGraphBuilder::TryBuildStoreField(
|
||||
diff --git a/src/maglev/maglev-ir.h b/src/maglev/maglev-ir.h
|
||||
index 9d5cf70429ed5513aa7cd23baf1a044acfc25feb..707d8fd56a367856a648dbe152e3d945416c70af 100644
|
||||
--- a/src/maglev/maglev-ir.h
|
||||
+++ b/src/maglev/maglev-ir.h
|
||||
@@ -9084,8 +9084,10 @@ class ExtendPropertiesBackingStore
|
||||
using Base = FixedInputValueNodeT<2, ExtendPropertiesBackingStore>;
|
||||
|
||||
public:
|
||||
- explicit ExtendPropertiesBackingStore(uint64_t bitfield, int old_length)
|
||||
- : Base(bitfield), old_length_(old_length) {}
|
||||
+ explicit ExtendPropertiesBackingStore(uint64_t bitfield,
|
||||
+ const compiler::MapRef& old_map,
|
||||
+ int old_length)
|
||||
+ : Base(bitfield), old_map_(old_map), old_length_(old_length) {}
|
||||
|
||||
static constexpr OpProperties kProperties =
|
||||
OpProperties::CanAllocate() | OpProperties::CanRead() |
|
||||
@@ -9105,9 +9107,11 @@ class ExtendPropertiesBackingStore
|
||||
void GenerateCode(MaglevAssembler*, const ProcessingState&);
|
||||
void PrintParams(std::ostream&) const;
|
||||
|
||||
+ const compiler::MapRef& old_map() const { return old_map_; }
|
||||
int old_length() const { return old_length_; }
|
||||
|
||||
private:
|
||||
+ const compiler::MapRef old_map_;
|
||||
const int old_length_;
|
||||
};
|
||||
|
||||
475
patches/v8/turboshaft_avoid_introducing_too_many_variables.patch
Normal file
475
patches/v8/turboshaft_avoid_introducing_too_many_variables.patch
Normal file
@@ -0,0 +1,475 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Darius Mercadier <dmercadier@chromium.org>
|
||||
Date: Wed, 5 Nov 2025 14:06:54 +0100
|
||||
Subject: [turboshaft] Avoid introducing too many Variables
|
||||
|
||||
.... if we have very large merges.
|
||||
|
||||
Cf https://crbug.com/418027512#comment5 for explanations of why this
|
||||
is necessary (and the following comment for why I don't see a good
|
||||
alternative to this CL).
|
||||
|
||||
I've locally confirmed that this fixes the OOM from
|
||||
https://crbug.com/457625181, and it reduces memory consumption on
|
||||
binaries/crbug-40219016-zelda/zelda.wasm (from
|
||||
https://crbug.com/418027512) by 20+%.
|
||||
|
||||
Bug: 418027512, 457625181
|
||||
Change-Id: If55af659667723ce85ff71bcac66a43aff863e05
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7119378
|
||||
Commit-Queue: Darius Mercadier <dmercadier@chromium.org>
|
||||
Auto-Submit: Darius Mercadier <dmercadier@chromium.org>
|
||||
Reviewed-by: Matthias Liedtke <mliedtke@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#103534}
|
||||
|
||||
diff --git a/src/compiler/turboshaft/branch-elimination-reducer.h b/src/compiler/turboshaft/branch-elimination-reducer.h
|
||||
index f115c86894f0cf739d6381f7844e5589831cc209..d917d27bd3964ba07b41efa49b86435ae7720064 100644
|
||||
--- a/src/compiler/turboshaft/branch-elimination-reducer.h
|
||||
+++ b/src/compiler/turboshaft/branch-elimination-reducer.h
|
||||
@@ -323,6 +323,10 @@ class BranchEliminationReducer : public Next {
|
||||
goto no_change;
|
||||
}
|
||||
|
||||
+ if (!__ CanCreateNVariables(destination_origin->OpCountUpperBound())) {
|
||||
+ goto no_change;
|
||||
+ }
|
||||
+
|
||||
if (const BranchOp* branch = last_op.template TryCast<BranchOp>()) {
|
||||
V<Word32> condition =
|
||||
__ template MapToNewGraph<true>(branch->condition());
|
||||
diff --git a/src/compiler/turboshaft/copying-phase.h b/src/compiler/turboshaft/copying-phase.h
|
||||
index 875861d005435b1c2a1591886c053ca360c3e2f2..b43958499d5b6d6e72b81d965d0729bb213c7ae6 100644
|
||||
--- a/src/compiler/turboshaft/copying-phase.h
|
||||
+++ b/src/compiler/turboshaft/copying-phase.h
|
||||
@@ -714,9 +714,23 @@ class GraphVisitor : public OutputGraphAssembler<GraphVisitor<AfterNext>,
|
||||
if (Asm().CanAutoInlineBlocksWithSinglePredecessor() &&
|
||||
terminator.Is<GotoOp>()) {
|
||||
Block* destination = terminator.Cast<GotoOp>().destination;
|
||||
- if (destination->PredecessorCount() == 1) {
|
||||
- block_to_inline_now_ = destination;
|
||||
- return;
|
||||
+ // Inlining the destination will require setting it in needs_variables_
|
||||
+ // mode; we thus check that we can actually create enough variables to do
|
||||
+ // this.
|
||||
+ // TODO(dmercadier): in practice, the only reason we need variables for
|
||||
+ // the destination is because we could be currently in a phase that cloned
|
||||
+ // the current block, which could lead to {destination} being cloned as
|
||||
+ // well. No all phases can do this, so we could check that we're not in
|
||||
+ // such a phase, and if so, not use variables for the destination. One way
|
||||
+ // to do this would be to have a DisallowCloningReducer which would
|
||||
+ // static_assert that LoopUnrolling/LoopPeeling/BranchElimination aren't
|
||||
+ // on the stack and would also prevent using CloneSubGraph,
|
||||
+ // CloneAndInlineBlock and CloneBlockAndGoto.
|
||||
+ if (Asm().CanCreateNVariables(destination->OpCountUpperBound())) {
|
||||
+ if (destination->PredecessorCount() == 1) {
|
||||
+ block_to_inline_now_ = destination;
|
||||
+ return;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
// Just going through the regular VisitOp function.
|
||||
diff --git a/src/compiler/turboshaft/graph.h b/src/compiler/turboshaft/graph.h
|
||||
index 936c8b0269a9b87a4ffa20c40bbd908fb8c69010..a3c1c40e4e7097f518e107d85786c7cc5466e595 100644
|
||||
--- a/src/compiler/turboshaft/graph.h
|
||||
+++ b/src/compiler/turboshaft/graph.h
|
||||
@@ -608,6 +608,7 @@ class Graph {
|
||||
operation_origins_.Reset();
|
||||
operation_types_.Reset();
|
||||
dominator_tree_depth_ = 0;
|
||||
+ max_merge_pred_count_ = 0;
|
||||
#ifdef DEBUG
|
||||
block_type_refinement_.Reset();
|
||||
// Do not reset of graph_created_from_turbofan_ as it is propagated along
|
||||
@@ -791,6 +792,8 @@ class Graph {
|
||||
bound_blocks_.push_back(block);
|
||||
uint32_t depth = block->ComputeDominator();
|
||||
dominator_tree_depth_ = std::max<uint32_t>(dominator_tree_depth_, depth);
|
||||
+ max_merge_pred_count_ =
|
||||
+ std::max<uint32_t>(max_merge_pred_count_, block->PredecessorCount());
|
||||
|
||||
#ifdef DEBUG
|
||||
if (v8_flags.turboshaft_trace_emitted) {
|
||||
@@ -1016,6 +1019,8 @@ class Graph {
|
||||
|
||||
uint32_t DominatorTreeDepth() const { return dominator_tree_depth_; }
|
||||
|
||||
+ uint32_t max_merge_pred_count() const { return max_merge_pred_count_; }
|
||||
+
|
||||
const GrowingOpIndexSidetable<Type>& operation_types() const {
|
||||
return operation_types_;
|
||||
}
|
||||
@@ -1068,6 +1073,7 @@ class Graph {
|
||||
std::swap(next_block_, companion.next_block_);
|
||||
std::swap(block_permutation_, companion.block_permutation_);
|
||||
std::swap(graph_zone_, companion.graph_zone_);
|
||||
+ std::swap(max_merge_pred_count_, companion.max_merge_pred_count_);
|
||||
op_to_block_.SwapData(companion.op_to_block_);
|
||||
source_positions_.SwapData(companion.source_positions_);
|
||||
operation_origins_.SwapData(companion.operation_origins_);
|
||||
@@ -1206,6 +1212,9 @@ class Graph {
|
||||
GrowingOpIndexSidetable<SourcePosition> source_positions_;
|
||||
GrowingOpIndexSidetable<OpIndex> operation_origins_;
|
||||
uint32_t dominator_tree_depth_ = 0;
|
||||
+ // {max_merge_pred_count_} stores the maximum number of predecessors that any
|
||||
+ // Merge in the graph has.
|
||||
+ uint32_t max_merge_pred_count_ = 0;
|
||||
GrowingOpIndexSidetable<Type> operation_types_;
|
||||
#ifdef DEBUG
|
||||
GrowingBlockSidetable<TypeRefinements> block_type_refinement_;
|
||||
diff --git a/src/compiler/turboshaft/loop-peeling-reducer.h b/src/compiler/turboshaft/loop-peeling-reducer.h
|
||||
index a9b5eaaf4c88354164b3a5833d4bd6b2760b12a0..b7df7acb61d048669a2cacfbc4e2156df69788dc 100644
|
||||
--- a/src/compiler/turboshaft/loop-peeling-reducer.h
|
||||
+++ b/src/compiler/turboshaft/loop-peeling-reducer.h
|
||||
@@ -57,8 +57,7 @@ class LoopPeelingReducer : public Next {
|
||||
const Block* dst = gto.destination;
|
||||
if (dst->IsLoop() && !gto.is_backedge && CanPeelLoop(dst)) {
|
||||
if (ShouldSkipOptimizationStep()) goto no_change;
|
||||
- PeelFirstIteration(dst);
|
||||
- return {};
|
||||
+ if (PeelFirstIteration(dst)) return {};
|
||||
} else if (IsEmittingPeeledIteration() && dst == current_loop_header_) {
|
||||
// We skip the backedge of the loop: PeelFirstIeration will instead emit a
|
||||
// forward edge to the non-peeled header.
|
||||
@@ -111,13 +110,21 @@ class LoopPeelingReducer : public Next {
|
||||
kEmittingUnpeeledBody
|
||||
};
|
||||
|
||||
- void PeelFirstIteration(const Block* header) {
|
||||
+ bool PeelFirstIteration(const Block* header) {
|
||||
TRACE("LoopPeeling: peeling loop at " << header->index());
|
||||
DCHECK_EQ(peeling_, PeelingStatus::kNotPeeling);
|
||||
ScopedModification<PeelingStatus> scope(&peeling_,
|
||||
PeelingStatus::kEmittingPeeledLoop);
|
||||
current_loop_header_ = header;
|
||||
|
||||
+ constexpr int kNumberOfLoopCopies = 2; // peeled + unpeeled
|
||||
+ size_t op_count_upper_bound =
|
||||
+ loop_finder_.GetLoopInfo(header).op_count * kNumberOfLoopCopies;
|
||||
+ if (!__ CanCreateNVariables(op_count_upper_bound)) {
|
||||
+ TRACE("> Too many variables, skipping peeling");
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
// Emitting the peeled iteration.
|
||||
auto loop_body = loop_finder_.GetLoopBody(header);
|
||||
// Note that this call to CloneSubGraph will not emit the backedge because
|
||||
@@ -133,7 +140,7 @@ class LoopPeelingReducer : public Next {
|
||||
// While peeling, we realized that the 2nd iteration of the loop is not
|
||||
// reachable.
|
||||
TRACE("> Second iteration is not reachable, stopping now");
|
||||
- return;
|
||||
+ return true;
|
||||
}
|
||||
|
||||
// We now emit the regular unpeeled loop.
|
||||
@@ -141,6 +148,7 @@ class LoopPeelingReducer : public Next {
|
||||
TRACE("> Emitting unpeeled loop body");
|
||||
__ CloneSubGraph(loop_body, /* keep_loop_kinds */ true,
|
||||
/* is_loop_after_peeling */ true);
|
||||
+ return true;
|
||||
}
|
||||
|
||||
bool CanPeelLoop(const Block* header) {
|
||||
diff --git a/src/compiler/turboshaft/loop-unrolling-reducer.h b/src/compiler/turboshaft/loop-unrolling-reducer.h
|
||||
index 181d298bfa27d21f013016b34a586078d12f8a58..92d6f7b36d4c5c0a64723f7d18427a62347bad9f 100644
|
||||
--- a/src/compiler/turboshaft/loop-unrolling-reducer.h
|
||||
+++ b/src/compiler/turboshaft/loop-unrolling-reducer.h
|
||||
@@ -211,6 +211,11 @@ class V8_EXPORT_PRIVATE LoopUnrollingAnalyzer {
|
||||
info.op_count < kMaxLoopSizeForPartialUnrolling;
|
||||
}
|
||||
|
||||
+ size_t GetLoopOpCount(const Block* loop_header) {
|
||||
+ DCHECK(loop_header->IsLoop());
|
||||
+ return loop_finder_.GetLoopInfo(loop_header).op_count;
|
||||
+ }
|
||||
+
|
||||
// The returned unroll count is the total number of copies of the loop body
|
||||
// in the resulting graph, i.e., an unroll count of N means N-1 copies of the
|
||||
// body which were partially unrolled, and 1 for the original/remaining body.
|
||||
@@ -383,14 +388,12 @@ class LoopUnrollingReducer : public Next {
|
||||
// header (note that loop headers only have 2 predecessor, including the
|
||||
// backedge), and that isn't the backedge.
|
||||
if (ShouldSkipOptimizationStep()) goto no_change;
|
||||
- if (analyzer_.ShouldRemoveLoop(dst)) {
|
||||
- RemoveLoop(dst);
|
||||
+ if (analyzer_.ShouldRemoveLoop(dst) && RemoveLoop(dst)) {
|
||||
return {};
|
||||
- } else if (analyzer_.ShouldFullyUnrollLoop(dst)) {
|
||||
- FullyUnrollLoop(dst);
|
||||
+ } else if (analyzer_.ShouldFullyUnrollLoop(dst) && FullyUnrollLoop(dst)) {
|
||||
return {};
|
||||
- } else if (analyzer_.ShouldPartiallyUnrollLoop(dst)) {
|
||||
- PartiallyUnrollLoop(dst);
|
||||
+ } else if (analyzer_.ShouldPartiallyUnrollLoop(dst) &&
|
||||
+ PartiallyUnrollLoop(dst)) {
|
||||
return {};
|
||||
}
|
||||
} else if ((unrolling_ == UnrollingStatus::kUnrolling) &&
|
||||
@@ -467,9 +470,9 @@ class LoopUnrollingReducer : public Next {
|
||||
// and would like to not emit the loop body that follows.
|
||||
kRemoveLoop,
|
||||
};
|
||||
- void RemoveLoop(const Block* header);
|
||||
- void FullyUnrollLoop(const Block* header);
|
||||
- void PartiallyUnrollLoop(const Block* header);
|
||||
+ bool RemoveLoop(const Block* header);
|
||||
+ bool FullyUnrollLoop(const Block* header);
|
||||
+ bool PartiallyUnrollLoop(const Block* header);
|
||||
void FixLoopPhis(const Block* input_graph_loop, Block* output_graph_loop,
|
||||
const Block* backedge_block);
|
||||
bool IsRunningBuiltinPipeline() {
|
||||
@@ -508,10 +511,16 @@ class LoopUnrollingReducer : public Next {
|
||||
};
|
||||
|
||||
template <class Next>
|
||||
-void LoopUnrollingReducer<Next>::PartiallyUnrollLoop(const Block* header) {
|
||||
+bool LoopUnrollingReducer<Next>::PartiallyUnrollLoop(const Block* header) {
|
||||
TRACE("LoopUnrolling: partially unrolling loop at " << header->index().id());
|
||||
DCHECK_EQ(unrolling_, UnrollingStatus::kNotUnrolling);
|
||||
DCHECK(!skip_next_stack_check_);
|
||||
+
|
||||
+ if (!__ CanCreateNVariables(analyzer_.GetLoopOpCount(header))) {
|
||||
+ TRACE("> Too many variables, skipping unrolling");
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
unrolling_ = UnrollingStatus::kUnrolling;
|
||||
|
||||
auto loop_body = analyzer_.GetLoopBody(header);
|
||||
@@ -533,7 +542,7 @@ void LoopUnrollingReducer<Next>::PartiallyUnrollLoop(const Block* header) {
|
||||
__ CloneSubGraph(loop_body, /* keep_loop_kinds */ true);
|
||||
if (StopUnrollingIfUnreachable(output_graph_header)) {
|
||||
TRACE("> Next iteration is unreachable, stopping unrolling");
|
||||
- return;
|
||||
+ return true;
|
||||
}
|
||||
|
||||
// Emitting the subsequent folded iterations. We set `unrolling_` to
|
||||
@@ -549,7 +558,7 @@ void LoopUnrollingReducer<Next>::PartiallyUnrollLoop(const Block* header) {
|
||||
__ CloneSubGraph(loop_body, /* keep_loop_kinds */ false);
|
||||
if (StopUnrollingIfUnreachable(output_graph_header)) {
|
||||
TRACE("> Next iteration is unreachable, stopping unrolling");
|
||||
- return;
|
||||
+ return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,6 +576,7 @@ void LoopUnrollingReducer<Next>::PartiallyUnrollLoop(const Block* header) {
|
||||
|
||||
unrolling_ = UnrollingStatus::kNotUnrolling;
|
||||
TRACE("> Finished partially unrolling loop " << header->index().id());
|
||||
+ return true;
|
||||
}
|
||||
|
||||
template <class Next>
|
||||
@@ -622,10 +632,20 @@ void LoopUnrollingReducer<Next>::FixLoopPhis(const Block* input_graph_loop,
|
||||
}
|
||||
|
||||
template <class Next>
|
||||
-void LoopUnrollingReducer<Next>::RemoveLoop(const Block* header) {
|
||||
+bool LoopUnrollingReducer<Next>::RemoveLoop(const Block* header) {
|
||||
TRACE("LoopUnrolling: removing loop at " << header->index().id());
|
||||
DCHECK_EQ(unrolling_, UnrollingStatus::kNotUnrolling);
|
||||
DCHECK(!skip_next_stack_check_);
|
||||
+
|
||||
+ if (!__ CanCreateNVariables(analyzer_.GetLoopOpCount(header))) {
|
||||
+ TRACE("> Too many variables, skipping removal");
|
||||
+ // TODO(dmercadier): in theory, RemoveLoop shouldn't need Variables, since
|
||||
+ // it cannot be called while unrolling an outer loop, since we only unroll
|
||||
+ // innermost loops. We should teach CloneAndInlineBlock that it doesn't
|
||||
+ // always need to introduce Variables, and then remove this bailout.
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
// When removing a loop, we still need to emit the header (since it has to
|
||||
// always be executed before the 1st iteration anyways), but by setting
|
||||
// {unrolling_} to `kRemoveLoop`, the final Branch of the loop will become a
|
||||
@@ -633,15 +653,21 @@ void LoopUnrollingReducer<Next>::RemoveLoop(const Block* header) {
|
||||
unrolling_ = UnrollingStatus::kRemoveLoop;
|
||||
__ CloneAndInlineBlock(header);
|
||||
unrolling_ = UnrollingStatus::kNotUnrolling;
|
||||
+ return true;
|
||||
}
|
||||
|
||||
template <class Next>
|
||||
-void LoopUnrollingReducer<Next>::FullyUnrollLoop(const Block* header) {
|
||||
+bool LoopUnrollingReducer<Next>::FullyUnrollLoop(const Block* header) {
|
||||
TRACE("LoopUnrolling: fully unrolling loop at " << header->index().id());
|
||||
DCHECK_EQ(unrolling_, UnrollingStatus::kNotUnrolling);
|
||||
DCHECK(!skip_next_stack_check_);
|
||||
ScopedModification<bool> skip_stack_checks(&skip_next_stack_check_, true);
|
||||
|
||||
+ if (!__ CanCreateNVariables(analyzer_.GetLoopOpCount(header))) {
|
||||
+ TRACE("> Too many variables, skipping unrolling");
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
size_t iter_count = analyzer_.GetIterationCount(header).exact_count();
|
||||
TRACE("> iter_count: " << iter_count);
|
||||
|
||||
@@ -654,7 +680,7 @@ void LoopUnrollingReducer<Next>::FullyUnrollLoop(const Block* header) {
|
||||
__ CloneSubGraph(loop_body, /* keep_loop_kinds */ false);
|
||||
if (StopUnrollingIfUnreachable()) {
|
||||
TRACE("> Next iteration is unreachable, stopping unrolling");
|
||||
- return;
|
||||
+ return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,6 +693,7 @@ void LoopUnrollingReducer<Next>::FullyUnrollLoop(const Block* header) {
|
||||
|
||||
unrolling_ = UnrollingStatus::kNotUnrolling;
|
||||
TRACE("> Finished fully unrolling loop " << header->index().id());
|
||||
+ return true;
|
||||
}
|
||||
|
||||
#undef TRACE
|
||||
diff --git a/src/compiler/turboshaft/turbolev-graph-builder.cc b/src/compiler/turboshaft/turbolev-graph-builder.cc
|
||||
index 9d1dafe2b0b733c88283b21eddb36c9827912eca..bcb461488899bcbf3a130845f39f0005bd34131e 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 {
|
||||
}
|
||||
void Bind(Block* block) {
|
||||
Next::Bind(block);
|
||||
- // The 1st block we bind doesn't exist in Maglev and is meant to hold
|
||||
- // Constants (which in Maglev are not in any block), and thus
|
||||
- // {maglev_input_block_} should still be nullptr. In all other cases,
|
||||
- // {maglev_input_block_} should not be nullptr.
|
||||
- DCHECK_EQ(maglev_input_block_ == nullptr,
|
||||
- block == &__ output_graph().StartBlock());
|
||||
+ DCHECK_NOT_NULL(maglev_input_block_);
|
||||
turboshaft_block_origins_[block->index()] = maglev_input_block_;
|
||||
}
|
||||
|
||||
@@ -519,9 +514,11 @@ class GraphBuildingNodeProcessor {
|
||||
block_mapping_[block] =
|
||||
block->is_loop() ? __ NewLoopHeader() : __ NewBlock();
|
||||
}
|
||||
- // Constants are not in a block in Maglev but are in Turboshaft. We bind a
|
||||
- // block now, so that Constants can then be emitted.
|
||||
- __ Bind(__ NewBlock());
|
||||
+ // Constants are not in a block in Maglev but are in Turboshaft. We bind the
|
||||
+ // 1st block now, so that Constants can then be emitted.
|
||||
+ const maglev::BasicBlock* first_maglev_block = graph->blocks().front();
|
||||
+ __ SetMaglevInputBlock(first_maglev_block);
|
||||
+ __ Bind(block_mapping_[first_maglev_block]);
|
||||
|
||||
// Initializing undefined constant so that we don't need to recreate it too
|
||||
// often.
|
||||
@@ -607,9 +604,20 @@ class GraphBuildingNodeProcessor {
|
||||
Block* turboshaft_block = Map(maglev_block);
|
||||
|
||||
if (__ current_block() != nullptr) {
|
||||
- // The first block for Constants doesn't end with a Jump, so we add one
|
||||
- // now.
|
||||
- __ Goto(turboshaft_block);
|
||||
+ // We must be in the first block of the graph, inserted by Turboshaft in
|
||||
+ // PreProcessGraph so that constants can be bound in a block. No need to
|
||||
+ // do anything else: we don't emit a Goto so that the actual 1st block of
|
||||
+ // the Maglev graph gets inlined into this first block of the Turboshaft
|
||||
+ // graph, which, in addition to saving a Goto, saves the need to clone the
|
||||
+ // destination into the current block later, and also ensures that
|
||||
+ // Parameters are always in the 1st block.
|
||||
+ DCHECK_EQ(__ output_graph().block_count(), 1);
|
||||
+ DCHECK_EQ(maglev_block->id(), 0);
|
||||
+ DCHECK_EQ(__ current_block(), block_mapping_[maglev_block]);
|
||||
+ // maglev_input_block should have been set by calling SetMaglevInputBlock
|
||||
+ // in PreProcessGraph.
|
||||
+ DCHECK_EQ(__ maglev_input_block(), maglev_block);
|
||||
+ return maglev::BlockProcessResult::kContinue;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
diff --git a/src/compiler/turboshaft/variable-reducer.h b/src/compiler/turboshaft/variable-reducer.h
|
||||
index b11338bdf6e928cd09a0bdbad42fd835c8210c36..03cc2fa77f0d4a194893a8be5747d6de887e5ee9 100644
|
||||
--- a/src/compiler/turboshaft/variable-reducer.h
|
||||
+++ b/src/compiler/turboshaft/variable-reducer.h
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include "src/base/logging.h"
|
||||
+#include "src/base/macros.h"
|
||||
#include "src/codegen/machine-type.h"
|
||||
#include "src/compiler/turboshaft/assembler.h"
|
||||
#include "src/compiler/turboshaft/graph.h"
|
||||
@@ -91,6 +92,15 @@ class VariableReducer : public RequiredOptimizationReducer<AfterNext> {
|
||||
public:
|
||||
TURBOSHAFT_REDUCER_BOILERPLATE(VariableReducer)
|
||||
|
||||
+ ~VariableReducer() {
|
||||
+ if (too_many_variables_bailouts_count_ != 0 &&
|
||||
+ V8_UNLIKELY(v8_flags.trace_turbo_bailouts)) {
|
||||
+ std::cout << "Bailing out from block cloning "
|
||||
+ << too_many_variables_bailouts_count_ << " time"
|
||||
+ << (too_many_variables_bailouts_count_ > 1 ? "s" : "") << "\n";
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
void Bind(Block* new_block) {
|
||||
Next::Bind(new_block);
|
||||
|
||||
@@ -190,6 +200,26 @@ class VariableReducer : public RequiredOptimizationReducer<AfterNext> {
|
||||
return table_.GetPredecessorValue(var, predecessor_index);
|
||||
}
|
||||
|
||||
+ bool CanCreateNVariables(size_t n) {
|
||||
+ // Merges with many predecessors combined with many variables can quickly
|
||||
+ // blow up memory since the SnapshotTable needs to create a state whose
|
||||
+ // size can be up to number_of_predecessor*variable_count (note: in
|
||||
+ // practice, it's often not quite variable_count but less since only
|
||||
+ // variables that are live in at least one predecessor are counted). To
|
||||
+ // avoid OOM or otherwise huge memory consumption, we thus stop creating
|
||||
+ // variables (and bail out on optimizations that need variables) when this
|
||||
+ // number becomes too large. I somewhat arbitrarily selected 100K here,
|
||||
+ // which sounds high, but in terms of memory, it's just 100K*8=800KB, which
|
||||
+ // is less than 1MB, which isn't going to amount for much in a function
|
||||
+ // that is probably very large if it managed to reach this limit.
|
||||
+ constexpr uint32_t kMaxAllowedMergeStateSize = 100'000;
|
||||
+ bool can_create =
|
||||
+ __ input_graph().max_merge_pred_count() * (variable_count_ + n) <
|
||||
+ kMaxAllowedMergeStateSize;
|
||||
+ if (!can_create) too_many_variables_bailouts_count_++;
|
||||
+ return can_create;
|
||||
+ }
|
||||
+
|
||||
void SetVariable(Variable var, OpIndex new_index) {
|
||||
DCHECK(!is_temporary_);
|
||||
if (V8_UNLIKELY(__ generating_unreachable_operations())) return;
|
||||
@@ -206,10 +236,12 @@ class VariableReducer : public RequiredOptimizationReducer<AfterNext> {
|
||||
|
||||
Variable NewLoopInvariantVariable(MaybeRegisterRepresentation rep) {
|
||||
DCHECK(!is_temporary_);
|
||||
+ variable_count_++;
|
||||
return table_.NewKey(VariableData{rep, true}, OpIndex::Invalid());
|
||||
}
|
||||
Variable NewVariable(MaybeRegisterRepresentation rep) {
|
||||
DCHECK(!is_temporary_);
|
||||
+ variable_count_++;
|
||||
return table_.NewKey(VariableData{rep, false}, OpIndex::Invalid());
|
||||
}
|
||||
|
||||
@@ -314,6 +346,10 @@ class VariableReducer : public RequiredOptimizationReducer<AfterNext> {
|
||||
__ input_graph().block_count(), std::nullopt, __ phase_zone()};
|
||||
bool is_temporary_ = false;
|
||||
|
||||
+ // Tracks the number of variables that have been created.
|
||||
+ uint32_t variable_count_ = 0;
|
||||
+ uint32_t too_many_variables_bailouts_count_ = 0;
|
||||
+
|
||||
// {predecessors_} is used during merging, but we use an instance variable for
|
||||
// it, in order to save memory and not reallocate it for each merge.
|
||||
ZoneVector<Snapshot> predecessors_{__ phase_zone()};
|
||||
diff --git a/test/unittests/compiler/turboshaft/control-flow-unittest.cc b/test/unittests/compiler/turboshaft/control-flow-unittest.cc
|
||||
index 49e1c8c2561bd010d12e5229c4d6594b9846b40b..b39b073a2ea899550fe0df6a81dcebc2d75efa49 100644
|
||||
--- a/test/unittests/compiler/turboshaft/control-flow-unittest.cc
|
||||
+++ b/test/unittests/compiler/turboshaft/control-flow-unittest.cc
|
||||
@@ -55,7 +55,7 @@ TEST_F(ControlFlowTest, DefaultBlockInlining) {
|
||||
// BranchElimination should remove such branches by cloning the block with the
|
||||
// branch. In the end, the graph should contain (almost) no branches anymore.
|
||||
TEST_F(ControlFlowTest, BranchElimination) {
|
||||
- static constexpr int kSize = 10000;
|
||||
+ static constexpr int kSize = 200;
|
||||
|
||||
auto test = CreateFromGraph(1, [](auto& Asm) {
|
||||
V<Word32> cond =
|
||||
@@ -52,7 +52,8 @@ def get_repo_root(path):
|
||||
|
||||
def am(repo, patch_data, threeway=False, directory=None, exclude=None,
|
||||
committer_name=None, committer_email=None, keep_cr=True):
|
||||
args = []
|
||||
# --keep-non-patch prevents stripping leading bracketed strings on the subject line
|
||||
args = ['--keep-non-patch']
|
||||
if threeway:
|
||||
args += ['--3way']
|
||||
if directory is not None:
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -586,6 +586,7 @@ Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
|
||||
if (auto* service =
|
||||
SpellcheckServiceFactory::GetForContext(browser_context)) {
|
||||
service->SetHunspellObserver(this);
|
||||
service->InitializeDictionaries(base::DoNothing());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -64,10 +64,6 @@ void InitializeFeatureList() {
|
||||
std::string(",") + network::features::kLocalNetworkAccessChecks.name;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
disable_features +=
|
||||
// Delayed spellcheck initialization is causing the
|
||||
// 'custom dictionary word list API' spec to crash.
|
||||
std::string(",") + spellcheck::kWinDelaySpellcheckServiceInit.name;
|
||||
// Refs https://issues.chromium.org/issues/401996981
|
||||
// TODO(deepak1556): Remove this once test added in
|
||||
// https://github.com/electron/electron/pull/12904
|
||||
|
||||
@@ -104,7 +104,8 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
|
||||
transparent_{options.ValueOrDefault(options::kTransparent, false)},
|
||||
enable_larger_than_screen_{
|
||||
options.ValueOrDefault(options::kEnableLargerThanScreen, false)},
|
||||
is_modal_{parent != nullptr && options.ValueOrDefault("modal", false)},
|
||||
is_modal_{parent != nullptr &&
|
||||
options.ValueOrDefault(options::kModal, false)},
|
||||
has_frame_{options.ValueOrDefault(options::kFrame, true) &&
|
||||
title_bar_style_ == TitleBarStyle::kNormal},
|
||||
parent_{parent} {
|
||||
|
||||
@@ -554,7 +554,7 @@ bool NativeWindowViews::IsFocused() const {
|
||||
}
|
||||
|
||||
void NativeWindowViews::Show() {
|
||||
if (is_modal() && NativeWindow::parent() && !widget()->IsVisible())
|
||||
if (is_modal() && NativeWindow::parent())
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -468,6 +468,12 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
if (!represented)
|
||||
return;
|
||||
|
||||
if (![represented
|
||||
isKindOfClass:[WeakPtrToElectronMenuModelAsNSObject class]]) {
|
||||
NSLog(@"representedObject is not a WeakPtrToElectronMenuModelAsNSObject");
|
||||
return;
|
||||
}
|
||||
|
||||
electron::ElectronMenuModel* model =
|
||||
[WeakPtrToElectronMenuModelAsNSObject getFrom:represented];
|
||||
if (!model)
|
||||
|
||||
@@ -100,13 +100,19 @@ void ElectronDesktopWindowTreeHostLinux::OnWindowStateChanged(
|
||||
void ElectronDesktopWindowTreeHostLinux::OnWindowTiledStateChanged(
|
||||
ui::WindowTiledEdges new_tiled_edges) {
|
||||
if (auto* const view = native_window_view_->GetClientFrameViewLinux()) {
|
||||
bool maximized = new_tiled_edges.top && new_tiled_edges.left &&
|
||||
new_tiled_edges.bottom && new_tiled_edges.right;
|
||||
// GNOME on Ubuntu reports all edges as tiled
|
||||
// even if the window is only half-tiled so do not trust individual edge
|
||||
// values.
|
||||
bool maximized = native_window_view_->IsMaximized();
|
||||
bool tiled = new_tiled_edges.top || new_tiled_edges.left ||
|
||||
new_tiled_edges.bottom || new_tiled_edges.right;
|
||||
view->set_tiled(tiled && !maximized);
|
||||
}
|
||||
UpdateFrameHints();
|
||||
ScheduleRelayout();
|
||||
if (GetWidget()->non_client_view()) {
|
||||
GetWidget()->non_client_view()->SchedulePaint();
|
||||
}
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::UpdateWindowState(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -40,7 +40,6 @@ namespace {
|
||||
|
||||
// These values should be the same as Chromium uses.
|
||||
constexpr int kResizeBorder = 10;
|
||||
constexpr int kResizeInsideBoundsSize = 5;
|
||||
|
||||
ui::NavButtonProvider::ButtonState ButtonStateToNavButtonProviderState(
|
||||
views::Button::ButtonState state) {
|
||||
@@ -151,8 +150,19 @@ gfx::Insets ClientFrameViewLinux::RestoredMirroredFrameBorderInsets() const {
|
||||
|
||||
gfx::Insets ClientFrameViewLinux::RestoredFrameBorderInsets() const {
|
||||
gfx::Insets insets = GetFrameProvider()->GetFrameThicknessDip();
|
||||
insets.SetToMax(GetInputInsets());
|
||||
return insets;
|
||||
const gfx::Insets input = GetInputInsets();
|
||||
|
||||
auto expand_if_visible = [](int side_thickness, int min_band) {
|
||||
return side_thickness > 0 ? std::max(side_thickness, min_band) : 0;
|
||||
};
|
||||
|
||||
gfx::Insets merged;
|
||||
merged.set_top(expand_if_visible(insets.top(), input.top()));
|
||||
merged.set_left(expand_if_visible(insets.left(), input.left()));
|
||||
merged.set_bottom(expand_if_visible(insets.bottom(), input.bottom()));
|
||||
merged.set_right(expand_if_visible(insets.right(), input.right()));
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
gfx::Insets ClientFrameViewLinux::GetInputInsets() const {
|
||||
@@ -197,9 +207,7 @@ void ClientFrameViewLinux::OnWindowButtonOrderingChange() {
|
||||
}
|
||||
|
||||
int ClientFrameViewLinux::ResizingBorderHitTest(const gfx::Point& point) {
|
||||
return ResizingBorderHitTestImpl(point,
|
||||
RestoredMirroredFrameBorderInsets() +
|
||||
gfx::Insets(kResizeInsideBoundsSize));
|
||||
return ResizingBorderHitTestImpl(point, RestoredMirroredFrameBorderInsets());
|
||||
}
|
||||
|
||||
gfx::Rect ClientFrameViewLinux::GetBoundsForClientView() const {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -222,6 +222,8 @@ inline constexpr std::string_view kSpellcheck = "spellcheck";
|
||||
inline constexpr std::string_view kEnableDeprecatedPaste =
|
||||
"enableDeprecatedPaste";
|
||||
|
||||
inline constexpr std::string_view kModal = "modal";
|
||||
|
||||
} // namespace options
|
||||
|
||||
// Following are actually command line switches, should be moved to other files.
|
||||
|
||||
@@ -302,8 +302,8 @@ SkPath DrawSmoothRoundRect(float x,
|
||||
bottom_right_smoothness, SkPoint::Make(x + width, y + height), 2);
|
||||
|
||||
// Bottom left corner
|
||||
DrawCorner(path, bottom_left_radius, left_bottom_smoothness,
|
||||
bottom_left_smoothness, SkPoint::Make(x, y + height), 3);
|
||||
DrawCorner(path, bottom_left_radius, bottom_left_smoothness,
|
||||
left_bottom_smoothness, SkPoint::Make(x, y + height), 3);
|
||||
|
||||
path.close();
|
||||
return path;
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BrowserWindow, ipcMain, webContents, session, app, BrowserView, WebContents, BaseWindow, WebContentsView } from 'electron/main';
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { assert, expect } from 'chai';
|
||||
|
||||
import * as cp from 'node:child_process';
|
||||
import { once } from 'node:events';
|
||||
@@ -1013,6 +1013,41 @@ describe('webContents module', () => {
|
||||
await devToolsClosed;
|
||||
expect(() => { webContents.getFocusedWebContents(); }).to.not.throw();
|
||||
});
|
||||
|
||||
it('Inspect activates detached devtools window', async () => {
|
||||
const window = new BrowserWindow({ show: true });
|
||||
await window.loadURL('about:blank');
|
||||
const webContentsBeforeOpenedDevtools = webContents.getAllWebContents();
|
||||
|
||||
const windowWasBlurred = once(window, 'blur');
|
||||
window.webContents.openDevTools({ mode: 'detach' });
|
||||
await windowWasBlurred;
|
||||
|
||||
let devToolsWebContents = null;
|
||||
for (const newWebContents of webContents.getAllWebContents()) {
|
||||
const oldWebContents = webContentsBeforeOpenedDevtools.find(
|
||||
oldWebContents => {
|
||||
return newWebContents.id === oldWebContents.id;
|
||||
});
|
||||
if (oldWebContents !== null) {
|
||||
devToolsWebContents = newWebContents;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(devToolsWebContents !== null);
|
||||
|
||||
const windowFocused = once(window, 'focus');
|
||||
window.focus();
|
||||
await windowFocused;
|
||||
|
||||
expect(devToolsWebContents.isFocused()).to.be.false();
|
||||
const devToolsWebContentsFocused = once(devToolsWebContents, 'focus');
|
||||
window.webContents.inspectElement(100, 100);
|
||||
await devToolsWebContentsFocused;
|
||||
|
||||
expect(devToolsWebContents.isFocused()).to.be.true();
|
||||
expect(window.isFocused()).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setDevToolsWebContents() API', () => {
|
||||
|
||||
5
spec/fixtures/api/pdf-reader.mjs
vendored
5
spec/fixtures/api/pdf-reader.mjs
vendored
@@ -1,7 +1,8 @@
|
||||
import * as pdfjs from 'pdfjs-dist';
|
||||
import { app } from 'electron';
|
||||
|
||||
async function getPDFDoc () {
|
||||
try {
|
||||
const pdfjs = await import('pdfjs-dist');
|
||||
const doc = await pdfjs.getDocument(process.argv[2]).promise;
|
||||
const page = await doc.getPage(1);
|
||||
const { items } = await page.getTextContent();
|
||||
@@ -20,4 +21,4 @@ async function getPDFDoc () {
|
||||
}
|
||||
}
|
||||
|
||||
getPDFDoc();
|
||||
app.whenReady().then(() => getPDFDoc());
|
||||
|
||||
Reference in New Issue
Block a user