Compare commits

..

33 Commits

Author SHA1 Message Date
dependabot[bot]
441729c3a0 build(deps): bump actions/checkout from 6.0.1 to 6.0.2 (#49541)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](8e8c483db8...de0fac2e45)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 11:37:15 +01:00
Shelley Vohr
c521c2b868 test: update/fix Node.js disabled specs (#49467) 2026-01-27 11:03:09 +01:00
Shelley Vohr
b2c5093542 docs: correct type for process.noDeprecation (#49524)
* docs: correct type for process.noDeprecation

* docs: mark `noDeprecation` as optional instead

Co-authored-by: René <contact.9a5d6388@renegade334.me.uk>

---------

Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
Co-authored-by: René <contact.9a5d6388@renegade334.me.uk>
2026-01-27 11:02:51 +01:00
dependabot[bot]
ec5eb64788 build(deps): bump github/codeql-action from 4.31.10 to 4.32.0 (#49540)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.10 to 4.32.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](cdefb33c0f...b20883b0cd)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 11:00:13 +01:00
David Sanders
0cc15a6386 ci: reapply patches if PR base branch updates them (#49516) 2026-01-26 15:13:34 -05:00
David Sanders
4aa89b9c3c ci: add pipeline segment to run clang-tidy (#49072)
* ci: add pipeline segment to run clang-tidy

* chore: set -header-filter='' for clang-tidy

Breaking change in `clang-tidy` 22.0.0:

> clang-tidy now displays warnings from all non-system headers by default.
> Previously, users had to explicitly opt-in to header warnings using
> -header-filter=’.*’. To disable warnings from non-system, set
> -header-filter to an empty string.
2026-01-26 14:37:01 -05:00
Felix Rieseberg
ae43f17b79 build: Run gh workflows only on e/e (#49521) 2026-01-26 14:35:11 -05:00
electron-roller[bot]
b847299f19 chore: bump chromium to 146.0.7650.0 (main) (#49496)
* chore: bump chromium in DEPS to 146.0.7650.0

* 7496671: WaaP: Control hung delay for Initial WebUI

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7496671

* 7494302: [//media] Rename renderable_gpu_memory_buffer_video_frame_pool*

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7494302

* chore: update patches

* 7502996: [DevTools] Add ability to toggle Chromium feature flags from DevTools

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7502996

* 7456200: Vectorize StringImpl::CopyChars and EqualIgnoringASCIICase using Highway.

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7456200

* 7236627: spellcheck: supply full spelling marker info, incld. marker type

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7236627

* chore: rm dependency on wtf::string from blink public headers

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7456200
added a public dependency on //third_party/highway for
//third_party/blink/renderer/platform/wtf:wtf which will not be
inherited by //content/renderer since wtf is internal dependency of
blink leading to the following compilation error

```
In file included from ../../content/public/renderer/window_features_converter.cc:5:
In file included from ../../content/public/renderer/window_features_converter.h:10:
In file included from ../../third_party/blink/public/web/web_window_features.h:38:
In file included from ../../third_party/blink/renderer/platform/wtf/text/wtf_string.h:40:
../../third_party/blink/renderer/platform/wtf/text/string_impl.h:27:10: fatal error: 'hwy/highway.h' file not found
   27 | #include <hwy/highway.h>
      |          ^~~~~~~~~~~~~~~
1 error generated.
```

Use `gn desc out/Testing content/renderer:renderer_sources --blame`
to verify the inherited config and dependency list.

* 7493995: Restore directive part of wasm-eval error message

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7493995

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2026-01-26 11:18:09 -08:00
Charles Kerr
6766343173 refactor: simplify NativeWindow-to-BaseWindow lookup (#49520)
refactor: simplify native window to base window lookup
2026-01-26 12:28:57 +01:00
Sourav Bera
75be2fe8d6 docs: add type reference links in Menu and MenuItem API documentation (#49437)
* docs: add type reference links in Menu and MenuItem API documentation

* docs: revert type links in Menu return types to fix parser
2026-01-26 12:22:28 +01:00
Charles Kerr
30f365d9d8 refactor: use gin::Wrappable for electron::api::PowerMonitor (#49509)
* refactor: use `gin::Wrappable` for `electron::api::PowerMonitor`

* chore: update patches
2026-01-25 22:51:31 -06:00
Charles Kerr
e8250f9955 refactor: use gin::Wrappable for electron::api::Screen (#49506) 2026-01-25 13:46:25 -06:00
Kyle Cutler
d5de8883a2 feat: add focusOnNavigation flag to WebPreferences (#49425)
* feat: add focusOnNavigation webPreference

* WebContentsView tests

* fix

* fix
2026-01-23 14:29:34 -05:00
David Franco
8a11d5afb1 fix: avoid startup crash when V8 sandbox is disabled (#49210)
* fix: avoid startup crash when V8 sandbox is disabled

* chore: update patch

---------

Co-authored-by: David Franco <davidfrsan@gmail.com>
2026-01-23 11:49:15 -05:00
Shelley Vohr
89963618d9 fix: second argument to shell.writeShortcutLink is optional (#49476)
fix: second argument to shell.writeShortcutLink is optional
2026-01-23 11:24:47 -05:00
Charles Kerr
8c5c6a6088 refactor: use gin::Wrappable for electron::api::DataPipeHolder (#49495)
* refactor: make `DataPipeHolder` inherit from `gin::Wrappable`

* test: add a test to ensure GC clears the data pipe holder

* chore: e patches all

* chore: e patches all (trivial only)

* refactor: make AllDataPipeHolders a base::flat_map of WeakPersistent
2026-01-23 20:29:01 +09:00
Szymon Kurek
24526ccd39 docs: correct yarn part of tutorial-5-packaging (#49401)
docs: fix yarn part of `tutorial-5-packaging` - adding electron forge
2026-01-23 11:14:19 +01:00
Keeley Hammond
1134d95c6d chore: update main patches (#49498)
chore: update patches
2026-01-22 20:30:32 -08:00
Charles Kerr
d76608b945 refactor: use gin::Wrappable in electron::api::PowerSaveBlocker (#49381)
* refactor: PowerSaveBlocker subclasses gin::Wrappable

* chore: update chore_add_electron_objects_to_wrappablepointertag.patch

* chore: e patches all
2026-01-22 17:31:38 -06:00
electron-roller[bot]
d689f76ff9 chore: bump chromium to 146.0.7645.0 (main) (#49453)
* chore: bump chromium in DEPS to 146.0.7643.0

* chore: bump chromium in DEPS to 146.0.7645.0

* chore: update patches

* 7402162: Refactor app shims to call ContentMain

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7402162

* 7454282: Add master key management for HTTP Cache Encryption

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7454282

* 7490440: Reland "Delete unused base::Contains()"

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7490440

* chore: update patches

* 7414864: Pass CSSParserLocalContext down to CSSMathExpressionNodeParser

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7414864

* 7460969: Move child_process_id.h to common

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7460969

* 7474608: [api] Remove deprecated v8::PropertyCallbackInfo<T>::This()

Refs https://chromium-review.googlesource.com/c/v8/v8/+/7474608

* 7461067: [Viz] Rename kPreferGpuMemoryBuffer

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7461067

* 7487174: Remove GLHelper

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7487174

* 7457538: Set timeout from multi source page context fetcher

Refs https://chromium-review.googlesource.com/c/chromium/src/+/7457538

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2026-01-22 17:01:09 -05:00
reito
e3142865b2 fix: offscreen rendering with correct screen info. (#48730)
fix: osr use correct screen info.
2026-01-22 14:58:54 -05:00
David Sanders
1f8e4079cd test: fix version color output when running prebuilt Electron (#49491) 2026-01-22 16:17:56 +01:00
Shelley Vohr
bad88c6ed4 fix: potential devtools crash on empty (#49468) 2026-01-21 17:05:26 -08:00
Noah Gregory
534cb33465 fix: return early from platform_util::Beep() on Linux if there is no default GDK display (#49442)
fix: return early from beep on linux if there is no default gdk display
2026-01-21 15:55:24 -05:00
Soobin Rho
51e7753ae9 docs: clearer wording performance common pitfalls (#45308)
docs: clearer wording (performance common pitfalls)

Signed-off-by: Soobin Rho <soobinrho@gmail.com>
2026-01-21 14:46:02 -05:00
sam marshall
d141934885 docs: document that shell.trashItem requires backslashes (#49038)
docs: Update shell.md: Document that shell.trashItem requires backslashes

In Windows many functions relating to files (e.g. shell.openItem, the Node fs functions, as well as native Win32 APIs) will accept either type of slash / or \ as a folder separator.

shell.trashItem does not work with / as folder separator in Windows. This documentation change explains that.

See also:
https://github.com/electron/electron/issues/28831
2026-01-21 13:21:01 -05:00
Erick Zhao
22e8cf9416 docs: add a few API history fragments (#49340)
* docs: add a few API history fragments

* manual fixes
2026-01-21 12:57:02 -05:00
David Sanders
5856b2e01c ci: trigger website docs update on release published (#49429) 2026-01-21 10:44:18 -05:00
Shelley Vohr
49d91dd02b revert: use deprecated setAllowedFileTypes in macOS dialogs (#49444)
* revert: use deprectated setAllowedFileTypes in macOS dialogs

Closes https://github.com/electron/electron/issues/48191

* chore: remove stray import
2026-01-21 13:08:04 +01:00
David Franco
2f20d287d1 feat: import shared texture supports p010le (#49272)
feat: import shared texture supports p010le.
2026-01-20 11:50:30 +01:00
Shelley Vohr
73377af79e build: try removing zero-fill sandbox patch component (#49452) 2026-01-20 09:40:33 +01:00
dependabot[bot]
47766801e4 build(deps): bump actions/setup-node from 6.1.0 to 6.2.0 (#49456)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](395ad32622...6044e13b5d)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-20 09:40:02 +01:00
dependabot[bot]
bf0510dc22 build(deps): bump electron/github-app-auth-action from 1.1.1 to 2.0.0 (#49457)
Bumps [electron/github-app-auth-action](https://github.com/electron/github-app-auth-action) from 1.1.1 to 2.0.0.
- [Release notes](https://github.com/electron/github-app-auth-action/releases)
- [Commits](384fd19694...e14e47722e)

---
updated-dependencies:
- dependency-name: electron/github-app-auth-action
  dependency-version: 2.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-20 09:39:47 +01:00
116 changed files with 2010 additions and 2792 deletions

View File

@@ -26,6 +26,9 @@ inputs:
is-asan:
description: 'The ASan Linux build'
required: false
upload-out-gen-artifacts:
description: 'Whether to upload the out/${dir}/gen artifacts'
required: false
runs:
using: "composite"
steps:
@@ -274,3 +277,9 @@ runs:
with:
name: src_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src_artifacts_${{ inputs.artifact-platform }}_${{ inputs.target-arch }}
- name: Upload Out Gen Artifacts ${{ inputs.step-suffix }}
if: ${{ inputs.upload-out-gen-artifacts == 'true' }}
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
with:
name: out_gen_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src/out/Default/gen

View File

@@ -1,72 +0,0 @@
name: Apply Patches
on:
pull_request:
permissions: {}
concurrency:
group: apply-patches-${{ github.ref }}
cancel-in-progress: true
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-slim
permissions:
contents: read
pull-requests: read
outputs:
has-patches: ${{ steps.filter.outputs.patches }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha }}
# Use dorny/paths-filter instead of the path filter under the on: pull_request: block
# so that the output can be used to conditionally run the apply-patches job, which lets
# the job be marked as a required status check (conditional skip counts as a success).
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
patches:
- DEPS
- 'patches/**'
apply-patches:
needs: setup
if: ${{ needs.setup.outputs.has-patches == 'true' }}
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
- /var/run/sas:/var/run/sas
env:
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
steps:
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha }}
- name: Rebase onto Base Branch
working-directory: src/electron
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
git config user.email "electron@github.com"
git config user.name "Electron Bot"
git fetch origin ${BASE_REF}
git rebase origin/${BASE_REF}
- name: Checkout & Sync & Save
uses: ./src/electron/.github/actions/checkout
with:
target-platform: linux

View File

@@ -13,11 +13,11 @@ jobs:
contents: read
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
- name: Setup Node.js/npm
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238
with:
node-version: 24.12.x
- name: Setting Up Dig Site

View File

@@ -11,16 +11,17 @@ permissions: {}
jobs:
audit_branch_ci:
name: Audit CI on Branches
if: github.repository == 'electron/electron'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Setup Node.js
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 22.17.x
- name: Sparse checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: |
.

View File

@@ -14,7 +14,7 @@ permissions: {}
jobs:
release-branch-created:
name: Release Branch Created
if: ${{ github.event_name == 'workflow_dispatch' || (github.event.ref_type == 'branch' && endsWith(github.event.ref, '-x-y') && !startsWith(github.event.ref, 'roller')) }}
if: ${{ github.repository == 'electron/electron' && (github.event_name == 'workflow_dispatch' || (github.event.ref_type == 'branch' && endsWith(github.event.ref, '-x-y') && !startsWith(github.event.ref, 'roller'))) }}
permissions:
contents: read
pull-requests: write
@@ -68,7 +68,7 @@ jobs:
done
- name: Generate GitHub App token
if: ${{ steps.check-major-version.outputs.MAJOR }}
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.RELEASE_BOARD_GH_APP_CREDS }}

View File

@@ -10,6 +10,7 @@ permissions: {}
jobs:
build-git-cache-linux:
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
@@ -23,7 +24,7 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -33,6 +34,7 @@ jobs:
target-platform: linux
build-git-cache-windows:
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
@@ -47,7 +49,7 @@ jobs:
TARGET_OS: 'win'
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -57,6 +59,7 @@ jobs:
target-platform: win
build-git-cache-macos:
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
@@ -72,7 +75,7 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -47,6 +47,7 @@ permissions: {}
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-latest
permissions:
contents: read
@@ -56,8 +57,9 @@ jobs:
src: ${{ steps.filter.outputs.src }}
build-image-sha: ${{ steps.set-output.outputs.build-image-sha }}
docs-only: ${{ steps.set-output.outputs.docs-only }}
has-patches: ${{ steps.filter.outputs.patches }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
@@ -72,6 +74,9 @@ jobs:
- CODE_OF_CONDUCT.md
src:
- '!docs/**'
patches:
- DEPS
- 'patches/**'
- name: Set Outputs for Build Image SHA & Docs Only
id: set-output
run: |
@@ -104,6 +109,41 @@ jobs:
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
# Apply Patches Job
apply-patches:
needs: setup
if: ${{ needs.setup.outputs.has-patches == 'true' }}
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
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
- /var/run/sas:/var/run/sas
env:
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
steps:
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Rebase onto Base Branch
working-directory: src/electron
run: |
git config user.email "electron@github.com"
git config user.name "Electron Bot"
git fetch origin ${{ github.event.pull_request.base.ref }}
git rebase origin/${{ github.event.pull_request.base.ref }}
- name: Checkout & Sync & Save
uses: ./src/electron/.github/actions/checkout
with:
target-platform: linux
# Checkout Jobs
checkout-macos:
needs: setup
@@ -124,7 +164,7 @@ jobs:
build-image-sha: ${{ needs.setup.outputs.build-image-sha }}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -156,7 +196,7 @@ jobs:
build-image-sha: ${{ needs.setup.outputs.build-image-sha}}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -188,7 +228,7 @@ jobs:
build-image-sha: ${{ needs.setup.outputs.build-image-sha}}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -283,13 +323,15 @@ jobs:
contents: read
issues: read
pull-requests: read
uses: ./.github/workflows/pipeline-electron-build-and-test-and-nan.yml
uses: ./.github/workflows/pipeline-electron-build-and-tidy-and-test-and-nan.yml
needs: checkout-linux
if: ${{ needs.setup.outputs.src == 'true' }}
with:
build-runs-on: electron-arc-centralus-linux-amd64-32core
clang-tidy-runs-on: electron-arc-centralus-linux-amd64-8core
test-runs-on: electron-arc-centralus-linux-amd64-4core
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
clang-tidy-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
test-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}'
target-platform: linux
target-arch: x64
@@ -426,7 +468,7 @@ jobs:
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')
if: always() && github.repository == 'electron/electron' && !contains(needs.*.result, 'failure')
steps:
- name: GitHub Actions Jobs Done
run: |

View File

@@ -12,6 +12,7 @@ permissions: {}
jobs:
clean-src-cache:
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read

View File

@@ -21,7 +21,7 @@ jobs:
AUTHOR_ASSOCIATION=$(gh api /repos/electron/electron/issues/comments/${{ github.event.comment.id }} --jq '.author_association')
echo "author_association=$AUTHOR_ASSOCIATION" >> "$GITHUB_OUTPUT"
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
if: ${{ !contains(fromJSON('["MEMBER", "OWNER", "COLLABORATOR"]'), steps.get-author-association.outputs.author_association) }}
id: generate-token
with:

View File

@@ -15,7 +15,7 @@ jobs:
contents: read
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
@@ -36,7 +36,7 @@ jobs:
contents: read
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
@@ -69,7 +69,7 @@ jobs:
fi
- name: Generate GitHub App token
if: ${{ steps.check-for-comment.outputs.SHOULD_COMMENT }}
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}

View File

@@ -14,7 +14,7 @@ jobs:
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
@@ -32,13 +32,13 @@ jobs:
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Sparse checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: |
.

View File

@@ -14,7 +14,7 @@ jobs:
if: ${{ !github.event.changes.new_repository.private }}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}

View File

@@ -24,7 +24,7 @@ jobs:
fi
- name: Generate GitHub App token
if: ${{ steps.check-for-blocked-labels.outputs.NOT_BLOCKED }}
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}

View File

@@ -21,6 +21,7 @@ permissions: {}
jobs:
checkout-linux:
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
@@ -35,7 +36,7 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -13,6 +13,7 @@ permissions: {}
jobs:
macos-disk-cleanup:
if: github.repository == 'electron/electron'
strategy:
fail-fast: false
matrix:
@@ -25,7 +26,7 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: |
.github/actions/free-space-macos

View File

@@ -22,6 +22,7 @@ permissions: {}
jobs:
checkout-macos:
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
@@ -36,7 +37,7 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -0,0 +1,124 @@
name: Electron Build & Clang Tidy & Test (+ Node + NaN) Pipeline
on:
workflow_call:
inputs:
target-platform:
type: string
description: 'Platform to run on, can be macos, win or linux.'
required: true
target-arch:
type: string
description: 'Arch to build for, can be x64, arm64 or arm'
required: true
build-runs-on:
type: string
description: 'What host to run the build'
required: true
clang-tidy-runs-on:
type: string
description: 'What host to run clang-tidy on'
required: true
test-runs-on:
type: string
description: 'What host to run the tests on'
required: true
build-container:
type: string
description: 'JSON container information for aks runs-on'
required: false
default: '{"image":null}'
clang-tidy-container:
type: string
description: 'JSON container information to run clang-tidy on'
required: false
default: '{"image":null}'
test-container:
type: string
description: 'JSON container information for testing'
required: false
default: '{"image":null}'
is-release:
description: 'Whether this build job is a release job'
required: true
type: boolean
default: false
gn-build-type:
description: 'The gn build type - testing or release'
required: true
type: string
default: testing
generate-symbols:
description: 'Whether or not to generate symbols'
required: true
type: boolean
default: false
upload-to-storage:
description: 'Whether or not to upload build artifacts to external storage'
required: true
type: string
default: '0'
is-asan:
description: 'Building the Address Sanitizer (ASan) Linux build'
required: false
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 }}
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 }}
target-platform: ${{ inputs.target-platform }}
target-arch: ${{ inputs.target-arch }}
is-release: ${{ inputs.is-release }}
gn-build-type: ${{ inputs.gn-build-type }}
generate-symbols: ${{ inputs.generate-symbols }}
upload-to-storage: ${{ inputs.upload-to-storage }}
upload-out-gen-artifacts: true
secrets: inherit
clang-tidy:
uses: ./.github/workflows/pipeline-segment-electron-clang-tidy.yml
permissions:
contents: read
needs: build
with:
clang-tidy-runs-on: ${{ inputs.clang-tidy-runs-on }}
clang-tidy-container: ${{ inputs.clang-tidy-container }}
target-platform: ${{ inputs.target-platform }}
target-arch: ${{ inputs.target-arch }}
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 }}
target-platform: ${{ inputs.target-platform }}
test-runs-on: ${{ inputs.test-runs-on }}
test-container: ${{ inputs.test-container }}
secrets: inherit
nn-test:
uses: ./.github/workflows/pipeline-segment-node-nan-test.yml
permissions:
contents: read
needs: build
with:
target-arch: ${{ inputs.target-arch }}
target-platform: ${{ inputs.target-platform }}
test-runs-on: ${{ inputs.test-runs-on }}
test-container: ${{ inputs.test-container }}
gn-build-type: ${{ inputs.gn-build-type }}
secrets: inherit

View File

@@ -0,0 +1,121 @@
name: Electron Build & Clang Tidy & Test Pipeline
on:
workflow_call:
inputs:
target-platform:
type: string
description: 'Platform to run on, can be macos, win or linux'
required: true
target-arch:
type: string
description: 'Arch to build for, can be x64, arm64 or arm'
required: true
build-runs-on:
type: string
description: 'What host to run the build'
required: true
clang-tidy-runs-on:
type: string
description: 'What host to run clang-tidy on'
required: true
test-runs-on:
type: string
description: 'What host to run the tests on'
required: true
build-container:
type: string
description: 'JSON container information for aks runs-on'
required: false
default: '{"image":null}'
clang-tidy-container:
type: string
description: 'JSON container information to run clang-tidy on'
required: false
default: '{"image":null}'
test-container:
type: string
description: 'JSON container information for testing'
required: false
default: '{"image":null}'
is-release:
description: 'Whether this build job is a release job'
required: true
type: boolean
default: false
gn-build-type:
description: 'The gn build type - testing or release'
required: true
type: string
default: testing
generate-symbols:
description: 'Whether or not to generate symbols'
required: true
type: boolean
default: false
upload-to-storage:
description: 'Whether or not to upload build artifacts to external storage'
required: true
type: string
default: '0'
is-asan:
description: 'Building the Address Sanitizer (ASan) Linux build'
required: false
type: boolean
default: false
enable-ssh:
description: 'Enable SSH debugging'
required: false
type: boolean
default: false
concurrency:
group: electron-build-and-tidy-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: {}
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 }}
target-platform: ${{ inputs.target-platform }}
target-arch: ${{ inputs.target-arch }}
is-release: ${{ inputs.is-release }}
gn-build-type: ${{ inputs.gn-build-type }}
generate-symbols: ${{ inputs.generate-symbols }}
upload-to-storage: ${{ inputs.upload-to-storage }}
is-asan: ${{ inputs.is-asan }}
enable-ssh: ${{ inputs.enable-ssh }}
upload-out-gen-artifacts: true
secrets: inherit
clang-tidy:
uses: ./.github/workflows/pipeline-segment-electron-clang-tidy.yml
permissions:
contents: read
needs: build
with:
clang-tidy-runs-on: ${{ inputs.clang-tidy-runs-on }}
clang-tidy-container: ${{ inputs.clang-tidy-container }}
target-platform: ${{ inputs.target-platform }}
target-arch: ${{ inputs.target-arch }}
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 }}
target-platform: ${{ inputs.target-platform }}
test-runs-on: ${{ inputs.test-runs-on }}
test-container: ${{ inputs.test-container }}
is-asan: ${{ inputs.is-asan }}
enable-ssh: ${{ inputs.enable-ssh }}
secrets: inherit

View File

@@ -27,7 +27,7 @@ jobs:
container: ${{ fromJSON(inputs.container) }}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -43,7 +43,7 @@ jobs:
with:
target-platform: linux
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -27,7 +27,7 @@ jobs:
container: ${{ fromJSON(inputs.container) }}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -53,6 +53,11 @@ on:
required: false
type: boolean
default: false
upload-out-gen-artifacts:
description: 'Whether to upload the src/gen artifacts'
required: false
type: boolean
default: false
enable-ssh:
description: 'Enable SSH debugging'
required: false
@@ -95,7 +100,7 @@ jobs:
run: |
mkdir src
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -119,7 +124,7 @@ jobs:
run: df -h
- name: Setup Node.js/npm
if: ${{ inputs.target-platform == 'macos' }}
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238
with:
node-version: 22.21.x
cache: yarn
@@ -163,7 +168,7 @@ jobs:
if: ${{ inputs.target-platform == 'linux' }}
uses: ./src/electron/.github/actions/restore-cache-aks
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -201,6 +206,7 @@ jobs:
generate-symbols: '${{ inputs.generate-symbols }}'
upload-to-storage: '${{ inputs.upload-to-storage }}'
is-asan: '${{ inputs.is-asan }}'
upload-out-gen-artifacts: '${{ inputs.upload-out-gen-artifacts }}'
- name: Set GN_EXTRA_ARGS for MAS Build
if: ${{ inputs.target-platform == 'macos' && (inputs.target-variant == 'all' || inputs.target-variant == 'mas') }}
run: |

View File

@@ -0,0 +1,159 @@
name: Pipeline Segment - Electron Clang-Tidy
on:
workflow_call:
inputs:
target-platform:
type: string
description: 'Platform to run on, can be macos, win or linux'
required: true
target-arch:
type: string
description: 'Arch to build for, can be x64, arm64 or arm'
required: true
clang-tidy-runs-on:
type: string
description: 'What host to run clang-tidy on'
required: true
clang-tidy-container:
type: string
description: 'JSON container information for aks runs-on'
required: false
default: '{"image":null}'
permissions: {}
concurrency:
group: electron-clang-tidy-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ github.ref }}
cancel-in-progress: true
env:
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || (inputs.target-platform == 'linux' && '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' || '--custom-var=checkout_win=True') }}
ELECTRON_OUT_DIR: Default
jobs:
clang-tidy:
defaults:
run:
shell: bash
runs-on: ${{ inputs.clang-tidy-runs-on }}
permissions:
contents: read
container: ${{ fromJSON(inputs.clang-tidy-container) }}
env:
BUILD_TYPE: ${{ inputs.target-platform == 'macos' && 'darwin' || inputs.target-platform }}
TARGET_ARCH: ${{ inputs.target-arch }}
TARGET_PLATFORM: ${{ inputs.target-platform }}
ARTIFACT_KEY: ${{ inputs.target-platform == 'macos' && 'darwin' || inputs.target-platform }}_${{ inputs.target-arch }}
steps:
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Cleanup disk space on macOS
if: ${{ inputs.target-platform == 'macos' }}
shell: bash
run: |
sudo mkdir -p $TMPDIR/del-target
tmpify() {
if [ -d "$1" ]; then
sudo mv "$1" $TMPDIR/del-target/$(echo $1|shasum -a 256|head -n1|cut -d " " -f1)
fi
}
tmpify /Library/Developer/CoreSimulator
tmpify ~/Library/Developer/CoreSimulator
sudo rm -rf $TMPDIR/del-target
- name: Check disk space after freeing up space
if: ${{ inputs.target-platform == 'macos' }}
run: df -h
- name: Set Chromium Git Cookie
uses: ./src/electron/.github/actions/set-chromium-cookie
- name: Install Build Tools
uses: ./src/electron/.github/actions/install-build-tools
- name: Enable windows toolchain
if: ${{ inputs.target-platform == 'win' }}
run: |
echo "ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN=1" >> $GITHUB_ENV
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy
if: ${{ inputs.target-platform == 'macos' }}
uses: ./src/electron/.github/actions/restore-cache-azcopy
with:
target-platform: ${{ inputs.target-platform }}
- name: Restore src cache via AKS
if: ${{ inputs.target-platform == 'linux' || inputs.target-platform == 'win' }}
uses: ./src/electron/.github/actions/restore-cache-aks
with:
target-platform: ${{ inputs.target-platform }}
- name: Run Electron Only Hooks
run: |
echo "solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]" > tmpgclient
if [ "${{ inputs.target-platform }}" = "win" ]; then
echo "solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False,'install_sysroot':False,'checkout_win':True},'managed':False}]" > tmpgclient
echo "target_os=['win']" >> tmpgclient
fi
e d gclient runhooks --gclientfile=tmpgclient
# Fix VS Toolchain
if [ "${{ inputs.target-platform }}" = "win" ]; then
rm -rf src/third_party/depot_tools/win_toolchain/vs_files
e d python3 src/build/vs_toolchain.py update --force
fi
- name: Regenerate DEPS Hash
run: |
(cd src/electron && git checkout .) && node src/electron/script/generate-deps-hash.js
echo "DEPSHASH=$(cat src/electron/.depshash)" >> $GITHUB_ENV
- name: Add CHROMIUM_BUILDTOOLS_PATH to env
run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Install Dependencies
uses: ./src/electron/.github/actions/install-dependencies
- name: Default GN gen
run: |
cd src/electron
git pack-refs
- name: Download Out Gen Artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
with:
name: out_gen_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src/out/${{ env.ELECTRON_OUT_DIR }}/gen
- name: Add Clang problem matcher
shell: bash
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
- name: Run Clang-Tidy
run: |
e init -f --root=$(pwd) --out=${ELECTRON_OUT_DIR} testing --target-cpu ${TARGET_ARCH}
export GN_EXTRA_ARGS="target_cpu=\"${TARGET_ARCH}\""
if [ "${{ inputs.target-platform }}" = "win" ]; then
export GN_EXTRA_ARGS="$GN_EXTRA_ARGS use_v8_context_snapshot=true target_os=\"win\""
fi
e build --only-gen
cd src/electron
node script/yarn.js lint:clang-tidy --jobs 8 --out-dir ../out/${ELECTRON_OUT_DIR}
- name: Remove Clang problem matcher
shell: bash
run: echo "::remove-matcher owner=clang::"
- name: Wait for active SSH sessions
if: always() && !cancelled()
shell: bash
run: |
while [ -f /var/.ssh-lock ]
do
sleep 60
done

View File

@@ -48,7 +48,7 @@ jobs:
container: ${{ fromJSON(inputs.check-container) }}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -115,7 +115,7 @@ jobs:
- name: Add CHROMIUM_BUILDTOOLS_PATH to env
run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -72,7 +72,7 @@ jobs:
cp $(which node) /mnt/runner-externals/node24/bin/
- name: Setup Node.js/npm
if: ${{ inputs.target-platform == 'win' }}
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238
with:
node-version: 22.21.x
- name: Add TCC permissions on macOS
@@ -119,7 +119,7 @@ jobs:
if: ${{ inputs.target-platform == 'macos' }}
run: sudo xcode-select --switch /Applications/Xcode_16.4.app
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -50,7 +50,7 @@ jobs:
container: ${{ fromJSON(inputs.test-container) }}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
@@ -106,7 +106,7 @@ jobs:
container: ${{ fromJSON(inputs.test-container) }}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -32,7 +32,7 @@ jobs:
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.RELEASE_BOARD_GH_APP_CREDS }}

View File

@@ -15,9 +15,8 @@ jobs:
rerun-apply-patches:
runs-on: ubuntu-latest
permissions:
actions: write
checks: read
contents: read
actions: write
pull-requests: read
steps:
- name: Find PRs and Rerun Apply Patches
@@ -34,11 +33,11 @@ jobs:
PR_NUMBER=$(echo "$pr" | jq -r '.number')
echo "Processing PR #${PR_NUMBER}"
# Find the Apply Patches workflow check for this PR
CHECK=$(gh pr checks "$PR_NUMBER" --json link,name,state,workflow --jq '[.[] | select(.workflow == "Apply Patches" and .name == "apply-patches")] | first')
# Find the apply-patches job check for this PR
CHECK=$(gh pr checks "$PR_NUMBER" --json link,name,state,workflow --jq '[.[] | select(.workflow == "Build" and .name == "apply-patches")] | first')
if [ -z "$CHECK" ] || [ "$CHECK" = "null" ]; then
echo " No Apply Patches workflow found for PR #${PR_NUMBER}"
echo " No apply-patches job found for PR #${PR_NUMBER}"
continue
fi
@@ -58,14 +57,13 @@ jobs:
continue
fi
# Check if the workflow is currently in progress
RUN_STATUS=$(gh run view "$RUN_ID" --json status --jq '.status')
# Get the job database ID for the apply-patches job
JOB_ID=$(gh run view "$RUN_ID" --json jobs --jq '.jobs[] | select(.name == "apply-patches") | .databaseId')
if [ "$RUN_STATUS" = "in_progress" ] || [ "$RUN_STATUS" = "queued" ] || [ "$RUN_STATUS" = "waiting" ]; then
echo " Workflow run ${RUN_ID} is ${RUN_STATUS}, cancelling..."
gh run cancel "$RUN_ID" --force
gh run watch "$RUN_ID"
if [ -z "$JOB_ID" ]; then
echo " Could not find apply-patches job ID for run: ${RUN_ID}"
continue
fi
gh run rerun "$RUN_ID"
gh run rerun "$RUN_ID" --job "$JOB_ID"
done

View File

@@ -13,6 +13,7 @@ permissions: read-all
jobs:
analysis:
name: Scorecards analysis
if: github.repository == 'electron/electron'
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
@@ -22,7 +23,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
@@ -50,6 +51,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v3.29.5
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v3.29.5
with:
sarif_file: results.sarif

View File

@@ -10,11 +10,12 @@ permissions: {}
jobs:
check-stable-prep-items:
name: Check Stable Prep Items
if: github.repository == 'electron/electron'
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.RELEASE_BOARD_GH_APP_CREDS }}

View File

@@ -9,11 +9,12 @@ permissions: {}
jobs:
stale:
if: github.repository == 'electron/electron'
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
@@ -33,11 +34,11 @@ jobs:
pending-repro:
runs-on: ubuntu-latest
permissions: {}
if: ${{ always() }}
if: ${{ always() && github.repository == 'electron/electron' }}
needs: stale
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@384fd19694fe7b6dcc9a684746c6976ad78228ae # v1.1.1
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}

View File

@@ -22,6 +22,7 @@ permissions: {}
jobs:
checkout-windows:
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
@@ -40,7 +41,7 @@ jobs:
build-image-sha: ${{ inputs.build-image-sha }}
steps:
- name: Checkout Electron
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0

View File

@@ -420,37 +420,6 @@ action("electron_generate_node_defines") {
args = [ rebase_path(target_gen_dir) ] + rebase_path(inputs)
}
# MSIX updater needs to be in a separate source_set because it uses C++/WinRT
# headers that require exceptions to be enabled.
source_set("electron_msix_updater") {
sources = [
"shell/browser/api/electron_api_msix_updater.cc",
"shell/browser/api/electron_api_msix_updater.h",
]
configs += [ "//third_party/electron_node:node_external_config" ]
public_configs = [ ":electron_lib_config" ]
if (is_win) {
cflags_cc = [
"/EHsc", # Enable C++ exceptions for C++/WinRT
"-Wno-c++98-compat-extra-semi", #Suppress C++98 compatibility warnings
]
include_dirs = [ "//third_party/nearby/src/internal/platform/implementation/windows/generated" ]
}
deps = [
"//base",
"//content/public/browser",
"//gin",
"//third_party/electron_node/deps/simdjson",
"//third_party/electron_node/deps/uv",
"//v8",
]
}
source_set("electron_lib") {
configs += [
"//v8:external_startup_data",
@@ -466,7 +435,6 @@ source_set("electron_lib") {
":electron_fuses",
":electron_generate_node_defines",
":electron_js2c",
":electron_msix_updater",
":electron_version_header",
":resources",
"buildflags",

View File

@@ -1285,8 +1285,6 @@ For `infoType` equal to `basic`:
Using `basic` should be preferred if only basic information like `vendorId` or `deviceId` is needed.
Promise is rejected if the GPU is completely disabled, i.e. no hardware and software implementations are available.
### `app.setBadgeCount([count])` _Linux_ _macOS_
* `count` Integer (optional) - If a value is provided, set the badge to the provided value otherwise, on macOS, display a plain white dot (e.g. unknown number of notifications). On Linux, if a value is not provided the badge will not display.

View File

@@ -32,19 +32,9 @@ update process. Apps that need to disable ATS can add the
### Windows
On Windows, the `autoUpdater` module automatically selects the appropriate update mechanism
based on how your app is packaged:
* **MSIX packages**: If your app is running as an MSIX package (created with [electron-windows-msix][msix-lib] and detected via [`process.windowsStore`](process.md#processwindowsstore-readonly)),
the module uses the MSIX updater, which supports direct MSIX file links and JSON update feeds.
* **Squirrel.Windows**: For apps installed via traditional installers (created with
[electron-winstaller][installer-lib] or [Electron Forge's Squirrel.Windows maker][electron-forge-lib]),
the module uses Squirrel.Windows for updates.
You don't need to configure which updater to use; Electron automatically detects the packaging
format and uses the appropriate one.
#### Squirrel.Windows
On Windows, you have to install your app into a user's machine before you can
use the `autoUpdater`, so it is recommended that you use
[electron-winstaller][installer-lib] or [Electron Forge's Squirrel.Windows maker][electron-forge-lib] to generate a Windows installer.
Apps built with Squirrel.Windows will trigger [custom launch events](https://github.com/Squirrel/Squirrel.Windows/blob/51f5e2cb01add79280a53d51e8d0cfa20f8c9f9f/docs/using/custom-squirrel-events-non-cs.md#application-startup-commands)
that must be handled by your Electron application to ensure proper setup and teardown.
@@ -65,14 +55,6 @@ The installer generated with Squirrel.Windows will create a shortcut icon with a
same ID for your app with `app.setAppUserModelId` API, otherwise Windows will
not be able to pin your app properly in task bar.
#### MSIX Packages
When your app is packaged as an MSIX, the `autoUpdater` module provides additional
functionality:
* Use the `allowAnyVersion` option in `setFeedURL()` to allow updates to older versions (downgrades)
* Support for direct MSIX file links or JSON update feeds (similar to Squirrel.Mac format)
## Events
The `autoUpdater` object emits the following events:
@@ -110,7 +92,7 @@ Returns:
Emitted when an update has been downloaded.
With Squirrel.Windows only `releaseName` is available.
On Windows only `releaseName` is available.
> [!NOTE]
> It is not strictly necessary to handle this event. A successfully
@@ -146,12 +128,10 @@ changes:
-->
* `options` Object
* `url` string - The update server URL. For _Windows_ MSIX, this can be either a direct link to an MSIX file (e.g., `https://example.com/update.msix`) or a JSON endpoint that returns update information (see the [Squirrel.Mac][squirrel-mac] README for more information).
* `url` string
* `headers` Record\<string, string\> (optional) _macOS_ - HTTP request headers.
* `serverType` string (optional) _macOS_ - Can be `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
README for more information.
* `allowAnyVersion` boolean (optional) _Windows_ - If `true`, allows downgrades to older versions for MSIX packages.
Defaults to `false`.
Sets the `url` and initialize the auto updater.
@@ -188,4 +168,3 @@ closed.
[electron-forge-lib]: https://www.electronforge.io/config/makers/squirrel.windows
[app-user-model-id]: https://learn.microsoft.com/en-us/windows/win32/shell/appids
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
[msix-lib]: https://github.com/electron-userland/electron-windows-msix

View File

@@ -159,22 +159,6 @@ Notification activated (com.github.Electron:notification:EAF7B87C-A113-43D7-8E76
Notification replied to (com.github.Electron:notification:EAF7B87C-A113-43D7-8E76-F88EC9D73D44)
```
### `ELECTRON_DEBUG_MSIX_UPDATER`
Adds extra logs to MSIX updater operations on Windows to aid in debugging. Extra logging will be displayed when MSIX update operations are initiated, including package updates, package registration, and restart registration. This helps diagnose issues with MSIX package updates and deployments.
Sample output:
```sh
UpdateMsix called with URI: https://example.com/app.msix
DoUpdateMsix: Starting
Calling AddPackageByUriAsync... URI: https://example.com/app.msix
Update options - deferRegistration: true, developerMode: false, forceShutdown: false, forceTargetShutdown: false, forceUpdateFromAnyVersion: false
Waiting for deployment...
Deployment finished.
MSIX Deployment completed.
```
### `ELECTRON_LOG_ASAR_READS`
When Electron reads from an ASAR file, log the read offset and file path to

View File

@@ -128,8 +128,8 @@ A `string` representing Electron's version string.
### `process.windowsStore` _Readonly_
A `boolean`. If the app is running as an MSIX package (including AppX for Windows Store),
this property is `true`, otherwise it is `undefined`.
A `boolean`. If the app is running as a Windows Store app (appx), this property is `true`,
for otherwise it is `undefined`.
### `process.contextId` _Readonly_

View File

@@ -5,6 +5,7 @@
* `rgba` - 32bpp RGBA (byte-order), 1 plane.
* `rgbaf16` - Half float RGBA, 1 plane.
* `nv12` - 12bpp with Y plane followed by a 2x2 interleaved UV plane.
* `p010le` - 4:2:0 10-bit YUV (little-endian), Y plane followed by a 2x2 interleaved UV plane.
* `colorSpace` [ColorSpace](color-space.md) (optional) - The color space of the texture.
* `codedSize` [Size](size.md) - The full dimensions of the shared texture.
* `visibleRect` [Rectangle](rectangle.md) (optional) - A subsection of [0, 0, codedSize.width, codedSize.height]. In common cases, it is the full section area.

View File

@@ -94,6 +94,7 @@
The actual output pixel format and color space of the texture should refer to [`OffscreenSharedTexture`](../structures/offscreen-shared-texture.md) object in the `paint` event.
* `argb` - The requested output texture format is 8-bit unorm RGBA, with SRGB SDR color space.
* `rgbaf16` - The requested output texture format is 16-bit float RGBA, with scRGB HDR color space.
* `deviceScaleFactor` number (optional) _Experimental_ - The device scale factor of the offscreen rendering output. If not set, will use primary display's scale factor as default.
* `contextIsolation` boolean (optional) - Whether to run Electron APIs and
the specified `preload` script in a separate JavaScript context. Defaults
to `true`. The context that the `preload` script runs in will only have
@@ -156,6 +157,8 @@
`WebContents` when the preferred size changes. Default is `false`.
* `transparent` boolean (optional) - Whether to enable background transparency for the guest page. Default is `true`. **Note:** The guest page's text and background colors are derived from the [color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) of its root element. When transparency is enabled, the text color will still change accordingly but the background will remain transparent.
* `enableDeprecatedPaste` boolean (optional) _Deprecated_ - Whether to enable the `paste` [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand). Default is `false`.
* `focusOnNavigation` boolean (optional) - Whether to focus the WebContents
when navigating. Default is `true`.
[chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment
[runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5

View File

@@ -12,6 +12,16 @@ This document uses the following convention to categorize breaking changes:
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
## Planned Breaking API Changes (42.0)
### Behavior Changed: Offscreen rendering will use `1.0` as default device scale factor.
Previously, OSR used the primary display's device scale factor for rendering, which made the output frame size vary across users.
Developers had to manually calculate the correct size using `screen.getPrimaryDisplay().scaleFactor`. We now provide an optional property
`webPreferences.offscreen.deviceScaleFactor` to specify a custom value when creating an OSR window. At first, if the property is not set, it defaults
to the primary display's scale factor (preserving the old behavior). Starting from Electron 42, the default will change to a constant value of `1.0`
for more consistent output sizes.
## Planned Breaking API Changes (41.0)
### Behavior Changed: PDFs no longer create a separate WebContents

View File

@@ -238,20 +238,6 @@ with 3+ years of verifiable business history and to individual developers in the
Microsoft is looking to make the program more widely available. If you're reading this at a
later point, it could make sense to check if the eligibility criteria have changed.
#### Using `jsign` for Azure Trusted Signing
For developers on Linux or macOS, [`jsign`](https://ebourg.github.io/jsign/) can be used to sign Windows apps via Azure Trusted Signing. Example usage:
```bash
jsign --storetype TRUSTEDSIGNING \
--keystore https://eus.codesigning.azure.net/ \
--storepass $AZURE_ACCESS_TOKEN \
--alias trusted-sign-acct/AppName \
--tsaurl http://timestamp.acs.microsoft.com/ \
--tsmode RFC3161 \
--replace <file>
```
#### Using Electron Forge
Electron Forge is the recommended way to sign your app as well as your `Squirrel.Windows`

View File

@@ -60,7 +60,7 @@ at once, consider the [Chrome Tracing](https://www.chromium.org/developers/how-t
## Checklist: Performance recommendations
Chances are that your app could be a little leaner, faster, and generally less
resource-hungry if you attempt these steps.
resource-hungry if you avoid the following common pitfalls.
1. [Carelessly including modules](#1-carelessly-including-modules)
2. [Loading and running code too soon](#2-loading-and-running-code-too-soon)

View File

@@ -44,11 +44,25 @@ have to worry about wiring them all together.
You can install Electron Forge's CLI in your project's `devDependencies` and import your
existing project with a handy conversion script.
```sh npm2yarn
<Tabs>
<TabItem value="npm" label="npm">
```sh
npm install --save-dev @electron-forge/cli
npx electron-forge import
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```sh
yarn add --dev @electron-forge/cli
yarn electron-forge import
```
</TabItem>
</Tabs>
Once the conversion script is done, Forge should have added a few scripts
to your `package.json` file.

View File

@@ -230,10 +230,8 @@ auto_filenames = {
browser_bundle_deps = [
"lib/browser/api/app.ts",
"lib/browser/api/auto-updater.ts",
"lib/browser/api/auto-updater/auto-updater-msix.ts",
"lib/browser/api/auto-updater/auto-updater-native.ts",
"lib/browser/api/auto-updater/auto-updater-win.ts",
"lib/browser/api/auto-updater/msix-update-win.ts",
"lib/browser/api/auto-updater/squirrel-update-win.ts",
"lib/browser/api/base-window.ts",
"lib/browser/api/browser-view.ts",

View File

@@ -590,7 +590,6 @@ filenames = {
"shell/common/electron_command_line.cc",
"shell/common/electron_command_line.h",
"shell/common/electron_constants.h",
"shell/common/electron_paths.cc",
"shell/common/electron_paths.h",
"shell/common/gin_converters/accelerator_converter.cc",
"shell/common/gin_converters/accelerator_converter.h",

View File

@@ -1,10 +1,5 @@
if (process.platform === 'win32') {
// windowsStore indicates whether the app is running as a packaged app (MSIX), even outside of the store
if (process.windowsStore) {
module.exports = require('./auto-updater/auto-updater-msix');
} else {
module.exports = require('./auto-updater/auto-updater-win');
}
module.exports = require('./auto-updater/auto-updater-win');
} else {
module.exports = require('./auto-updater/auto-updater-native');
}

View File

@@ -1,449 +0,0 @@
import * as msixUpdate from '@electron/internal/browser/api/auto-updater/msix-update-win';
import { app, net } from 'electron/main';
import { EventEmitter } from 'events';
interface UpdateInfo {
ok: boolean; // False if error encountered
available?: boolean; // True if the update is available, false if not
updateUrl?: string; // The URL of the update
releaseNotes?: string; // The release notes of the update
releaseName?: string; // The release name of the update
releaseDate?: Date; // The release date of the update
}
interface MSIXPackageInfo {
id: string;
familyName: string;
developmentMode: boolean;
version: string;
signatureKind: 'developer' | 'enterprise' | 'none' | 'store' | 'system';
appInstallerUri?: string;
}
/**
* Options for updating an MSIX package.
* Used with `updateMsix()` to control how the package update behaves.
*
* These options correspond to the Windows.Management.Deployment.AddPackageOptions class properties.
*
* @see https://learn.microsoft.com/en-us/uwp/api/windows.management.deployment.addpackageoptions?view=winrt-26100
*/
export interface UpdateMsixOptions {
/**
* Gets or sets a value that indicates whether to delay registration of the main package
* or dependency packages if the packages are currently in use.
*
* Corresponds to `AddPackageOptions.DeferRegistrationWhenPackagesAreInUse`
*
* @default false
*/
deferRegistration?: boolean;
/**
* Gets or sets a value that indicates whether the app is installed in developer mode.
* When set, the app is installed in development mode which allows for a more rapid
* development cycle. The BlockMap.xml, [Content_Types].xml, and digital signature
* files are not required for app installation.
*
* Corresponds to `AddPackageOptions.DeveloperMode`
*
* @default false
*/
developerMode?: boolean;
/**
* Gets or sets a value that indicates whether the processes associated with the package
* will be shut down forcibly so that registration can continue if the package, or any
* package that depends on the package, is currently in use.
*
* Corresponds to `AddPackageOptions.ForceAppShutdown`
*
* @default false
*/
forceShutdown?: boolean;
/**
* Gets or sets a value that indicates whether the processes associated with the package
* will be shut down forcibly so that registration can continue if the package is
* currently in use.
*
* Corresponds to `AddPackageOptions.ForceTargetAppShutdown`
*
* @default false
*/
forceTargetShutdown?: boolean;
/**
* Gets or sets a value that indicates whether to force a specific version of a package
* to be added, regardless of if a higher version is already added.
*
* Corresponds to `AddPackageOptions.ForceUpdateFromAnyVersion`
*
* @default false
*/
forceUpdateFromAnyVersion?: boolean;
}
/**
* Options for registering an MSIX package.
* Used with `registerPackage()` to control how the package registration behaves.
*
* These options correspond to the Windows.Management.Deployment.DeploymentOptions enum.
*
* @see https://learn.microsoft.com/en-us/uwp/api/windows.management.deployment.deploymentoptions?view=winrt-26100
*/
interface RegisterPackageOptions {
/**
* Force shutdown of the application if it's currently running.
* If this package, or any package that depends on this package, is currently in use,
* the processes associated with the package are shut down forcibly so that registration can continue.
*
* Corresponds to `DeploymentOptions.ForceApplicationShutdown` (value: 1)
*
* @default false
*/
forceShutdown?: boolean;
/**
* Force shutdown of the target application if it's currently running.
* If this package is currently in use, the processes associated with the package
* are shut down forcibly so that registration can continue.
*
* Corresponds to `DeploymentOptions.ForceTargetApplicationShutdown` (value: 64)
*
* @default false
*/
forceTargetShutdown?: boolean;
/**
* Force a specific version of a package to be staged/registered, regardless of if
* a higher version is already staged/registered.
*
* Corresponds to `DeploymentOptions.ForceUpdateFromAnyVersion` (value: 262144)
*
* @default false
*/
forceUpdateFromAnyVersion?: boolean;
}
class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
updateAvailable: boolean = false;
updateURL: string | null = null;
updateHeaders: Record<string, string> | null = null;
allowAnyVersion: boolean = false;
// Private: Validate that the URL points to an MSIX file (following redirects)
private async validateMsixUrl (url: string): Promise<void> {
try {
// Make a HEAD request to follow redirects and get the final URL
const response = await net.fetch(url, {
method: 'HEAD',
headers: this.updateHeaders ? new Headers(this.updateHeaders) : undefined,
redirect: 'follow' // Follow redirects to get the final URL
});
// Get the final URL after redirects (response.url contains the final URL)
const finalUrl = response.url || url;
const urlObj = new URL(finalUrl);
const pathname = urlObj.pathname.toLowerCase();
// Check if final URL ends with .msix or .msixbundle extension
const hasMsixExtension = pathname.endsWith('.msix') || pathname.endsWith('.msixbundle');
if (!hasMsixExtension) {
throw new Error(`Update URL does not point to an MSIX file. Expected .msix or .msixbundle extension, got final URL: ${finalUrl}`);
}
} catch (error) {
if (error instanceof TypeError) {
throw new Error(`Invalid MSIX URL: ${url}`);
}
throw error;
}
}
// Private: Check if URL is a direct MSIX file (following redirects)
private async isDirectMsixUrl (url: string, emitError: boolean = false): Promise<boolean> {
try {
await this.validateMsixUrl(url);
return true;
} catch (error) {
if (emitError) {
this.emitError(error as Error);
}
return false;
}
}
// Supports both versioning (x.y.z) and Windows version format (x.y.z.a)
// Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if v1 === v2
private compareVersions (v1: string, v2: string): number {
const parts1 = v1.split('.').map(part => {
const parsed = parseInt(part, 10);
return isNaN(parsed) ? 0 : parsed;
});
const parts2 = v2.split('.').map(part => {
const parsed = parseInt(part, 10);
return isNaN(parsed) ? 0 : parsed;
});
const maxLength = Math.max(parts1.length, parts2.length);
for (let i = 0; i < maxLength; i++) {
const part1 = parts1[i] ?? 0;
const part2 = parts2[i] ?? 0;
if (part1 > part2) return 1;
if (part1 < part2) return -1;
}
return 0;
}
// Private: Parse the static releases array format
// This is a static JSON file containing all releases
private parseStaticReleasFile (json: any, currentVersion: string): { ok: boolean; available: boolean; url?: string; name?: string; notes?: string; pub_date?: string } {
if (!Array.isArray(json.releases) || !json.currentRelease || typeof json.currentRelease !== 'string') {
this.emitError(new Error('Invalid releases format. Expected \'releases\' array and \'currentRelease\' string.'));
return { ok: false, available: false };
}
// Use currentRelease property to determine if update is available
const currentReleaseVersion = json.currentRelease;
// Compare current version with currentRelease
const versionComparison = this.compareVersions(currentReleaseVersion, currentVersion);
// If versions match, we're up to date
if (versionComparison === 0) {
return { ok: true, available: false };
}
// If currentRelease is older than current version, check allowAnyVersion
if (versionComparison < 0) {
// If allowAnyVersion is true, allow downgrades
if (this.allowAnyVersion) {
// Continue to find the release entry for downgrade
} else {
return { ok: true, available: false };
}
}
// currentRelease is newer, find the release entry
const releaseEntry = json.releases.find((r: any) => r.version === currentReleaseVersion);
if (!releaseEntry || !releaseEntry.updateTo) {
this.emitError(new Error(`Release entry for version '${currentReleaseVersion}' not found or missing 'updateTo' property.`));
return { ok: false, available: false };
}
const updateTo = releaseEntry.updateTo;
if (!updateTo.url) {
this.emitError(new Error(`Invalid release entry. 'updateTo.url' is missing for version ${currentReleaseVersion}.`));
return { ok: false, available: false };
}
return {
ok: true,
available: true,
url: updateTo.url,
name: updateTo.name,
notes: updateTo.notes,
pub_date: updateTo.pub_date
};
}
private parseDynamicReleasFile (json: any): { ok: boolean; available: boolean; url?: string; name?: string; notes?: string; pub_date?: string } {
if (!json.url) {
this.emitError(new Error('Invalid releases format. Expected \'url\' string property.'));
return { ok: false, available: false };
}
return { ok: true, available: true, url: json.url, name: json.name, notes: json.notes, pub_date: json.pub_date };
}
private async fetchSquirrelJson (url: string) {
const headers: Record<string, string> = {
...this.updateHeaders,
Accept: 'application/json' // Always set Accept header, overriding any user-provided Accept
};
const response = await net.fetch(url, {
headers
});
if (response.status === 204) {
return { ok: true, available: false };
} else if (response.status === 200) {
const updateJson = await response.json();
// Check if this is the static releases array format
if (Array.isArray(updateJson.releases)) {
// Get current package version
const packageInfo = msixUpdate.getPackageInfo();
const currentVersion = packageInfo.version;
if (!currentVersion) {
this.emitError(new Error('Cannot determine current package version.'));
return { ok: false, available: false };
}
return this.parseStaticReleasFile(updateJson, currentVersion);
} else {
// Dynamic format: server returns JSON with update info for current version
return this.parseDynamicReleasFile(updateJson);
}
} else {
this.emitError(new Error(`Unexpected response status: ${response.status}`));
return { ok: false, available: false };
}
}
private async getUpdateInfo (url: string): Promise<UpdateInfo> {
if (url && await this.isDirectMsixUrl(url)) {
return { ok: true, available: true, updateUrl: url, releaseDate: new Date() };
} else {
const updateJson = await this.fetchSquirrelJson(url);
if (!updateJson.ok) {
return { ok: false };
} else if (updateJson.ok && !updateJson.available) {
return { ok: true, available: false };
} else {
// updateJson.ok && updateJson.available must be true here
// Parse the publication date if present (ISO 8601 format)
let releaseDate: Date | null = null;
if (updateJson.pub_date) {
releaseDate = new Date(updateJson.pub_date);
}
const updateUrl = updateJson.url ?? '';
const releaseNotes = updateJson.notes ?? '';
const releaseName = updateJson.name ?? '';
releaseDate = releaseDate ?? new Date();
if (!await this.isDirectMsixUrl(updateUrl, true)) {
return { ok: false };
} else {
return {
ok: true,
available: true,
updateUrl,
releaseNotes,
releaseName,
releaseDate
};
}
}
}
}
getFeedURL () {
return this.updateURL ?? '';
}
setFeedURL (options: { url: string; headers?: Record<string, string>; allowAnyVersion?: boolean } | string) {
let updateURL: string;
let headers: Record<string, string> | undefined;
let allowAnyVersion: boolean | undefined;
if (typeof options === 'object') {
if (typeof options.url === 'string') {
updateURL = options.url;
headers = options.headers;
allowAnyVersion = options.allowAnyVersion;
} else {
throw new TypeError('Expected options object to contain a \'url\' string property in setFeedUrl call');
}
} else if (typeof options === 'string') {
updateURL = options;
} else {
throw new TypeError('Expected an options object with a \'url\' property to be provided');
}
this.updateURL = updateURL;
this.updateHeaders = headers ?? null;
this.allowAnyVersion = allowAnyVersion ?? false;
}
getPackageInfo (): MSIXPackageInfo {
return msixUpdate.getPackageInfo() as MSIXPackageInfo;
}
async checkForUpdates () {
const url = this.updateURL;
if (!url) {
return this.emitError(new Error('Update URL is not set'));
}
// Check if running in MSIX package
const packageInfo = msixUpdate.getPackageInfo();
if (!packageInfo.familyName) {
return this.emitError(new Error('MSIX updates are not supported'));
}
// If appInstallerUri is set, Windows App Installer manages updates automatically
// Prevent updates here to avoid conflicts
if (packageInfo.appInstallerUri) {
return this.emitError(new Error('Auto-updates are managed by Windows App Installer. Updates are not allowed when installed via Application Manifest.'));
}
this.emit('checking-for-update');
try {
const msixUrlInfo = await this.getUpdateInfo(url);
if (!msixUrlInfo.ok) {
return this.emitError(new Error('Invalid update or MSIX URL. See previous errors.'));
}
if (!msixUrlInfo.available) {
this.emit('update-not-available');
} else {
this.updateAvailable = true;
this.emit('update-available');
await msixUpdate.updateMsix(msixUrlInfo.updateUrl, {
deferRegistration: true,
developerMode: false,
forceShutdown: false,
forceTargetShutdown: false,
forceUpdateFromAnyVersion: this.allowAnyVersion
} as UpdateMsixOptions);
this.emit('update-downloaded', {}, msixUrlInfo.releaseNotes, msixUrlInfo.releaseName, msixUrlInfo.releaseDate, msixUrlInfo.updateUrl, () => {
this.quitAndInstall();
});
}
} catch (error) {
this.emitError(error as Error);
}
}
async quitAndInstall () {
if (!this.updateAvailable) {
this.emitError(new Error('No update available, can\'t quit and install'));
app.quit();
return;
}
try {
// Get package info to get family name
const packageInfo = msixUpdate.getPackageInfo();
if (!packageInfo.familyName) {
return this.emitError(new Error('MSIX updates are not supported'));
}
msixUpdate.registerRestartOnUpdate('');
this.emit('before-quit-for-update');
// force shutdown of the application and register the package to be installed on restart
await msixUpdate.registerPackage(packageInfo.familyName, {
forceShutdown: true
} as RegisterPackageOptions);
} catch (error) {
this.emitError(error as Error);
}
}
// Private: Emit both error object and message, this is to keep compatibility
// with Old APIs.
emitError (error: Error) {
this.emit('error', error, error.message);
}
}
export default new AutoUpdater();

View File

@@ -20,11 +20,6 @@ class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
return this.updateURL ?? '';
}
getPackageInfo () {
// Squirrel-based Windows apps don't have MSIX package information
return undefined;
}
setFeedURL (options: { url: string } | string) {
let updateURL: string;
if (typeof options === 'object') {

View File

@@ -1,4 +0,0 @@
const { updateMsix, registerPackage, registerRestartOnUpdate, getPackageInfo } =
process._linkedBinding('electron_browser_msix_updater');
export { updateMsix, registerPackage, registerRestartOnUpdate, getPackageInfo };

View File

@@ -10,7 +10,7 @@
"@electron/docs-parser": "^2.0.0",
"@electron/fiddle-core": "^1.3.4",
"@electron/github-app-auth": "^3.2.0",
"@electron/lint-roller": "^3.2.0",
"@electron/lint-roller": "^3.1.2",
"@electron/typescript-definitions": "^9.1.5",
"@octokit/rest": "^20.1.2",
"@primer/octicons": "^10.0.0",

View File

@@ -143,3 +143,4 @@ fix_check_for_file_existence_before_setting_mtime.patch
fix_linux_tray_id.patch
expose_gtk_ui_platform_field.patch
fix_os_crypt_async_cookie_encryption.patch
patch_osr_control_screen_info.patch

View File

@@ -8,20 +8,24 @@ electron objects that extend gin::Wrappable and gets
allocated on the cpp heap
diff --git a/gin/public/wrappable_pointer_tags.h b/gin/public/wrappable_pointer_tags.h
index 573bcb2e56068a2ade6d8ab28964b077487874fd..42add73062b723b03fc15ddcce905e4d5061c384 100644
index 573bcb2e56068a2ade6d8ab28964b077487874fd..acb0c0b44f6530e49b32ea7602c25d498ae4f210 100644
--- a/gin/public/wrappable_pointer_tags.h
+++ b/gin/public/wrappable_pointer_tags.h
@@ -74,7 +74,15 @@ enum WrappablePointerTag : uint16_t {
@@ -74,7 +74,19 @@ enum WrappablePointerTag : uint16_t {
kTextInputControllerBindings, // content::TextInputControllerBindings
kWebAXObjectProxy, // content::WebAXObjectProxy
kWrappedExceptionHandler, // extensions::WrappedExceptionHandler
- kLastPointerTag = kWrappedExceptionHandler,
+ kElectronApp, // electron::api::App
+ kElectronDataPipeHolder, // electron::api::DataPipeHolder
+ kElectronDebugger, // electron::api::Debugger
+ kElectronEvent, // gin_helper::internal::Event
+ kElectronMenu, // electron::api::Menu
+ kElectronNetLog, // electron::api::NetLog
+ kElectronPowerMonitor, // electron::api::PowerMonitor
+ kElectronPowerSaveBlocker, // electron::api::PowerSaveBlocker
+ kElectronReplyChannel, // gin_helper::internal::ReplyChannel
+ kElectronScreen, // electron::api::Screen
+ kElectronSession, // electron::api::Session
+ kElectronWebRequest, // electron::api::WebRequest
+ kLastPointerTag = kElectronWebRequest,

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: reito <reito@chromium.org>
Date: Wed, 29 Oct 2025 00:50:03 +0800
Subject: patch: osr control screen info
We need to override GetNewScreenInfosForUpdate to ensure the screen info
is updated correctly, instead of overriding GetScreenInfo which seems not
working.
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index 1a18bdda39f76cfae36adc0ffde136e788a98262..1062bada30908399f5429b51031e245f4d010f84 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -680,7 +680,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
// Generates the most current set of ScreenInfos from the current set of
// displays in the system for use in UpdateScreenInfo.
- display::ScreenInfos GetNewScreenInfosForUpdate();
+ virtual display::ScreenInfos GetNewScreenInfosForUpdate();
// Called when display properties that need to be synchronized with the
// renderer process changes. This method is called before notifying

View File

@@ -6,10 +6,10 @@ Subject: Delete deprecated fields on v8::Isolate
https://chromium-review.googlesource.com/c/v8/v8/+/7081397
diff --git a/src/api/environment.cc b/src/api/environment.cc
index cb1e4e6176e7385f8bc2bc9510761d3fc9c3182d..730254bfc16eceb7394f5aa766b648da4b96511f 100644
index cfc9b3157d08d62f43e2e5bb01229fe663f3ca61..cce0e1cdc37aa324aa2c52ba134fc1a9a55b10ba 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -226,8 +226,6 @@ void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {
@@ -218,8 +218,6 @@ void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {
// heap based on the actual physical memory.
params->constraints.ConfigureDefaults(total_memory, 0);
}

View File

@@ -44,10 +44,10 @@ index 37d83e41b618a07aca98118260abe9618f11256d..26d5c1bd3c8191fce1d22b969996b6bf
template <typename T>
diff --git a/src/env-inl.h b/src/env-inl.h
index 5dfbd564d5bbd22ebf3b529a07b73e85cbe51986..b0c3c52cab63c6ae67079aa752bd58dd4f162451 100644
index 97c43afb487b58c0c77bd59b4a6b6d7a13690053..23a4d7b651935a4029249fb2f1dd3ed46ea3b26f 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -199,7 +199,8 @@ inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
@@ -189,7 +189,8 @@ inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
}
return static_cast<Environment*>(
context->GetAlignedPointerFromEmbedderData(

View File

@@ -6,10 +6,10 @@ Subject: Remove deprecated `GetIsolate`
https://chromium-review.googlesource.com/c/v8/v8/+/6905244
diff --git a/src/api/environment.cc b/src/api/environment.cc
index 0f19cb09ea0963a9c505c51f89d1c7a939f2730b..cb1e4e6176e7385f8bc2bc9510761d3fc9c3182d 100644
index d753ad6c6b49b26b86920124f7ac90c1e052638e..cfc9b3157d08d62f43e2e5bb01229fe663f3ca61 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -682,7 +682,7 @@ std::unique_ptr<MultiIsolatePlatform> MultiIsolatePlatform::Create(
@@ -668,7 +668,7 @@ std::unique_ptr<MultiIsolatePlatform> MultiIsolatePlatform::Create(
MaybeLocal<Object> GetPerContextExports(Local<Context> context,
IsolateData* isolate_data) {
@@ -18,7 +18,7 @@ index 0f19cb09ea0963a9c505c51f89d1c7a939f2730b..cb1e4e6176e7385f8bc2bc9510761d3f
EscapableHandleScope handle_scope(isolate);
Local<Object> global = context->Global();
@@ -728,7 +728,7 @@ void ProtoThrower(const FunctionCallbackInfo<Value>& info) {
@@ -714,7 +714,7 @@ void ProtoThrower(const FunctionCallbackInfo<Value>& info) {
// This runs at runtime, regardless of whether the context
// is created from a snapshot.
Maybe<void> InitializeContextRuntime(Local<Context> context) {
@@ -27,7 +27,7 @@ index 0f19cb09ea0963a9c505c51f89d1c7a939f2730b..cb1e4e6176e7385f8bc2bc9510761d3f
HandleScope handle_scope(isolate);
// When `IsCodeGenerationFromStringsAllowed` is true, V8 takes the fast path
@@ -807,7 +807,7 @@ Maybe<void> InitializeContextRuntime(Local<Context> context) {
@@ -793,7 +793,7 @@ Maybe<void> InitializeContextRuntime(Local<Context> context) {
}
Maybe<void> InitializeBaseContextForSnapshot(Local<Context> context) {
@@ -36,7 +36,7 @@ index 0f19cb09ea0963a9c505c51f89d1c7a939f2730b..cb1e4e6176e7385f8bc2bc9510761d3f
HandleScope handle_scope(isolate);
// Delete `Intl.v8BreakIterator`
@@ -832,7 +832,7 @@ Maybe<void> InitializeBaseContextForSnapshot(Local<Context> context) {
@@ -818,7 +818,7 @@ Maybe<void> InitializeBaseContextForSnapshot(Local<Context> context) {
}
Maybe<void> InitializeMainContextForSnapshot(Local<Context> context) {
@@ -45,7 +45,7 @@ index 0f19cb09ea0963a9c505c51f89d1c7a939f2730b..cb1e4e6176e7385f8bc2bc9510761d3f
HandleScope handle_scope(isolate);
// Initialize the default values.
@@ -850,7 +850,7 @@ Maybe<void> InitializeMainContextForSnapshot(Local<Context> context) {
@@ -836,7 +836,7 @@ Maybe<void> InitializeMainContextForSnapshot(Local<Context> context) {
MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
IsolateData* isolate_data) {
CHECK(isolate_data);
@@ -54,7 +54,7 @@ index 0f19cb09ea0963a9c505c51f89d1c7a939f2730b..cb1e4e6176e7385f8bc2bc9510761d3f
EscapableHandleScope scope(isolate);
Context::Scope context_scope(context);
@@ -874,7 +874,7 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
@@ -860,7 +860,7 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
IsolateData* isolate_data) {
CHECK(isolate_data);
@@ -63,7 +63,7 @@ index 0f19cb09ea0963a9c505c51f89d1c7a939f2730b..cb1e4e6176e7385f8bc2bc9510761d3f
EscapableHandleScope scope(isolate);
Context::Scope context_scope(context);
@@ -900,7 +900,7 @@ MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
@@ -886,7 +886,7 @@ MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
Maybe<void> InitializePrimordials(Local<Context> context,
IsolateData* isolate_data) {
// Run per-context JS files.

View File

@@ -10,18 +10,6 @@ This should be upstreamed in some form, though it may need to be tweaked
before it's acceptable to upstream, as this patch comments out a couple
of tests that upstream probably cares about.
diff --git a/test/fixtures/crypto/rsa_pss.js b/test/fixtures/crypto/rsa_pss.js
index 423f2c4d77bfc98bfbdab93c09aff8012c678cbd..fa0bcceb5697486930a9530732f9a9ab6e1bb5b0 100644
--- a/test/fixtures/crypto/rsa_pss.js
+++ b/test/fixtures/crypto/rsa_pss.js
@@ -1,6 +1,6 @@
'use strict';
-module.exports = function() {
+module.exports = function () {
const pkcs8 = Buffer.from(
'308204bf020100300d06092a864886f70d0101010500048204a9308204a5020100028' +
'2010100d3576092e62957364544e7e4233b7bdb293db2085122c479328546f9f0f712' +
diff --git a/test/parallel/test-crypto-async-sign-verify.js b/test/parallel/test-crypto-async-sign-verify.js
index 9876c4bb6ecd2e5b8879f153811cd0a0a22997aa..2c4bf03452eb10fec52c38a361b6aad93169f08d 100644
--- a/test/parallel/test-crypto-async-sign-verify.js
@@ -53,6 +41,102 @@ index 9876c4bb6ecd2e5b8879f153811cd0a0a22997aa..2c4bf03452eb10fec52c38a361b6aad9
// Test Parallel Execution w/ KeyObject is threadsafe in openssl3
{
diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js
index e8fedf2d5d5072e00afd493ac2ac44748212b02e..6fcbe244871d25b2151d39160149aaa50dc96012 100644
--- a/test/parallel/test-crypto-authenticated.js
+++ b/test/parallel/test-crypto-authenticated.js
@@ -627,21 +627,25 @@ for (const test of TEST_CASES) {
{
// CCM cipher without data should not crash, see https://github.com/nodejs/node/issues/38035.
const algo = 'aes-128-ccm';
- const key = Buffer.alloc(16);
- const iv = Buffer.alloc(12);
- const opts = { authTagLength: 10 };
+ if (!ciphers.includes(algo)) {
+ common.printSkipMessage(`unsupported ${algo} test`);
+ } else {
+ const key = Buffer.alloc(16);
+ const iv = Buffer.alloc(12);
+ const opts = { authTagLength: 10 };
- const cipher = crypto.createCipheriv(algo, key, iv, opts);
- assert.throws(() => {
- cipher.final();
- }, hasOpenSSL3 ? {
- code: 'ERR_OSSL_TAG_NOT_SET'
- } : {
- message: /Unsupported state/
- });
+ const cipher = crypto.createCipheriv(algo, key, iv, opts);
+ assert.throws(() => {
+ cipher.final();
+ }, hasOpenSSL3 ? {
+ code: 'ERR_OSSL_TAG_NOT_SET'
+ } : {
+ message: /Unsupported state/
+ });
+ }
}
-{
+if (!process.features.openssl_is_boringssl) {
const key = Buffer.alloc(32);
const iv = Buffer.alloc(12);
@@ -653,11 +657,13 @@ for (const test of TEST_CASES) {
message: errMessages.authTagLength
});
}
+} else {
+ common.printSkipMessage('Skipping unsupported chacha20-poly1305 test');
}
// ChaCha20-Poly1305 should respect the authTagLength option and should not
// require the authentication tag before calls to update() during decryption.
-{
+if (!process.features.openssl_is_boringssl) {
const key = Buffer.alloc(32);
const iv = Buffer.alloc(12);
@@ -697,6 +703,8 @@ for (const test of TEST_CASES) {
}
}
}
+} else {
+ common.printSkipMessage('Skipping unsupported chacha20-poly1305 test');
}
// ChaCha20-Poly1305 should default to an authTagLength of 16. When encrypting,
@@ -706,7 +714,7 @@ for (const test of TEST_CASES) {
// shorter tags as long as their length was valid according to NIST SP 800-38D.
// For ChaCha20-Poly1305, we intentionally deviate from that because there are
// no recommended or approved authentication tag lengths below 16 bytes.
-{
+if (!process.features.openssl_is_boringssl) {
const rfcTestCases = TEST_CASES.filter(({ algo, tampered }) => {
return algo === 'chacha20-poly1305' && tampered === false;
});
@@ -740,10 +748,12 @@ for (const test of TEST_CASES) {
assert.strictEqual(plaintext.toString('hex'), testCase.plain);
}
+} else {
+ common.printSkipMessage('Skipping unsupported chacha20-poly1305 test');
}
// https://github.com/nodejs/node/issues/45874
-{
+if (!process.features.openssl_is_boringssl) {
const rfcTestCases = TEST_CASES.filter(({ algo, tampered }) => {
return algo === 'chacha20-poly1305' && tampered === false;
});
@@ -771,4 +781,6 @@ for (const test of TEST_CASES) {
assert.throws(() => {
decipher.final();
}, /Unsupported state or unable to authenticate data/);
+} else {
+ common.printSkipMessage('Skipping unsupported chacha20-poly1305 test');
}
diff --git a/test/parallel/test-crypto-cipheriv-decipheriv.js b/test/parallel/test-crypto-cipheriv-decipheriv.js
index 6742722f9e90914b4dc8c079426d10040d476f72..8801ddfe7023fd0f7d5657b86a9164d75765322e 100644
--- a/test/parallel/test-crypto-cipheriv-decipheriv.js
@@ -68,6 +152,21 @@ index 6742722f9e90914b4dc8c079426d10040d476f72..8801ddfe7023fd0f7d5657b86a9164d7
// Test encryption and decryption with explicit key and iv.
// AES Key Wrap test vector comes from RFC3394
const plaintext = Buffer.from('00112233445566778899AABBCCDDEEFF', 'hex');
diff --git a/test/parallel/test-crypto-default-shake-lengths-oneshot.js b/test/parallel/test-crypto-default-shake-lengths-oneshot.js
index 247e58d93c4303ffde132e49fb25cf88d76fae7c..de1648d97c2189c2eb8a6509b19b0c462c203453 100644
--- a/test/parallel/test-crypto-default-shake-lengths-oneshot.js
+++ b/test/parallel/test-crypto-default-shake-lengths-oneshot.js
@@ -5,6 +5,10 @@ const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
+if (process.features.openssl_is_boringssl) {
+ common.skip('Skipping unsupported shake128 digest method test');
+}
+
const { hash } = require('crypto');
common.expectWarning({
diff --git a/test/parallel/test-crypto-dh-curves.js b/test/parallel/test-crypto-dh-curves.js
index 81a469c226c261564dee1e0b06b6571b18a41f1f..58b66045dba4201b7ebedd78b129420ffc316051 100644
--- a/test/parallel/test-crypto-dh-curves.js
@@ -82,18 +181,71 @@ index 81a469c226c261564dee1e0b06b6571b18a41f1f..58b66045dba4201b7ebedd78b129420f
const availableCurves = new Set(crypto.getCurves());
diff --git a/test/parallel/test-crypto-dh-errors.js b/test/parallel/test-crypto-dh-errors.js
index d7527d82617efccd931f0fc2f700ab876872c1e6..b14b4bbf88b902b6de916b92e3d48335c01df911 100644
index d7527d82617efccd931f0fc2f700ab876872c1e6..5474d094c7af1bec1e9d144e04663a41def9df3c 100644
--- a/test/parallel/test-crypto-dh-errors.js
+++ b/test/parallel/test-crypto-dh-errors.js
@@ -27,7 +27,7 @@ assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), {
@@ -27,13 +27,13 @@ assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), {
for (const bits of [-1, 0, 1]) {
if (hasOpenSSL3) {
assert.throws(() => crypto.createDiffieHellman(bits), {
- code: 'ERR_OSSL_DH_MODULUS_TOO_SMALL',
+ code: 'ERR_OSSL_BN_BITS_TOO_SMALL',
+ code: /ERR_OSSL_(BN_BITS|DH_MODULUS)_TOO_SMALL/,
name: 'Error',
message: /modulus too small/,
});
} else {
assert.throws(() => crypto.createDiffieHellman(bits), {
- code: 'ERR_OSSL_BN_BITS_TOO_SMALL',
+ code: /ERR_OSSL_(BN_BITS|DH_MODULUS)_TOO_SMALL/,
name: 'Error',
message: /bits[\s_]too[\s_]small/i,
});
diff --git a/test/parallel/test-crypto-dh-group-setters.js b/test/parallel/test-crypto-dh-group-setters.js
index 7c774111952eada92c62d45674c0845667ead1bf..37d0a44d0e1e102e5a9893cd8e48967050407c76 100644
--- a/test/parallel/test-crypto-dh-group-setters.js
+++ b/test/parallel/test-crypto-dh-group-setters.js
@@ -6,6 +6,10 @@ if (!common.hasCrypto)
const assert = require('assert');
const crypto = require('crypto');
+if (process.features.openssl_is_boringssl) {
+ common.skip('Skipping unsupported Diffie-Hellman tests');
+}
+
// Unlike DiffieHellman, DiffieHellmanGroup does not have any setters.
const dhg = crypto.getDiffieHellman('modp1');
assert.strictEqual(dhg.constructor, crypto.DiffieHellmanGroup);
diff --git a/test/parallel/test-crypto-dh-modp2-views.js b/test/parallel/test-crypto-dh-modp2-views.js
index 8d01731af79394cb33477a1ba4bb13561604e5e5..a28e615b7f35c7f4fc6ec6f7b065505336e6f832 100644
--- a/test/parallel/test-crypto-dh-modp2-views.js
+++ b/test/parallel/test-crypto-dh-modp2-views.js
@@ -7,6 +7,10 @@ const assert = require('assert');
const crypto = require('crypto');
const { modp2buf } = require('../common/crypto');
+if (process.features.openssl_is_boringssl) {
+ common.skip('Skipping unsupported Diffie-Hellman tests');
+}
+
const modp2 = crypto.createDiffieHellmanGroup('modp2');
const views = common.getArrayBufferViews(modp2buf);
diff --git a/test/parallel/test-crypto-dh-modp2.js b/test/parallel/test-crypto-dh-modp2.js
index 19767d26f4e5fbd1d82b5bfa6ebe0afddc412c3e..eb262f235ff30bf5dc988c1b34052c9856f4d186 100644
--- a/test/parallel/test-crypto-dh-modp2.js
+++ b/test/parallel/test-crypto-dh-modp2.js
@@ -6,6 +6,11 @@ if (!common.hasCrypto)
const assert = require('assert');
const crypto = require('crypto');
const { modp2buf } = require('../common/crypto');
+
+if (process.features.openssl_is_boringssl) {
+ common.skip('Skipping unsupported Diffie-Hellman tests');
+}
+
const modp2 = crypto.createDiffieHellmanGroup('modp2');
{
diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js
index 3c00a5fc73bb9f86f944df74f29d6b5225bc2f0e..b4e7002d862907d2af3b4f8e985700bd03300809 100644
--- a/test/parallel/test-crypto-dh.js
@@ -146,6 +298,234 @@ index d22281abbd5c3cab3aaa3ac494301fa6b4a8a968..5f0c6a4aed2e868a1a1049212edf2187
s.pipe(h).on('data', common.mustCall(function(c) {
assert.strictEqual(c, expect);
diff --git a/test/parallel/test-crypto-key-objects-to-crypto-key.js b/test/parallel/test-crypto-key-objects-to-crypto-key.js
index 141e51d1ab74a4fc3b176b303807fb1cf2a58ce1..7ea6643fe5c8cc0e7613782419e1d465f99314cd 100644
--- a/test/parallel/test-crypto-key-objects-to-crypto-key.js
+++ b/test/parallel/test-crypto-key-objects-to-crypto-key.js
@@ -26,9 +26,14 @@ function assertCryptoKey(cryptoKey, keyObject, algorithm, extractable, usages) {
{
for (const length of [128, 192, 256]) {
const key = createSecretKey(randomBytes(length >> 3));
- const algorithms = ['AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW'];
+ let algorithms = ['AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW'];
if (length === 256)
algorithms.push('ChaCha20-Poly1305');
+
+ if (process.features.openssl_is_boringssl) {
+ algorithms = algorithms.filter((a) => a !== 'AES-KW' && a !== 'ChaCha20-Poly1305');
+ }
+
for (const algorithm of algorithms) {
const usages = algorithm === 'AES-KW' ? ['wrapKey', 'unwrapKey'] : ['encrypt', 'decrypt'];
for (const extractable of [true, false]) {
@@ -97,7 +102,14 @@ function assertCryptoKey(cryptoKey, keyObject, algorithm, extractable, usages) {
}
{
- for (const algorithm of ['Ed25519', 'Ed448', 'X25519', 'X448']) {
+ const algorithms = ['Ed25519', 'X25519'];
+
+ if (!process.features.openssl_is_boringssl) {
+ algorithms.push('X448', 'Ed448');
+ }
+
+ for (const algorithm of algorithms) {
+ console.log(algorithm);
const { publicKey, privateKey } = generateKeyPairSync(algorithm.toLowerCase());
assert.throws(() => {
publicKey.toCryptoKey(algorithm === 'Ed25519' ? 'X25519' : 'Ed25519', true, []);
diff --git a/test/parallel/test-crypto-key-objects.js b/test/parallel/test-crypto-key-objects.js
index e8359ed6d0362c6e8da8be08b0fd42245fa7ae47..bd8211d98261a1acc928e849bf713578c85ff877 100644
--- a/test/parallel/test-crypto-key-objects.js
+++ b/test/parallel/test-crypto-key-objects.js
@@ -302,11 +302,11 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
}, hasOpenSSL3 ? {
message: 'error:1E08010C:DECODER routines::unsupported',
} : {
- message: 'error:0909006C:PEM routines:get_name:no start line',
+ message: /no.start.line/i,
code: 'ERR_OSSL_PEM_NO_START_LINE',
- reason: 'no start line',
+ reason: /no.start.line/i,
library: 'PEM routines',
- function: 'get_name',
+ function: /get_name|OPENSSL_internal/,
});
// This should not abort either: https://github.com/nodejs/node/issues/29904
@@ -329,12 +329,12 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
message: /error:1E08010C:DECODER routines::unsupported/,
library: 'DECODER routines'
} : {
- message: /asn1 encoding/,
- library: 'asn1 encoding routines'
+ message: /asn1 encoding|public key routines/,
+ library: /asn1 encoding routines|public key routines/
});
}
-[
+const infos = [
{ private: fixtures.readKey('ed25519_private.pem', 'ascii'),
public: fixtures.readKey('ed25519_public.pem', 'ascii'),
keyType: 'ed25519',
@@ -344,17 +344,6 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
d: 'wVK6M3SMhQh3NK-7GRrSV-BVWQx1FO5pW8hhQeu_NdA',
kty: 'OKP'
} },
- { private: fixtures.readKey('ed448_private.pem', 'ascii'),
- public: fixtures.readKey('ed448_public.pem', 'ascii'),
- keyType: 'ed448',
- jwk: {
- crv: 'Ed448',
- x: 'oX_ee5-jlcU53-BbGRsGIzly0V-SZtJ_oGXY0udf84q2hTW2RdstLktvwpkVJOoNb7o' +
- 'Dgc2V5ZUA',
- d: '060Ke71sN0GpIc01nnGgMDkp0sFNQ09woVo4AM1ffax1-mjnakK0-p-S7-Xf859QewX' +
- 'jcR9mxppY',
- kty: 'OKP'
- } },
{ private: fixtures.readKey('x25519_private.pem', 'ascii'),
public: fixtures.readKey('x25519_public.pem', 'ascii'),
keyType: 'x25519',
@@ -364,18 +353,37 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
d: 'mL_IWm55RrALUGRfJYzw40gEYWMvtRkesP9mj8o8Omc',
kty: 'OKP'
} },
- { private: fixtures.readKey('x448_private.pem', 'ascii'),
+]
+
+if (!process.features.openssl_is_boringssl) {
+ infos.push({
+ private: fixtures.readKey('ed448_private.pem', 'ascii'),
+ public: fixtures.readKey('ed448_public.pem', 'ascii'),
+ keyType: 'ed448',
+ jwk: {
+ crv: 'Ed448',
+ x: 'oX_ee5-jlcU53-BbGRsGIzly0V-SZtJ_oGXY0udf84q2hTW2RdstLktvwpkVJOoNb7o' +
+ 'Dgc2V5ZUA',
+ d: '060Ke71sN0GpIc01nnGgMDkp0sFNQ09woVo4AM1ffax1-mjnakK0-p-S7-Xf859QewX' +
+ 'jcR9mxppY',
+ kty: 'OKP'
+ }
+ }, {
+ private: fixtures.readKey('x448_private.pem', 'ascii'),
public: fixtures.readKey('x448_public.pem', 'ascii'),
keyType: 'x448',
jwk: {
crv: 'X448',
x: 'ioHSHVpTs6hMvghosEJDIR7ceFiE3-Xccxati64oOVJ7NWjfozE7ae31PXIUFq6cVYg' +
- 'vSKsDFPA',
+ 'vSKsDFPA',
d: 'tMNtrO_q8dlY6Y4NDeSTxNQ5CACkHiPvmukidPnNIuX_EkcryLEXt_7i6j6YZMKsrWy' +
- 'S0jlSYJk',
+ 'S0jlSYJk',
kty: 'OKP'
- } },
-].forEach((info) => {
+ }
+ });
+}
+
+infos.forEach((info) => {
const keyType = info.keyType;
{
@@ -417,7 +425,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
}
});
-[
+const ecInfos = [
{ private: fixtures.readKey('ec_p256_private.pem', 'ascii'),
public: fixtures.readKey('ec_p256_public.pem', 'ascii'),
keyType: 'ec',
@@ -429,17 +437,6 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs',
y: 'UbJuPy2Xi0lW7UYTBxPK3yGgDu9EAKYIecjkHX5s2lI'
} },
- { private: fixtures.readKey('ec_secp256k1_private.pem', 'ascii'),
- public: fixtures.readKey('ec_secp256k1_public.pem', 'ascii'),
- keyType: 'ec',
- namedCurve: 'secp256k1',
- jwk: {
- crv: 'secp256k1',
- d: 'c34ocwTwpFa9NZZh3l88qXyrkoYSxvC0FEsU5v1v4IM',
- kty: 'EC',
- x: 'cOzhFSpWxhalCbWNdP2H_yUkdC81C9T2deDpfxK7owA',
- y: '-A3DAZTk9IPppN-f03JydgHaFvL1fAHaoXf4SX4NXyo'
- } },
{ private: fixtures.readKey('ec_p384_private.pem', 'ascii'),
public: fixtures.readKey('ec_p384_public.pem', 'ascii'),
keyType: 'ec',
@@ -465,7 +462,25 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
y: 'Ad3flexBeAfXceNzRBH128kFbOWD6W41NjwKRqqIF26vmgW_8COldGKZjFkOSEASxPB' +
'cvA2iFJRUyQ3whC00j0Np'
} },
-].forEach((info) => {
+]
+
+if (!process.features.openssl_is_boringssl) {
+ ecInfos.push({
+ private: fixtures.readKey('ec_secp256k1_private.pem', 'ascii'),
+ public: fixtures.readKey('ec_secp256k1_public.pem', 'ascii'),
+ keyType: 'ec',
+ namedCurve: 'secp256k1',
+ jwk: {
+ crv: 'secp256k1',
+ d: 'c34ocwTwpFa9NZZh3l88qXyrkoYSxvC0FEsU5v1v4IM',
+ kty: 'EC',
+ x: 'cOzhFSpWxhalCbWNdP2H_yUkdC81C9T2deDpfxK7owA',
+ y: '-A3DAZTk9IPppN-f03JydgHaFvL1fAHaoXf4SX4NXyo'
+ }
+ });
+}
+
+ecInfos.forEach((info) => {
const { keyType, namedCurve } = info;
{
@@ -540,7 +555,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
format: 'pem',
passphrase: Buffer.alloc(1024, 'a')
}), {
- message: /bad decrypt/
+ message: /bad.decrypt/i
});
const publicKey = createPublicKey(publicDsa);
@@ -566,7 +581,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
{
// Test RSA-PSS.
- {
+ if (!process.features.openssl_is_boringssl) {
// This key pair does not restrict the message digest algorithm or salt
// length.
const publicPem = fixtures.readKey('rsa_pss_public_2048.pem');
@@ -625,6 +640,8 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
}, {
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS'
});
+ } else {
+ common.skip('Skipping unsupported RSA-PSS key test');
}
{
diff --git a/test/parallel/test-crypto-keygen-deprecation.js b/test/parallel/test-crypto-keygen-deprecation.js
index 926dfbbc4ae987217ab404ec25a3ca0a2ef2edcf..df0b379c1b1e982b96ea97c9814f38991d734ce4 100644
--- a/test/parallel/test-crypto-keygen-deprecation.js
+++ b/test/parallel/test-crypto-keygen-deprecation.js
@@ -4,6 +4,10 @@ const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
+if (process.features.openssl_is_boringssl) {
+ common.skip('Skipping unsupported RSA-PSS key tests');
+}
+
const DeprecationWarning = [];
DeprecationWarning.push([
'"options.hash" is deprecated, use "options.hashAlgorithm" instead.',
diff --git a/test/parallel/test-crypto-oneshot-hash-xof.js b/test/parallel/test-crypto-oneshot-hash-xof.js
index 75cb4800ff1bd51fedd7bc4e2d7e6af6f4f48346..b4363c31592763235116d970a5f45d4cf63de373 100644
--- a/test/parallel/test-crypto-oneshot-hash-xof.js
@@ -162,7 +542,7 @@ index 75cb4800ff1bd51fedd7bc4e2d7e6af6f4f48346..b4363c31592763235116d970a5f45d4c
{
// Default outputLengths.
diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js
index 119bc3c2d20ea7d681f0b579f9d91ad46cdc3634..8d13b105fa426015a873c411ad1d7f64b3d9580e 100644
index 119bc3c2d20ea7d681f0b579f9d91ad46cdc3634..ad9cd4fd81aff32ec175f469176e1012b81872ac 100644
--- a/test/parallel/test-crypto-rsa-dsa.js
+++ b/test/parallel/test-crypto-rsa-dsa.js
@@ -29,12 +29,11 @@ const dsaPkcs8KeyPem = fixtures.readKey('dsa_private_pkcs8.pem');
@@ -175,24 +555,29 @@ index 119bc3c2d20ea7d681f0b579f9d91ad46cdc3634..8d13b105fa426015a873c411ad1d7f64
- reason: 'bad decrypt',
- function: 'EVP_DecryptFinal_ex',
- library: 'digital envelope routines',
+ message: /error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt|error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT/,
+ code: /ERR_OSSL(_EVP)?_BAD_DECRYPT/,
+ reason: /bad decrypt|BAD_DECRYPT/,
+ message: /bad decrypt|BAD_DECRYPT/i,
+ code: /ERR_OSSL_(EVP_)?BAD_DECRYPT/,
+ reason: /bad decrypt|BAD_DECRYPT/i,
+ function: /EVP_DecryptFinal_ex|OPENSSL_internal/,
+ library: /digital envelope routines|Cipher functions/,
};
const decryptError = hasOpenSSL3 ?
@@ -223,7 +222,7 @@ function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
}, bufferToEncrypt);
@@ -325,9 +324,12 @@ function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
}
test_rsa('RSA_NO_PADDING');
-test_rsa('RSA_PKCS1_PADDING');
test_rsa('RSA_PKCS1_OAEP_PADDING');
- if (padding === constants.RSA_PKCS1_PADDING) {
+ if (!process.features.openssl_is_boringssl) {
if (!process.config.variables.node_shared_openssl) {
// TODO(richardlau) remove check and else branch after deps/openssl
// is upgraded.
@@ -489,7 +488,7 @@ assert.throws(() => {
+if (!process.features.openssl_is_boringssl) {
+ test_rsa('RSA_PKCS1_PADDING');
+}
+
// Test OAEP with different hash functions.
test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1');
test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined);
@@ -489,7 +491,7 @@ assert.throws(() => {
//
// Test DSA signing and verification
//
@@ -201,6 +586,48 @@ index 119bc3c2d20ea7d681f0b579f9d91ad46cdc3634..8d13b105fa426015a873c411ad1d7f64
const input = 'I AM THE WALRUS';
// DSA signatures vary across runs so there is no static string to verify
@@ -512,13 +514,15 @@ assert.throws(() => {
verify2.update(input);
assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true);
+} else {
+ common.printSkipMessage('Skipping unsupported DSA test case');
}
//
// Test DSA signing and verification with PKCS#8 private key
//
-{
+if (!process.features.openssl_is_boringssl) {
const input = 'I AM THE WALRUS';
// DSA signatures vary across runs so there is no static string to verify
@@ -531,6 +535,8 @@ assert.throws(() => {
verify.update(input);
assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
+} else {
+ common.printSkipMessage('Skipping unsupported DSA test case');
}
@@ -547,7 +553,7 @@ const input = 'I AM THE WALRUS';
}, decryptPrivateKeyError);
}
-{
+if (!process.features.openssl_is_boringssl) {
// DSA signatures vary across runs so there is no static string to verify
// against.
const sign = crypto.createSign('SHA1');
@@ -559,4 +565,6 @@ const input = 'I AM THE WALRUS';
verify.update(input);
assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
+} else {
+ common.printSkipMessage('Skipping unsupported DSA test case');
}
diff --git a/test/parallel/test-crypto-scrypt.js b/test/parallel/test-crypto-scrypt.js
index eafdfe392bde8eb1fde1dc7e7e9ae51682c74b87..2907e0175379266c90acb9df829d10283bd46652 100644
--- a/test/parallel/test-crypto-scrypt.js
@@ -273,7 +700,7 @@ index a66f0a94efd7c952c1d2320fbc7a39fe3a88a8a1..dc5846db0e3dcf8f7cb5f7efcdbc81c1
for (const [file, length] of keys) {
const privKey = fixtures.readKey(file);
diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js
index 84111740cd9ef6425b747e24e984e66e46b0b2ef..b1621d310536fae3fdec91a6a9d275ec8fc99a98 100644
index 84111740cd9ef6425b747e24e984e66e46b0b2ef..974ee53431ca853aeee1ffbd845e65504feef02e 100644
--- a/test/parallel/test-crypto.js
+++ b/test/parallel/test-crypto.js
@@ -62,7 +62,7 @@ assert.throws(() => {
@@ -312,81 +739,297 @@ index 84111740cd9ef6425b747e24e984e66e46b0b2ef..b1621d310536fae3fdec91a6a9d275ec
validateList(crypto.getHashes());
// Make sure all of the hashes are supported by OpenSSL
for (const algo of crypto.getHashes())
@@ -197,6 +195,7 @@ assert.throws(
@@ -197,61 +195,63 @@ assert.throws(
}
);
-assert.throws(() => {
- const priv = [
- '-----BEGIN RSA PRIVATE KEY-----',
- 'MIGrAgEAAiEA+3z+1QNF2/unumadiwEr+C5vfhezsb3hp4jAnCNRpPcCAwEAAQIgQNriSQK4',
- 'EFwczDhMZp2dvbcz7OUUyt36z3S4usFPHSECEQD/41K7SujrstBfoCPzwC1xAhEA+5kt4BJy',
- 'eKN7LggbF3Dk5wIQN6SL+fQ5H/+7NgARsVBp0QIRANxYRukavs4QvuyNhMx+vrkCEQCbf6j/',
- 'Ig6/HueCK/0Jkmp+',
- '-----END RSA PRIVATE KEY-----',
- '',
- ].join('\n');
- crypto.createSign('SHA256').update('test').sign(priv);
-}, (err) => {
- if (!hasOpenSSL3)
- assert.ok(!('opensslErrorStack' in err));
- assert.throws(() => { throw err; }, hasOpenSSL3 ? {
- name: 'Error',
- message: 'error:02000070:rsa routines::digest too big for rsa key',
- library: 'rsa routines',
- } : {
- name: 'Error',
- message: /routines:RSA_sign:digest too big for rsa key$/,
- library: /rsa routines/i,
- function: 'RSA_sign',
- reason: /digest[\s_]too[\s_]big[\s_]for[\s_]rsa[\s_]key/i,
- code: 'ERR_OSSL_RSA_DIGEST_TOO_BIG_FOR_RSA_KEY'
- });
- return true;
-});
-
-if (!hasOpenSSL3) {
+if (!process.features.openssl_is_boringssl) {
assert.throws(() => {
const priv = [
'-----BEGIN RSA PRIVATE KEY-----',
@@ -253,7 +252,7 @@ if (!hasOpenSSL3) {
assert.throws(() => {
- // The correct header inside `rsa_private_pkcs8_bad.pem` should have been
- // -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----
- // instead of
- // -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----
- const sha1_privateKey = fixtures.readKey('rsa_private_pkcs8_bad.pem',
- 'ascii');
- // This would inject errors onto OpenSSL's error stack
- crypto.createSign('sha1').sign(sha1_privateKey);
+ const priv = [
+ '-----BEGIN RSA PRIVATE KEY-----',
+ 'MIGrAgEAAiEA+3z+1QNF2/unumadiwEr+C5vfhezsb3hp4jAnCNRpPcCAwEAAQIgQNriSQK4',
+ 'EFwczDhMZp2dvbcz7OUUyt36z3S4usFPHSECEQD/41K7SujrstBfoCPzwC1xAhEA+5kt4BJy',
+ 'eKN7LggbF3Dk5wIQN6SL+fQ5H/+7NgARsVBp0QIRANxYRukavs4QvuyNhMx+vrkCEQCbf6j/',
+ 'Ig6/HueCK/0Jkmp+',
+ '-----END RSA PRIVATE KEY-----',
+ '',
+ ].join('\n');
+ crypto.createSign('SHA256').update('test').sign(priv);
}, (err) => {
- // Do the standard checks, but then do some custom checks afterwards.
- assert.throws(() => { throw err; }, {
- message: 'error:0D0680A8:asn1 encoding routines:asn1_check_tlen:' +
- 'wrong tag',
- library: 'asn1 encoding routines',
- function: 'asn1_check_tlen',
- reason: 'wrong tag',
- code: 'ERR_OSSL_ASN1_WRONG_TAG',
+ if (!hasOpenSSL3)
+ assert.ok(!('opensslErrorStack' in err));
+ assert.throws(() => { throw err; }, hasOpenSSL3 ? {
+ name: 'Error',
+ message: 'error:02000070:rsa routines::digest too big for rsa key',
+ library: 'rsa routines',
+ } : {
+ name: 'Error',
+ message: /routines:RSA_sign:digest too big for rsa key$/,
+ library: /rsa routines/i,
+ function: 'RSA_sign',
+ reason: /digest[\s_]too[\s_]big[\s_]for[\s_]rsa[\s_]key/i,
+ code: 'ERR_OSSL_RSA_DIGEST_TOO_BIG_FOR_RSA_KEY'
});
- // Throws crypto error, so there is an opensslErrorStack property.
- // The openSSL stack should have content.
- assert(Array.isArray(err.opensslErrorStack));
- assert(err.opensslErrorStack.length > 0);
return true;
});
+
+ if (!hasOpenSSL3) {
+ assert.throws(() => {
+ // The correct header inside `rsa_private_pkcs8_bad.pem` should have been
+ // -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----
+ // instead of
+ // -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----
+ const sha1_privateKey = fixtures.readKey('rsa_private_pkcs8_bad.pem',
+ 'ascii');
+ // This would inject errors onto OpenSSL's error stack
+ crypto.createSign('sha1').sign(sha1_privateKey);
+ }, (err) => {
+ // Do the standard checks, but then do some custom checks afterwards.
+ assert.throws(() => { throw err; }, {
+ message: 'error:0D0680A8:asn1 encoding routines:asn1_check_tlen:' +
+ 'wrong tag',
+ library: 'asn1 encoding routines',
+ function: 'asn1_check_tlen',
+ reason: 'wrong tag',
+ code: 'ERR_OSSL_ASN1_WRONG_TAG',
+ });
+ // Throws crypto error, so there is an opensslErrorStack property.
+ // The openSSL stack should have content.
+ assert(Array.isArray(err.opensslErrorStack));
+ assert(err.opensslErrorStack.length > 0);
+ return true;
+ });
+ }
}
-
+}
// Make sure memory isn't released before being returned
console.log(crypto.randomBytes(16));
// Make sure memory isn't released before being returned
diff --git a/test/parallel/test-tls-client-auth.js b/test/parallel/test-tls-client-auth.js
index b347c0a88df571296127985f8e7b70de66726cc0..66465783d344dab1330069e36577d41fc75db962 100644
--- a/test/parallel/test-tls-client-auth.js
+++ b/test/parallel/test-tls-client-auth.js
@@ -112,7 +112,7 @@ if (tls.DEFAULT_MAX_VERSION === 'TLSv1.3') connect({
// and sends a fatal Alert to the client that the client discovers there has
// been a fatal error.
pair.client.conn.once('error', common.mustCall((err) => {
- assert.strictEqual(err.code, 'ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED');
+ //assert.strictEqual(err.code, 'ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED');
cleanup();
}));
});
diff --git a/test/parallel/test-tls-peer-certificate.js b/test/parallel/test-tls-peer-certificate.js
index 41e3c883d950e074dffcdd6df888eaf47696039c..304724b564956ff3c38cb42793141ddcc57dfd75 100644
--- a/test/parallel/test-tls-peer-certificate.js
+++ b/test/parallel/test-tls-peer-certificate.js
@@ -55,7 +55,7 @@ connect({
assert.strictEqual(peerCert.ca, false);
assert.strictEqual(peerCert.issuerCertificate.ca, true);
assert.strictEqual(peerCert.subject.emailAddress, 'ry@tinyclouds.org');
- assert.strictEqual(peerCert.serialNumber, '147D36C1C2F74206DE9FAB5F2226D78ADB00A426');
+ assert.match(peerCert.serialNumber, /147D36C1C2F74206DE9FAB5F2226D78ADB00A426/i);
assert.strictEqual(peerCert.exponent, '0x10001');
assert.strictEqual(peerCert.bits, 2048);
// The conversion to bits is odd because modulus isn't a buffer, its a hex
@@ -95,7 +95,7 @@ connect({
const issuer = peerCert.issuerCertificate;
assert.strictEqual(issuer.issuerCertificate, issuer);
- assert.strictEqual(issuer.serialNumber, '4AB16C8DFD6A7D0D2DFCABDF9C4B0E92C6AD0229');
+ assert.match(issuer.serialNumber, /4AB16C8DFD6A7D0D2DFCABDF9C4B0E92C6AD0229/i);
return cleanup();
});
@@ -114,7 +114,7 @@ connect({
assert.ok(peerCert.issuerCertificate);
assert.strictEqual(peerCert.subject.emailAddress, 'ry@tinyclouds.org');
- assert.strictEqual(peerCert.serialNumber, '32E8197681DA33185867B52885F678BFDBA51727');
+ assert.match(peerCert.serialNumber, /32E8197681DA33185867B52885F678BFDBA51727/i);
assert.strictEqual(peerCert.exponent, undefined);
assert.strictEqual(peerCert.pubKey, undefined);
assert.strictEqual(peerCert.modulus, undefined);
@@ -146,7 +146,6 @@ connect({
const issuer = peerCert.issuerCertificate;
assert.strictEqual(issuer.issuerCertificate, issuer);
- assert.strictEqual(issuer.serialNumber, '32E8197681DA33185867B52885F678BFDBA51727');
-
+ assert.match(issuer.serialNumber, /32E8197681DA33185867B52885F678BFDBA51727/i);
return cleanup();
});
diff --git a/test/parallel/test-tls-pfx-authorizationerror.js b/test/parallel/test-tls-pfx-authorizationerror.js
index eb705d591ef23a90bd78d52797fd1a58bc84a7dd..da428f1320e9e7bd1683724806a7438ed5aa38cc 100644
--- a/test/parallel/test-tls-pfx-authorizationerror.js
+++ b/test/parallel/test-tls-pfx-authorizationerror.js
@@ -22,13 +22,13 @@ const server = tls
rejectUnauthorized: false
},
common.mustCall(function(c) {
- assert.strictEqual(c.getPeerCertificate().serialNumber,
- '147D36C1C2F74206DE9FAB5F2226D78ADB00A426');
+ assert.match(c.getPeerCertificate().serialNumber,
+ /147D36C1C2F74206DE9FAB5F2226D78ADB00A426/i);
assert.strictEqual(c.authorizationError, null);
c.end();
})
)
- .listen(0, function() {
+ .listen(0, common.mustCall(function() {
const client = tls.connect(
{
port: this.address().port,
@@ -36,16 +36,16 @@ const server = tls
passphrase: 'sample',
rejectUnauthorized: false
},
- function() {
+ common.mustCall(() => {
for (let i = 0; i < 10; ++i) {
// Calling this repeatedly is a regression test that verifies
// that .getCertificate() does not accidentally decrease the
// reference count of the X509* certificate on the native side.
- assert.strictEqual(client.getCertificate().serialNumber,
- '147D36C1C2F74206DE9FAB5F2226D78ADB00A426');
+ assert.match(client.getCertificate().serialNumber,
+ /147D36C1C2F74206DE9FAB5F2226D78ADB00A426/i);
}
client.end();
server.close();
- }
+ }),
);
- });
+ }));
diff --git a/test/parallel/test-tls-set-sigalgs.js b/test/parallel/test-tls-set-sigalgs.js
index 985ca13ba2ac7d58f87c263c7654c4f4087efddf..21c199bdb12739f82a075c4e10e08faf8c587cf4 100644
--- a/test/parallel/test-tls-set-sigalgs.js
+++ b/test/parallel/test-tls-set-sigalgs.js
@@ -65,13 +65,14 @@ test('RSA-PSS+SHA256:RSA-PSS+SHA512:ECDSA+SHA256',
'RSA-PSS+SHA256:ECDSA+SHA256',
['RSA-PSS+SHA256', 'ECDSA+SHA256']);
+const cerr = process.features.openssl_is_boringssl ?
+ 'ERR_SSL_NO_COMMON_SIGNATURE_ALGORITHMS' : 'ERR_SSL_NO_SHARED_SIGNATURE_ALGORITHMS';
+
// Do not have shared sigalgs.
const handshakeErr = hasOpenSSL(3, 2) ?
'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE';
test('RSA-PSS+SHA384', 'ECDSA+SHA256',
- undefined, handshakeErr,
- 'ERR_SSL_NO_SHARED_SIGNATURE_ALGORITHMS');
+ undefined, handshakeErr, cerr);
test('RSA-PSS+SHA384:ECDSA+SHA256', 'ECDSA+SHA384:RSA-PSS+SHA256',
- undefined, handshakeErr,
- 'ERR_SSL_NO_SHARED_SIGNATURE_ALGORITHMS');
+ undefined, handshakeErr, cerr);
\ No newline at end of file
diff --git a/test/parallel/test-webcrypto-export-import-cfrg.js b/test/parallel/test-webcrypto-export-import-cfrg.js
index ae203e1005de0ab4370bd611f4f2ae64bb7a9a6a..216ce5fd14001183e7deb2abadc93178e7a18a58 100644
--- a/test/parallel/test-webcrypto-export-import-cfrg.js
+++ b/test/parallel/test-webcrypto-export-import-cfrg.js
@@ -411,7 +411,7 @@ async function testImportRaw({ name, publicUsages }) {
await Promise.all(tests);
})().then(common.mustCall());
-{
+if (!process.features.openssl_is_boringssl) {
const rsaPublic = crypto.createPublicKey(
fixtures.readKey('rsa_public_2048.pem'));
const rsaPrivate = crypto.createPrivateKey(
@@ -432,4 +432,6 @@ async function testImportRaw({ name, publicUsages }) {
{ name },
true, privateUsages), { message: /Invalid key type/ }).then(common.mustCall());
}
+} else {
+ common.printSkipMessage('Skipping RSA key import tests');
}
diff --git a/test/parallel/test-webcrypto-wrap-unwrap.js b/test/parallel/test-webcrypto-wrap-unwrap.js
index bd788ec4ed88289d35798b8af8c9490a68e081a2..1a5477ba928bce93320f8056db02e1a7b8ddcdf3 100644
index bd788ec4ed88289d35798b8af8c9490a68e081a2..c6a6f33490595faabaefc9b58afdd813f0887258 100644
--- a/test/parallel/test-webcrypto-wrap-unwrap.js
+++ b/test/parallel/test-webcrypto-wrap-unwrap.js
@@ -20,14 +20,15 @@ const kWrappingData = {
wrap: { label: new Uint8Array(8) },
pair: true
},
- 'AES-CTR': {
+ 'AES-CBC': {
generate: { length: 128 },
- wrap: { counter: new Uint8Array(16), length: 64 },
+ wrap: { iv: new Uint8Array(16) },
pair: false
},
- 'AES-CBC': {
+ /*
+ 'AES-CTR': {
generate: { length: 128 },
- wrap: { iv: new Uint8Array(16) },
+ wrap: { counter: new Uint8Array(16), length: 64 },
pair: false
},
'AES-GCM': {
@@ -46,30 +47,9 @@ if (!process.features.openssl_is_boringssl) {
generate: { length: 128 },
wrap: { },
pair: false
- };
- kWrappingData['ChaCha20-Poly1305'] = {
- wrap: {
- iv: new Uint8Array(12),
- additionalData: new Uint8Array(16),
- tagLength: 128
@@ -179,13 +179,6 @@ async function generateKeysToWrap() {
usages: ['encrypt', 'decrypt'],
pair: false,
},
- {
- algorithm: {
- name: 'ChaCha20-Poly1305'
- },
- usages: ['encrypt', 'decrypt'],
- pair: false,
- },
- pair: false
- };
-} else {
- common.printSkipMessage('Skipping unsupported AES-KW test case');
-}
-
-if (hasOpenSSL(3)) {
- kWrappingData['AES-OCB'] = {
- generate: { length: 128 },
- wrap: {
- iv: new Uint8Array(15),
- additionalData: new Uint8Array(16),
- tagLength: 128
- },
- pair: false
- };
-}
+ }
+ */
+};
{
algorithm: {
name: 'HMAC',
@@ -210,6 +203,18 @@ async function generateKeysToWrap() {
common.printSkipMessage('Skipping unsupported AES-KW test case');
}
function generateWrappingKeys() {
return Promise.all(Object.keys(kWrappingData).map(async (name) => {
+ if (!process.features.openssl_is_boringssl) {
+ parameters.push({
+ algorithm: {
+ name: 'ChaCha20-Poly1305'
+ },
+ usages: ['encrypt', 'decrypt'],
+ pair: false,
+ });
+ } else {
+ common.printSkipMessage('Skipping unsupported ChaCha20-Poly1305 test case');
+ }
+
if (hasOpenSSL(3, 5)) {
for (const name of ['ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']) {
parameters.push({
diff --git a/test/parallel/test-x509-escaping.js b/test/parallel/test-x509-escaping.js
index c8fc4abbb108a6d6849e8452d97d29187da2ebe6..825ba4c8dce775f401080a0522565bb7a087bcc3 100644
--- a/test/parallel/test-x509-escaping.js

View File

@@ -18,10 +18,10 @@ This can be removed when Node.js upgrades to a version of V8 containing CLs
from the above issue.
diff --git a/src/api/environment.cc b/src/api/environment.cc
index 5c8bc870dcf2e974036cf3bcb60fd288e59045d9..0f19cb09ea0963a9c505c51f89d1c7a939f2730b 100644
index 53f05293bd94e159dfedf48735989e668acdd08e..d753ad6c6b49b26b86920124f7ac90c1e052638e 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -331,6 +331,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
@@ -323,6 +323,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
MultiIsolatePlatform* platform,
const SnapshotData* snapshot_data,
const IsolateSettings& settings) {
@@ -32,7 +32,7 @@ index 5c8bc870dcf2e974036cf3bcb60fd288e59045d9..0f19cb09ea0963a9c505c51f89d1c7a9
IsolateGroup group = GetOrCreateIsolateGroup();
Isolate* isolate = Isolate::Allocate(group);
if (isolate == nullptr) return nullptr;
@@ -387,9 +391,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator,
@@ -373,9 +377,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator,
uv_loop_t* event_loop,
MultiIsolatePlatform* platform,
const EmbedderSnapshotData* snapshot_data,

View File

@@ -6,75 +6,6 @@ Subject: support V8 sandboxed pointers
This refactors several allocators to allocate within the V8 memory cage,
allowing them to be compatible with the V8_SANDBOXED_POINTERS feature.
diff --git a/src/api/environment.cc b/src/api/environment.cc
index 53f05293bd94e159dfedf48735989e668acdd08e..5c8bc870dcf2e974036cf3bcb60fd288e59045d9 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -111,6 +111,14 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
return result;
}
+NodeArrayBufferAllocator::NodeArrayBufferAllocator() {
+ zero_fill_field_ = static_cast<uint32_t*>(allocator_->Allocate(sizeof(*zero_fill_field_)));
+}
+
+NodeArrayBufferAllocator::~NodeArrayBufferAllocator() {
+ allocator_->Free(zero_fill_field_, sizeof(*zero_fill_field_));
+}
+
void* NodeArrayBufferAllocator::Allocate(size_t size) {
void* ret;
COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.ZeroFilled");
@@ -337,6 +345,12 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
// but also otherwise just doesn't work, and the only real alternative
// is disabling shared-readonly-heap mode altogether.
static Isolate::CreateParams first_params = *params;
+ // Clear allocator pointers to prevent use-after-free during static
+ // destruction. The static first_params can outlive V8's internal
+ // allocator systems, causing crashes when its destructor tries to
+ // free resources after V8 has shut down.
+ first_params.array_buffer_allocator = nullptr;
+ first_params.array_buffer_allocator_shared.reset();
params->snapshot_blob = first_params.snapshot_blob;
params->external_references = first_params.external_references;
}
diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc
index 46a7d1396dc1a175ae99f4e403721f1730fdd320..bbb0abb3b9563074d350578e0f5a8fa211046b17 100644
--- a/src/crypto/crypto_dh.cc
+++ b/src/crypto/crypto_dh.cc
@@ -61,17 +61,22 @@ MaybeLocal<Value> DataPointerToBuffer(Environment* env, DataPointer&& data) {
bool secure;
};
#ifdef V8_ENABLE_SANDBOX
- auto backing = ArrayBuffer::NewBackingStore(
- env->isolate(),
- data.size(),
- BackingStoreInitializationMode::kUninitialized,
- BackingStoreOnFailureMode::kReturnNull);
- if (!backing) {
- THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
- return MaybeLocal<Value>();
- }
+ std::unique_ptr<v8::BackingStore> backing;
if (data.size() > 0) {
- memcpy(backing->Data(), data.get(), data.size());
+ std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
+ void* v8_data = allocator->Allocate(data.size());
+ CHECK(v8_data);
+ memcpy(v8_data, data.get(), data.size());
+ backing = ArrayBuffer::NewBackingStore(
+ v8_data,
+ data.size(),
+ [](void* data, size_t length, void*) {
+ std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
+ allocator->Free(data, length);
+ }, nullptr);
+ } else {
+ NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
+ backing = v8::ArrayBuffer::NewBackingStore(env->isolate(), data.size());
}
#else
auto backing = ArrayBuffer::NewBackingStore(
diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
index 12b0d804c6f1d4998b85160b0aac8eb7a3b5576b..27bd93769233dc65a064710db4095d9cdc3a8b1a 100644
--- a/src/crypto/crypto_util.cc
@@ -189,110 +120,11 @@ index b30297eac08ad9587642b723f91d7e3b954294d4..4c5427596d1c90d3a413cdd9ff4f1151
#else
auto backing = ArrayBuffer::NewBackingStore(
mem->data,
diff --git a/src/env-inl.h b/src/env-inl.h
index 97c43afb487b58c0c77bd59b4a6b6d7a13690053..5dfbd564d5bbd22ebf3b529a07b73e85cbe51986 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -44,6 +44,16 @@
namespace node {
+NoArrayBufferZeroFillScope::NoArrayBufferZeroFillScope(
+ IsolateData* isolate_data)
+ : node_allocator_(isolate_data->node_allocator()) {
+ if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 0;
+}
+
+NoArrayBufferZeroFillScope::~NoArrayBufferZeroFillScope() {
+ if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 1;
+}
+
inline v8::Isolate* IsolateData::isolate() const {
return isolate_;
}
diff --git a/src/env.h b/src/env.h
index 84a650885a79bc5c49efdc26f62ec8db48de775c..ba442937bf0d7831c9a84b5a57211e9f90c81705 100644
--- a/src/env.h
+++ b/src/env.h
@@ -111,6 +111,19 @@ class ModuleWrap;
class Environment;
class Realm;
+// Disables zero-filling for ArrayBuffer allocations in this scope. This is
+// similar to how we implement Buffer.allocUnsafe() in JS land.
+class NoArrayBufferZeroFillScope {
+ public:
+ inline explicit NoArrayBufferZeroFillScope(IsolateData* isolate_data);
+ inline ~NoArrayBufferZeroFillScope();
+
+ private:
+ NodeArrayBufferAllocator* node_allocator_;
+
+ friend class Environment;
+};
+
struct IsolateDataSerializeInfo {
std::vector<SnapshotIndex> primitive_values;
std::vector<PropInfo> template_values;
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index 49df0b4284748effb27b05ecd69f05699dd8be75..eb104ab80fd2052d1523cb2f2ecdb6d23ac3be98 100644
index 49df0b4284748effb27b05ecd69f05699dd8be75..77da37881bb351249c45e405289193c10d083e0c 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -81,6 +81,7 @@ using v8::SharedArrayBuffer;
using v8::String;
using v8::Uint32;
using v8::Uint8Array;
+using v8::Uint32Array;
using v8::Value;
namespace {
@@ -1243,6 +1244,45 @@ void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) {
realm->set_buffer_prototype_object(proto);
}
+void GetZeroFillToggle(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator();
+ Local<ArrayBuffer> ab;
+ // It can be a nullptr when running inside an isolate where we
+ // do not own the ArrayBuffer allocator.
+ if (allocator == nullptr || env->isolate_data()->is_building_snapshot()) {
+ // Create a dummy Uint32Array - the JS land can only toggle the C++ land
+ // setting when the allocator uses our toggle. With this the toggle in JS
+ // land results in no-ops.
+ // When building a snapshot, just use a dummy toggle as well to avoid
+ // introducing the dynamic external reference. We'll re-initialize the
+ // toggle with a real one connected to the C++ allocator after snapshot
+ // deserialization.
+
+ ab = ArrayBuffer::New(env->isolate(), sizeof(uint32_t));
+ } else {
+ // TODO(joyeecheung): save ab->GetBackingStore()->Data() in the Node.js
+ // array buffer allocator and include it into the C++ toggle while the
+ // Environment is still alive.
+ uint32_t* zero_fill_field = allocator->zero_fill_field();
+ std::unique_ptr<BackingStore> backing =
+ ArrayBuffer::NewBackingStore(zero_fill_field,
+ sizeof(*zero_fill_field),
+ [](void*, size_t, void*) {},
+ nullptr);
+ ab = ArrayBuffer::New(env->isolate(), std::move(backing));
+ }
+
+ if (ab->SetPrivate(env->context(),
+ env->untransferable_object_private_symbol(),
+ True(env->isolate()))
+ .IsNothing()) {
+ return;
+ }
+
+ args.GetReturnValue().Set(Uint32Array::New(ab, 0, 1));
+}
+
static void Btoa(const FunctionCallbackInfo<Value>& args) {
CHECK_EQ(args.Length(), 1);
Environment* env = Environment::GetCurrent(args);
@@ -1420,7 +1460,7 @@ inline size_t CheckNumberToSize(Local<Value> number) {
@@ -1420,7 +1420,7 @@ inline size_t CheckNumberToSize(Local<Value> number) {
CHECK(value >= 0 && value < maxSize);
size_t size = static_cast<size_t>(value);
#ifdef V8_ENABLE_SANDBOX
@@ -301,22 +133,6 @@ index 49df0b4284748effb27b05ecd69f05699dd8be75..eb104ab80fd2052d1523cb2f2ecdb6d2
#endif
return size;
}
@@ -1638,6 +1678,7 @@ void Initialize(Local<Object> target,
"utf8WriteStatic",
SlowWriteString<UTF8>,
&fast_write_string_utf8);
+ SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle);
}
} // anonymous namespace
@@ -1686,6 +1727,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(StringWrite<HEX>);
registry->Register(StringWrite<UCS2>);
registry->Register(StringWrite<UTF8>);
+ registry->Register(GetZeroFillToggle);
registry->Register(CopyArrayBuffer);
registry->Register(CreateUnsafeArrayBuffer);
diff --git a/src/node_i18n.cc b/src/node_i18n.cc
index 3c4f419aa29470b3280174b58680b9421b0340b5..3b24ad2a2316f89d98b067e2c13988f87a9a00d2 100644
--- a/src/node_i18n.cc
@@ -357,28 +173,6 @@ index 3c4f419aa29470b3280174b58680b9421b0340b5..3b24ad2a2316f89d98b067e2c13988f8
}
constexpr const char* EncodingName(const enum encoding encoding) {
diff --git a/src/node_internals.h b/src/node_internals.h
index 61a58b6ccfb26efefd6d3b61a1c8741f9550ae8d..29d1ecc2b209c9c3c2e956263ba2d57fb688b34c 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -124,6 +124,9 @@ v8::MaybeLocal<v8::Object> InitializePrivateSymbols(
class NodeArrayBufferAllocator : public ArrayBufferAllocator {
public:
+ NodeArrayBufferAllocator();
+ ~NodeArrayBufferAllocator() override;
+ inline uint32_t* zero_fill_field() { return zero_fill_field_; }
void* Allocate(size_t size) override; // Defined in src/node.cc
void* AllocateUninitialized(size_t size) override;
void Free(void* data, size_t size) override;
@@ -140,6 +143,7 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
}
private:
+ uint32_t* zero_fill_field_ = nullptr; // Boolean but exposed as uint32 to JS land.
std::atomic<size_t> total_mem_usage_ {0};
// Delegate to V8's allocator for compatibility with the V8 memory cage.
diff --git a/src/node_serdes.cc b/src/node_serdes.cc
index 00fcd4b6afccce47ff21c4447d9cd60f25c11835..5f96ee2051e5339456185efddb149c4d43093f31 100644
--- a/src/node_serdes.cc

View File

@@ -8,24 +8,10 @@
"parallel/test-cluster-primary-error",
"parallel/test-cluster-primary-kill",
"parallel/test-config-file",
"parallel/test-crypto-aes-wrap",
"parallel/test-crypto-authenticated",
"parallel/test-crypto-authenticated-stream",
"parallel/test-crypto-default-shake-lengths",
"parallel/test-crypto-des3-wrap",
"parallel/test-crypto-dh-group-setters",
"parallel/test-crypto-dh-modp2",
"parallel/test-crypto-dh-modp2-views",
"parallel/test-crypto-dh-stateless",
"parallel/test-crypto-ecb",
"parallel/test-crypto-fips",
"parallel/test-crypto-key-objects",
"parallel/test-crypto-keygen",
"parallel/test-crypto-keygen-deprecation",
"parallel/test-crypto-rsa-dsa",
"parallel/test-crypto-padding-aes256",
"parallel/test-crypto-secure-heap",
"parallel/test-dgram-send-cb-quelches-error",
"parallel/test-domain-error-types",
"parallel/test-fs-utimes-y2K38",
"parallel/test-http2-clean-output",
@@ -44,34 +30,10 @@
"parallel/test-os-checked-function",
"parallel/test-process-versions",
"parallel/test-process-get-builtin",
"parallel/test-repl",
"parallel/test-repl-mode",
"parallel/test-repl-underscore",
"parallel/test-shadow-realm-custom-loaders",
"parallel/test-snapshot-api",
"parallel/test-snapshot-argv1",
"parallel/test-snapshot-basic",
"parallel/test-snapshot-cjs-main",
"parallel/test-snapshot-cwd",
"parallel/test-snapshot-console",
"parallel/test-snapshot-config",
"parallel/test-snapshot-dns-lookup-localhost",
"parallel/test-snapshot-dns-lookup-localhost-promise",
"parallel/test-snapshot-dns-resolve-localhost",
"parallel/test-snapshot-dns-resolve-localhost-promise",
"parallel/test-snapshot-error",
"parallel/test-snapshot-eval",
"parallel/test-snapshot-gzip",
"parallel/test-snapshot-incompatible",
"parallel/test-snapshot-namespaced-builtin",
"parallel/test-snapshot-net",
"parallel/test-snapshot-reproducible",
"parallel/test-snapshot-stack-trace-limit",
"parallel/test-snapshot-stack-trace-limit-mutation",
"parallel/test-snapshot-typescript",
"parallel/test-snapshot-umd",
"parallel/test-snapshot-warning",
"parallel/test-snapshot-weak-reference",
"parallel/test-snapshot-worker",
"parallel/test-snapshot",
"parallel/test-strace-openat-openssl",
"parallel/test-sqlite-backup",
"parallel/test-max-old-space-size-percentage",
@@ -107,15 +69,12 @@
"parallel/test-tls-multi-key",
"parallel/test-tls-multi-pfx",
"parallel/test-tls-no-cert-required",
"parallel/test-tls-no-sslv23.js",
"parallel/test-tls-no-sslv23",
"parallel/test-tls-options-boolean-check",
"parallel/test-tls-passphrase",
"parallel/test-tls-peer-certificate",
"parallel/test-tls-pfx-authorizationerror",
"parallel/test-tls-psk-alpn-callback-exception-handling",
"parallel/test-tls-psk-circuit",
"parallel/test-tls-reduced-SECLEVEL-in-cipher",
"parallel/test-tls-root-certificates",
"parallel/test-tls-server-failed-handshake-emits-clienterror",
"parallel/test-tls-set-ciphers",
"parallel/test-tls-set-ciphers-error",
@@ -125,45 +84,13 @@
"parallel/test-tls-socket-failed-handshake-emits-error",
"parallel/test-tls-ticket",
"parallel/test-tls-ticket-cluster",
"parallel/test-trace-events-all",
"parallel/test-trace-events-async-hooks",
"parallel/test-trace-events-binding",
"parallel/test-trace-events-bootstrap",
"parallel/test-trace-events-category-used",
"parallel/test-trace-events-console",
"parallel/test-trace-events-dynamic-enable",
"parallel/test-trace-events-dynamic-enable-workers-disabled",
"parallel/test-trace-events-environment",
"parallel/test-trace-events-file-pattern",
"parallel/test-trace-events-fs-async",
"parallel/test-trace-events-fs-sync",
"parallel/test-trace-events-http",
"parallel/test-trace-events-metadata",
"parallel/test-trace-events-net",
"parallel/test-trace-events-none",
"parallel/test-trace-events-process-exit",
"parallel/test-trace-events-promises",
"parallel/test-trace-events-threadpool",
"parallel/test-trace-events-v8",
"parallel/test-trace-events-vm",
"parallel/test-trace-events-worker-metadata",
"parallel/test-trace-events",
"parallel/test-tz-version",
"parallel/test-webcrypto-derivebits-cfrg",
"parallel/test-webcrypto-derivekey-cfrg",
"parallel/test-webcrypto-encrypt-decrypt",
"parallel/test-webcrypto-encrypt-decrypt-aes",
"parallel/test-webcrypto-encrypt-decrypt-rsa",
"parallel/test-webcrypto-export-import-cfrg",
"parallel/test-webcrypto-keygen",
"parallel/test-webcrypto-sign-verify-eddsa",
"parallel/test-webcrypto-wrap-unwrap",
"parallel/test-worker-no-sab",
"parallel/test-worker-resource-limits",
"parallel/test-zlib-unused-weak",
"report/test-report-fatalerror-oomerror-compact",
"report/test-report-fatalerror-oomerror-directory",
"report/test-report-fatalerror-oomerror-filename",
"report/test-report-fatalerror-oomerror-set",
"report/test-report-fatalerror-oomerror",
"report/test-report-getreport",
"report/test-report-signal",
"report/test-report-uncaught-exception",
@@ -173,12 +100,6 @@
"report/test-report-writereport",
"sea/test-single-executable-blob-config",
"sea/test-single-executable-blob-config-errors",
"sequential/test-single-executable-application",
"sequential/test-single-executable-application-disable-experimental-sea-warning",
"sequential/test-single-executable-application-empty",
"sequential/test-single-executable-application-snapshot",
"sequential/test-single-executable-application-snapshot-and-code-cache",
"sequential/test-single-executable-application-use-code-cache",
"sequential/test-tls-connect",
"wpt/test-webcrypto",
"wasm-allocation/test-wasm-allocation"

View File

@@ -118,7 +118,7 @@ async function runClangTidy (
fix: boolean = false
): Promise<boolean> {
const cmd = path.resolve(LLVM_BIN, 'clang-tidy');
const args = [`-p=${outDir}`];
const args = [`-p=${outDir}`, "-header-filter=''"];
if (!process.env.CI) args.push('--use-color');
if (fix) args.push('--fix');

View File

@@ -88,7 +88,7 @@ async function main () {
}
const versionString = `v${args.electronVersion}`;
console.log(`Running against Electron ${versionString.green}`);
console.log(`Running against Electron ${chalk.green(versionString)}`);
}
const [lastSpecHash, lastSpecInstallHash] = loadLastSpecHash();

View File

@@ -43,10 +43,13 @@
#include "shell/browser/electron_gpu_client.h"
#include "shell/browser/feature_list.h"
#include "shell/browser/relauncher.h"
#include "shell/common/application_info.h"
#include "shell/common/electron_paths.h"
#include "shell/common/logging.h"
#include "shell/common/options_switches.h"
#include "shell/common/platform_util.h"
#include "shell/common/process_util.h"
#include "shell/common/thread_restrictions.h"
#include "shell/renderer/electron_renderer_client.h"
#include "shell/renderer/electron_sandboxed_renderer_client.h"
#include "shell/utility/electron_content_utility_client.h"
@@ -118,6 +121,100 @@ void InvalidParameterHandler(const wchar_t*,
}
#endif
// TODO(nornagon): move path provider overriding to its own file in
// shell/common
bool ElectronPathProvider(int key, base::FilePath* result) {
bool create_dir = false;
base::FilePath cur;
switch (key) {
case chrome::DIR_USER_DATA:
if (!base::PathService::Get(DIR_APP_DATA, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
GetPossiblyOverriddenApplicationName()));
create_dir = true;
break;
case DIR_CRASH_DUMPS:
if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
return false;
cur = cur.Append(FILE_PATH_LITERAL("Crashpad"));
create_dir = true;
break;
case chrome::DIR_APP_DICTIONARIES:
// TODO(nornagon): can we just default to using Chrome's logic here?
if (!base::PathService::Get(DIR_SESSION_DATA, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe("Dictionaries"));
create_dir = true;
break;
case DIR_SESSION_DATA:
// By default and for backward, equivalent to DIR_USER_DATA.
return base::PathService::Get(chrome::DIR_USER_DATA, result);
case DIR_USER_CACHE: {
#if BUILDFLAG(IS_POSIX)
int parent_key = base::DIR_CACHE;
#else
// On Windows, there's no OS-level centralized location for caches, so
// store the cache in the app data directory.
int parent_key = base::DIR_ROAMING_APP_DATA;
#endif
if (!base::PathService::Get(parent_key, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
GetPossiblyOverriddenApplicationName()));
create_dir = true;
break;
}
#if BUILDFLAG(IS_LINUX)
case DIR_APP_DATA: {
auto env = base::Environment::Create();
cur = base::nix::GetXDGDirectory(
env.get(), base::nix::kXdgConfigHomeEnvVar, base::nix::kDotConfigDir);
break;
}
#endif
#if BUILDFLAG(IS_WIN)
case DIR_RECENT:
if (!platform_util::GetFolderPath(DIR_RECENT, &cur))
return false;
create_dir = true;
break;
#endif
case DIR_APP_LOGS:
#if BUILDFLAG(IS_MAC)
if (!base::PathService::Get(base::DIR_HOME, &cur))
return false;
cur = cur.Append(FILE_PATH_LITERAL("Library"));
cur = cur.Append(FILE_PATH_LITERAL("Logs"));
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
GetPossiblyOverriddenApplicationName()));
#else
if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe("logs"));
#endif
create_dir = true;
break;
default:
return false;
}
// TODO(bauerb): http://crbug.com/259796
ScopedAllowBlockingForElectron allow_blocking;
if (create_dir && !base::PathExists(cur) && !base::CreateDirectory(cur)) {
return false;
}
*result = cur;
return true;
}
void RegisterPathProvider() {
base::PathService::RegisterProvider(ElectronPathProvider, PATH_START,
PATH_END);
}
void ValidateV8Snapshot(v8::StartupData* data) {
if (data->data &&
electron::fuses::IsEmbeddedAsarIntegrityValidationEnabled()) {

View File

@@ -111,8 +111,8 @@ BaseWindow::BaseWindow(v8::Isolate* isolate,
}
// Creates NativeWindow.
window_ = NativeWindow::Create(
options, parent.IsEmpty() ? nullptr : parent->window_.get());
NativeWindow* parent_native = parent.IsEmpty() ? nullptr : parent->window();
window_ = NativeWindow::Create(GetID(), options, parent_native);
window_->AddObserver(this);
SetContentView(View::Create(isolate));
@@ -146,7 +146,6 @@ BaseWindow::~BaseWindow() {
}
void BaseWindow::InitWith(v8::Isolate* isolate, v8::Local<v8::Object> wrapper) {
AttachAsUserData(window_.get());
gin_helper::TrackableObject<BaseWindow>::InitWith(isolate, wrapper);
// We can only append this window to parent window's child windows after this

View File

@@ -336,11 +336,12 @@ void BrowserWindow::BuildPrototype(v8::Isolate* isolate,
// static
v8::Local<v8::Value> BrowserWindow::From(v8::Isolate* isolate,
NativeWindow* native_window) {
auto* existing = TrackableObject::FromWrappedClass(isolate, native_window);
if (existing)
return existing->GetWrapper();
else
return v8::Null(isolate);
if (native_window != nullptr) {
if (auto* base = FromWeakMapID(isolate, native_window->base_window_id()))
return base->GetWrapper();
}
return v8::Null(isolate);
}
} // namespace electron::api

View File

@@ -7,6 +7,8 @@
#include <utility>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/map_util.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
@@ -14,10 +16,10 @@
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/simple_watcher.h"
#include "net/base/net_errors.h"
#include "shell/common/gin_helper/handle.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/key_weak_map.h"
#include "shell/common/node_util.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"
#include "shell/common/node_includes.h"
@@ -29,9 +31,11 @@ namespace {
int g_next_id = 0;
// Map that manages all the DataPipeHolder objects.
KeyWeakMap<std::string>& AllDataPipeHolders() {
static base::NoDestructor<KeyWeakMap<std::string>> weak_map;
return *weak_map.get();
[[nodiscard]] auto& AllDataPipeHolders() {
static base::NoDestructor<
base::flat_map<std::string, cppgc::WeakPersistent<DataPipeHolder>>>
weak_map;
return *weak_map;
}
// Utility class to read from data pipe.
@@ -143,8 +147,9 @@ class DataPipeReader {
} // namespace
gin::DeprecatedWrapperInfo DataPipeHolder::kWrapperInfo = {
gin::kEmbedderNativeGin};
const gin::WrapperInfo DataPipeHolder::kWrapperInfo = {
{gin::kEmbedderNativeGin},
gin::kElectronDataPipeHolder};
DataPipeHolder::DataPipeHolder(const network::DataElement& element)
: id_(base::NumberToString(++g_next_id)) {
@@ -166,30 +171,28 @@ v8::Local<v8::Promise> DataPipeHolder::ReadAll(v8::Isolate* isolate) {
return handle;
}
const char* DataPipeHolder::GetTypeName() {
return "DataPipeHolder";
const gin::WrapperInfo* DataPipeHolder::wrapper_info() const {
return &kWrapperInfo;
}
const char* DataPipeHolder::GetHumanReadableName() const {
return "Electron / DataPipeHolder";
}
// static
gin_helper::Handle<DataPipeHolder> DataPipeHolder::Create(
v8::Isolate* isolate,
const network::DataElement& element) {
auto handle = gin_helper::CreateHandle(isolate, new DataPipeHolder(element));
AllDataPipeHolders().Set(isolate, handle->id(),
handle->GetWrapper(isolate).ToLocalChecked());
return handle;
DataPipeHolder* DataPipeHolder::Create(v8::Isolate* isolate,
const network::DataElement& element) {
auto* holder = cppgc::MakeGarbageCollected<DataPipeHolder>(
isolate->GetCppHeap()->GetAllocationHandle(), element);
AllDataPipeHolders().insert_or_assign(holder->id(), holder);
return holder;
}
// static
gin_helper::Handle<DataPipeHolder> DataPipeHolder::From(v8::Isolate* isolate,
const std::string& id) {
v8::MaybeLocal<v8::Object> object = AllDataPipeHolders().Get(isolate, id);
if (!object.IsEmpty()) {
gin_helper::Handle<DataPipeHolder> handle;
if (gin::ConvertFromV8(isolate, object.ToLocalChecked(), &handle))
return handle;
}
return {};
DataPipeHolder* DataPipeHolder::From(v8::Isolate* isolate,
const std::string_view id) {
auto* found = base::FindOrNull(AllDataPipeHolders(), id);
return found ? found->Get() : nullptr;
}
} // namespace electron::api

View File

@@ -7,31 +7,28 @@
#include <string>
#include "gin/wrappable.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/public/cpp/data_element.h"
#include "services/network/public/mojom/data_pipe_getter.mojom.h"
#include "shell/common/gin_helper/wrappable.h"
namespace gin_helper {
template <typename T>
class Handle;
} // namespace gin_helper
namespace electron::api {
// Retains reference to the data pipe.
class DataPipeHolder final
: public gin_helper::DeprecatedWrappable<DataPipeHolder> {
class DataPipeHolder final : public gin::Wrappable<DataPipeHolder> {
public:
// gin_helper::Wrappable
static gin::DeprecatedWrapperInfo kWrapperInfo;
const char* GetTypeName() override;
// gin::Wrappable
static const gin::WrapperInfo kWrapperInfo;
const gin::WrapperInfo* wrapper_info() const override;
const char* GetHumanReadableName() const override;
static gin_helper::Handle<DataPipeHolder> Create(
v8::Isolate* isolate,
const network::DataElement& element);
static gin_helper::Handle<DataPipeHolder> From(v8::Isolate* isolate,
const std::string& id);
static DataPipeHolder* Create(v8::Isolate* isolate,
const network::DataElement& element);
static DataPipeHolder* From(v8::Isolate* isolate, std::string_view id);
// Make public for cppgc::MakeGarbageCollected.
explicit DataPipeHolder(const network::DataElement& element);
~DataPipeHolder() override;
// Read all data at once.
//
@@ -47,9 +44,6 @@ class DataPipeHolder final
DataPipeHolder& operator=(const DataPipeHolder&) = delete;
private:
explicit DataPipeHolder(const network::DataElement& element);
~DataPipeHolder() override;
std::string id_;
mojo::Remote<network::mojom::DataPipeGetter> data_pipe_;
};

View File

@@ -1,528 +0,0 @@
// Copyright (c) 2025 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/api/electron_api_msix_updater.h"
#include <sstream>
#include <string_view>
#include "base/environment.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "shell/browser/browser.h"
#include "shell/browser/javascript_environment.h"
#include "shell/browser/native_window.h"
#include "shell/browser/window_list.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/error_thrower.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/node_includes.h"
#if BUILDFLAG(IS_WIN)
#include <appmodel.h>
#include <roapi.h>
#include <windows.applicationmodel.h>
#include <windows.foundation.collections.h>
#include <windows.foundation.h>
#include <windows.foundation.metadata.h>
#include <windows.h>
#include <windows.management.deployment.h>
// Use pre-generated C++/WinRT headers from //third_party/nearby instead of the
// SDK's cppwinrt headers, which are missing implementation files.
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.ApplicationModel.h"
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h"
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Metadata.h"
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h"
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.Management.Deployment.h"
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/base.h"
#include "base/win/scoped_com_initializer.h"
#endif
namespace electron {
const bool debug_msix_updater =
base::Environment::Create()->HasVar("ELECTRON_DEBUG_MSIX_UPDATER");
} // namespace electron
namespace {
#if BUILDFLAG(IS_WIN)
// Helper function for debug logging
void DebugLog(std::string_view log_msg) {
if (electron::debug_msix_updater)
LOG(INFO) << std::string(log_msg);
}
// Check if the process has a package identity
bool HasPackageIdentity() {
UINT32 length = 0;
LONG rc = GetCurrentPackageFullName(&length, NULL);
return rc != APPMODEL_ERROR_NO_PACKAGE;
}
// POD struct to hold MSIX update options
struct UpdateMsixOptions {
bool defer_registration = false;
bool developer_mode = false;
bool force_shutdown = false;
bool force_target_shutdown = false;
bool force_update_from_any_version = false;
};
// POD struct to hold package registration options
struct RegisterPackageOptions {
bool force_shutdown = false;
bool force_target_shutdown = false;
bool force_update_from_any_version = false;
};
// Performs MSIX update on IO thread
void DoUpdateMsix(const std::string& package_uri,
UpdateMsixOptions opts,
scoped_refptr<base::SingleThreadTaskRunner> reply_runner,
gin_helper::Promise<void> promise) {
DebugLog("DoUpdateMsix: Starting");
using winrt::Windows::Foundation::AsyncStatus;
using winrt::Windows::Foundation::Uri;
using winrt::Windows::Management::Deployment::AddPackageOptions;
using winrt::Windows::Management::Deployment::DeploymentResult;
using winrt::Windows::Management::Deployment::PackageManager;
std::string error;
std::wstring packageUriString =
std::wstring(package_uri.begin(), package_uri.end());
Uri uri{packageUriString};
PackageManager packageManager;
AddPackageOptions packageOptions;
// Use the pre-parsed options
packageOptions.DeferRegistrationWhenPackagesAreInUse(opts.defer_registration);
packageOptions.DeveloperMode(opts.developer_mode);
packageOptions.ForceAppShutdown(opts.force_shutdown);
packageOptions.ForceTargetAppShutdown(opts.force_target_shutdown);
packageOptions.ForceUpdateFromAnyVersion(opts.force_update_from_any_version);
{
std::ostringstream oss;
oss << "Calling AddPackageByUriAsync... URI: " << package_uri;
DebugLog(oss.str());
}
{
std::ostringstream oss;
oss << "Update options - deferRegistration: " << opts.defer_registration
<< ", developerMode: " << opts.developer_mode
<< ", forceShutdown: " << opts.force_shutdown
<< ", forceTargetShutdown: " << opts.force_target_shutdown
<< ", forceUpdateFromAnyVersion: "
<< opts.force_update_from_any_version;
DebugLog(oss.str());
}
auto deploymentOperation =
packageManager.AddPackageByUriAsync(uri, packageOptions);
if (!deploymentOperation) {
DebugLog("Deployment operation is null");
error =
"Deployment is NULL. See "
"http://go.microsoft.com/fwlink/?LinkId=235160 for diagnosing.";
} else {
if (!opts.force_shutdown && !opts.force_target_shutdown) {
DebugLog("Waiting for deployment...");
deploymentOperation.get();
DebugLog("Deployment finished.");
if (deploymentOperation.Status() == AsyncStatus::Error) {
auto deploymentResult{deploymentOperation.GetResults()};
std::string errorText = winrt::to_string(deploymentResult.ErrorText());
std::string errorCode =
std::to_string(static_cast<int>(deploymentOperation.ErrorCode()));
error = errorText + " (" + errorCode + ")";
{
std::ostringstream oss;
oss << "Deployment failed: " << error;
DebugLog(oss.str());
}
} else if (deploymentOperation.Status() == AsyncStatus::Canceled) {
DebugLog("Deployment canceled");
error = "Deployment canceled";
} else if (deploymentOperation.Status() == AsyncStatus::Completed) {
DebugLog("MSIX Deployment completed.");
} else {
error = "Deployment status unknown";
DebugLog("Deployment status unknown");
}
} else {
// At this point, we can not await the deployment because we require a
// shutdown of the app to continue, so we do a fire and forget. When the
// deployment process tries ot shutdown the app, the process waits for us
// to finish here. But to finish we need to shutdow. That leads to a 30s
// dealock, till we forcefully get shutdown by the OS.
DebugLog(
"Deployment initiated. Force shutdown or target shutdown requested. "
"Good bye!");
}
}
// Post result back
reply_runner->PostTask(
FROM_HERE, base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
if (error.empty()) {
promise.Resolve();
} else {
promise.RejectWithErrorMessage(error);
}
},
std::move(promise), error));
}
// Performs package registration on IO thread
void DoRegisterPackage(const std::string& family_name,
RegisterPackageOptions opts,
scoped_refptr<base::SingleThreadTaskRunner> reply_runner,
gin_helper::Promise<void> promise) {
DebugLog("DoRegisterPackage: Starting");
using winrt::Windows::Foundation::AsyncStatus;
using winrt::Windows::Foundation::Collections::IIterable;
using winrt::Windows::Management::Deployment::DeploymentOptions;
using winrt::Windows::Management::Deployment::PackageManager;
std::string error;
auto familyNameH = winrt::to_hstring(family_name);
PackageManager packageManager;
DeploymentOptions deploymentOptions = DeploymentOptions::None;
// Use the pre-parsed options (no V8 access needed)
if (opts.force_shutdown) {
deploymentOptions |= DeploymentOptions::ForceApplicationShutdown;
}
if (opts.force_target_shutdown) {
deploymentOptions |= DeploymentOptions::ForceTargetApplicationShutdown;
}
if (opts.force_update_from_any_version) {
deploymentOptions |= DeploymentOptions::ForceUpdateFromAnyVersion;
}
// Create empty collections for dependency and optional packages
IIterable<winrt::hstring> emptyDependencies{nullptr};
IIterable<winrt::hstring> emptyOptional{nullptr};
{
std::ostringstream oss;
oss << "Calling RegisterPackageByFamilyNameAsync... FamilyName: "
<< family_name;
DebugLog(oss.str());
}
{
std::ostringstream oss;
oss << "Registration options - forceShutdown: " << opts.force_shutdown
<< ", forceTargetShutdown: " << opts.force_target_shutdown
<< ", forceUpdateFromAnyVersion: "
<< opts.force_update_from_any_version;
DebugLog(oss.str());
}
auto deploymentOperation = packageManager.RegisterPackageByFamilyNameAsync(
familyNameH, emptyDependencies, deploymentOptions, nullptr,
emptyOptional);
if (!deploymentOperation) {
error =
"Deployment is NULL. See "
"http://go.microsoft.com/fwlink/?LinkId=235160 for diagnosing.";
} else {
if (!opts.force_shutdown && !opts.force_target_shutdown) {
DebugLog("Waiting for registration...");
deploymentOperation.get();
DebugLog("Registration finished.");
if (deploymentOperation.Status() == AsyncStatus::Error) {
auto deploymentResult{deploymentOperation.GetResults()};
std::string errorText = winrt::to_string(deploymentResult.ErrorText());
std::string errorCode =
std::to_string(static_cast<int>(deploymentOperation.ErrorCode()));
error = errorText + " (" + errorCode + ")";
{
std::ostringstream oss;
oss << "Registration failed: " << error;
DebugLog(oss.str());
}
} else if (deploymentOperation.Status() == AsyncStatus::Canceled) {
DebugLog("Registration canceled");
error = "Registration canceled";
} else if (deploymentOperation.Status() == AsyncStatus::Completed) {
DebugLog("MSIX Registration completed.");
} else {
error = "Registration status unknown";
DebugLog("Registration status unknown");
}
} else {
// At this point, we can not await the registration because we require a
// shutdown of the app to continue, so we do a fire and forget. When the
// registration process tries ot shutdown the app, the process waits for
// us to finish here. But to finish we need to shutdown. That leads to a
// 30s dealock, till we forcefully get shutdown by the OS.
DebugLog(
"Registration initiated. Force shutdown or target shutdown "
"requested. Good bye!");
}
}
// Post result back to UI thread
reply_runner->PostTask(
FROM_HERE, base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
if (error.empty()) {
promise.Resolve();
} else {
promise.RejectWithErrorMessage(error);
}
},
std::move(promise), error));
}
#endif
// Update MSIX package
v8::Local<v8::Promise> UpdateMsix(const std::string& package_uri,
gin_helper::Dictionary options) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
gin_helper::Promise<void> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
#if BUILDFLAG(IS_WIN)
if (!HasPackageIdentity()) {
DebugLog("UpdateMsix: The process has no package identity");
promise.RejectWithErrorMessage("The process has no package identity.");
return handle;
}
// Parse options on UI thread (where V8 is available)
UpdateMsixOptions opts;
options.Get("deferRegistration", &opts.defer_registration);
options.Get("developerMode", &opts.developer_mode);
options.Get("forceShutdown", &opts.force_shutdown);
options.Get("forceTargetShutdown", &opts.force_target_shutdown);
options.Get("forceUpdateFromAnyVersion", &opts.force_update_from_any_version);
{
std::ostringstream oss;
oss << "UpdateMsix called with URI: " << package_uri;
DebugLog(oss.str());
}
// Post to IO thread
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&DoUpdateMsix, package_uri, opts,
base::SingleThreadTaskRunner::GetCurrentDefault(),
std::move(promise)));
#else
promise.RejectWithErrorMessage(
"MSIX updates are only supported on Windows with identity.");
#endif
return handle;
}
// Register MSIX package
v8::Local<v8::Promise> RegisterPackage(const std::string& family_name,
gin_helper::Dictionary options) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
gin_helper::Promise<void> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
#if BUILDFLAG(IS_WIN)
if (!HasPackageIdentity()) {
DebugLog("RegisterPackage: The process has no package identity");
promise.RejectWithErrorMessage("The process has no package identity.");
return handle;
}
// Parse options on UI thread (where V8 is available)
RegisterPackageOptions opts;
options.Get("forceShutdown", &opts.force_shutdown);
options.Get("forceTargetShutdown", &opts.force_target_shutdown);
options.Get("forceUpdateFromAnyVersion", &opts.force_update_from_any_version);
{
std::ostringstream oss;
oss << "RegisterPackage called with family name: " << family_name;
DebugLog(oss.str());
}
// Post to IO thread with POD options (no V8 objects)
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&DoRegisterPackage, family_name, opts,
base::SingleThreadTaskRunner::GetCurrentDefault(),
std::move(promise)));
#else
promise.RejectWithErrorMessage(
"MSIX package registration is only supported on Windows.");
#endif
return handle;
}
// Register application restart
// Only registers for update restarts (not crashes, hangs, or reboots)
bool RegisterRestartOnUpdate(const std::string& command_line) {
#if BUILDFLAG(IS_WIN)
if (!HasPackageIdentity()) {
DebugLog("Cannot restart: no package identity");
return false;
}
const wchar_t* commandLine = nullptr;
// Flags: RESTART_NO_CRASH | RESTART_NO_HANG | RESTART_NO_REBOOT
// This means: only restart on updates (RESTART_NO_PATCH is NOT set)
const DWORD dwFlags = 1 | 2 | 8; // 11
if (!command_line.empty()) {
std::wstring commandLineW =
std::wstring(command_line.begin(), command_line.end());
commandLine = commandLineW.c_str();
}
HRESULT hr = RegisterApplicationRestart(commandLine, dwFlags);
if (FAILED(hr)) {
{
std::ostringstream oss;
oss << "RegisterApplicationRestart failed with error code: " << hr;
DebugLog(oss.str());
}
return false;
}
{
std::ostringstream oss;
oss << "RegisterApplicationRestart succeeded"
<< (command_line.empty() ? "" : " with command line");
DebugLog(oss.str());
}
return true;
#else
return false;
#endif
}
// Get package information
v8::Local<v8::Value> GetPackageInfo() {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
#if BUILDFLAG(IS_WIN)
// Check if running in a package
if (!HasPackageIdentity()) {
DebugLog("GetPackageInfo: The process has no package identity");
gin_helper::ErrorThrower thrower(isolate);
thrower.ThrowTypeError("The process has no package identity.");
return v8::Null(isolate);
}
DebugLog("GetPackageInfo: Retrieving package information");
gin_helper::Dictionary result(isolate, v8::Object::New(isolate));
// Check API contract version (Windows 10 version 1703 or later)
if (winrt::Windows::Foundation::Metadata::ApiInformation::
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 7)) {
using winrt::Windows::ApplicationModel::Package;
using winrt::Windows::ApplicationModel::PackageSignatureKind;
Package package = Package::Current();
// Get package ID and family name
std::string packageId = winrt::to_string(package.Id().FullName());
std::string familyName = winrt::to_string(package.Id().FamilyName());
result.Set("id", packageId);
result.Set("familyName", familyName);
result.Set("developmentMode", package.IsDevelopmentMode());
// Get package version
auto packageVersion = package.Id().Version();
std::string version = std::to_string(packageVersion.Major) + "." +
std::to_string(packageVersion.Minor) + "." +
std::to_string(packageVersion.Build) + "." +
std::to_string(packageVersion.Revision);
result.Set("version", version);
// Convert signature kind to string
std::string signatureKind;
switch (package.SignatureKind()) {
case PackageSignatureKind::Developer:
signatureKind = "developer";
break;
case PackageSignatureKind::Enterprise:
signatureKind = "enterprise";
break;
case PackageSignatureKind::None:
signatureKind = "none";
break;
case PackageSignatureKind::Store:
signatureKind = "store";
break;
case PackageSignatureKind::System:
signatureKind = "system";
break;
default:
signatureKind = "none";
break;
}
result.Set("signatureKind", signatureKind);
// Get app installer info if available
auto appInstallerInfo = package.GetAppInstallerInfo();
if (appInstallerInfo != nullptr) {
std::string uriStr = winrt::to_string(appInstallerInfo.Uri().ToString());
result.Set("appInstallerUri", uriStr);
}
} else {
// Windows version doesn't meet minimum API requirements
result.Set("familyName", "");
result.Set("id", "");
result.Set("developmentMode", false);
result.Set("signatureKind", "none");
result.Set("version", "");
}
return result.GetHandle();
#else
// Non-Windows platforms
gin_helper::Dictionary result(isolate, v8::Object::New(isolate));
result.Set("familyName", "");
result.Set("id", "");
result.Set("developmentMode", false);
result.Set("signatureKind", "none");
result.Set("version", "");
return result.GetHandle();
#endif
}
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
v8::Isolate* const isolate = electron::JavascriptEnvironment::GetIsolate();
gin_helper::Dictionary dict(isolate, exports);
dict.SetMethod("updateMsix", base::BindRepeating(&UpdateMsix));
dict.SetMethod("registerPackage", base::BindRepeating(&RegisterPackage));
dict.SetMethod("registerRestartOnUpdate",
base::BindRepeating(&RegisterRestartOnUpdate));
dict.SetMethod("getPackageInfo",
base::BindRepeating([]() { return GetPackageInfo(); }));
}
} // namespace
NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_msix_updater, Initialize)

View File

@@ -1,14 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_MSIX_UPDATER_H_
#define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_MSIX_UPDATER_H_
namespace electron {
extern const bool debug_msix_updater;
} // namespace electron
#endif // ELECTRON_SHELL_BROWSER_API_ELECTRON_API_MSIX_UPDATER_H_

View File

@@ -5,16 +5,15 @@
#include "shell/browser/api/electron_api_power_monitor.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_device_source.h"
#include "base/power_monitor/power_observer.h"
#include "gin/data_object_builder.h"
#include "shell/browser/browser.h"
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/handle.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "ui/base/idle/idle.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"
namespace gin {
@@ -60,10 +59,11 @@ struct Converter<base::PowerThermalObserver::DeviceThermalState> {
namespace electron::api {
gin::DeprecatedWrapperInfo PowerMonitor::kWrapperInfo = {
gin::kEmbedderNativeGin};
const gin::WrapperInfo PowerMonitor::kWrapperInfo = {
{gin::kEmbedderNativeGin},
gin::kElectronPowerMonitor};
PowerMonitor::PowerMonitor(v8::Isolate* isolate) {
PowerMonitor::PowerMonitor() {
#if BUILDFLAG(IS_MAC)
Browser::Get()->SetShutdownHandler(base::BindRepeating(
&PowerMonitor::ShouldShutdown, base::Unretained(this)));
@@ -142,11 +142,9 @@ void PowerMonitor::SetListeningForShutdown(bool is_listening) {
#endif
// static
v8::Local<v8::Value> PowerMonitor::Create(v8::Isolate* isolate) {
auto* pm = new PowerMonitor(isolate);
auto handle = gin_helper::CreateHandle(isolate, pm).ToV8();
pm->Pin(isolate);
return handle;
PowerMonitor* PowerMonitor::Create(v8::Isolate* isolate) {
return cppgc::MakeGarbageCollected<PowerMonitor>(
isolate->GetCppHeap()->GetAllocationHandle());
}
gin::ObjectTemplateBuilder PowerMonitor::GetObjectTemplateBuilder(
@@ -161,8 +159,12 @@ gin::ObjectTemplateBuilder PowerMonitor::GetObjectTemplateBuilder(
return builder;
}
const char* PowerMonitor::GetTypeName() {
return "PowerMonitor";
const gin::WrapperInfo* PowerMonitor::wrapper_info() const {
return &kWrapperInfo;
}
const char* PowerMonitor::GetHumanReadableName() const {
return "Electron / PowerMonitor";
}
} // namespace electron::api

View File

@@ -6,40 +6,45 @@
#define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_POWER_MONITOR_H_
#include "base/power_monitor/power_observer.h"
#include "gin/wrappable.h"
#include "shell/browser/event_emitter_mixin.h"
#include "shell/common/gin_helper/pinnable.h"
#include "shell/common/gin_helper/wrappable.h"
#include "ui/base/idle/idle.h"
#include "shell/common/gin_helper/self_keep_alive.h"
#if BUILDFLAG(IS_LINUX)
#include "shell/browser/lib/power_observer_linux.h"
#endif
namespace gin {
class ObjectTemplateBuilder;
} // namespace gin
namespace electron::api {
class PowerMonitor final : public gin_helper::DeprecatedWrappable<PowerMonitor>,
class PowerMonitor final : public gin::Wrappable<PowerMonitor>,
public gin_helper::EventEmitterMixin<PowerMonitor>,
public gin_helper::Pinnable<PowerMonitor>,
private base::PowerStateObserver,
private base::PowerSuspendObserver,
private base::PowerThermalObserver {
public:
static v8::Local<v8::Value> Create(v8::Isolate* isolate);
static PowerMonitor* Create(v8::Isolate* isolate);
// gin_helper::Wrappable
static gin::DeprecatedWrapperInfo kWrapperInfo;
// gin::Wrappable
static const gin::WrapperInfo kWrapperInfo;
const gin::WrapperInfo* wrapper_info() const override;
const char* GetHumanReadableName() const override;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
const char* GetTypeName() override;
const char* GetClassName() const { return "PowerMonitor"; }
// Make public for cppgc::MakeGarbageCollected.
PowerMonitor();
~PowerMonitor() override;
// disable copy
PowerMonitor(const PowerMonitor&) = delete;
PowerMonitor& operator=(const PowerMonitor&) = delete;
private:
explicit PowerMonitor(v8::Isolate* isolate);
~PowerMonitor() override;
#if BUILDFLAG(IS_LINUX)
void SetListeningForShutdown(bool);
#endif
@@ -88,6 +93,8 @@ class PowerMonitor final : public gin_helper::DeprecatedWrappable<PowerMonitor>,
#if BUILDFLAG(IS_LINUX)
PowerObserverLinux power_observer_linux_{this};
#endif
gin_helper::SelfKeepAlive<PowerMonitor> keep_alive_{this};
};
} // namespace electron::api

View File

@@ -16,6 +16,8 @@
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_helper/handle.h"
#include "shell/common/node_includes.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"
namespace gin {
@@ -41,14 +43,39 @@ struct Converter<device::mojom::WakeLockType> {
namespace electron::api {
gin::DeprecatedWrapperInfo PowerSaveBlocker::kWrapperInfo = {
gin::kEmbedderNativeGin};
const gin::WrapperInfo PowerSaveBlocker::kWrapperInfo = {
{gin::kEmbedderNativeGin},
gin::kElectronPowerSaveBlocker};
PowerSaveBlocker::PowerSaveBlocker(v8::Isolate* isolate)
: current_lock_type_(device::mojom::WakeLockType::kPreventAppSuspension) {}
PowerSaveBlocker::~PowerSaveBlocker() = default;
// static
gin_helper::Handle<PowerSaveBlocker> PowerSaveBlocker::Create(
v8::Isolate* isolate) {
return gin_helper::CreateHandle(
isolate, cppgc::MakeGarbageCollected<PowerSaveBlocker>(
isolate->GetCppHeap()->GetAllocationHandle(), isolate));
}
const gin::WrapperInfo* PowerSaveBlocker::wrapper_info() const {
return &kWrapperInfo;
}
const char* PowerSaveBlocker::GetHumanReadableName() const {
return "Electron / PowerSaveBlocker";
}
gin::ObjectTemplateBuilder PowerSaveBlocker::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin::Wrappable<PowerSaveBlocker>::GetObjectTemplateBuilder(isolate)
.SetMethod("start", &PowerSaveBlocker::Start)
.SetMethod("stop", &PowerSaveBlocker::Stop)
.SetMethod("isStarted", &PowerSaveBlocker::IsStarted);
}
void PowerSaveBlocker::UpdatePowerSaveBlocker() {
if (wake_lock_types_.empty()) {
if (is_wake_lock_active_) {
@@ -66,6 +93,7 @@ void PowerSaveBlocker::UpdatePowerSaveBlocker() {
// Only the highest-precedence blocker type takes effect.
device::mojom::WakeLockType new_lock_type =
device::mojom::WakeLockType::kPreventAppSuspension;
for (const auto& element : wake_lock_types_) {
if (element.second == device::mojom::WakeLockType::kPreventDisplaySleep) {
new_lock_type = device::mojom::WakeLockType::kPreventDisplaySleep;
@@ -114,25 +142,6 @@ bool PowerSaveBlocker::IsStarted(int id) const {
return wake_lock_types_.contains(id);
}
// static
gin_helper::Handle<PowerSaveBlocker> PowerSaveBlocker::Create(
v8::Isolate* isolate) {
return gin_helper::CreateHandle(isolate, new PowerSaveBlocker(isolate));
}
gin::ObjectTemplateBuilder PowerSaveBlocker::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin_helper::DeprecatedWrappable<
PowerSaveBlocker>::GetObjectTemplateBuilder(isolate)
.SetMethod("start", &PowerSaveBlocker::Start)
.SetMethod("stop", &PowerSaveBlocker::Stop)
.SetMethod("isStarted", &PowerSaveBlocker::IsStarted);
}
const char* PowerSaveBlocker::GetTypeName() {
return "PowerSaveBlocker";
}
} // namespace electron::api
namespace {

View File

@@ -6,9 +6,9 @@
#define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_POWER_SAVE_BLOCKER_H_
#include "base/containers/flat_map.h"
#include "gin/wrappable.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/public/mojom/wake_lock.mojom.h"
#include "shell/common/gin_helper/wrappable.h"
namespace gin {
class ObjectTemplateBuilder;
@@ -21,25 +21,25 @@ class Handle;
namespace electron::api {
class PowerSaveBlocker final
: public gin_helper::DeprecatedWrappable<PowerSaveBlocker> {
class PowerSaveBlocker final : public gin::Wrappable<PowerSaveBlocker> {
public:
static gin_helper::Handle<PowerSaveBlocker> Create(v8::Isolate* isolate);
// gin_helper::Wrappable
static gin::DeprecatedWrapperInfo kWrapperInfo;
// gin::Wrappable
static const gin::WrapperInfo kWrapperInfo;
const gin::WrapperInfo* wrapper_info() const override;
const char* GetHumanReadableName() const override;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
const char* GetTypeName() override;
// Make public for cppgc::MakeGarbageCollected.
explicit PowerSaveBlocker(v8::Isolate* isolate);
~PowerSaveBlocker() override;
// disable copy
PowerSaveBlocker(const PowerSaveBlocker&) = delete;
PowerSaveBlocker& operator=(const PowerSaveBlocker&) = delete;
protected:
explicit PowerSaveBlocker(v8::Isolate* isolate);
~PowerSaveBlocker() override;
private:
void UpdatePowerSaveBlocker();
int Start(device::mojom::WakeLockType type);

View File

@@ -9,12 +9,10 @@
#include "base/functional/bind.h"
#include "shell/browser/browser.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/native_window_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/error_thrower.h"
#include "shell/common/gin_helper/handle.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "ui/display/display.h"
@@ -23,6 +21,8 @@
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"
#if BUILDFLAG(IS_WIN)
#include "ui/display/win/screen_win.h"
@@ -38,7 +38,8 @@
namespace electron::api {
gin::DeprecatedWrapperInfo Screen::kWrapperInfo = {gin::kEmbedderNativeGin};
const gin::WrapperInfo Screen::kWrapperInfo = {{gin::kEmbedderNativeGin},
gin::kElectronScreen};
namespace {
@@ -71,13 +72,15 @@ void DelayEmitWithMetrics(Screen* screen,
} // namespace
Screen::Screen(v8::Isolate* isolate, display::Screen* screen)
: screen_(screen) {
Screen::Screen(display::Screen* screen) : screen_{screen} {
screen_->AddObserver(this);
}
Screen::~Screen() {
screen_->RemoveObserver(this);
// Use `display::Screen::Get()` here, not our cached `screen_`:
// during shutdown, it can get torn down before us.
if (auto* screen = display::Screen::Get())
screen->RemoveObserver(this);
}
gfx::Point Screen::GetCursorScreenPoint(v8::Isolate* isolate) {
@@ -172,22 +175,21 @@ gfx::Point Screen::DIPToScreenPoint(const gfx::Point& point_dip) {
}
// static
v8::Local<v8::Value> Screen::Create(gin_helper::ErrorThrower error_thrower) {
Screen* Screen::Create(gin_helper::ErrorThrower error_thrower) {
if (!Browser::Get()->is_ready()) {
error_thrower.ThrowError(
"The 'screen' module can't be used before the app 'ready' event");
return v8::Null(error_thrower.isolate());
return {};
}
display::Screen* screen = display::Screen::Get();
if (!screen) {
error_thrower.ThrowError("Failed to get screen information");
return v8::Null(error_thrower.isolate());
return {};
}
return gin_helper::CreateHandle(error_thrower.isolate(),
new Screen(error_thrower.isolate(), screen))
.ToV8();
return cppgc::MakeGarbageCollected<Screen>(
error_thrower.isolate()->GetCppHeap()->GetAllocationHandle(), screen);
}
gin::ObjectTemplateBuilder Screen::GetObjectTemplateBuilder(
@@ -209,8 +211,12 @@ gin::ObjectTemplateBuilder Screen::GetObjectTemplateBuilder(
.SetMethod("getDisplayMatching", &Screen::GetDisplayMatching);
}
const char* Screen::GetTypeName() {
return "Screen";
const gin::WrapperInfo* Screen::wrapper_info() const {
return &kWrapperInfo;
}
const char* Screen::GetHumanReadableName() const {
return "Electron / Screen";
}
} // namespace electron::api

View File

@@ -8,8 +8,8 @@
#include <vector>
#include "base/memory/raw_ptr.h"
#include "gin/wrappable.h"
#include "shell/browser/event_emitter_mixin.h"
#include "shell/common/gin_helper/wrappable.h"
#include "ui/display/display_observer.h"
#include "ui/display/screen.h"
@@ -17,7 +17,6 @@ namespace gfx {
class Point;
class PointF;
class Rect;
class Screen;
} // namespace gfx
namespace gin_helper {
@@ -26,23 +25,25 @@ class ErrorThrower;
namespace electron::api {
class Screen final : public gin_helper::DeprecatedWrappable<Screen>,
class Screen final : public gin::Wrappable<Screen>,
public gin_helper::EventEmitterMixin<Screen>,
private display::DisplayObserver {
public:
static v8::Local<v8::Value> Create(gin_helper::ErrorThrower error_thrower);
static Screen* Create(gin_helper::ErrorThrower error_thrower);
static gin::DeprecatedWrapperInfo kWrapperInfo;
static const gin::WrapperInfo kWrapperInfo;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
const char* GetTypeName() override;
const gin::WrapperInfo* wrapper_info() const override;
const char* GetHumanReadableName() const override;
const char* GetClassName() const { return "Screen"; }
// disable copy
Screen(const Screen&) = delete;
Screen& operator=(const Screen&) = delete;
protected:
Screen(v8::Isolate* isolate, display::Screen* screen);
// Make public for cppgc::MakeGarbageCollected.
explicit Screen(display::Screen* screen);
~Screen() override;
gfx::Point GetCursorScreenPoint(v8::Isolate* isolate);

View File

@@ -1044,15 +1044,13 @@ bool Session::IsPersistent() {
v8::Local<v8::Promise> Session::GetBlobData(v8::Isolate* isolate,
const std::string& uuid) {
gin_helper::Handle<DataPipeHolder> holder =
DataPipeHolder::From(isolate, uuid);
if (holder.IsEmpty()) {
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
promise.RejectWithErrorMessage("Could not get blob data handle");
return promise.GetHandle();
if (DataPipeHolder* holder = DataPipeHolder::From(isolate, uuid)) {
return holder->ReadAll(isolate);
}
return holder->ReadAll(isolate);
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
promise.RejectWithErrorMessage("Could not get blob data handle");
return promise.GetHandle();
}
void Session::DownloadURL(const GURL& url, gin::Arguments* args) {

View File

@@ -849,6 +849,8 @@ WebContents::WebContents(v8::Isolate* isolate,
&offscreen_use_shared_texture_);
use_offscreen_dict.Get(options::kSharedTexturePixelFormat,
&offscreen_shared_texture_pixel_format_);
use_offscreen_dict.Get(options::kDeviceScaleFactor,
&offscreen_device_scale_factor_);
}
}
@@ -887,6 +889,7 @@ WebContents::WebContents(v8::Isolate* isolate,
auto* view = new OffScreenWebContentsView(
false, offscreen_use_shared_texture_,
offscreen_shared_texture_pixel_format_,
offscreen_device_scale_factor_,
base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
params.view = view;
params.delegate_view = view;
@@ -908,7 +911,7 @@ WebContents::WebContents(v8::Isolate* isolate,
content::WebContents::CreateParams params(session->browser_context());
auto* view = new OffScreenWebContentsView(
transparent, offscreen_use_shared_texture_,
offscreen_shared_texture_pixel_format_,
offscreen_shared_texture_pixel_format_, offscreen_device_scale_factor_,
base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
params.view = view;
params.delegate_view = view;
@@ -1272,6 +1275,7 @@ void WebContents::MaybeOverrideCreateParamsForNewWindow(
auto* view = new OffScreenWebContentsView(
false, offscreen_use_shared_texture_,
offscreen_shared_texture_pixel_format_,
offscreen_device_scale_factor_,
base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
create_params->view = view;
create_params->delegate_view = view;
@@ -2094,6 +2098,10 @@ void WebContents::ReadyToCommitNavigation(
// Only focus for top-level contents.
if (type_ != Type::kBrowserWindow)
return;
// Don't focus if focusOnNavigation is disabled.
auto* prefs = WebContentsPreferences::From(web_contents());
if (prefs && !prefs->ShouldFocusOnNavigation())
return;
web_contents()->SetInitialFocus();
}

View File

@@ -820,6 +820,13 @@ class WebContents final : public ExclusiveAccessContext,
bool offscreen_use_shared_texture_ = false;
std::string offscreen_shared_texture_pixel_format_ = "argb";
// TODO(reito): 0.0f means the device scale factor is not set, it's a
// migration of the breaking change so that we can read the device scale
// factor from physical primary screen's info. In Electron 42, we need to set
// this to 1.0f so that the offscreen rendering use 1.0 as default when
// `deviceScaleFactor` is not specified in webPreferences.
float offscreen_device_scale_factor_ = 0.0f;
// Whether window is fullscreened by HTML5 api.
bool html_fullscreen_ = false;

View File

@@ -56,6 +56,11 @@ static NSDictionary* UNNotificationResponseToNSDictionary(
}
- (void)applicationWillFinishLaunching:(NSNotification*)notify {
// Don't add the "Enter Full Screen" menu item automatically.
[[NSUserDefaults standardUserDefaults]
setBool:NO
forKey:@"NSFullScreenMenuItemEverywhere"];
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(willPowerOff:)

View File

@@ -96,9 +96,11 @@ gfx::Size GetExpandedWindowSize(const NativeWindow* window,
} // namespace
NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
NativeWindow::NativeWindow(const int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent)
: title_bar_style_{options.ValueOrDefault(options::kTitleBarStyle,
: base_window_id_{base_window_id},
title_bar_style_{options.ValueOrDefault(options::kTitleBarStyle,
TitleBarStyle::kNormal)},
transparent_{options.ValueOrDefault(options::kTransparent, false)},
enable_larger_than_screen_{
@@ -108,6 +110,8 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
has_frame_{options.ValueOrDefault(options::kFrame, true) &&
title_bar_style_ == TitleBarStyle::kNormal},
parent_{parent} {
DCHECK_NE(base_window_id_, 0);
#if BUILDFLAG(IS_WIN)
options.Get(options::kBackgroundMaterial, &background_material_);
#elif BUILDFLAG(IS_MAC)

View File

@@ -17,7 +17,6 @@
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/strings/cstring_view.h"
#include "base/supports_user_data.h"
#include "content/public/browser/desktop_media_id.h"
#include "content/public/browser/web_contents_user_data.h"
#include "extensions/browser/app_window/size_constraints.h"
@@ -56,8 +55,7 @@ using NativeWindowHandle = gfx::NativeView;
using NativeWindowHandle = gfx::AcceleratedWidget;
#endif
class NativeWindow : public base::SupportsUserData,
public views::WidgetDelegate {
class NativeWindow : public views::WidgetDelegate {
public:
~NativeWindow() override;
@@ -68,6 +66,7 @@ class NativeWindow : public base::SupportsUserData,
// Create window with existing WebContents, the caller is responsible for
// managing the window's live.
static std::unique_ptr<NativeWindow> Create(
int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent = nullptr);
@@ -429,8 +428,12 @@ class NativeWindow : public base::SupportsUserData,
// throttling, then throttling in the `ui::Compositor` will be disabled.
void UpdateBackgroundThrottlingState();
[[nodiscard]] auto base_window_id() const { return base_window_id_; }
protected:
NativeWindow(const gin_helper::Dictionary& options, NativeWindow* parent);
NativeWindow(int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent);
void set_titlebar_overlay_height(int height) {
titlebar_overlay_height_ = height;
@@ -488,6 +491,9 @@ class NativeWindow : public base::SupportsUserData,
static inline int32_t next_id_ = 0;
const int32_t window_id_ = ++next_id_;
// ID of the api::BaseWindow that owns this NativeWindow.
const int32_t base_window_id_;
// The "titleBarStyle" option.
const TitleBarStyle title_bar_style_;

View File

@@ -34,7 +34,9 @@ class NativeWindowMac : public NativeWindow,
public ui::NativeThemeObserver,
public display::DisplayObserver {
public:
NativeWindowMac(const gin_helper::Dictionary& options, NativeWindow* parent);
NativeWindowMac(int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent);
~NativeWindowMac() override;
// NativeWindow:

View File

@@ -151,9 +151,11 @@ class NativeAppWindowFrameViewMacClient
NativeWindowMac::~NativeWindowMac() = default;
NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
NativeWindowMac::NativeWindowMac(const int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent)
: NativeWindow(options, parent), root_view_(new RootViewMac(this)) {
: NativeWindow{base_window_id, options, parent},
root_view_(new RootViewMac(this)) {
ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this);
display::Screen::Get()->AddObserver(this);
@@ -1876,9 +1878,10 @@ void NativeWindowMac::OnWidgetInitialized() {
// static
std::unique_ptr<NativeWindow> NativeWindow::Create(
const int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent) {
return std::make_unique<NativeWindowMac>(options, parent);
return std::make_unique<NativeWindowMac>(base_window_id, options, parent);
}
} // namespace electron

View File

@@ -193,9 +193,10 @@ class NativeWindowClientView : public views::ClientView {
} // namespace
NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
NativeWindowViews::NativeWindowViews(const int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent)
: NativeWindow(options, parent) {
: NativeWindow{base_window_id, options, parent} {
if (std::string val; options.Get(options::kTitle, &val))
SetTitle(val);
@@ -1952,9 +1953,10 @@ void NativeWindowViews::MoveBehindTaskBarIfNeeded() {
// static
std::unique_ptr<NativeWindow> NativeWindow::Create(
const int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent) {
return std::make_unique<NativeWindowViews>(options, parent);
return std::make_unique<NativeWindowViews>(base_window_id, options, parent);
}
} // namespace electron

View File

@@ -48,7 +48,8 @@ class NativeWindowViews : public NativeWindow,
private views::WidgetObserver,
private ui::EventHandler {
public:
NativeWindowViews(const gin_helper::Dictionary& options,
NativeWindowViews(int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent);
~NativeWindowViews() override;

View File

@@ -52,8 +52,6 @@ namespace electron {
namespace {
const float kDefaultScaleFactor = 1.0;
ui::MouseEvent UiMouseEventFromWebMouseEvent(blink::WebMouseEvent event) {
int button_flags = 0;
switch (event.button) {
@@ -95,6 +93,15 @@ ui::MouseWheelEvent UiMouseWheelEventFromWebMouseEvent(
base::ClampFloor<int>(event.delta_y)};
}
// TODO(reito): Remove this function and use default 1.0f when Electron 42.
float GetDefaultDeviceScaleFactorFromDisplayInfo() {
display::Display display =
display::Screen::Get()->GetDisplayNearestView(gfx::NativeView());
const float factor = display.device_scale_factor();
return factor > 0 ? factor : 1.0f;
}
} // namespace
class ElectronDelegatedFrameHostClient
@@ -154,6 +161,7 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
bool transparent,
bool offscreen_use_shared_texture,
const std::string& offscreen_shared_texture_pixel_format,
float offscreen_device_scale_factor,
bool painting,
int frame_rate,
const OnPaintCallback& callback,
@@ -167,6 +175,7 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
offscreen_use_shared_texture_(offscreen_use_shared_texture),
offscreen_shared_texture_pixel_format_(
offscreen_shared_texture_pixel_format),
offscreen_device_scale_factor_(offscreen_device_scale_factor),
callback_(callback),
frame_rate_(frame_rate),
size_(initial_size),
@@ -183,11 +192,11 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
DCHECK(render_widget_host_);
DCHECK(!render_widget_host_->GetView());
// Initialize a screen_infos_ struct as needed, to cache the scale factor.
if (screen_infos_.screen_infos.empty()) {
UpdateScreenInfo();
// TODO(reito): Remove this when Electron 42.
if (cc::MathUtil::IsWithinEpsilon(offscreen_device_scale_factor_, 0.0f)) {
offscreen_device_scale_factor_ =
GetDefaultDeviceScaleFactorFromDisplayInfo();
}
screen_infos_.mutable_current().device_scale_factor = kDefaultScaleFactor;
delegated_frame_host_allocator_.GenerateId();
delegated_frame_host_surface_id_ =
@@ -209,15 +218,6 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
compositor_->SetDelegate(this);
compositor_->SetRootLayer(root_layer_.get());
// For offscreen rendering with format rgbaf16, we need to set correct display
// color spaces to the compositor, otherwise it won't support hdr.
if (offscreen_use_shared_texture_ &&
offscreen_shared_texture_pixel_format_ == "rgbaf16") {
gfx::DisplayColorSpaces hdr_display_color_spaces(
gfx::ColorSpace::CreateSRGBLinear(), viz::SinglePlaneFormat::kRGBA_F16);
compositor_->SetDisplayColorSpaces(hdr_display_color_spaces);
}
ResizeRootLayer(false);
render_widget_host_->SetView(this);
@@ -503,19 +503,6 @@ void OffScreenRenderWidgetHostView::CopyFromSurface(
src_rect, output_size, base::TimeDelta(), std::move(callback));
}
display::ScreenInfo OffScreenRenderWidgetHostView::GetScreenInfo() const {
display::ScreenInfo screen_info;
screen_info.depth = 24;
screen_info.depth_per_component = 8;
screen_info.orientation_angle = 0;
screen_info.device_scale_factor = GetDeviceScaleFactor();
screen_info.orientation_type =
display::mojom::ScreenOrientation::kLandscapePrimary;
screen_info.rect = gfx::Rect(size_);
screen_info.available_rect = gfx::Rect(size_);
return screen_info;
}
gfx::Rect OffScreenRenderWidgetHostView::GetBoundsInRootWindow() {
return gfx::Rect(size_);
}
@@ -561,8 +548,8 @@ OffScreenRenderWidgetHostView::CreateViewForWidget(
return new OffScreenRenderWidgetHostView(
transparent_, offscreen_use_shared_texture_,
offscreen_shared_texture_pixel_format_, true,
embedder_host_view->frame_rate(), callback_, render_widget_host,
offscreen_shared_texture_pixel_format_, offscreen_device_scale_factor_,
true, embedder_host_view->frame_rate(), callback_, render_widget_host,
embedder_host_view, size());
}
@@ -970,35 +957,55 @@ void OffScreenRenderWidgetHostView::InvalidateBounds(const gfx::Rect& bounds) {
CompositeFrame(bounds);
}
display::ScreenInfos
OffScreenRenderWidgetHostView::GetNewScreenInfosForUpdate() {
display::ScreenInfo screen_info;
screen_info.depth = 24;
screen_info.depth_per_component = 8;
screen_info.orientation_angle = 0;
screen_info.orientation_type =
display::mojom::ScreenOrientation::kLandscapePrimary;
screen_info.rect = gfx::Rect(size_);
screen_info.available_rect = gfx::Rect(size_);
screen_info.device_scale_factor = offscreen_device_scale_factor_;
// When pixel format is 'rgbaf16', we need to set screen info to support HDR.
if (offscreen_use_shared_texture_ &&
offscreen_shared_texture_pixel_format_ == "rgbaf16") {
gfx::DisplayColorSpaces hdr_display_color_spaces{
gfx::ColorSpace::CreateSRGBLinear(), viz::SinglePlaneFormat::kRGBA_F16};
// The max luminance value doesn't matter so we set to a large value.
hdr_display_color_spaces.SetHDRMaxLuminanceRelative(100.0f);
screen_info.display_color_spaces = hdr_display_color_spaces;
}
display::ScreenInfos screen_infos{screen_info};
return screen_infos;
}
void OffScreenRenderWidgetHostView::ResizeRootLayer(bool force) {
SetupFrameRate(false);
display::Display display =
display::Screen::Get()->GetDisplayNearestView(GetNativeView());
const float scaleFactor = display.device_scale_factor();
float sf = GetDeviceScaleFactor();
const bool sf_did_change = scaleFactor != sf;
// Initialize a screen_infos_ struct as needed, to cache the scale factor.
if (screen_infos_.screen_infos.empty()) {
UpdateScreenInfo();
}
screen_infos_.mutable_current().device_scale_factor = scaleFactor;
auto old_screen_info = screen_infos_.current();
UpdateScreenInfo();
auto new_screen_info = screen_infos_.current();
gfx::Size size = GetViewBounds().size();
if (!force && !sf_did_change && size == root_layer()->bounds().size())
if (!force && size == root_layer()->bounds().size() &&
old_screen_info == new_screen_info)
return;
root_layer()->SetBounds(gfx::Rect(size));
const gfx::Size& size_in_pixels =
gfx::ToFlooredSize(gfx::ConvertSizeToPixels(size, sf));
auto sf = GetDeviceScaleFactor();
const gfx::Size& size_in_pixels = SizeInPixels();
if (compositor_) {
compositor_allocator_.GenerateId();
compositor_surface_id_ = compositor_allocator_.GetCurrentLocalSurfaceId();
compositor_->SetScaleAndSize(sf, size_in_pixels, compositor_surface_id_);
compositor_->SetDisplayColorSpaces(new_screen_info.display_color_spaces);
}
delegated_frame_host_allocator_.GenerateId();

View File

@@ -73,6 +73,7 @@ class OffScreenRenderWidgetHostView
bool transparent,
bool offscreen_use_shared_texture,
const std::string& offscreen_shared_texture_pixel_format,
float offscreen_device_scale_factor,
bool painting,
int frame_rate,
const OnPaintCallback& callback,
@@ -151,7 +152,6 @@ class OffScreenRenderWidgetHostView
base::TimeDelta timeout,
base::OnceCallback<void(const content::CopyFromSurfaceResult&)> callback)
override;
display::ScreenInfo GetScreenInfo() const override;
void TransformPointToRootSurface(gfx::PointF* point) override {}
gfx::Rect GetBoundsInRootWindow() override;
std::optional<content::DisplayFeature> GetDisplayFeature() override;
@@ -171,6 +171,7 @@ class OffScreenRenderWidgetHostView
const std::optional<std::vector<gfx::Rect>>& character_bounds) override {}
gfx::Size GetCompositorViewportPixelSize() override;
ui::Compositor* GetCompositor() override;
display::ScreenInfos GetNewScreenInfosForUpdate() override;
content::RenderWidgetHostViewBase* CreateViewForWidget(
content::RenderWidgetHost*,
@@ -293,6 +294,8 @@ class OffScreenRenderWidgetHostView
const bool transparent_;
const bool offscreen_use_shared_texture_;
const std::string offscreen_shared_texture_pixel_format_;
float offscreen_device_scale_factor_;
OnPaintCallback callback_;
OnPopupPaintCallback parent_callback_;

View File

@@ -17,11 +17,13 @@ OffScreenWebContentsView::OffScreenWebContentsView(
bool transparent,
bool offscreen_use_shared_texture,
const std::string& offscreen_shared_texture_pixel_format,
float offscreen_device_scale_factor,
const OnPaintCallback& callback)
: transparent_(transparent),
offscreen_use_shared_texture_(offscreen_use_shared_texture),
offscreen_shared_texture_pixel_format_(
offscreen_shared_texture_pixel_format),
offscreen_device_scale_factor_(offscreen_device_scale_factor),
callback_(callback) {
#if BUILDFLAG(IS_MAC)
PlatformCreate();
@@ -116,8 +118,9 @@ OffScreenWebContentsView::CreateViewForWidget(
return new OffScreenRenderWidgetHostView(
transparent_, offscreen_use_shared_texture_,
offscreen_shared_texture_pixel_format_, painting_, GetFrameRate(),
callback_, render_widget_host, nullptr, GetSize());
offscreen_shared_texture_pixel_format_, offscreen_device_scale_factor_,
painting_, GetFrameRate(), callback_, render_widget_host, nullptr,
GetSize());
}
content::RenderWidgetHostViewBase*
@@ -137,9 +140,9 @@ OffScreenWebContentsView::CreateViewForChildWidget(
return new OffScreenRenderWidgetHostView(
transparent_, offscreen_use_shared_texture_,
offscreen_shared_texture_pixel_format_, painting_,
embedder_host_view->frame_rate(), callback_, render_widget_host,
embedder_host_view, GetSize());
offscreen_shared_texture_pixel_format_, offscreen_device_scale_factor_,
painting_, embedder_host_view->frame_rate(), callback_,
render_widget_host, embedder_host_view, GetSize());
}
void OffScreenWebContentsView::RenderViewReady() {

View File

@@ -38,6 +38,7 @@ class OffScreenWebContentsView : public content::WebContentsView,
bool transparent,
bool offscreen_use_shared_texture,
const std::string& offscreen_shared_texture_pixel_format,
float offscreen_device_scale_factor,
const OnPaintCallback& callback);
~OffScreenWebContentsView() override;
@@ -113,6 +114,7 @@ class OffScreenWebContentsView : public content::WebContentsView,
const bool transparent_;
const bool offscreen_use_shared_texture_;
const std::string offscreen_shared_texture_pixel_format_;
const float offscreen_device_scale_factor_;
bool painting_ = true;
int frame_rate_ = 60;
OnPaintCallback callback_;

View File

@@ -484,11 +484,8 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
if (index < 0 || index >= count)
return;
// When the menu is closed, we need to allow shortcuts to be triggered even
// if the menu item is disabled. So we only disable the menu item when the
// menu is open. This matches behavior of |validateUserInterfaceItem|.
item.enabled = model->IsEnabledAt(index) || !isMenuOpen_;
item.hidden = !model->IsVisibleAt(index);
item.enabled = model->IsEnabledAt(index);
item.state = model->IsItemCheckedAt(index) ? NSControlStateValueOn
: NSControlStateValueOff;
}
@@ -560,14 +557,6 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
- (void)menuWillOpen:(NSMenu*)menu {
isMenuOpen_ = YES;
// macOS automatically injects a duplicate "Toggle Full Screen" menu item
// when we set menu.delegate on submenus. Remove hidden duplicates.
for (NSMenuItem* item in menu.itemArray) {
if (item.isHidden && item.action == @selector(toggleFullScreenMode:))
[menu removeItem:item];
}
[self refreshMenuTree:menu];
if (model_)
model_->MenuWillShow();
@@ -578,19 +567,18 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
if (!isMenuOpen_)
return;
isMenuOpen_ = NO;
[self refreshMenuTree:menu];
bool has_close_cb = !popupCloseCallback.is_null();
// There are two scenarios where we should emit menu-did-close:
// 1. It's a popup and the top level menu is closed.
// 2. It's an application menu, and the current menu's supermenu
// is the top-level menu.
bool has_close_cb = !popupCloseCallback.is_null();
if (menu != menu_) {
if (has_close_cb || menu.supermenu != menu_)
return;
}
isMenuOpen_ = NO;
if (model_)
model_->MenuWillClose();
// Post async task so that itemSelected runs before the close callback

View File

@@ -49,10 +49,6 @@
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#if BUILDFLAG(IS_WIN)
#include "ui/accessibility/platform/ax_platform_node_win.h"
#endif
namespace {
constexpr std::string_view kTargetsDataFile = "targets-data.json";
@@ -155,28 +151,6 @@ bool ShouldHandleAccessibilityRequestCallback(const std::string& path) {
return path == kTargetsDataFile;
}
// Sets boolean values in `data` for each bit in `new_ax_mode` that differs from
// that in `last_ax_mode`. Returns `true` if `data` was modified.
void SetProcessModeBools(ui::AXMode ax_mode, base::Value::Dict& data) {
data.Set(kNative, ax_mode.has_mode(ui::AXMode::kNativeAPIs));
data.Set(kWeb, ax_mode.has_mode(ui::AXMode::kWebContents));
data.Set(kText, ax_mode.has_mode(ui::AXMode::kInlineTextBoxes));
data.Set(kExtendedProperties,
ax_mode.has_mode(ui::AXMode::kExtendedProperties));
data.Set(kHTML, ax_mode.has_mode(ui::AXMode::kHTML));
data.Set(kScreenReader, ax_mode.has_mode(ui::AXMode::kScreenReader));
}
#if BUILDFLAG(IS_WIN)
// Sets values in `data` for the platform node counts in `counts`.
void SetNodeCounts(const ui::AXPlatformNodeWin::Counts& counts,
base::Value::Dict& data) {
data.Set("dormantCount", base::NumberToString(counts.dormant_nodes));
data.Set("liveCount", base::NumberToString(counts.live_nodes));
data.Set("ghostCount", base::NumberToString(counts.ghost_nodes));
}
#endif
void HandleAccessibilityRequestCallback(
content::BrowserContext* current_context,
ui::AXMode initial_process_mode,
@@ -320,10 +294,6 @@ void HandleAccessibilityRequestCallback(
}
data.Set(kBrowsersField, std::move(window_list));
#if BUILDFLAG(IS_WIN)
SetNodeCounts(ui::AXPlatformNodeWin::GetCounts(), data);
#endif
std::move(callback).Run(base::MakeRefCounted<base::RefCountedString>(
base::WriteJson(data).value_or("")));
}
@@ -411,13 +381,8 @@ ElectronAccessibilityUI::ElectronAccessibilityUI(content::WebUI* web_ui)
ElectronAccessibilityUI::~ElectronAccessibilityUI() = default;
ElectronAccessibilityUIMessageHandler::ElectronAccessibilityUIMessageHandler()
: update_display_timer_(
FROM_HERE,
base::Seconds(1),
base::BindRepeating(
&ElectronAccessibilityUIMessageHandler::OnUpdateDisplayTimer,
base::Unretained(this))) {}
ElectronAccessibilityUIMessageHandler::ElectronAccessibilityUIMessageHandler() =
default;
void ElectronAccessibilityUIMessageHandler::GetRequestTypeAndFilters(
const base::Value::Dict& data,
@@ -507,10 +472,6 @@ void ElectronAccessibilityUIMessageHandler::RegisterMessages() {
base::BindRepeating(
&AccessibilityUIMessageHandler::RequestAccessibilityEvents,
base::Unretained(this)));
auto* web_contents = web_ui()->GetWebContents();
Observe(web_contents);
OnVisibilityChanged(web_contents->GetVisibility());
}
// static
@@ -521,45 +482,3 @@ void ElectronAccessibilityUIMessageHandler::RegisterPrefs(
registry->RegisterStringPref(prefs::kShownAccessibilityApiType,
std::string(default_api_type));
}
void ElectronAccessibilityUIMessageHandler::OnVisibilityChanged(
content::Visibility visibility) {
if (visibility == content::Visibility::HIDDEN) {
update_display_timer_.Stop();
} else {
update_display_timer_.Reset();
}
}
void ElectronAccessibilityUIMessageHandler::OnUpdateDisplayTimer() {
// Collect the current state.
base::Value::Dict data;
SetProcessModeBools(
content::BrowserAccessibilityState::GetInstance()->GetAccessibilityMode(),
data);
#if BUILDFLAG(IS_WIN)
SetNodeCounts(ui::AXPlatformNodeWin::GetCounts(), data);
#endif // BUILDFLAG(IS_WIN)
// Compute the delta from the last transmission.
for (auto scan = data.begin(); scan != data.end();) {
const auto& [new_key, new_value] = *scan;
if (const auto* old_value = last_data_.Find(new_key);
!old_value || *old_value != new_value) {
// This is a new value; remember it for the future.
last_data_.Set(new_key, new_value.Clone());
++scan;
} else {
// This is the same as the last value; forget about it.
scan = data.erase(scan);
}
}
// Transmit any new values to the UI.
if (!data.empty()) {
AllowJavascript();
FireWebUIListener("updateDisplay", data);
}
}

View File

@@ -40,19 +40,6 @@ class ElectronAccessibilityUIMessageHandler
std::string& allow_empty,
std::string& deny);
void RequestNativeUITree(const base::Value::List& args);
// content::WebContentsObserver:
void OnVisibilityChanged(content::Visibility visibility) override;
// Updates the UI with new data. Called periodically to keep the UI up-to-date
// while it is visible.
void OnUpdateDisplayTimer();
// The last data for display sent to the UI by OnUpdateDisplayTimer.
base::Value::Dict last_data_;
// A timer that runs while the UI is visible to call OnUpdateDisplayTimer.
base::RepeatingTimer update_display_timer_;
};
#endif // ELECTRON_SHELL_BROWSER_UI_WEBUI_ACCESSIBILITY_UI_H_

View File

@@ -149,6 +149,7 @@ void WebContentsPreferences::Clear() {
preload_path_ = std::nullopt;
v8_cache_options_ = blink::mojom::V8CacheOptions::kDefault;
deprecated_paste_enabled_ = false;
focus_on_navigation_ = true;
#if BUILDFLAG(IS_MAC)
scroll_bounce_ = false;
@@ -249,6 +250,8 @@ void WebContentsPreferences::SetFromDictionary(
web_preferences.Get(options::kEnableDeprecatedPaste,
&deprecated_paste_enabled_);
web_preferences.Get(options::kFocusOnNavigation, &focus_on_navigation_);
#if BUILDFLAG(IS_MAC)
web_preferences.Get(options::kScrollBounce, &scroll_bounce_);
#endif

View File

@@ -78,6 +78,7 @@ class WebContentsPreferences
bool ShouldDisablePopups() const { return disable_popups_; }
bool IsWebSecurityEnabled() const { return web_security_; }
std::optional<base::FilePath> GetPreloadPath() const { return preload_path_; }
bool ShouldFocusOnNavigation() const { return focus_on_navigation_; }
bool IsSandboxed() const;
private:
@@ -134,6 +135,7 @@ class WebContentsPreferences
std::optional<base::FilePath> preload_path_;
blink::mojom::V8CacheOptions v8_cache_options_;
bool deprecated_paste_enabled_ = false;
bool focus_on_navigation_;
#if BUILDFLAG(IS_MAC)
bool scroll_bounce_;

View File

@@ -127,6 +127,8 @@ std::string TransferVideoPixelFormatToString(media::VideoPixelFormat format) {
return "rgbaf16";
case media::PIXEL_FORMAT_NV12:
return "nv12";
case media::PIXEL_FORMAT_P010LE:
return "p010le";
default:
NOTREACHED();
}
@@ -569,6 +571,8 @@ struct Converter<ImportSharedTextureInfo> {
out->pixel_format = media::PIXEL_FORMAT_RGBAF16;
else if (pixel_format_str == "nv12")
out->pixel_format = media::PIXEL_FORMAT_NV12;
else if (pixel_format_str == "p010le")
out->pixel_format = media::PIXEL_FORMAT_P010LE;
else
return false;
}

View File

@@ -1,117 +0,0 @@
// Copyright (c) 2026 Microsoft GmbH.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/electron_paths.h"
#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "shell/common/application_info.h"
#include "shell/common/platform_util.h"
#include "shell/common/thread_restrictions.h"
#if BUILDFLAG(IS_LINUX)
#include "base/nix/xdg_util.h"
#endif
namespace electron {
namespace {
bool ElectronPathProvider(int key, base::FilePath* result) {
bool create_dir = false;
base::FilePath cur;
switch (key) {
case chrome::DIR_USER_DATA:
if (!base::PathService::Get(DIR_APP_DATA, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
GetPossiblyOverriddenApplicationName()));
create_dir = true;
break;
case DIR_CRASH_DUMPS:
if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
return false;
cur = cur.Append(FILE_PATH_LITERAL("Crashpad"));
create_dir = true;
break;
case chrome::DIR_APP_DICTIONARIES:
// TODO(nornagon): can we just default to using Chrome's logic here?
if (!base::PathService::Get(DIR_SESSION_DATA, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe("Dictionaries"));
create_dir = true;
break;
case DIR_SESSION_DATA:
// By default and for backward, equivalent to DIR_USER_DATA.
return base::PathService::Get(chrome::DIR_USER_DATA, result);
case DIR_USER_CACHE: {
#if BUILDFLAG(IS_POSIX)
int parent_key = base::DIR_CACHE;
#else
// On Windows, there's no OS-level centralized location for caches, so
// store the cache in the app data directory.
int parent_key = base::DIR_ROAMING_APP_DATA;
#endif
if (!base::PathService::Get(parent_key, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
GetPossiblyOverriddenApplicationName()));
create_dir = true;
break;
}
#if BUILDFLAG(IS_LINUX)
case DIR_APP_DATA: {
auto env = base::Environment::Create();
cur = base::nix::GetXDGDirectory(
env.get(), base::nix::kXdgConfigHomeEnvVar, base::nix::kDotConfigDir);
break;
}
#endif
#if BUILDFLAG(IS_WIN)
case DIR_RECENT:
if (!platform_util::GetFolderPath(DIR_RECENT, &cur))
return false;
create_dir = true;
break;
#endif
case DIR_APP_LOGS:
#if BUILDFLAG(IS_MAC)
if (!base::PathService::Get(base::DIR_HOME, &cur))
return false;
cur = cur.Append(FILE_PATH_LITERAL("Library"));
cur = cur.Append(FILE_PATH_LITERAL("Logs"));
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
GetPossiblyOverriddenApplicationName()));
#else
if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe("logs"));
#endif
create_dir = true;
break;
default:
return false;
}
// TODO(bauerb): http://crbug.com/259796
ScopedAllowBlockingForElectron allow_blocking;
if (create_dir && !base::PathExists(cur) && !base::CreateDirectory(cur)) {
return false;
}
*result = cur;
return true;
}
} // namespace
void RegisterPathProvider() {
base::PathService::RegisterProvider(ElectronPathProvider, PATH_START,
PATH_END);
}
} // namespace electron

View File

@@ -6,7 +6,6 @@
#define ELECTRON_SHELL_COMMON_ELECTRON_PATHS_H_
#include "base/base_paths.h"
#include "base/files/file_path.h"
#if BUILDFLAG(IS_WIN)
#include "base/base_paths_win.h"
@@ -48,9 +47,6 @@ enum {
static_assert(PATH_START < PATH_END, "invalid PATH boundaries");
// Register the path provider with the base::PathService.
void RegisterPathProvider();
} // namespace electron
#endif // ELECTRON_SHELL_COMMON_ELECTRON_PATHS_H_

View File

@@ -33,6 +33,7 @@
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/handle.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/gin_helper/wrappable.h"
#include "shell/common/node_includes.h"
#include "shell/common/node_util.h"
#include "shell/common/v8_util.h"
@@ -538,7 +539,7 @@ v8::Local<v8::Value> Converter<network::ResourceRequestBody>::ToV8(
// TODO(zcbenz): After the NetworkService refactor, the old blobUUID API
// becomes unnecessarily complex, we should deprecate the getBlobData
// API and return the DataPipeHolder wrapper directly.
auto holder = electron::api::DataPipeHolder::Create(isolate, element);
auto* holder = electron::api::DataPipeHolder::Create(isolate, element);
upload_data.Set("blobUUID", holder->id());
// The lifetime of data pipe is bound to the uploadData object.
upload_data.Set("dataPipe", holder);

View File

@@ -4,31 +4,11 @@
#include "shell/common/gin_helper/trackable_object.h"
#include <memory>
#include "base/functional/bind.h"
#include "base/supports_user_data.h"
#include "shell/browser/electron_browser_main_parts.h"
#include "shell/common/process_util.h"
namespace gin_helper {
namespace {
const char kTrackedObjectKey[] = "TrackedObjectKey";
class IDUserData : public base::SupportsUserData::Data {
public:
explicit IDUserData(int32_t id) : id_(id) {}
[[nodiscard]] auto id() { return id_; }
private:
int32_t id_;
};
} // namespace
TrackableObjectBase::TrackableObjectBase() {
// TODO(zcbenz): Make TrackedObject work in renderer process.
DCHECK(electron::IsBrowserProcess())
@@ -46,21 +26,4 @@ void TrackableObjectBase::Destroy() {
delete this;
}
void TrackableObjectBase::AttachAsUserData(base::SupportsUserData* wrapped) {
wrapped->SetUserData(kTrackedObjectKey,
std::make_unique<IDUserData>(weak_map_id_));
}
// static
int32_t TrackableObjectBase::GetIDFromWrappedClass(
base::SupportsUserData* wrapped) {
if (wrapped) {
auto* id =
static_cast<IDUserData*>(wrapped->GetUserData(kTrackedObjectKey));
if (id)
return id->id();
}
return 0;
}
} // namespace gin_helper

View File

@@ -12,10 +12,6 @@
#include "shell/common/gin_helper/event_emitter.h"
#include "shell/common/key_weak_map.h"
namespace base {
class SupportsUserData;
}
namespace gin_helper {
// Users should use TrackableObject instead.
@@ -30,12 +26,6 @@ class TrackableObjectBase : public CleanedUpAtExit {
// The ID in weak map.
[[nodiscard]] constexpr int32_t weak_map_id() const { return weak_map_id_; }
// Wrap TrackableObject into a class that SupportsUserData.
void AttachAsUserData(base::SupportsUserData* wrapped);
// Get the weak_map_id from SupportsUserData.
static int32_t GetIDFromWrappedClass(base::SupportsUserData* wrapped);
protected:
~TrackableObjectBase() override;
@@ -90,15 +80,6 @@ class TrackableObject : public TrackableObjectBase, public EventEmitter<T> {
return self;
}
// Finds out the TrackableObject from the class it wraps.
static T* FromWrappedClass(v8::Isolate* isolate,
base::SupportsUserData* wrapped) {
int32_t id = GetIDFromWrappedClass(wrapped);
if (!id)
return nullptr;
return FromWeakMapID(isolate, id);
}
// Returns all objects in this class's weak map.
static std::vector<v8::Local<v8::Object>> GetAll(v8::Isolate* isolate) {
if (weak_map_)

View File

@@ -68,7 +68,6 @@
V(electron_browser_in_app_purchase) \
V(electron_browser_menu) \
V(electron_browser_message_port) \
V(electron_browser_msix_updater) \
V(electron_browser_native_theme) \
V(electron_browser_notification) \
V(electron_browser_power_monitor) \

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