diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml
index 762eee223b..81f4d631ea 100644
--- a/.github/actions/build-electron/action.yml
+++ b/.github/actions/build-electron/action.yml
@@ -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
diff --git a/.github/workflows/apply-patches.yml b/.github/workflows/apply-patches.yml
index 980a3b3cba..181b085cd8 100644
--- a/.github/workflows/apply-patches.yml
+++ b/.github/workflows/apply-patches.yml
@@ -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:
diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml
index 0c197a23de..11ff9ac195 100644
--- a/.github/workflows/linux-publish.yml
+++ b/.github/workflows/linux-publish.yml
@@ -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
diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml
index 6adb2a88a1..f1673ad64c 100644
--- a/.github/workflows/macos-publish.yml
+++ b/.github/workflows/macos-publish.yml
@@ -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
diff --git a/.github/workflows/pipeline-electron-lint.yml b/.github/workflows/pipeline-electron-lint.yml
index 9bbb40b5cf..e850def965 100644
--- a/.github/workflows/pipeline-electron-lint.yml
+++ b/.github/workflows/pipeline-electron-lint.yml
@@ -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
diff --git a/.github/workflows/pipeline-segment-electron-build.yml b/.github/workflows/pipeline-segment-electron-build.yml
index 1012a3d1a8..a70ac7e9d5 100644
--- a/.github/workflows/pipeline-segment-electron-build.yml
+++ b/.github/workflows/pipeline-segment-electron-build.yml
@@ -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 }}'
diff --git a/.github/workflows/pipeline-segment-electron-clang-tidy.yml b/.github/workflows/pipeline-segment-electron-clang-tidy.yml
index 27b2d79db9..7881e619d3 100644
--- a/.github/workflows/pipeline-segment-electron-clang-tidy.yml
+++ b/.github/workflows/pipeline-segment-electron-clang-tidy.yml
@@ -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
diff --git a/.github/workflows/pipeline-segment-electron-gn-check.yml b/.github/workflows/pipeline-segment-electron-gn-check.yml
index 9974edbaa9..0c28e2c8c1 100644
--- a/.github/workflows/pipeline-segment-electron-gn-check.yml
+++ b/.github/workflows/pipeline-segment-electron-gn-check.yml
@@ -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:
diff --git a/.github/workflows/pipeline-segment-electron-publish.yml b/.github/workflows/pipeline-segment-electron-publish.yml
new file mode 100644
index 0000000000..805d1e7ca0
--- /dev/null
+++ b/.github/workflows/pipeline-segment-electron-publish.yml
@@ -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)
diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml
index 532b9f1f12..68c46f3ef4 100644
--- a/.github/workflows/pipeline-segment-electron-test.yml
+++ b/.github/workflows/pipeline-segment-electron-test.yml
@@ -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 }}
diff --git a/.github/workflows/pull-request-labeled.yml b/.github/workflows/pull-request-labeled.yml
index 0c540cf165..00ae179146 100644
--- a/.github/workflows/pull-request-labeled.yml
+++ b/.github/workflows/pull-request-labeled.yml
@@ -60,6 +60,7 @@ jobs:
with:
actions: 'create-comment'
token: ${{ steps.generate-token.outputs.token }}
+ issue-number: ${{ github.event.pull_request.number }}
body: |
diff --git a/.github/workflows/windows-publish.yml b/.github/workflows/windows-publish.yml
index 991c535e15..7b53c04213 100644
--- a/.github/workflows/windows-publish.yml
+++ b/.github/workflows/windows-publish.yml
@@ -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
diff --git a/BUILD.gn b/BUILD.gn
index 781d80f718..9dc7f22a66 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -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",
diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md
index 2f0ed2eb66..18af1af4bd 100644
--- a/docs/api/menu-item.md
+++ b/docs/api/menu-item.md
@@ -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_
diff --git a/docs/api/utility-process.md b/docs/api/utility-process.md
index e6cf1b79ba..bac030bbfa 100644
--- a/docs/api/utility-process.md
+++ b/docs/api/utility-process.md
@@ -36,6 +36,12 @@ Process: [Main](../glossary.md#main-process)
`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
diff --git a/filenames.gni b/filenames.gni
index 3c351e260a..6b93b01b8e 100644
--- a/filenames.gni
+++ b/filenames.gni
@@ -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",
diff --git a/lib/browser/api/menu-item-roles.ts b/lib/browser/api/menu-item-roles.ts
index 1bb48b636e..2d8e8eb215 100644
--- a/lib/browser/api/menu-item-roles.ts
+++ b/lib/browser/api/menu-item-roles.ts
@@ -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) {
diff --git a/lib/browser/api/menu-item.ts b/lib/browser/api/menu-item.ts
index 6c58a80d33..d08fdb86cd 100644
--- a/lib/browser/api/menu-item.ts
+++ b/lib/browser/api/menu-item.ts
@@ -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');
diff --git a/package.json b/package.json
index 49cd225135..4ca93a3bfe 100644
--- a/package.json
+++ b/package.json
@@ -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": {
diff --git a/patches/chromium/feat_configure_launch_options_for_service_process.patch b/patches/chromium/feat_configure_launch_options_for_service_process.patch
index f2a12ca50a..c8ad7240ad 100644
--- a/patches/chromium/feat_configure_launch_options_for_service_process.patch
+++ b/patches/chromium/feat_configure_launch_options_for_service_process.patch
@@ -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 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> 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 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> 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 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 site;
std::optional child_flags;
std::vector 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
diff --git a/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch b/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch
index e0d2ac5d73..3b7fc085fd 100644
--- a/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch
+++ b/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch
@@ -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 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
diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch
index 88336065e4..42549d056e 100644
--- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch
+++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch
@@ -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& argv,
+@@ -301,16 +321,16 @@ Process LaunchProcess(const std::vector& 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 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
diff --git a/patches/node/.patches b/patches/node/.patches
index 03db9c8c3d..0bc1e46fc2 100644
--- a/patches/node/.patches
+++ b/patches/node/.patches
@@ -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
diff --git a/patches/node/build_restore_macos_deployment_target_to_12_0.patch b/patches/node/build_restore_macos_deployment_target_to_12_0.patch
new file mode 100644
index 0000000000..4cfa72e7f8
--- /dev/null
+++ b/patches/node/build_restore_macos_deployment_target_to_12_0.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Keeley Hammond
+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',
diff --git a/script/copy-pipeline-segment-publish.js b/script/copy-pipeline-segment-publish.js
new file mode 100644
index 0000000000..f210f245d5
--- /dev/null
+++ b/script/copy-pipeline-segment-publish.js
@@ -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)
+ );
+}
diff --git a/script/release/uploaders/upload.py b/script/release/uploaders/upload.py
index 36b5a3dd7f..640ddf9e46 100755
--- a/script/release/uploaders/upload.py
+++ b/script/release/uploaders/upload.py
@@ -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'
diff --git a/shell/browser/api/electron_api_msix_updater.cc b/shell/browser/api/electron_api_msix_updater.cc
index 21ed3fc33e..e9b90446f4 100644
--- a/shell/browser/api/electron_api_msix_updater.cc
+++ b/shell/browser/api/electron_api_msix_updater.cc
@@ -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
#include
#include
-// 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
-#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
+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* 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 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* uri) {
+ ComPtr uri_factory;
+ HRESULT hr =
+ base::win::GetActivationFactory(
+ &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* 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 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 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* package) {
+ ComPtr 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 reply_runner;
+ gin_helper::Promise promise;
+ bool fire_and_forget;
+ ComPtr 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 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 promise) { promise.Resolve(); },
+ std::move(data->promise)));
+ return;
+ }
+
+ if (status == AsyncStatus::Error) {
+ ComPtr 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 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(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 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 reply_runner,
gin_helper::Promise 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 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 promise, std::string error) {
+ promise.RejectWithErrorMessage(error);
+ },
+ std::move(promise), std::move(error)));
+ return;
+ }
+
+ // Get IPackageManager9 for AddPackageByUriAsync
+ ComPtr 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 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 uri;
+ hr = CreateUri(uri_wstring, &uri);
+ if (FAILED(hr)) {
+ error = "Failed to create URI";
+ reply_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ [](gin_helper::Promise promise, std::string error) {
+ promise.RejectWithErrorMessage(error);
+ },
+ std::move(promise), std::move(error)));
+ return;
+ }
+
+ // Create AddPackageOptions
+ ComPtr 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 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 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(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 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 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();
+ 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([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 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 reply_runner,
gin_helper::Promise 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 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 promise, std::string error) {
+ promise.RejectWithErrorMessage(error);
+ },
+ std::move(promise), std::move(error)));
+ return;
+ }
+
+ // Get IPackageManager5 for RegisterPackageByFamilyNameAsync
+ ComPtr 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 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(
+ deployment_options | DeploymentOptions_ForceApplicationShutdown);
}
if (opts.force_target_shutdown) {
- deploymentOptions |= DeploymentOptions::ForceTargetApplicationShutdown;
+ deployment_options = static_cast(
+ deployment_options | DeploymentOptions_ForceTargetApplicationShutdown);
}
if (opts.force_update_from_any_version) {
- deploymentOptions |= DeploymentOptions::ForceUpdateFromAnyVersion;
+ deployment_options = static_cast(
+ deployment_options | DeploymentOptions_ForceUpdateFromAnyVersion);
}
- // Create empty collections for dependency and optional packages
- IIterable emptyDependencies{nullptr};
- IIterable 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 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 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(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 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 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();
+ 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([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 promise) {
+ promise.RejectWithErrorMessage(
+ "Failed to register completion handler");
+ },
+ std::move(raw_data->promise)));
+ }
}
#endif
@@ -307,6 +608,16 @@ v8::Local 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 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 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 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 package2;
+ ComPtr package4;
+ ComPtr 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 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 app_installer_info;
+ hr = package6->GetAppInstallerInfo(&app_installer_info);
+ if (SUCCEEDED(hr) && app_installer_info) {
+ ComPtr 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
diff --git a/shell/browser/api/electron_api_utility_process.cc b/shell/browser/api/electron_api_utility_process.cc
index 8a7b07bcbd..a3e68e8b9a 100644
--- a/shell/browser/api/electron_api_utility_process.cc
+++ b/shell/browser/api/electron_api_utility_process.cc
@@ -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::Create(
std::u16string display_name;
bool use_plugin_helper = false;
bool create_network_observer = false;
+ bool disclaim_responsibility = false;
std::map stdio;
base::FilePath current_working_directory;
base::EnvironmentMap env_map;
@@ -495,13 +498,15 @@ gin_helper::Handle 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;
}
diff --git a/shell/browser/api/electron_api_utility_process.h b/shell/browser/api/electron_api_utility_process.h
index c62fb96795..2dcd6fc71c 100644
--- a/shell/browser/api/electron_api_utility_process.h
+++ b/shell/browser/api/electron_api_utility_process.h
@@ -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();
diff --git a/shell/common/mac/codesign_util.cc b/shell/common/mac/codesign_util.cc
index 3ad25421e6..d40275d897 100644
--- a/shell/common/mac/codesign_util.cc
+++ b/shell/common/mac/codesign_util.cc
@@ -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;
diff --git a/spec/api-menu-item-spec.ts b/spec/api-menu-item-spec.ts
index dd71140cac..22294d6992 100644
--- a/spec/api-menu-item-spec.ts
+++ b/spec/api-menu-item-spec.ts
@@ -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 = {
+ 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' }
]);
diff --git a/spec/api-utility-process-spec.ts b/spec/api-utility-process-spec.ts
index 37d0897f3d..484190200a 100644
--- a/spec/api-utility-process-spec.ts
+++ b/spec/api-utility-process-spec.ts
@@ -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;
+ });
});
});
diff --git a/spec/fixtures/api/singleton-userdata/main.js b/spec/fixtures/api/singleton-userdata/main.js
index a0ec2f90a2..569206e258 100644
--- a/spec/fixtures/api/singleton-userdata/main.js
+++ b/spec/fixtures/api/singleton-userdata/main.js
@@ -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);
diff --git a/yarn.lock b/yarn.lock
index 447278be9f..2fafb315ed 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"