Merge remote-tracking branch 'origin/main' into roller/chromium/main

This commit is contained in:
Samuel Maddock
2026-02-06 12:42:24 -05:00
34 changed files with 1192 additions and 328 deletions

View File

@@ -40,7 +40,7 @@ runs:
echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV
- name: Set GN_EXTRA_ARGS for Windows
shell: bash
if: ${{ inputs.target-arch != 'x64' && inputs.target-platform == 'win' }}
if: ${{inputs.target-arch != 'x64' && inputs.target-platform == 'win' }}
run: |
GN_APPENDED_ARGS="$GN_EXTRA_ARGS target_cpu=\"${{ inputs.target-arch }}\""
echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV
@@ -219,6 +219,7 @@ runs:
- name: Publish Electron Dist ${{ inputs.step-suffix }}
if: ${{ inputs.is-release == 'true' }}
shell: bash
id: github-upload
run: |
rm -rf src/out/Default/obj
cd src/electron
@@ -229,6 +230,11 @@ runs:
echo 'Uploading Electron release distribution to GitHub releases'
script/release/uploaders/upload.py --verbose
fi
- name: Generate artifact attestation
if: ${{ inputs.is-release == 'true' }}
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
with:
subject-path: ${{ steps.github-upload.outputs.UPLOADED_PATHS }}
- name: Generate siso report
if: ${{ inputs.target-platform != 'win' && !cancelled() }}
shell: bash

View File

@@ -56,16 +56,16 @@ jobs:
path: src/electron
fetch-depth: 0
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha }}
- name: Rebase onto Base Branch
ref: ${{ github.event.pull_request.base.ref }}
- name: Merge PR HEAD
working-directory: src/electron
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
git config user.email "electron@github.com"
git config user.name "Electron Bot"
git fetch origin ${BASE_REF}
git rebase origin/${BASE_REF}
git fetch origin refs/pull/${PR_NUMBER}/head
git merge --squash FETCH_HEAD
- name: Checkout & Sync & Save
uses: ./src/electron/.github/actions/checkout
with:

View File

@@ -44,9 +44,11 @@ jobs:
uses: ./src/electron/.github/actions/checkout
publish-x64:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-linux
with:
environment: production-release
@@ -61,9 +63,11 @@ jobs:
secrets: inherit
publish-arm:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-linux
with:
environment: production-release
@@ -78,9 +82,11 @@ jobs:
secrets: inherit
publish-arm64:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-linux
with:
environment: production-release

View File

@@ -48,9 +48,11 @@ jobs:
target-platform: macos
publish-x64-darwin:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-macos
with:
environment: production-release
@@ -65,9 +67,11 @@ jobs:
secrets: inherit
publish-x64-mas:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-macos
with:
environment: production-release
@@ -82,9 +86,11 @@ jobs:
secrets: inherit
publish-arm64-darwin:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-macos
with:
environment: production-release
@@ -99,9 +105,11 @@ jobs:
secrets: inherit
publish-arm64-mas:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-macos
with:
environment: production-release

View File

@@ -85,4 +85,8 @@ jobs:
run: |
cd src/electron
node script/yarn.js tsc -p tsconfig.script.json
- name: Check GHA Workflows
shell: bash
run: |
cd src/electron
node script/copy-pipeline-segment-publish.js --check

View File

@@ -78,12 +78,7 @@ env:
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
SUDOWOODO_EXCHANGE_URL: ${{ secrets.SUDOWOODO_EXCHANGE_URL }}
SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }}
GCLIENT_EXTRA_ARGS: |-
${{ case(
inputs.target-platform == 'macos', '--custom-var=checkout_mac=True --custom-var=host_os=mac',
inputs.target-platform == 'win', '--custom-var=checkout_win=True',
'--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
) }}
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || inputs.target-platform == 'win' && '--custom-var=checkout_win=True' || '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
ELECTRON_OUT_DIR: Default
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
@@ -206,7 +201,7 @@ jobs:
with:
target-arch: ${{ inputs.target-arch }}
target-platform: ${{ inputs.target-platform }}
artifact-platform: ${{ case(inputs.target-platform == 'macos', 'darwin', inputs.target-platform) }}
artifact-platform: ${{ inputs.target-platform == 'macos' && 'darwin' || inputs.target-platform }}
is-release: '${{ inputs.is-release }}'
generate-symbols: '${{ inputs.generate-symbols }}'
upload-to-storage: '${{ inputs.upload-to-storage }}'

View File

@@ -28,12 +28,7 @@ concurrency:
cancel-in-progress: true
env:
GCLIENT_EXTRA_ARGS: |-
${{ case(
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'
) }}
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:
@@ -46,10 +41,10 @@ jobs:
contents: read
container: ${{ fromJSON(inputs.clang-tidy-container) }}
env:
BUILD_TYPE: ${{ case(inputs.target-platform == 'macos', 'darwin', inputs.target-platform) }}
BUILD_TYPE: ${{ inputs.target-platform == 'macos' && 'darwin' || inputs.target-platform }}
TARGET_ARCH: ${{ inputs.target-arch }}
TARGET_PLATFORM: ${{ inputs.target-platform }}
ARTIFACT_KEY: ${{ case(inputs.target-platform == 'macos', 'darwin', inputs.target-platform) }}_${{ inputs.target-arch }}
ARTIFACT_KEY: ${{ inputs.target-platform == 'macos' && 'darwin' || inputs.target-platform }}_${{ inputs.target-arch }}
steps:
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

View File

@@ -34,12 +34,7 @@ concurrency:
env:
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
GCLIENT_EXTRA_ARGS: |-
${{ case(
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'
) }}
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:

View File

@@ -0,0 +1,242 @@
# AUTOGENERATED FILE - DO NOT EDIT MANUALLY
# ONLY EDIT .github/workflows/pipeline-segment-electron-build.yml
name: Pipeline Segment - Electron Build
on:
workflow_call:
inputs:
environment:
description: using the production or testing environment
required: false
type: string
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, ia32 or arm
required: true
target-variant:
type: string
description: Variant to build for, no effect on non-macOS target platforms. Can
be darwin, mas or all.
default: all
build-runs-on:
type: string
description: What host to run the build
required: true
build-container:
type: string
description: JSON container information for aks runs-on
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
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
type: boolean
default: false
permissions: {}
concurrency:
group: electron-build-${{ inputs.target-platform }}-${{ inputs.target-arch
}}-${{ inputs.target-variant }}-${{ inputs.is-asan }}-${{
github.ref_protected == true && github.run_id || github.ref }}
cancel-in-progress: ${{ github.ref_protected != true }}
env:
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}
DD_API_KEY: ${{ secrets.DD_API_KEY }}
ELECTRON_ARTIFACTS_BLOB_STORAGE: ${{ secrets.ELECTRON_ARTIFACTS_BLOB_STORAGE }}
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
SUDOWOODO_EXCHANGE_URL: ${{ secrets.SUDOWOODO_EXCHANGE_URL }}
SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }}
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' &&
'--custom-var=checkout_mac=True --custom-var=host_os=mac' ||
inputs.target-platform == 'win' && '--custom-var=checkout_win=True' ||
'--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
ELECTRON_OUT_DIR: Default
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
jobs:
build:
defaults:
run:
shell: bash
runs-on: ${{ inputs.build-runs-on }}
permissions:
attestations: write
contents: read
id-token: write
container: ${{ fromJSON(inputs.build-container) }}
environment: ${{ inputs.environment }}
env:
TARGET_ARCH: ${{ inputs.target-arch }}
TARGET_PLATFORM: ${{ inputs.target-platform }}
steps:
- name: Create src dir
run: |
mkdir src
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup SSH Debugging
if: ${{ inputs.target-platform == 'macos' && (inputs.enable-ssh ||
env.ACTIONS_STEP_DEBUG == 'true') }}
uses: ./src/electron/.github/actions/ssh-debug
with:
tunnel: "true"
env:
CLOUDFLARE_TUNNEL_CERT: ${{ secrets.CLOUDFLARE_TUNNEL_CERT }}
CLOUDFLARE_TUNNEL_HOSTNAME: ${{ vars.CLOUDFLARE_TUNNEL_HOSTNAME }}
CLOUDFLARE_USER_CA_CERT: ${{ secrets.CLOUDFLARE_USER_CA_CERT }}
AUTHORIZED_USERS: ${{ secrets.SSH_DEBUG_AUTHORIZED_USERS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Free up space (macOS)
if: ${{ inputs.target-platform == 'macos' }}
uses: ./src/electron/.github/actions/free-space-macos
- name: Check disk space after freeing up space
if: ${{ inputs.target-platform == 'macos' }}
run: df -h
- name: Setup Node.js/npm
if: ${{ inputs.target-platform == 'macos' }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238
with:
node-version: 22.21.x
cache: yarn
cache-dependency-path: src/electron/yarn.lock
- name: Install Dependencies
uses: ./src/electron/.github/actions/install-dependencies
- name: Install AZCopy
if: ${{ inputs.target-platform == 'macos' }}
run: brew install azcopy
- name: Set GN_EXTRA_ARGS for Linux
if: ${{ inputs.target-platform == 'linux' }}
run: >
if [ "${{ inputs.target-arch }}" = "arm" ]; then
if [ "${{ inputs.is-release }}" = true ]; then
GN_EXTRA_ARGS='target_cpu="arm" build_tflite_with_xnnpack=false symbol_level=1'
else
GN_EXTRA_ARGS='target_cpu="arm" build_tflite_with_xnnpack=false'
fi
elif [ "${{ inputs.target-arch }}" = "arm64" ]; then
GN_EXTRA_ARGS='target_cpu="arm64" fatal_linker_warnings=false enable_linux_installer=false'
elif [ "${{ inputs.is-asan }}" = true ]; then
GN_EXTRA_ARGS='is_asan=true'
fi
echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV
- name: Set Chromium Git Cookie
uses: ./src/electron/.github/actions/set-chromium-cookie
- name: Install Build Tools
uses: ./src/electron/.github/actions/install-build-tools
- name: 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 != 'linux' }}
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' }}
uses: ./src/electron/.github/actions/restore-cache-aks
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Fix Sync
if: ${{ inputs.target-platform != 'linux' }}
uses: ./src/electron/.github/actions/fix-sync
with:
target-platform: ${{ inputs.target-platform }}
env:
ELECTRON_DEPOT_TOOLS_DISABLE_LOG: true
- name: Init Build Tools
run: >
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }}
--import ${{ inputs.gn-build-type }} --target-cpu ${{
inputs.target-arch }} --remote-build siso
- name: Run Electron Only Hooks
run: |
e d gclient runhooks --spec="solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]"
- 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: Free up space (macOS)
if: ${{ inputs.target-platform == 'macos' }}
uses: ./src/electron/.github/actions/free-space-macos
- name: Build Electron
if: ${{ inputs.target-platform != 'macos' || (inputs.target-variant == 'all' ||
inputs.target-variant == 'darwin') }}
uses: ./src/electron/.github/actions/build-electron
with:
target-arch: ${{ inputs.target-arch }}
target-platform: ${{ inputs.target-platform }}
artifact-platform: ${{ inputs.target-platform == 'macos' && 'darwin' ||
inputs.target-platform }}
is-release: ${{ inputs.is-release }}
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: |
echo "MAS_BUILD=true" >> $GITHUB_ENV
GN_EXTRA_ARGS='is_mas_build=true'
echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV
- name: Build Electron (MAS)
if: ${{ inputs.target-platform == 'macos' && (inputs.target-variant == 'all' ||
inputs.target-variant == 'mas') }}
uses: ./src/electron/.github/actions/build-electron
with:
target-arch: ${{ inputs.target-arch }}
target-platform: ${{ inputs.target-platform }}
artifact-platform: mas
is-release: ${{ inputs.is-release }}
generate-symbols: ${{ inputs.generate-symbols }}
upload-to-storage: ${{ inputs.upload-to-storage }}
step-suffix: (mas)

View File

@@ -58,13 +58,8 @@ jobs:
strategy:
fail-fast: false
matrix:
build-type: |-
${{ case(
inputs.target-platform == 'macos', fromJSON('["darwin","mas"]'),
inputs.target-platform == 'win', fromJSON('["win"]'),
fromJSON('["linux"]')
) }}
shard: ${{ case(inputs.target-platform == 'linux', fromJSON('[1, 2, 3]'), fromJSON('[1, 2]')) }}
build-type: ${{ inputs.target-platform == 'macos' && fromJSON('["darwin","mas"]') || (inputs.target-platform == 'win' && fromJSON('["win"]') || fromJSON('["linux"]')) }}
shard: ${{ inputs.target-platform == 'linux' && fromJSON('[1, 2, 3]') || fromJSON('[1, 2]') }}
env:
BUILD_TYPE: ${{ matrix.build-type }}
TARGET_ARCH: ${{ inputs.target-arch }}

View File

@@ -60,6 +60,7 @@ jobs:
with:
actions: 'create-comment'
token: ${{ steps.generate-token.outputs.token }}
issue-number: ${{ github.event.pull_request.number }}
body: |
<!-- ai-pr -->

View File

@@ -52,9 +52,11 @@ jobs:
target-platform: win
publish-x64-win:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-windows
with:
environment: production-release
@@ -68,9 +70,11 @@ jobs:
secrets: inherit
publish-arm64-win:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-windows
with:
environment: production-release
@@ -84,9 +88,11 @@ jobs:
secrets: inherit
publish-x86-win:
uses: ./.github/workflows/pipeline-segment-electron-build.yml
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
attestations: write
contents: read
id-token: write
needs: checkout-windows
with:
environment: production-release

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

@@ -107,7 +107,7 @@ A `string` (optional) indicating the item's role, if set. Can be `undo`, `redo`,
#### `menuItem.accelerator`
An `Accelerator` (optional) indicating the item's accelerator, if set.
An `Accelerator | null` indicating the item's accelerator, if set.
#### `menuItem.userAccelerator` _Readonly_ _macOS_

View File

@@ -36,6 +36,12 @@ Process: [Main](../glossary.md#main-process)<br />
`com.apple.security.cs.allow-unsigned-executable-memory` entitlements. This will allow the utility process
to load unsigned libraries. Unless you specifically need this capability, it is best to leave this disabled.
Default is `false`.
* `disclaim` boolean (optional) _macOS_ - With this flag, the utility process will disclaim
responsibility for the child process. This causes the operating system to consider the child
process as a separate entity for purposes of security policies like Transparency, Consent, and
Control (TCC). When responsibility is disclaimed, the parent process will not be attributed
for any TCC requests initiated by the child process. This is useful when launching processes
that run third-party or otherwise untrusted code. Default is `false`.
* `respondToAuthRequestsFromMainProcess` boolean (optional) - With this flag, all HTTP 401 and 407 network
requests created via the [net module](net.md) will allow responding to them via the
[`app#login`](app.md#event-login) event in the main process instead of the default

View File

@@ -279,6 +279,8 @@ filenames = {
"shell/browser/api/electron_api_in_app_purchase.h",
"shell/browser/api/electron_api_menu.cc",
"shell/browser/api/electron_api_menu.h",
"shell/browser/api/electron_api_msix_updater.cc",
"shell/browser/api/electron_api_msix_updater.h",
"shell/browser/api/electron_api_native_theme.cc",
"shell/browser/api/electron_api_native_theme.h",
"shell/browser/api/electron_api_net_log.cc",

View File

@@ -353,6 +353,7 @@ export function shouldOverrideCheckStatus (role: RoleId) {
export function getDefaultAccelerator (role: RoleId) {
if (hasRole(role)) return roleList[role].accelerator;
return undefined;
}
export function shouldRegisterAccelerator (role: RoleId) {

View File

@@ -25,7 +25,7 @@ const MenuItem = function (this: any, options: any) {
this.overrideReadOnlyProperty('type', roles.getDefaultType(this.role));
this.overrideReadOnlyProperty('role');
this.overrideReadOnlyProperty('accelerator');
this.overrideReadOnlyProperty('accelerator', roles.getDefaultAccelerator(this.role));
this.overrideReadOnlyProperty('icon');
this.overrideReadOnlyProperty('submenu');

View File

@@ -57,7 +57,8 @@
"url": "^0.11.4",
"webpack": "^5.95.0",
"webpack-cli": "^6.0.1",
"wrapper-webpack-plugin": "^2.2.0"
"wrapper-webpack-plugin": "^2.2.0",
"yaml": "^2.8.1"
},
"private": true,
"scripts": {
@@ -132,6 +133,10 @@
"DEPS": [
"node script/gen-hunspell-filenames.js",
"node script/gen-libc++-filenames.js"
],
".github/workflows/pipeline-segment-electron-build.yml": [
"node script/copy-pipeline-segment-publish.js",
"git add .github/workflows/pipeline-segment-electron-publish.yml"
]
},
"resolutions": {

View File

@@ -9,6 +9,8 @@ Subject: feat: configure launch options for service process
Allows configuring base::LaunchOptions::handles_to_inherit, base::LaunchOptions::stdout_handle,
base::LaunchOptions::stderr_handle and base::LaunchOptions::feedback_cursor_off when launching
the child process.
- Mac:
Allows configuring base::LaunchOptions::disclaim_responsibility when launching the child process.
- All:
Allows configuring base::LauncOptions::current_directory, base::LaunchOptions::enviroment
and base::LaunchOptions::clear_environment.
@@ -165,10 +167,10 @@ index 34b4e7c1b449312e9cb517be192a39a6b5286e3c..4f39415717f423b9a87f83779851e08b
FinishStartSandboxedProcessOnLauncherThread,
this));
diff --git a/content/browser/service_host/service_process_host_impl.cc b/content/browser/service_host/service_process_host_impl.cc
index d9c14f91747bde0e76056d7f2f2ada166e67f994..53be16879777a3b9bef58ead5f7e420c1bf6acbe 100644
index d9c14f91747bde0e76056d7f2f2ada166e67f994..09335acac17f526fb8d8e42e4b2d993b11045786 100644
--- a/content/browser/service_host/service_process_host_impl.cc
+++ b/content/browser/service_host/service_process_host_impl.cc
@@ -69,6 +69,17 @@ void LaunchServiceProcess(mojo::GenericPendingReceiver receiver,
@@ -69,6 +69,21 @@ void LaunchServiceProcess(mojo::GenericPendingReceiver receiver,
utility_options.WithGpuClientAllowed();
}
@@ -179,6 +181,10 @@ index d9c14f91747bde0e76056d7f2f2ada166e67f994..53be16879777a3b9bef58ead5f7e420c
+#elif BUILDFLAG(IS_POSIX)
+ utility_options.WithAdditionalFds(std::move(service_options.fds_to_remap));
+#endif
+#if BUILDFLAG(IS_MAC)
+ utility_options.WithDisclaimResponsibility(
+ service_options.disclaim_responsibility);
+#endif
+ utility_options.WithCurrentDirectory(service_options.current_directory);
+ utility_options.WithEnvironment(service_options.environment,
+ service_options.clear_environment);
@@ -187,7 +193,7 @@ index d9c14f91747bde0e76056d7f2f2ada166e67f994..53be16879777a3b9bef58ead5f7e420c
UtilityProcessHost::Start(std::move(utility_options),
diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc
index 5101e22a804554d7e289d37f8a4191976763b69f..de1c1443812da9e6eb7c21222be414c545071d5b 100644
index 5101e22a804554d7e289d37f8a4191976763b69f..f00b3a784de14d564b6d02eeb72bc80711ac7395 100644
--- a/content/browser/service_host/utility_process_host.cc
+++ b/content/browser/service_host/utility_process_host.cc
@@ -241,13 +241,13 @@ UtilityProcessHost::Options& UtilityProcessHost::Options::WithFileToPreload(
@@ -207,7 +213,7 @@ index 5101e22a804554d7e289d37f8a4191976763b69f..de1c1443812da9e6eb7c21222be414c5
#if BUILDFLAG(USE_ZYGOTE)
UtilityProcessHost::Options& UtilityProcessHost::Options::WithZygoteForTesting(
@@ -257,6 +257,36 @@ UtilityProcessHost::Options& UtilityProcessHost::Options::WithZygoteForTesting(
@@ -257,6 +257,45 @@ UtilityProcessHost::Options& UtilityProcessHost::Options::WithZygoteForTesting(
}
#endif // BUILDFLAG(USE_ZYGOTE)
@@ -240,11 +246,20 @@ index 5101e22a804554d7e289d37f8a4191976763b69f..de1c1443812da9e6eb7c21222be414c5
+ return *this;
+}
+#endif // BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_MAC)
+UtilityProcessHost::Options&
+UtilityProcessHost::Options::WithDisclaimResponsibility(
+ bool disclaim_responsibility) {
+ disclaim_responsibility_ = disclaim_responsibility;
+ return *this;
+}
+#endif // BUILDFLAG(IS_MAC)
+
UtilityProcessHost::Options&
UtilityProcessHost::Options::WithBoundReceiverOnChildProcessForTesting(
mojo::GenericPendingReceiver receiver) {
@@ -531,9 +561,26 @@ bool UtilityProcessHost::StartProcess() {
@@ -531,9 +570,30 @@ bool UtilityProcessHost::StartProcess() {
}
#endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) && !BUILDFLAG(IS_WIN)
@@ -269,11 +284,15 @@ index 5101e22a804554d7e289d37f8a4191976763b69f..de1c1443812da9e6eb7c21222be414c5
+#if BUILDFLAG(IS_WIN)
+ delegate->SetFeedbackCursorOff(options_.feedback_cursor_off_);
+#endif // BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_MAC)
+ delegate->SetDisclaimResponsibility(options_.disclaim_responsibility_);
+#endif // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_WIN)
if (!options_.preload_libraries_.empty()) {
diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h
index dfdcb66d65f07f4543703396eb529a6ec02b3f4a..d731211d727f6e96533a058106c13f339263712d 100644
index dfdcb66d65f07f4543703396eb529a6ec02b3f4a..96c0cadf5caf5bf27f2a767c43f0f1da04298800 100644
--- a/content/browser/service_host/utility_process_host.h
+++ b/content/browser/service_host/utility_process_host.h
@@ -30,6 +30,7 @@
@@ -284,7 +303,7 @@ index dfdcb66d65f07f4543703396eb529a6ec02b3f4a..d731211d727f6e96533a058106c13f33
#endif // BUILDFLAG(IS_WIN)
namespace base {
@@ -133,14 +134,31 @@ class CONTENT_EXPORT UtilityProcessHost final
@@ -133,14 +134,36 @@ class CONTENT_EXPORT UtilityProcessHost final
std::variant<base::FilePath, base::ScopedFD> file);
#endif
@@ -315,11 +334,16 @@ index dfdcb66d65f07f4543703396eb529a6ec02b3f4a..d731211d727f6e96533a058106c13f33
+ // Specifies if the process should trigger mouse cursor feedback.
+ Options& WithFeedbackCursorOff(bool feedback_cursor_off);
+#endif // BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_MAC)
+ // Specifies if the process should disclaim TCC responsibility.
+ Options& WithDisclaimResponsibility(bool disclaim_responsibility);
+#endif // BUILDFLAG(IS_MAC)
+
// Requests that the process bind a receiving pipe targeting the interface
// named by `receiver`. Calls to this method generally end up in
// `ChildThreadImpl::OnBindReceiver()` and the option is used for testing
@@ -184,6 +202,27 @@ class CONTENT_EXPORT UtilityProcessHost final
@@ -184,6 +207,32 @@ class CONTENT_EXPORT UtilityProcessHost final
std::optional<raw_ptr<ZygoteCommunication>> zygote_for_testing_;
#endif // BUILDFLAG(USE_ZYGOTE)
@@ -343,12 +367,17 @@ index dfdcb66d65f07f4543703396eb529a6ec02b3f4a..d731211d727f6e96533a058106c13f33
+ // Specifies if the process should trigger mouse cursor feedback.
+ bool feedback_cursor_off_ = false;
+#endif // BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_MAC)
+ // Specifies if the process should disclaim TCC responsibility.
+ bool disclaim_responsibility_ = false;
+#endif // BUILDFLAG(IS_MAC)
+
#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
// Whether or not to bind viz::mojom::Gpu to the utility process.
bool allowed_gpu_;
diff --git a/content/browser/service_host/utility_sandbox_delegate.cc b/content/browser/service_host/utility_sandbox_delegate.cc
index 47a64f9c60ef359fc0015dff566c8041e34b5405..223891619099a8e43114c5a84cb5748fcb2f77b0 100644
index 47a64f9c60ef359fc0015dff566c8041e34b5405..cd17ff94f279cf32c0bf0aef6c2b53bae37f80fb 100644
--- a/content/browser/service_host/utility_sandbox_delegate.cc
+++ b/content/browser/service_host/utility_sandbox_delegate.cc
@@ -39,17 +39,19 @@ UtilitySandboxedProcessLauncherDelegate::
@@ -406,8 +435,24 @@ index 47a64f9c60ef359fc0015dff566c8041e34b5405..223891619099a8e43114c5a84cb5748f
#if BUILDFLAG(USE_ZYGOTE)
ZygoteCommunication* UtilitySandboxedProcessLauncherDelegate::GetZygote() {
@@ -189,6 +208,15 @@ UtilitySandboxedProcessLauncherDelegate::GetProcessRequirement() {
return std::nullopt;
}
+
+void UtilitySandboxedProcessLauncherDelegate::SetDisclaimResponsibility(
+ bool disclaim_responsibility) {
+ disclaim_responsibility_ = disclaim_responsibility;
+}
+
+bool UtilitySandboxedProcessLauncherDelegate::DisclaimResponsibility() {
+ return disclaim_responsibility_;
+}
#endif // BUILDFLAG(IS_MAC)
} // namespace content
diff --git a/content/browser/service_host/utility_sandbox_delegate.h b/content/browser/service_host/utility_sandbox_delegate.h
index ee2df2f709b17571747f53efc208ea9d234d076f..008310e8e2dc23b51b515c85b35764238c66f396 100644
index ee2df2f709b17571747f53efc208ea9d234d076f..e20e5d5233db5bf1bbb81560bbf3d403cd6f79c2 100644
--- a/content/browser/service_host/utility_sandbox_delegate.h
+++ b/content/browser/service_host/utility_sandbox_delegate.h
@@ -36,7 +36,9 @@ class CONTENT_EXPORT UtilitySandboxedProcessLauncherDelegate
@@ -438,7 +483,12 @@ index ee2df2f709b17571747f53efc208ea9d234d076f..008310e8e2dc23b51b515c85b3576423
#if BUILDFLAG(USE_ZYGOTE)
void SetZygote(ZygoteCommunication* handle);
@@ -77,9 +84,7 @@ class CONTENT_EXPORT UtilitySandboxedProcessLauncherDelegate
@@ -74,12 +81,12 @@ class CONTENT_EXPORT UtilitySandboxedProcessLauncherDelegate
#if BUILDFLAG(IS_MAC)
std::optional<base::mac::ProcessRequirement> GetProcessRequirement() override;
+ void SetDisclaimResponsibility(bool disclaim_responsibility);
+ bool DisclaimResponsibility() override;
#endif // BUILDFLAG(IS_MAC)
private:
@@ -448,7 +498,7 @@ index ee2df2f709b17571747f53efc208ea9d234d076f..008310e8e2dc23b51b515c85b3576423
#if BUILDFLAG(IS_WIN)
// Adds preload-libraries to the delegate blob for utility_main() to access
@@ -95,12 +100,17 @@ class CONTENT_EXPORT UtilitySandboxedProcessLauncherDelegate
@@ -95,12 +102,20 @@ class CONTENT_EXPORT UtilitySandboxedProcessLauncherDelegate
std::optional<raw_ptr<ZygoteCommunication>> zygote_;
#endif // BUILDFLAG(USE_ZYGOTE)
@@ -463,6 +513,9 @@ index ee2df2f709b17571747f53efc208ea9d234d076f..008310e8e2dc23b51b515c85b3576423
+#if BUILDFLAG(IS_WIN)
+ bool feedback_cursor_off_ = false;
+#endif // BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_MAC)
+ bool disclaim_responsibility_ = false;
+#endif // BUILDFLAG(IS_MAC)
};
} // namespace content
@@ -531,10 +584,10 @@ index cf86232460f117627f1c822fcf18b334bcf62341..7e559235533a36ceecc24787b07987a4
#if BUILDFLAG(IS_MAC)
// Whether or not to disclaim TCC responsibility for the process, defaults to
diff --git a/content/public/browser/service_process_host.cc b/content/public/browser/service_process_host.cc
index d1bc550a891979e2d41d8d5b18a2f9287468e460..5fcac7a8493e5065f80303067a04f59e7c4509ef 100644
index d1bc550a891979e2d41d8d5b18a2f9287468e460..5d255f628788bc8b40d8df0039b08c06ffec8730 100644
--- a/content/public/browser/service_process_host.cc
+++ b/content/public/browser/service_process_host.cc
@@ -53,12 +53,53 @@ ServiceProcessHost::Options::WithExtraCommandLineSwitches(
@@ -53,12 +53,62 @@ ServiceProcessHost::Options::WithExtraCommandLineSwitches(
return *this;
}
@@ -584,12 +637,21 @@ index d1bc550a891979e2d41d8d5b18a2f9287468e460..5fcac7a8493e5065f80303067a04f59e
+ return *this;
+}
+#endif // #if BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_MAC)
+ServiceProcessHost::Options&
+ServiceProcessHost::Options::WithDisclaimResponsibility(
+ bool should_disclaim_responsibility) {
+ disclaim_responsibility = should_disclaim_responsibility;
+ return *this;
+}
+#endif // BUILDFLAG(IS_MAC)
+
#if BUILDFLAG(IS_WIN)
ServiceProcessHost::Options&
ServiceProcessHost::Options::WithPreloadedLibraries(
diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
index 0062d2cb6634b8b29977a0312516b1b13936b40a..611a52e908f4cb70fbe5628e220a082e45320b70 100644
index 0062d2cb6634b8b29977a0312516b1b13936b40a..888ff36d70c83010f1f45e9eeb2dd6b573158db5 100644
--- a/content/public/browser/service_process_host.h
+++ b/content/public/browser/service_process_host.h
@@ -14,6 +14,7 @@
@@ -611,7 +673,7 @@ index 0062d2cb6634b8b29977a0312516b1b13936b40a..611a52e908f4cb70fbe5628e220a082e
namespace base {
class Process;
} // namespace base
@@ -94,11 +99,35 @@ class CONTENT_EXPORT ServiceProcessHost {
@@ -94,11 +99,40 @@ class CONTENT_EXPORT ServiceProcessHost {
// Specifies extra command line switches to append before launch.
Options& WithExtraCommandLineSwitches(std::vector<std::string> switches);
@@ -643,11 +705,16 @@ index 0062d2cb6634b8b29977a0312516b1b13936b40a..611a52e908f4cb70fbe5628e220a082e
+ // Specifies if the process should trigger mouse cursor feedback.
+ Options& WithFeedbackCursorOff(bool feedback_cursor_off);
+#endif // #if BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_MAC)
+ // Specifies if the process should disclaim TCC responsibility.
+ Options& WithDisclaimResponsibility(bool disclaim_responsibility);
+#endif // BUILDFLAG(IS_MAC)
+
#if BUILDFLAG(IS_WIN)
// Specifies libraries to preload before the sandbox is locked down. Paths
// should be absolute paths. Libraries will be preloaded before sandbox
@@ -127,11 +156,23 @@ class CONTENT_EXPORT ServiceProcessHost {
@@ -127,11 +161,26 @@ class CONTENT_EXPORT ServiceProcessHost {
std::optional<GURL> site;
std::optional<int> child_flags;
std::vector<std::string> extra_switches;
@@ -668,6 +735,9 @@ index 0062d2cb6634b8b29977a0312516b1b13936b40a..611a52e908f4cb70fbe5628e220a082e
+#if BUILDFLAG(IS_WIN)
+ bool feedback_cursor_off = false;
+#endif // BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_MAC)
+ bool disclaim_responsibility = false;
+#endif // BUILDFLAG(IS_MAC)
};
// An interface which can be implemented and registered/unregistered with

View File

@@ -84,10 +84,10 @@ index 2648adb1cf38ab557b66ffd0e3034b26b04d76d6..98eab587f343f6ca472efc3d4e7b31b2
private:
const std::string service_interface_name_;
diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc
index de1c1443812da9e6eb7c21222be414c545071d5b..101f5f44e66ee43797e7582c8c49402f4d9efef0 100644
index f00b3a784de14d564b6d02eeb72bc80711ac7395..e14f4b3b4cde8182431faee7c46d0bf901f98d9e 100644
--- a/content/browser/service_host/utility_process_host.cc
+++ b/content/browser/service_host/utility_process_host.cc
@@ -635,7 +635,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) {
@@ -648,7 +648,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) {
: Client::CrashType::kPreIpcInitialization;
}
#endif // BUILDFLAG(IS_WIN)
@@ -97,7 +97,7 @@ index de1c1443812da9e6eb7c21222be414c545071d5b..101f5f44e66ee43797e7582c8c49402f
std::optional<std::string> UtilityProcessHost::GetServiceName() {
diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h
index d731211d727f6e96533a058106c13f339263712d..19e35a0684746d6f5703ac4237de4d8aeddbaa4e 100644
index 96c0cadf5caf5bf27f2a767c43f0f1da04298800..5a16fe5c01ae7777064168e8883ec8ec0b82a873 100644
--- a/content/browser/service_host/utility_process_host.h
+++ b/content/browser/service_host/utility_process_host.h
@@ -87,7 +87,7 @@ class CONTENT_EXPORT UtilityProcessHost final

View File

@@ -130,7 +130,7 @@ index 419a051968c58ae5a761708e4d942e8975c70852..a77032dd43f5fcbe29c54b622b34607f
} // namespace base::mac
diff --git a/base/process/launch_mac.cc b/base/process/launch_mac.cc
index b63d58da9837ba4d1e4aff8f24f2cd977c5ed02d..8387fd7d2bcf8951b6cc024829c16d970799190c 100644
index b63d58da9837ba4d1e4aff8f24f2cd977c5ed02d..49b4c0b69731386ef5a4b7dfb782aa8f4ae09cdd 100644
--- a/base/process/launch_mac.cc
+++ b/base/process/launch_mac.cc
@@ -84,6 +84,10 @@ int posix_spawnattr_set_csm_np(const posix_spawnattr_t*, uint32_t)
@@ -184,15 +184,26 @@ index b63d58da9837ba4d1e4aff8f24f2cd977c5ed02d..8387fd7d2bcf8951b6cc024829c16d97
}
#endif
@@ -301,7 +321,7 @@ Process LaunchProcess(const std::vector<std::string>& argv,
@@ -301,16 +321,16 @@ Process LaunchProcess(const std::vector<std::string>& argv,
file_actions.Inherit(STDERR_FILENO);
}
-#if BUILDFLAG(IS_MAC)
+#if 0
+#if !IS_MAS_BUILD()
if (options.disclaim_responsibility) {
DPSXCHECK(responsibility_spawnattrs_setdisclaim(attr.get(), 1));
}
+#endif
EnvironmentMap new_environment_map = options.environment;
+#if !IS_MAS_BUILD()
MachPortRendezvousServerMac::AddFeatureStateToEnvironment(
new_environment_map);
-#else
- const EnvironmentMap& new_environment_map = options.environment;
#endif
std::vector<char*> argv_cstr;
diff --git a/base/process/process_info_mac.mm b/base/process/process_info_mac.mm
index e12c1d078147d956a1d9b1bc498c1b1d6fe7b974..233362259dc4e728ed37435e650417647b45a6af 100644
--- a/base/process/process_info_mac.mm

View File

@@ -46,3 +46,4 @@ test_make_buffer_sizes_32bit-aware_in.patch
src_refactor_module_wrap_cc_to_update_fixedarray_get_params.patch
src_refactor_wasmstreaming_finish_to_accept_a_callback.patch
src_stop_using_v8_propertycallbackinfo_t_this.patch
build_restore_macos_deployment_target_to_12_0.patch

View File

@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Keeley Hammond <vertedinde@electronjs.org>
Date: Thu, 5 Feb 2026 15:29:44 -0800
Subject: build: restore macos deployment target to 12.0
Partially reverts https://github.com/nodejs/node/commit/8b4022177750530d2c142a5a0349d98fb82f16e2
Electron will follow Chromium's lead and deprecate macos 12 with
M151, and so we should allow for building until then.
This patch can be removed at the M151 branch point.
diff --git a/common.gypi b/common.gypi
index bdadbdaa607b2f668749fc484271de8d126bbd17..8df2802191b7fe6ae14edbd85cb3a5d16eb5a76a 100644
--- a/common.gypi
+++ b/common.gypi
@@ -677,7 +677,7 @@
'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings
'GCC_STRICT_ALIASING': 'NO', # -fno-strict-aliasing
'PREBINDING': 'NO', # No -Wl,-prebind
- 'MACOSX_DEPLOYMENT_TARGET': '13.5', # -mmacosx-version-min=13.5
+ 'MACOSX_DEPLOYMENT_TARGET': '12.0', # -mmacosx-version-min=12.0
'USE_HEADERMAP': 'NO',
'WARNING_CFLAGS': [
'-Wall',

View File

@@ -0,0 +1,31 @@
const yaml = require('yaml');
const fs = require('node:fs');
const path = require('node:path');
const PREFIX = '# AUTOGENERATED FILE - DO NOT EDIT MANUALLY\n# ONLY EDIT .github/workflows/pipeline-segment-electron-build.yml\n\n';
const base = path.resolve(__dirname, '../.github/workflows/pipeline-segment-electron-build.yml');
const target = path.resolve(__dirname, '../.github/workflows/pipeline-segment-electron-publish.yml');
const baseContents = fs.readFileSync(base, 'utf-8');
const parsedBase = yaml.parse(baseContents);
parsedBase.jobs.build.permissions = {
attestations: 'write',
contents: 'read',
'id-token': 'write'
};
if (process.argv.includes('--check')) {
if (fs.readFileSync(target, 'utf-8') !== PREFIX + yaml.stringify(parsedBase)) {
console.error(`${target} is out of date`);
console.error('Please run "copy-pipeline-segment-publish.js" to update it');
process.exit(1);
}
} else {
fs.writeFileSync(
target,
PREFIX + yaml.stringify(parsedBase)
);
}

View File

@@ -369,6 +369,14 @@ def upload_io_to_github(release, filename, filepath, version):
sys.stdout.buffer.write(c)
sys.stdout.flush()
if "GITHUB_OUTPUT" in os.environ:
output_path = os.environ["GITHUB_OUTPUT"]
with open(output_path, "r+", encoding='utf-8') as github_output:
if len(github_output.readlines()) > 0:
github_output.write(",")
else:
github_output.write('UPLOADED_PATHS=')
github_output.write(filename)
def upload_sha256_checksum(version, file_path, key_prefix=None):
checksum_path = f'{file_path}.sha256sum'

View File

@@ -11,14 +11,9 @@
#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"
@@ -33,16 +28,10 @@
#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 <wrl.h>
#include "base/win/scoped_com_initializer.h"
#include "base/win/core_winrt_util.h"
#include "base/win/scoped_hstring.h"
#endif
namespace electron {
@@ -55,6 +44,53 @@ const bool debug_msix_updater =
namespace {
#if BUILDFLAG(IS_WIN)
// Type aliases for cleaner code
using ABI::Windows::ApplicationModel::IAppInstallerInfo;
using ABI::Windows::ApplicationModel::IPackage;
using ABI::Windows::ApplicationModel::IPackage2;
using ABI::Windows::ApplicationModel::IPackage4;
using ABI::Windows::ApplicationModel::IPackage6;
using ABI::Windows::ApplicationModel::IPackageId;
using ABI::Windows::ApplicationModel::IPackageStatics;
using ABI::Windows::ApplicationModel::PackageSignatureKind;
using ABI::Windows::ApplicationModel::PackageSignatureKind_Developer;
using ABI::Windows::ApplicationModel::PackageSignatureKind_Enterprise;
using ABI::Windows::ApplicationModel::PackageSignatureKind_None;
using ABI::Windows::ApplicationModel::PackageSignatureKind_Store;
using ABI::Windows::ApplicationModel::PackageSignatureKind_System;
using ABI::Windows::Foundation::AsyncStatus;
using ABI::Windows::Foundation::IAsyncInfo;
using ABI::Windows::Foundation::IUriRuntimeClass;
using ABI::Windows::Foundation::IUriRuntimeClassFactory;
using ABI::Windows::Foundation::Metadata::IApiInformationStatics;
using ABI::Windows::Management::Deployment::DeploymentOptions;
using ABI::Windows::Management::Deployment::
DeploymentOptions_ForceApplicationShutdown;
using ABI::Windows::Management::Deployment::
DeploymentOptions_ForceTargetApplicationShutdown;
using ABI::Windows::Management::Deployment::
DeploymentOptions_ForceUpdateFromAnyVersion;
using ABI::Windows::Management::Deployment::DeploymentOptions_None;
using ABI::Windows::Management::Deployment::IAddPackageOptions;
using ABI::Windows::Management::Deployment::IDeploymentResult;
using ABI::Windows::Management::Deployment::IPackageManager;
using ABI::Windows::Management::Deployment::IPackageManager5;
using ABI::Windows::Management::Deployment::IPackageManager9;
using Microsoft::WRL::Callback;
using Microsoft::WRL::ComPtr;
// Type alias for deployment async operation
// AddPackageByUriAsync returns IAsyncOperationWithProgress<DeploymentResult*,
// DeploymentProgress>
using DeploymentAsyncOp = ABI::Windows::Foundation::IAsyncOperationWithProgress<
ABI::Windows::Management::Deployment::DeploymentResult*,
ABI::Windows::Management::Deployment::DeploymentProgress>;
using DeploymentCompletedHandler =
ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<
ABI::Windows::Management::Deployment::DeploymentResult*,
ABI::Windows::Management::Deployment::DeploymentProgress>;
// Helper function for debug logging
void DebugLog(std::string_view log_msg) {
if (electron::debug_msix_updater)
@@ -84,32 +120,274 @@ struct RegisterPackageOptions {
bool force_update_from_any_version = false;
};
// Helper: Create PackageManager using RoActivateInstance
//
// Note on COM interface versioning: In COM/WinRT, each interface version
// (IPackageManager, IPackageManager5, IPackageManager9, etc.) is a separate
// interface that must be queried independently. Unlike C++ inheritance,
// IPackageManager9 does NOT inherit methods from IPackageManager5 or the base
// IPackageManager. Each version only contains the methods that were newly
// added in that version. To call methods from different versions, you must
// QueryInterface (or ComPtr::As) for each specific interface version needed.
HRESULT CreatePackageManager(ComPtr<IPackageManager>* package_manager) {
base::win::ScopedHString class_id = base::win::ScopedHString::Create(
RuntimeClass_Windows_Management_Deployment_PackageManager);
if (!class_id.is_valid()) {
return E_FAIL;
}
ComPtr<IInspectable> inspectable;
HRESULT hr = base::win::RoActivateInstance(class_id.get(), &inspectable);
if (FAILED(hr)) {
return hr;
}
return inspectable.As(package_manager);
}
// Helper: Create URI using IUriRuntimeClassFactory
HRESULT CreateUri(const std::wstring& uri_string,
ComPtr<IUriRuntimeClass>* uri) {
ComPtr<IUriRuntimeClassFactory> uri_factory;
HRESULT hr =
base::win::GetActivationFactory<IUriRuntimeClassFactory,
RuntimeClass_Windows_Foundation_Uri>(
&uri_factory);
if (FAILED(hr)) {
return hr;
}
base::win::ScopedHString uri_hstring =
base::win::ScopedHString::Create(uri_string);
if (!uri_hstring.is_valid()) {
return E_FAIL;
}
return uri_factory->CreateUri(uri_hstring.get(), uri->GetAddressOf());
}
// Helper: Create and configure AddPackageOptions
HRESULT CreateAddPackageOptions(const UpdateMsixOptions& opts,
ComPtr<IAddPackageOptions>* package_options) {
base::win::ScopedHString class_id = base::win::ScopedHString::Create(
RuntimeClass_Windows_Management_Deployment_AddPackageOptions);
if (!class_id.is_valid()) {
return E_FAIL;
}
ComPtr<IInspectable> inspectable;
HRESULT hr = base::win::RoActivateInstance(class_id.get(), &inspectable);
if (FAILED(hr)) {
return hr;
}
hr = inspectable.As(package_options);
if (FAILED(hr)) {
return hr;
}
// Configure options using ABI interface methods
(*package_options)
->put_DeferRegistrationWhenPackagesAreInUse(opts.defer_registration);
(*package_options)->put_DeveloperMode(opts.developer_mode);
(*package_options)->put_ForceAppShutdown(opts.force_shutdown);
(*package_options)->put_ForceTargetAppShutdown(opts.force_target_shutdown);
(*package_options)
->put_ForceUpdateFromAnyVersion(opts.force_update_from_any_version);
return S_OK;
}
// Helper: Check if API contract is present
HRESULT CheckApiContractPresent(UINT16 version, boolean* is_present) {
ComPtr<IApiInformationStatics> api_info;
HRESULT hr = base::win::GetActivationFactory<
IApiInformationStatics,
RuntimeClass_Windows_Foundation_Metadata_ApiInformation>(&api_info);
if (FAILED(hr)) {
return hr;
}
base::win::ScopedHString contract_name = base::win::ScopedHString::Create(
L"Windows.Foundation.UniversalApiContract");
if (!contract_name.is_valid()) {
return E_FAIL;
}
return api_info->IsApiContractPresentByMajor(contract_name.get(), version,
is_present);
}
// Helper: Get current package using IPackageStatics
HRESULT GetCurrentPackage(ComPtr<IPackage>* package) {
ComPtr<IPackageStatics> package_statics;
HRESULT hr = base::win::GetActivationFactory<
IPackageStatics, RuntimeClass_Windows_ApplicationModel_Package>(
&package_statics);
if (FAILED(hr)) {
return hr;
}
return package_statics->get_Current(package->GetAddressOf());
}
// Structure to hold callback data for async operations
struct DeploymentCallbackData {
scoped_refptr<base::SingleThreadTaskRunner> reply_runner;
gin_helper::Promise<void> promise;
bool fire_and_forget;
ComPtr<DeploymentAsyncOp> async_op; // Keep async_op alive
std::string operation_name; // "Deployment" or "Registration" for logs
};
// Handler for deployment/registration completion
void OnDeploymentCompleted(std::unique_ptr<DeploymentCallbackData> data,
DeploymentAsyncOp* async_op,
AsyncStatus status) {
std::string error;
const std::string& op_name = data->operation_name;
if (data->fire_and_forget) {
std::ostringstream oss;
oss << op_name
<< " initiated. Force shutdown or target shutdown requested. "
"Good bye!";
DebugLog(oss.str());
// Don't wait for result in fire-and-forget mode
data->reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise) { promise.Resolve(); },
std::move(data->promise)));
return;
}
if (status == AsyncStatus::Error) {
ComPtr<IDeploymentResult> result;
HRESULT hr = async_op->GetResults(&result);
if (SUCCEEDED(hr) && result) {
HSTRING error_text_hstring;
hr = result->get_ErrorText(&error_text_hstring);
if (SUCCEEDED(hr)) {
base::win::ScopedHString scoped_error(error_text_hstring);
error = scoped_error.GetAsUTF8();
}
ComPtr<IAsyncInfo> async_info;
hr = async_op->QueryInterface(IID_PPV_ARGS(&async_info));
if (SUCCEEDED(hr)) {
HRESULT error_code;
hr = async_info->get_ErrorCode(&error_code);
if (SUCCEEDED(hr)) {
error += " (" + std::to_string(static_cast<int>(error_code)) + ")";
}
}
}
if (error.empty()) {
error = op_name + " failed with unknown error";
}
{
std::ostringstream oss;
oss << op_name << " failed: " << error;
DebugLog(oss.str());
}
} else if (status == AsyncStatus::Canceled) {
std::ostringstream oss;
oss << op_name << " canceled";
DebugLog(oss.str());
error = op_name + " canceled";
} else if (status == AsyncStatus::Completed) {
std::ostringstream oss;
oss << "MSIX " << op_name << " completed.";
DebugLog(oss.str());
} else {
error = op_name + " status unknown";
std::ostringstream oss;
oss << op_name << " status unknown";
DebugLog(oss.str());
}
// Post result back to UI thread
data->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(data->promise), std::move(error)));
}
// 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);
// Create PackageManager
ComPtr<IPackageManager> package_manager;
HRESULT hr = CreatePackageManager(&package_manager);
if (FAILED(hr)) {
error = "Failed to create PackageManager";
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
// Get IPackageManager9 for AddPackageByUriAsync
ComPtr<IPackageManager9> package_manager9;
hr = package_manager.As(&package_manager9);
if (FAILED(hr)) {
error = "Failed to get IPackageManager9 interface";
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
// Create URI
std::wstring uri_wstring = base::UTF8ToWide(package_uri);
ComPtr<IUriRuntimeClass> uri;
hr = CreateUri(uri_wstring, &uri);
if (FAILED(hr)) {
error = "Failed to create URI";
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
// Create AddPackageOptions
ComPtr<IAddPackageOptions> package_options;
hr = CreateAddPackageOptions(opts, &package_options);
if (FAILED(hr)) {
error = "Failed to create AddPackageOptions";
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
{
std::ostringstream oss;
@@ -127,63 +405,54 @@ void DoUpdateMsix(const std::string& package_uri,
DebugLog(oss.str());
}
auto deploymentOperation =
packageManager.AddPackageByUriAsync(uri, packageOptions);
if (!deploymentOperation) {
DebugLog("Deployment operation is null");
// Start async operation
ComPtr<DeploymentAsyncOp> async_op;
hr = package_manager9->AddPackageByUriAsync(uri.Get(), package_options.Get(),
&async_op);
if (FAILED(hr) || !async_op) {
DebugLog("AddPackageByUriAsync failed or returned 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!");
}
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
// 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));
// Set up callback data
auto callback_data = std::make_unique<DeploymentCallbackData>();
callback_data->reply_runner = reply_runner;
callback_data->promise = std::move(promise);
callback_data->fire_and_forget =
opts.force_shutdown || opts.force_target_shutdown;
callback_data->async_op = async_op; // Keep async_op alive
callback_data->operation_name = "Deployment";
// Register completion handler
DeploymentCallbackData* raw_data = callback_data.get();
hr = async_op->put_Completed(
Callback<DeploymentCompletedHandler>([data = std::move(callback_data)](
DeploymentAsyncOp* op,
AsyncStatus status) mutable {
OnDeploymentCompleted(std::move(data), op, status);
return S_OK;
}).Get());
if (FAILED(hr)) {
DebugLog("Failed to register completion handler");
raw_data->reply_runner->PostTask(
FROM_HERE, base::BindOnce(
[](gin_helper::Promise<void> promise) {
promise.RejectWithErrorMessage(
"Failed to register completion handler");
},
std::move(raw_data->promise)));
}
}
// Performs package registration on IO thread
@@ -192,31 +461,67 @@ void DoRegisterPackage(const std::string& family_name,
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)
// Create PackageManager
ComPtr<IPackageManager> package_manager;
HRESULT hr = CreatePackageManager(&package_manager);
if (FAILED(hr)) {
error = "Failed to create PackageManager";
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
// Get IPackageManager5 for RegisterPackageByFamilyNameAsync
ComPtr<IPackageManager5> package_manager5;
hr = package_manager.As(&package_manager5);
if (FAILED(hr)) {
error = "Failed to get IPackageManager5 interface";
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
// Build DeploymentOptions flags
DeploymentOptions deployment_options = DeploymentOptions_None;
if (opts.force_shutdown) {
deploymentOptions |= DeploymentOptions::ForceApplicationShutdown;
deployment_options = static_cast<DeploymentOptions>(
deployment_options | DeploymentOptions_ForceApplicationShutdown);
}
if (opts.force_target_shutdown) {
deploymentOptions |= DeploymentOptions::ForceTargetApplicationShutdown;
deployment_options = static_cast<DeploymentOptions>(
deployment_options | DeploymentOptions_ForceTargetApplicationShutdown);
}
if (opts.force_update_from_any_version) {
deploymentOptions |= DeploymentOptions::ForceUpdateFromAnyVersion;
deployment_options = static_cast<DeploymentOptions>(
deployment_options | DeploymentOptions_ForceUpdateFromAnyVersion);
}
// Create empty collections for dependency and optional packages
IIterable<winrt::hstring> emptyDependencies{nullptr};
IIterable<winrt::hstring> emptyOptional{nullptr};
// Create HSTRING for family name
base::win::ScopedHString family_name_hstring =
base::win::ScopedHString::Create(family_name);
if (!family_name_hstring.is_valid()) {
error = "Failed to create family name string";
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
{
std::ostringstream oss;
@@ -233,63 +538,59 @@ void DoRegisterPackage(const std::string& family_name,
DebugLog(oss.str());
}
auto deploymentOperation = packageManager.RegisterPackageByFamilyNameAsync(
familyNameH, emptyDependencies, deploymentOptions, nullptr,
emptyOptional);
// RegisterPackageByFamilyNameAndOptionalPackagesAsync (ABI name)
ComPtr<DeploymentAsyncOp> async_op;
hr = package_manager5->RegisterPackageByFamilyNameAndOptionalPackagesAsync(
family_name_hstring.get(),
nullptr, // dependencyPackageFamilyNames
deployment_options,
nullptr, // appDataVolume
nullptr, // optionalPackageFamilyNames
&async_op);
if (!deploymentOperation) {
if (FAILED(hr) || !async_op) {
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!");
}
reply_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gin_helper::Promise<void> promise, std::string error) {
promise.RejectWithErrorMessage(error);
},
std::move(promise), std::move(error)));
return;
}
// 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));
// Set up callback data
auto callback_data = std::make_unique<DeploymentCallbackData>();
callback_data->reply_runner = reply_runner;
callback_data->promise = std::move(promise);
callback_data->fire_and_forget =
opts.force_shutdown || opts.force_target_shutdown;
callback_data->async_op = async_op; // Keep async_op alive
callback_data->operation_name = "Registration";
// Register completion handler
DeploymentCallbackData* raw_data = callback_data.get();
hr = async_op->put_Completed(
Callback<DeploymentCompletedHandler>([data = std::move(callback_data)](
DeploymentAsyncOp* op,
AsyncStatus status) mutable {
OnDeploymentCompleted(std::move(data), op, status);
return S_OK;
}).Get());
if (FAILED(hr)) {
DebugLog("Failed to register completion handler");
raw_data->reply_runner->PostTask(
FROM_HERE, base::BindOnce(
[](gin_helper::Promise<void> promise) {
promise.RejectWithErrorMessage(
"Failed to register completion handler");
},
std::move(raw_data->promise)));
}
}
#endif
@@ -307,6 +608,16 @@ v8::Local<v8::Promise> UpdateMsix(const std::string& package_uri,
return handle;
}
// Check for required API contract (IPackageManager9 requires v10)
boolean is_api_present = FALSE;
if (FAILED(CheckApiContractPresent(10, &is_api_present)) || !is_api_present) {
DebugLog("UpdateMsix: Required Windows API contract not present");
promise.RejectWithErrorMessage(
"This Windows version does not support MSIX updates via this API. "
"Windows 10 version 2004 or later is required.");
return handle;
}
// Parse options on UI thread (where V8 is available)
UpdateMsixOptions opts;
options.Get("deferRegistration", &opts.defer_registration);
@@ -349,6 +660,16 @@ v8::Local<v8::Promise> RegisterPackage(const std::string& family_name,
return handle;
}
// Check for required API contract (IPackageManager5 requires v3)
boolean is_api_present = FALSE;
if (FAILED(CheckApiContractPresent(3, &is_api_present)) || !is_api_present) {
DebugLog("RegisterPackage: Required Windows API contract not present");
promise.RejectWithErrorMessage(
"This Windows version does not support package registration via this "
"API. Windows 10 version 1607 or later is required.");
return handle;
}
// Parse options on UI thread (where V8 is available)
RegisterPackageOptions opts;
options.Get("forceShutdown", &opts.force_shutdown);
@@ -384,32 +705,30 @@ bool RegisterRestartOnUpdate(const std::string& command_line) {
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
// Convert command line to wide string (keep in scope for API call)
std::wstring command_line_wide;
const wchar_t* command_line_ptr = nullptr;
if (!command_line.empty()) {
std::wstring commandLineW =
std::wstring(command_line.begin(), command_line.end());
commandLine = commandLineW.c_str();
command_line_wide = base::UTF8ToWide(command_line);
command_line_ptr = command_line_wide.c_str();
}
HRESULT hr = RegisterApplicationRestart(commandLine, dwFlags);
HRESULT hr = RegisterApplicationRestart(command_line_ptr, dwFlags);
if (FAILED(hr)) {
{
std::ostringstream oss;
oss << "RegisterApplicationRestart failed with error code: " << hr;
DebugLog(oss.str());
}
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());
}
std::ostringstream oss;
oss << "RegisterApplicationRestart succeeded"
<< (command_line.empty() ? "" : " with command line");
DebugLog(oss.str());
return true;
#else
return false;
@@ -434,57 +753,119 @@ v8::Local<v8::Value> GetPackageInfo() {
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();
boolean is_present = FALSE;
HRESULT hr = CheckApiContractPresent(7, &is_present);
if (SUCCEEDED(hr) && is_present) {
ComPtr<IPackage> package;
hr = GetCurrentPackage(&package);
if (SUCCEEDED(hr) && package) {
// Query all needed package interface versions upfront.
// Note: Like IPackageManager, each IPackage version (IPackage2,
// IPackage4, IPackage6) is a separate COM interface. IPackage6 does NOT
// inherit methods from earlier versions. We must query each version
// separately to access its specific methods:
// - IPackage2: get_IsDevelopmentMode
// - IPackage4: get_SignatureKind
// - IPackage6: GetAppInstallerInfo
ComPtr<IPackage2> package2;
ComPtr<IPackage4> package4;
ComPtr<IPackage6> package6;
package.As(&package2);
package.As(&package4);
package.As(&package6);
// Get package ID and family name
std::string packageId = winrt::to_string(package.Id().FullName());
std::string familyName = winrt::to_string(package.Id().FamilyName());
// Get package ID (from base IPackage)
ComPtr<IPackageId> package_id;
hr = package->get_Id(&package_id);
if (SUCCEEDED(hr) && package_id) {
// Get FullName
HSTRING full_name;
hr = package_id->get_FullName(&full_name);
if (SUCCEEDED(hr)) {
base::win::ScopedHString scoped_name(full_name);
result.Set("id", scoped_name.GetAsUTF8());
}
result.Set("id", packageId);
result.Set("familyName", familyName);
result.Set("developmentMode", package.IsDevelopmentMode());
// Get FamilyName
HSTRING family_name;
hr = package_id->get_FamilyName(&family_name);
if (SUCCEEDED(hr)) {
base::win::ScopedHString scoped_name(family_name);
result.Set("familyName", scoped_name.GetAsUTF8());
}
// 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);
// Get Version
ABI::Windows::ApplicationModel::PackageVersion pkg_version;
hr = package_id->get_Version(&pkg_version);
if (SUCCEEDED(hr)) {
std::string version = std::to_string(pkg_version.Major) + "." +
std::to_string(pkg_version.Minor) + "." +
std::to_string(pkg_version.Build) + "." +
std::to_string(pkg_version.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 IsDevelopmentMode (from IPackage2)
if (package2) {
boolean is_dev_mode = FALSE;
hr = package2->get_IsDevelopmentMode(&is_dev_mode);
result.Set("developmentMode", SUCCEEDED(hr) && is_dev_mode != FALSE);
} else {
result.Set("developmentMode", false);
}
// 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);
// Get SignatureKind (from IPackage4)
if (package4) {
PackageSignatureKind sig_kind;
hr = package4->get_SignatureKind(&sig_kind);
if (SUCCEEDED(hr)) {
std::string signature_kind;
switch (sig_kind) {
case PackageSignatureKind_Developer:
signature_kind = "developer";
break;
case PackageSignatureKind_Enterprise:
signature_kind = "enterprise";
break;
case PackageSignatureKind_None:
signature_kind = "none";
break;
case PackageSignatureKind_Store:
signature_kind = "store";
break;
case PackageSignatureKind_System:
signature_kind = "system";
break;
default:
signature_kind = "none";
break;
}
result.Set("signatureKind", signature_kind);
} else {
result.Set("signatureKind", "none");
}
} else {
result.Set("signatureKind", "none");
}
// Get AppInstallerInfo (from IPackage6)
if (package6) {
ComPtr<IAppInstallerInfo> app_installer_info;
hr = package6->GetAppInstallerInfo(&app_installer_info);
if (SUCCEEDED(hr) && app_installer_info) {
ComPtr<IUriRuntimeClass> uri;
hr = app_installer_info->get_Uri(&uri);
if (SUCCEEDED(hr) && uri) {
HSTRING uri_string;
hr = uri->get_AbsoluteUri(&uri_string);
if (SUCCEEDED(hr)) {
base::win::ScopedHString scoped_uri(uri_string);
result.Set("appInstallerUri", scoped_uri.GetAsUTF8());
}
}
}
}
}
} else {
// Windows version doesn't meet minimum API requirements

View File

@@ -71,7 +71,8 @@ UtilityProcessWrapper::UtilityProcessWrapper(
base::EnvironmentMap env_map,
base::FilePath current_working_directory,
bool use_plugin_helper,
bool create_network_observer) {
bool create_network_observer,
bool disclaim_responsibility) {
#if BUILDFLAG(IS_WIN)
base::win::ScopedHandle stdout_write(nullptr);
base::win::ScopedHandle stderr_write(nullptr);
@@ -185,6 +186,7 @@ UtilityProcessWrapper::UtilityProcessWrapper(
.WithChildFlags(use_plugin_helper
? content::ChildProcessHost::CHILD_PLUGIN
: content::ChildProcessHost::CHILD_NORMAL)
.WithDisclaimResponsibility(disclaim_responsibility)
#endif
.WithProcessCallback(
base::BindOnce(&UtilityProcessWrapper::OnServiceProcessLaunch,
@@ -452,6 +454,7 @@ gin_helper::Handle<UtilityProcessWrapper> UtilityProcessWrapper::Create(
std::u16string display_name;
bool use_plugin_helper = false;
bool create_network_observer = false;
bool disclaim_responsibility = false;
std::map<IOHandle, IOType> stdio;
base::FilePath current_working_directory;
base::EnvironmentMap env_map;
@@ -495,13 +498,15 @@ gin_helper::Handle<UtilityProcessWrapper> UtilityProcessWrapper::Create(
#if BUILDFLAG(IS_MAC)
opts.Get("allowLoadingUnsignedLibraries", &use_plugin_helper);
opts.Get("disclaim", &disclaim_responsibility);
#endif
}
auto handle = gin_helper::CreateHandle(
args->isolate(), new UtilityProcessWrapper(
std::move(params), display_name, std::move(stdio),
env_map, current_working_directory,
use_plugin_helper, create_network_observer));
args->isolate(),
new UtilityProcessWrapper(
std::move(params), display_name, std::move(stdio), env_map,
current_working_directory, use_plugin_helper, create_network_observer,
disclaim_responsibility));
handle->Pin(args->isolate());
return handle;
}

View File

@@ -72,7 +72,8 @@ class UtilityProcessWrapper final
base::EnvironmentMap env_map,
base::FilePath current_working_directory,
bool use_plugin_helper,
bool create_network_observer);
bool create_network_observer,
bool disclaim_responsibility);
void OnServiceProcessLaunch(const base::Process& process);
void CloseConnectorPort();

View File

@@ -106,7 +106,10 @@ bool ProcessSignatureIsSameWithCurrentApp(pid_t pid) {
status = SecCodeCheckValidity(process_code.get(), kSecCSDefaultFlags,
self_requirement.get());
if (status != errSecSuccess && status != errSecCSReqFailed) {
OSSTATUS_LOG(ERROR, status) << "SecCodeCheckValidity";
// If the code is unsigned, don't log that (it's not an actual error).
if (status != errSecCSUnsigned) {
OSSTATUS_LOG(ERROR, status) << "SecCodeCheckValidity";
}
return false;
}
return status == errSecSuccess;

View File

@@ -43,6 +43,65 @@ describe('MenuItems', () => {
expect(item).to.have.property('role').that.is.a('string');
expect(item).to.have.property('icon');
});
it('should have a default accelerator for certain roles', () => {
const items: Record<string, Electron.MenuItem['accelerator']> = {
undo: 'CommandOrControl+Z',
redo: process.platform === 'win32' ? 'Control+Y' : 'Shift+CommandOrControl+Z',
cut: 'CommandOrControl+X',
copy: 'CommandOrControl+C',
paste: 'CommandOrControl+V',
pasteAndMatchStyle: process.platform === 'darwin' ? 'Cmd+Option+Shift+V' : 'Shift+CommandOrControl+V',
delete: null,
selectAll: 'CommandOrControl+A',
reload: 'CmdOrCtrl+R',
forceReload: 'Shift+CmdOrCtrl+R',
toggleDevTools: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
resetZoom: 'CommandOrControl+0',
zoomIn: 'CommandOrControl+Plus',
zoomOut: 'CommandOrControl+-',
toggleSpellChecker: null,
togglefullscreen: process.platform === 'darwin' ? 'Control+Command+F' : 'F11',
window: null,
minimize: 'CommandOrControl+M',
close: 'CommandOrControl+W',
help: null,
about: null,
services: null,
hide: 'Command+H',
hideOthers: 'Command+Alt+H',
unhide: null,
quit: process.platform === 'win32' ? null : 'CommandOrControl+Q',
showSubstitutions: null,
toggleSmartQuotes: null,
toggleSmartDashes: null,
toggleTextReplacement: null,
startSpeaking: null,
stopSpeaking: null,
zoom: null,
front: null,
appMenu: null,
fileMenu: null,
editMenu: null,
viewMenu: null,
shareMenu: null,
recentDocuments: null,
toggleTabBar: null,
selectNextTab: null,
selectPreviousTab: null,
showAllTabs: null,
mergeAllWindows: null,
clearRecentDocuments: null,
moveTabToNewWindow: null,
windowMenu: null
};
for (const role in items) {
if (!Object.hasOwn(items, role)) continue;
const item = new MenuItem({ role: role as any });
expect(item.accelerator).to.equal(items[role]);
}
});
});
describe('MenuItem.click', () => {
@@ -480,7 +539,7 @@ describe('MenuItems', () => {
it('should display modifiers correctly for simple keys', () => {
const menu = Menu.buildFromTemplate([
{ label: 'text', accelerator: 'CmdOrCtrl+A' },
{ label: 'text', accelerator: 'CommandOrControl+A' },
{ label: 'text', accelerator: 'Shift+A' },
{ label: 'text', accelerator: 'Alt+A' }
]);
@@ -492,7 +551,7 @@ describe('MenuItems', () => {
it('should display modifiers correctly for special keys', () => {
const menu = Menu.buildFromTemplate([
{ label: 'text', accelerator: 'CmdOrCtrl+Tab' },
{ label: 'text', accelerator: 'CommandOrControl+Tab' },
{ label: 'text', accelerator: 'Shift+Tab' },
{ label: 'text', accelerator: 'Alt+Tab' }
]);

View File

@@ -863,5 +863,24 @@ describe('utilityProcess module', () => {
await exit;
}
});
// Note: This doesn't test that disclaiming works (that requires stubbing / mocking TCC which is
// just straight up not possible generically). This just tests that utility processes still launch
// when disclaimed.
ifit(process.platform === 'darwin')('supports disclaim option on macOS', async () => {
const child = utilityProcess.fork(path.join(fixturesPath, 'post-message.js'), [], {
disclaim: true
});
await once(child, 'spawn');
expect(child.pid).to.be.a('number');
// Verify the process can communicate normally
const testMessage = 'test-disclaim';
child.postMessage(testMessage);
const [data] = await once(child, 'message');
expect(data).to.equal(testMessage);
const exit = once(child, 'exit');
expect(child.kill()).to.be.true();
await exit;
});
});
});

View File

@@ -3,11 +3,17 @@ const { app } = require('electron');
const fs = require('node:fs');
const path = require('node:path');
const userDataFolder = path.join(app.getPath('temp'), 'electron-test-singleton-userdata');
// non-existent user data folder should not break requestSingleInstanceLock()
// ref: https://github.com/electron/electron/issues/33547
const userDataFolder = path.join(app.getPath('home'), 'electron-test-singleton-userdata');
fs.rmSync(userDataFolder, { force: true, recursive: true });
app.setPath('userData', userDataFolder);
fs.rmSync(userDataFolder, { recursive: true, force: true });
// set the user data path after clearing out old state and right before we use it
app.setPath('userData', userDataFolder);
const gotTheLock = app.requestSingleInstanceLock();
app.releaseSingleInstanceLock();
fs.rmSync(userDataFolder, { recursive: true, force: true });
app.exit(gotTheLock ? 0 : 1);

View File

@@ -640,6 +640,7 @@ __metadata:
webpack: "npm:^5.95.0"
webpack-cli: "npm:^6.0.1"
wrapper-webpack-plugin: "npm:^2.2.0"
yaml: "npm:^2.8.1"
dependenciesMeta:
abstract-socket:
built: true
@@ -15088,6 +15089,15 @@ __metadata:
languageName: node
linkType: hard
"yaml@npm:^2.8.1":
version: 2.8.2
resolution: "yaml@npm:2.8.2"
bin:
yaml: bin.mjs
checksum: 10c0/703e4dc1e34b324aa66876d63618dcacb9ed49f7e7fe9b70f1e703645be8d640f68ab84f12b86df8ac960bac37acf5513e115de7c970940617ce0343c8c9cd96
languageName: node
linkType: hard
"yamux-js@npm:0.1.2":
version: 0.1.2
resolution: "yamux-js@npm:0.1.2"