Compare commits

..

7 Commits

Author SHA1 Message Date
Shelley Vohr
5c7a639151 docs: note macOS 14+ 2026-04-09 09:24:06 +02:00
Shelley Vohr
c7fb0d30a1 feat: add menuItem.badge support on macOS
Adds a `badge` property to `MenuItem` on macOS, backed by AppKit's `NSMenuItemBadge` API (macOS 14+). The property accepts a `MenuItemBadge` object with a `type` (`alerts`, `updates`, `new-items`, or `none`) and an optional `count` or custom `content` string.

The badge can be set at construction time or updated dynamically after the item has been added to a menu.
2026-04-08 14:46:48 +02:00
Michaela Laurencin
6b5a4ff66c ci: allow ai-pr label without comment (#50792) 2026-04-08 13:09:23 +02:00
Charles Kerr
ca28023d4d chore: remove unused enum classes (#50782)
chore: remove unused FileSystemAccessPermissionContext::Access enum class

chore: remove unused FileSystemAccessPermissionContext::RequestType enum class

declared in 344aba08 but never used
2026-04-08 09:41:38 +02:00
Samuel Attard
e60441ad60 build: update build-tools to latest (#50786) 2026-04-08 09:31:12 +02:00
Charles Kerr
a189425373 fix: dangling raw_ptr api::Session::browser_context_ (#50784)
* fix: dangling raw_ptr api::Session::browser_context_

* fix: address code review feedback
2026-04-08 15:04:29 +09:00
Charles Kerr
7eccea1315 refactor: remove use of deprecated class base::MemoryPressureListener (#50763) 2026-04-07 20:11:02 -05:00
31 changed files with 310 additions and 466 deletions

View File

@@ -40,113 +40,10 @@ runs:
echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV
- name: Set GN_EXTRA_ARGS for Windows
shell: bash
if: ${{ 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 }}\" use_v8_context_snapshot=true v8_win_cross_compile_using_wine=true target_os=\"win\""
GN_APPENDED_ARGS="$GN_EXTRA_ARGS target_cpu=\"${{ inputs.target-arch }}\""
echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV
echo "ELECTRON_BUILD_TOOLS_MAIN_STAR=`pwd`/src/electron/build/siso/main.star" >> $GITHUB_ENV
- name: Install wine for Windows builds on non-Windows hosts
shell: bash
if: ${{ inputs.target-platform == 'win' && runner.os != 'Windows' }}
run: |
sudo mkdir -pm755 /etc/apt/keyrings
wget -O - https://dl.winehq.org/wine-builds/winehq.key | sudo gpg --dearmor -o /etc/apt/keyrings/winehq-archive.key -
sudo dpkg --add-architecture i386
sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/jammy/winehq-jammy.sources
sudo apt-get update
sudo apt install -y --install-recommends winehq-stable libgcc-s1:i386 libgcc-s1
# Increase virtual memory limits for Wine's VirtualAlloc (V8 sandbox
# reserves a very large contiguous region that fails under default limits)
sudo sysctl vm.mmap_min_addr
sudo sysctl -w vm.mmap_min_addr=65536
sudo sysctl -w vm.max_map_count=1048576
sudo sysctl -w vm.overcommit_memory=1
sudo sysctl -w kernel.randomize_va_space=0
if [ -x /usr/bin/wine ]; then
WINE_PATH=/usr/bin/wine
elif command -v wine >/dev/null 2>&1; then
WINE_PATH=$(command -v wine)
fi
if [ -z "$WINE_PATH" ] || [ ! -x "$WINE_PATH" ]; then
echo "ERROR: wine binary not found anywhere after install"
exit 1
fi
echo "Found wine at: $WINE_PATH"
export WINEPREFIX="$HOME/.wine"
mkdir -p "$WINEPREFIX"
echo "WINEPREFIX=$WINEPREFIX" >> $GITHUB_ENV
# Start a persistent Xvfb server for wine (xvfb-run per-invocation hangs
# because wineserver keeps the X connection alive)
export DISPLAY=:99.0
echo "DISPLAY=:99.0" >> $GITHUB_ENV
export XDG_RUNTIME_DIR=/tmp/runtime-$(whoami)
echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" >> $GITHUB_ENV
mkdir -p "$XDG_RUNTIME_DIR"
echo "Starting Xvfb on :99 with 1024x768x24 resolution"
Xvfb :99 -screen 0 1024x768x24 &
echo "Wait for Xvfb to be ready"
# Wait for Xvfb to be ready (up to 5 seconds)
for i in $(seq 1 10); do
if xdpyinfo -display :99 >/dev/null 2>&1; then
echo "Xvfb is running on :99"
break
fi
echo "xvfb not ready yet, waiting..."
sleep 0.5
done
if ! xdpyinfo -display :99 >/dev/null 2>&1; then
echo "ERROR: Xvfb failed to start"
exit 1
fi
echo "Xvfb is ready; about to run wineboot"
export WINEDEBUG=-all
echo "WINEDEBUG=$WINEDEBUG" >> $GITHUB_ENV
export WINEARCH=win64
echo "WINEARCH=$WINEARCH" >> $GITHUB_ENV
WINEDLLOVERRIDES="mscoree,mshtml=" wineboot --init
echo "Done running wineboot"
# Initialize wine prefix to avoid first-run delays during build
#"$WINE_PATH" wineboot --init 2>/dev/null || true
WINE_WRAPPER="/usr/local/bin/xvfb-wine"
printf '#!/bin/sh\nsetarch x86_64 -R %s "$@"\n' "$WINE_PATH" | sudo tee "$WINE_WRAPPER" > /dev/null
sudo chmod +x "$WINE_WRAPPER"
GN_APPENDED_ARGS="$GN_EXTRA_ARGS v8_wine_path=\"$WINE_WRAPPER\""
# Pass wine path to GN so siso can find it regardless of PATH
GN_APPENDED_ARGS="$GN_EXTRA_ARGS v8_wine_path=\"$WINE_PATH\""
echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV
echo "GN APPENDED_ARGS set with wine path: $GN_APPENDED_ARGS"
echo "Wine setup complete"
# - name: Download mksnapshot.zip
# shell: bash
# if: ${{ inputs.target-platform == 'win' }}
# run: |
# # Download mksnapshot.zip from https://electronbuildtools.blob.core.windows.net/windows-toolchains/mksnapshot.zip
# curl -L -o mksnapshot.zip https://electronbuildtools.blob.core.windows.net/windows-toolchains/mksnapshot.zip
# if [ $? -ne 0 ]; then
# echo "Failed to download mksnapshot.zip"
# exit 1
# fi
# if [ -f mksnapshot.zip ]; then
# echo "Successfully downloaded mksnapshot.zip"
# else
# echo "mksnapshot.zip not found after download"
# exit 1
# fi
# if [ -f mksnapshot.zip ]; then
# unzip mksnapshot.zip -d mksnapshot || true
# PREBUILT_MKSNAPSHOT="$(pwd)/mksnapshot/mksnapshot.exe"
# if [ -f "$PREBUILT_MKSNAPSHOT" ]; then
# GN_APPENDED_ARGS="$GN_EXTRA_ARGS v8_prebuilt_mksnapshot=\"$PREBUILT_MKSNAPSHOT\""
# echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV
# echo "Using pre-built mksnapshot at $PREBUILT_MKSNAPSHOT"
# else
# echo "mksnapshot binary not found in mksnapshot.zip, will build from source"
# fi
# else
# echo "mksnapshot.zip not found, skipping unzip and PATH update"
# fi
- name: Add Clang problem matcher
shell: bash
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
@@ -161,6 +58,7 @@ runs:
OUTPUT_PATH: src/previous-object-checksums.json
run: node src/electron/.github/actions/build-electron/download-previous-object-checksums.mjs
- name: Build Electron ${{ inputs.step-suffix }}
if: ${{ inputs.target-platform != 'win' }}
shell: bash
run: |
rm -rf "src/out/Default/Electron Framework.framework"
@@ -185,11 +83,7 @@ runs:
node electron/script/check-symlinks.js
# Build stats and object checksums
if [ "$TARGET_PLATFORM" = "win" ]; then
BUILD_STATS_ARGS="out/Default/siso.exe.INFO --out-dir out/Default --output-object-checksums object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json"
else
BUILD_STATS_ARGS="out/Default/siso.INFO --out-dir out/Default --output-object-checksums object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json"
fi
BUILD_STATS_ARGS="out/Default/siso.INFO --out-dir out/Default --output-object-checksums object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json"
if [ -f previous-object-checksums.json ]; then
BUILD_STATS_ARGS="$BUILD_STATS_ARGS --input-object-checksums previous-object-checksums.json"
fi
@@ -199,6 +93,38 @@ runs:
echo "Skipping build-stats.mjs upload because DD_API_KEY is not set"
fi
node electron/script/build-stats.mjs $BUILD_STATS_ARGS || true
- name: Build Electron (Windows) ${{ inputs.step-suffix }}
if: ${{ inputs.target-platform == 'win' }}
shell: powershell
run: |
cd src\electron
git pack-refs
cd ..
$env:NINJA_SUMMARIZE_BUILD = 1
if ("${{ inputs.is-release }}" -eq "true") {
e build --target electron:release_build
} else {
e build --target electron:testing_build
}
Copy-Item out\Default\.ninja_log out\electron_ninja_log
node electron\script\check-symlinks.js
# Build stats and object checksums
$statsArgs = @("out\Default\siso.exe.INFO", "--out-dir", "out\Default", "--output-object-checksums", "object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json")
if (Test-Path previous-object-checksums.json) {
$statsArgs += @("--input-object-checksums", "previous-object-checksums.json")
}
if ($env:DD_API_KEY) {
$statsArgs += "--upload-stats"
} else {
Write-Host "Skipping build-stats.mjs upload because DD_API_KEY is not set"
}
try {
& node electron\script\build-stats.mjs @statsArgs ; $LASTEXITCODE = 0
} catch {
Write-Host "Build stats failed, continuing..."
}
- name: Verify dist.zip ${{ inputs.step-suffix }}
shell: bash
run: |
@@ -226,7 +152,15 @@ runs:
sed $SEDOPTION '/--warn-about-builtin-profile-data/d' out/Default/mksnapshot_args
sed $SEDOPTION '/--abort-on-bad-builtin-profile-data/d' out/Default/mksnapshot_args
(cd out/Default; zip mksnapshot.zip mksnapshot_args gen/v8/embedded.S)
if [ "${{ inputs.target-platform }}" = "win" ]; then
cd out/Default
powershell Compress-Archive -update mksnapshot_args mksnapshot.zip
powershell mkdir mktmp\\gen\\v8
powershell Copy-Item gen\\v8\\embedded.S mktmp\\gen\\v8
powershell Compress-Archive -update -Path mktmp\\gen mksnapshot.zip
else
(cd out/Default; zip mksnapshot.zip mksnapshot_args gen/v8/embedded.S)
fi
- name: Generate Cross-Arch Snapshot (arm/arm64) ${{ inputs.step-suffix }}
shell: bash
if: ${{ (inputs.target-arch == 'arm' || inputs.target-arch == 'arm64') && inputs.target-platform == 'linux' }}
@@ -260,6 +194,12 @@ runs:
fi
electron/script/zip_manifests/check-zip-manifest.py out/Default/chromedriver.zip electron/script/zip_manifests/chromedriver_zip.$target_os.${{ inputs.target-arch }}.manifest
fi
- name: Create installed_software.json ${{ inputs.step-suffix }}
shell: powershell
if: ${{ inputs.is-release == 'true' && inputs.target-platform == 'win' }}
run: |
cd src
Get-CimInstance -Namespace root\cimv2 -Class Win32_product | Select vendor, description, @{l='install_location';e='InstallLocation'}, @{l='install_date';e='InstallDate'}, @{l='install_date_2';e='InstallDate2'}, caption, version, name, @{l='sku_number';e='SKUNumber'} | ConvertTo-Json | Out-File -Encoding utf8 -FilePath .\installed_software.json
- name: Profile Windows Toolchain ${{ inputs.step-suffix }}
shell: bash
if: ${{ inputs.is-release == 'true' && inputs.target-platform == 'win' }}
@@ -329,7 +269,7 @@ runs:
with:
subject-path: ${{ steps.github-upload.outputs.UPLOADED_PATHS }}
- name: Generate siso report
if: ${{ !cancelled() }}
if: ${{ inputs.target-platform != 'win' && !cancelled() }}
shell: bash
run: |
cd src
@@ -338,6 +278,17 @@ runs:
echo "SISO_REPORT_PATH=$SISO_REPORT_PATH" >> $GITHUB_ENV
cat siso_report.txt
echo "SISO REPORT AT $SISO_REPORT_PATH"
- name: Generate siso report (Windows)
if: ${{ inputs.target-platform == 'win' && !cancelled() }}
shell: powershell
run: |
cd src
e d siso report -C out\Default > siso_report.txt
$SISO_REPORT_PATH = Get-Content "siso_report.txt" | Select-String "report file:\s*(.+)" | ForEach-Object {
$_.Matches.Groups[1].Value.Trim()
}
echo "SISO_REPORT_PATH=$SISO_REPORT_PATH"
echo "SISO_REPORT_PATH=$SISO_REPORT_PATH" >> $env:GITHUB_ENV
- name: Generate Artifact Key
if: always() && !cancelled()
shell: bash

View File

@@ -15,7 +15,7 @@ runs:
git config --global core.preloadindex true
git config --global core.longpaths true
fi
export BUILD_TOOLS_SHA=1aa9dce0b3fa9353c38a6c3d4aac0249f0f68973
export BUILD_TOOLS_SHA=1b7bd25dae4a780bb3170fff56c9327b53aaf7eb
npm i -g @electron/build-tools
# Update depot_tools to ensure python
e d update_depot_tools
@@ -29,4 +29,4 @@ runs:
else
echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH
echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH
fi
fi

View File

@@ -11,12 +11,12 @@ on:
skip-macos:
type: boolean
description: 'Skip macOS builds'
default: true
default: false
required: false
skip-linux:
type: boolean
description: 'Skip Linux builds'
default: true
default: false
required: false
skip-windows:
type: boolean
@@ -108,7 +108,7 @@ jobs:
# Checkout Jobs
checkout-macos:
needs: setup
if: ${{ needs.setup.outputs.src == 'true' && inputs.skip-macos == 'false'}}
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-macos}}
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
@@ -138,7 +138,7 @@ jobs:
checkout-linux:
needs: setup
if: ${{ inputs.skip-linux == 'false' }}
if: ${{ !inputs.skip-linux}}
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
@@ -387,9 +387,8 @@ jobs:
needs: checkout-windows
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
with:
build-runs-on: electron-arc-centralus-linux-amd64-32core
build-runs-on: electron-arc-centralus-windows-amd64-16core
test-runs-on: windows-latest
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/win-cache:/mnt/win-cache"]}'
target-platform: win
target-arch: x64
is-release: false
@@ -407,9 +406,8 @@ jobs:
needs: checkout-windows
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
with:
build-runs-on: electron-arc-centralus-linux-amd64-32core
build-runs-on: electron-arc-centralus-windows-amd64-16core
test-runs-on: windows-latest
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/win-cache:/mnt/win-cache"]}'
target-platform: win
target-arch: x86
is-release: false
@@ -427,9 +425,8 @@ jobs:
needs: checkout-windows
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
with:
build-runs-on: electron-arc-centralus-linux-amd64-32core
build-runs-on: electron-arc-centralus-windows-amd64-16core
test-runs-on: windows-11-arm
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/win-cache:/mnt/win-cache"]}'
target-platform: win
target-arch: arm64
is-release: false

View File

@@ -134,10 +134,6 @@ jobs:
- name: Install AZCopy
if: ${{ inputs.target-platform == 'macos' }}
run: brew install azcopy
- name: Enable windows toolchain
if: ${{ inputs.target-platform == 'win' }}
run: |
echo "ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN=1" >> $GITHUB_ENV
- name: Set GN_EXTRA_ARGS for Linux
if: ${{ inputs.target-platform == 'linux' }}
run: |
@@ -164,15 +160,13 @@ jobs:
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy
if: ${{ inputs.target-platform == 'macos' }}
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 != 'macos' }}
if: ${{ inputs.target-platform == 'linux' }}
uses: ./src/electron/.github/actions/restore-cache-aks
with:
target-platform: ${{ inputs.target-platform }}
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
@@ -180,7 +174,7 @@ jobs:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Fix Sync
if: ${{ inputs.target-platform == 'macos' }}
if: ${{ inputs.target-platform != 'linux' }}
uses: ./src/electron/.github/actions/fix-sync
with:
target-platform: ${{ inputs.target-platform }}
@@ -189,21 +183,9 @@ jobs:
- 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
e sanitize-config
- name: Run Electron Only Hooks
run: |
if [ "${{ inputs.target-platform }}" = "win" ]; then
rm -rf src/third_party/depot_tools/win_toolchain/vs_files*
fi
echo "solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]" > tmpgclient
if [ "${{ inputs.target-platform }}" = "win" ]; then
echo "target_os=['win']" >> tmpgclient
fi
gclient runhooks --gclientfile=tmpgclient
# Fix VS Toolchain
if [ "${{ inputs.target-platform }}" = "win" ]; then
e d python3 src/build/vs_toolchain.py update --force
fi
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
@@ -242,11 +224,3 @@ jobs:
generate-symbols: '${{ inputs.generate-symbols }}'
upload-to-storage: '${{ inputs.upload-to-storage }}'
step-suffix: '(mas)'
- name: Wait for active SSH sessions
shell: bash
if: always() && !cancelled()
run: |
while [ -f /var/.ssh-lock ]
do
sleep 60
done

View File

@@ -50,7 +50,7 @@ jobs:
field-value: ✅ Reviewed
pull-request-labeled-ai-pr:
name: ai-pr label added
if: github.event.label.name == 'ai-pr'
if: github.event.label.name == 'ai-pr' && github.event.pull_request.state != 'closed'
runs-on: ubuntu-latest
permissions: {}
steps:

View File

@@ -1,103 +0,0 @@
load("@builtin//encoding.star", "json")
load("@builtin//lib/gn.star", "gn")
load("@builtin//path.star", "path")
load("@builtin//runtime.star", "runtime")
load("@builtin//struct.star", "module")
load("@config//main.star", upstream_init = "init")
load("@config//win_sdk.star", "win_sdk")
load("@config//gn_logs.star", "gn_logs")
def init(ctx):
mod = upstream_init(ctx)
step_config = json.decode(mod.step_config)
# Buildbarn doesn't support input_root_absolute_path so disable that
for rule in step_config["rules"]:
input_root_absolute_path = rule.get("input_root_absolute_path", False)
if input_root_absolute_path:
rule.pop("input_root_absolute_path", None)
# Only wrap clang rules with a remote wrapper if not on Linux. These are currently only
# needed for X-Compile builds, which run on Windows and Mac.
if runtime.os != "linux":
for rule in step_config["rules"]:
if rule["name"].startswith("clang/") or rule["name"].startswith("clang-cl/"):
rule["remote_wrapper"] = "../../buildtools/reclient_cfgs/chromium-browser-clang/clang_remote_wrapper"
if "inputs" not in rule:
rule["inputs"] = []
rule["inputs"].append("buildtools/reclient_cfgs/chromium-browser-clang/clang_remote_wrapper")
rule["inputs"].append("third_party/llvm-build/Release+Asserts_linux/bin/clang")
if "executables" not in step_config:
step_config["executables"] = []
step_config["executables"].append("buildtools/reclient_cfgs/chromium-browser-clang/clang_remote_wrapper")
step_config["executables"].append("third_party/llvm-build/Release+Asserts_linux/bin/clang")
if runtime.os == "darwin":
# Update platforms to match our default siso config instead of reclient configs.
step_config["platforms"].update({
"clang": step_config["platforms"]["default"],
"clang_large": step_config["platforms"]["default"],
})
# Add additional Windows SDK headers needed by Electron
win_toolchain_dir = win_sdk.toolchain_dir(ctx)
if win_toolchain_dir:
sdk_version = gn_logs.read(ctx).get("windows_sdk_version")
step_config["input_deps"][win_toolchain_dir + ":headers"].extend([
# third_party/electron_node/deps/uv/include/uv/win.h includes mswsock.h
path.join(win_toolchain_dir, "Windows Kits", "10/Include", sdk_version, "um/mswsock.h"),
# third_party/electron_node/src/debug_utils.cc includes lm.h
path.join(win_toolchain_dir, "Windows Kits", "10/Include", sdk_version, "um/Lm.h"),
])
if runtime.os == "windows":
# Update platforms to match our default siso config instead of reclient configs.
step_config["platforms"].update({
"clang-cl": step_config["platforms"]["default"],
"clang-cl_large": step_config["platforms"]["default"],
"lld-link": step_config["platforms"]["default"],
})
# When cross-compiling for Windows using wine, mksnapshot.exe is run via
# wine64 on the local machine. The remote execution service cannot handle
# this, so force mksnapshot to run locally. The wine64 prefix also changes
# the command so the upstream v8/mksnapshot rule's command_prefix won't
# match — add an explicit rule for the wine-prefixed command.
if "args.gn" in ctx.metadata:
gn_args = gn.args(ctx)
if gn_args.get("v8_win_cross_compile_using_wine", "").strip('"') == "true":
# Force the existing v8/mksnapshot rule to run locally
for rule in step_config["rules"]:
if rule.get("name") == "v8/mksnapshot":
rule["remote"] = False
# Add a rule that matches the wine64-prefixed mksnapshot command.
# With wine, GN emits: python3 ../../v8/tools/run.py <wine_path> ./mksnapshot.exe
# The upstream command_prefix ("python3 ...run.py ./mksnapshot") won't
# match because wine_path appears before ./mksnapshot.exe.
wine_path = gn_args.get("v8_wine_path", "").strip('"')
if wine_path:
step_config["rules"].insert(0, {
"name": "v8/mksnapshot_wine",
"command_prefix": "python3 ../../v8/tools/run.py " + wine_path,
"remote": False,
"timeout": "2m",
"output_local": True,
})
# Same treatment for v8_context_snapshot_generator which uses
# gn_run_binary.py instead of v8/tools/run.py.
step_config["rules"].insert(0, {
"name": "v8/context_snapshot_wine",
"command_prefix": "python3 ../../build/gn_run_binary.py " + wine_path,
"remote": False,
"timeout": "2m",
"output_local": True,
})
return module(
"config",
step_config = json.encode(step_config),
filegroups = mod.filegroups,
handlers = mod.handlers,
)

View File

@@ -63,10 +63,17 @@ See [`Menu`](menu.md) for examples.
* `afterGroupContaining` string[] (optional) - Provides a means for a single context menu to declare
the placement of their containing group after the containing group of the item
with the specified id.
* `badge` Object (optional) _macOS_ - Only available on macOS 14 and up.
* `type` string (optional) - Can be one of `alerts`, `updates`, `new-items` or `none`. Default is `none`.
* `count` number (optional) - The number of items the badge displays. Cannot be used with `type: 'none'`.
* `content` string (optional) - A custom string to display in the badge. Only usable with `type: 'none'`.
> [!NOTE]
> `acceleratorWorksWhenHidden` is specified as being macOS-only because accelerators always work when items are hidden on Windows and Linux. The option is exposed to users to give them the option to turn it off, as this is possible in native macOS development.
> [!NOTE]
> If you use one of the predefined badge types on macOS (not 'none'), the system localizes and pluralizes the string for you. If you create your own custom badge string, you need to localize and pluralize that string yourself.
### Instance Properties
The following properties are available on instances of `MenuItem`:
@@ -181,3 +188,9 @@ A `number` indicating an item's sequential unique id.
#### `menuItem.menu`
A [`Menu`](menu.md) that the item is a part of.
#### `menuItem.badge` _macOS_
An [`MenuItemBadge`](structures/menu-item-badge.md) indicating the badge for the menu item.
This property can be dynamically changed. Only available on macOS 14 and up.

View File

@@ -0,0 +1,5 @@
# MenuItemBadge Object
* `type` string (optional) - Can be one of `alerts`, `updates`, `new-items` or `none`. Default is `none`.
* `count` number (optional) - The number of items the badge displays. Cannot be used with `type: 'none'`.
* `content` string (optional) - A custom string to display in the badge. Only usable with `type: 'none'`.

View File

@@ -111,6 +111,7 @@ auto_filenames = {
"docs/api/structures/media-access-permission-request.md",
"docs/api/structures/memory-info.md",
"docs/api/structures/memory-usage-details.md",
"docs/api/structures/menu-item-badge.md",
"docs/api/structures/mime-typed-buffer.md",
"docs/api/structures/mouse-input-event.md",
"docs/api/structures/mouse-wheel-input-event.md",

View File

@@ -38,6 +38,24 @@ const MenuItem = function (this: any, options: any) {
this.overrideProperty('acceleratorWorksWhenHidden', true);
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role));
if (process.platform === 'darwin') {
let badgeValue = options.badge;
Object.defineProperty(this, 'badge', {
get: () => badgeValue,
set: (newValue) => {
badgeValue = newValue;
// Update native badge if this item is already in a menu
if (this.menu) {
const index = this.menu.getIndexOfCommandId(this.commandId);
if (index !== -1 && badgeValue) {
this.menu.setBadge(index, badgeValue);
}
}
},
enumerable: true
});
}
if (!MenuItem.types.includes(this.type)) {
throw new Error(`Unknown menu item type: ${this.type}`);
}

View File

@@ -176,6 +176,9 @@ Menu.prototype.insert = function (pos, item) {
if (item.type === 'palette' || item.type === 'header') {
this.setCustomType(pos, item.type);
}
if (process.platform === 'darwin' && item.badge) {
this.setBadge(pos, item.badge);
}
// Make menu accessible to items.
item.overrideReadOnlyProperty('menu', this);

View File

@@ -150,4 +150,3 @@ fix_use_fresh_lazynow_for_onendworkitemimpl_after_didruntask.patch
fix_pulseaudio_stream_and_icon_names.patch
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
feat_allow_enabling_extensions_on_custom_protocols.patch
chore_run_v8_context_snapshot_generator_through_wine_when.patch

View File

@@ -1,49 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: John Kleinschmidt <kleinschmidtorama@gmail.com>
Date: Tue, 7 Apr 2026 14:05:09 -0400
Subject: chore: run v8_context_snapshot_generator through Wine when
cross-compiling
When cross-compiling for Windows on Linux with Wine, the
v8_context_snapshot_generator binary is a Windows PE executable (.exe)
that cannot be run directly. Prepend the wine path to args and append
.exe to the binary name, matching the approach used for mksnapshot.
diff --git a/tools/v8_context_snapshot/BUILD.gn b/tools/v8_context_snapshot/BUILD.gn
index ec2357139826613e8b295cb01be341acb03b4737..6c4171dbaf7a76cfc89d71871b73058747675811 100644
--- a/tools/v8_context_snapshot/BUILD.gn
+++ b/tools/v8_context_snapshot/BUILD.gn
@@ -50,12 +50,27 @@ if (use_v8_context_snapshot) {
output_file = "$root_build_dir/$v8_context_snapshot_filename"
output_path = rebase_path(output_file, root_build_dir)
- args = [
- "./" + rebase_path(
- get_label_info(
- ":v8_context_snapshot_generator($v8_snapshot_toolchain)",
- "root_out_dir") + "/v8_context_snapshot_generator",
- root_build_dir),
+ _generator_dir = get_label_info(
+ ":v8_context_snapshot_generator($v8_snapshot_toolchain)",
+ "root_out_dir")
+ if (v8_win_cross_compile_using_wine) {
+ _generator_bin = "./" + rebase_path(
+ _generator_dir + "/v8_context_snapshot_generator.exe",
+ root_build_dir)
+ } else {
+ _generator_bin = "./" + rebase_path(
+ _generator_dir + "/v8_context_snapshot_generator",
+ root_build_dir)
+ }
+
+ args = []
+ if (v8_win_cross_compile_using_wine) {
+ assert(v8_wine_path != "",
+ "v8_wine_path must be set when v8_win_cross_compile_using_wine is true")
+ args += [ v8_wine_path ]
+ }
+ args += [
+ _generator_bin,
"--snapshot_blob=$snapshot_blob_path",
"--output_file=$output_path",
]

View File

@@ -1,3 +1,2 @@
chore_allow_customizing_microtask_policy_per_context.patch
build_warn_instead_of_abort_on_builtin_pgo_profile_mismatch.patch
feat_support_running_mksnapshot_exe_via_wine_for_windows.patch

View File

@@ -1,113 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: John Kleinschmidt <kleinschmidtorama@gmail.com>
Date: Tue, 24 Mar 2026 16:29:55 -0400
Subject: feat: support running mksnapshot.exe via Wine for Windows
cross-compile
When v8_win_cross_compile_using_wine=true is set, mksnapshot is built as a
Windows .exe using the target toolchain and executed via Wine on the Linux
host. This avoids needing source-level cross-compile patches for the
unwinding info code paths.
diff --git a/BUILD.gn b/BUILD.gn
index 751265ba83c04f0c545248a71b35e46034bfc90a..9ca41b06b569c729496abde7cba085518a9f0d71 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2699,7 +2699,11 @@ template("run_mksnapshot") {
suffix = "_$name"
}
action("run_mksnapshot_" + name) {
- deps = [ ":mksnapshot($v8_snapshot_toolchain)" ]
+ if (v8_prebuilt_mksnapshot != "") {
+ deps = []
+ } else {
+ deps = [ ":mksnapshot($v8_snapshot_toolchain)" ]
+ }
if (v8_verify_deterministic_mksnapshot) {
# We archive the snapshot executable when verifying snapshot
# determinism to ease debugging.
@@ -2753,10 +2757,26 @@ template("run_mksnapshot") {
data += [ "$root_out_dir/mksnapshot_output${suffix}.log" ]
}
+ if (v8_prebuilt_mksnapshot != "") {
+ _mksnapshot_bin = v8_prebuilt_mksnapshot
+ } else {
+ _mksnapshot_dir =
+ get_label_info(":mksnapshot($v8_snapshot_toolchain)", "root_out_dir")
+ if (v8_win_cross_compile_using_wine) {
+ _mksnapshot_bin = _mksnapshot_dir + "/mksnapshot.exe"
+ } else {
+ _mksnapshot_bin = _mksnapshot_dir + "/mksnapshot"
+ }
+ }
+
+ if (v8_win_cross_compile_using_wine) {
+ assert(
+ v8_wine_path != "",
+ "v8_wine_path must be set when v8_win_cross_compile_using_wine is true")
+ args += [ v8_wine_path ]
+ }
args += [
- "./" + rebase_path(get_label_info(":mksnapshot($v8_snapshot_toolchain)",
- "root_out_dir") + "/mksnapshot",
- root_build_dir),
+ "./" + rebase_path(_mksnapshot_bin, root_build_dir),
"--turbo_instruction_scheduling",
"--turbo-always-optimize-spills",
diff --git a/gni/snapshot_toolchain.gni b/gni/snapshot_toolchain.gni
index 37ed262640d048a6fa5923279ae0388c1c0ae766..d9830914f0abd919143f0cf8b3069c5a3f70516e 100644
--- a/gni/snapshot_toolchain.gni
+++ b/gni/snapshot_toolchain.gni
@@ -32,6 +32,21 @@ declare_args() {
# toolchain that matches the bit-width of the target CPU, but runs on
# the host.
v8_snapshot_toolchain = ""
+
+ # When cross-compiling for Windows on a non-Windows host, build mksnapshot
+ # as a Windows .exe and run it via Wine instead of building a host-native
+ # mksnapshot with cross-compile patches.
+ v8_win_cross_compile_using_wine = false
+
+ # Absolute path to the wine64 binary. Required when
+ # v8_win_cross_compile_using_wine is true.
+ v8_wine_path = ""
+
+ # Path to a pre-built mksnapshot binary to use instead of building one from
+ # source. When set, run_mksnapshot will use this binary directly and skip
+ # building the mksnapshot target. The path should be absolute or relative
+ # to the source root.
+ v8_prebuilt_mksnapshot = ""
}
# Try to infer the appropriate snapshot toolchain for the v8_current_cpu
@@ -69,7 +84,12 @@ if (v8_snapshot_toolchain == "") {
# Cross-build from same ISA on one OS to another. For example:
# * targeting win/x64 on a linux/x64 host
# * targeting win/arm64 on a mac/arm64 host
- v8_snapshot_toolchain = host_toolchain
+ if (v8_win_cross_compile_using_wine && is_win && host_os != "win") {
+ # Build mksnapshot as a Windows .exe and run it via Wine.
+ v8_snapshot_toolchain = current_toolchain
+ } else {
+ v8_snapshot_toolchain = host_toolchain
+ }
} else if (host_cpu == "arm64" && current_cpu == "x64") {
# Cross-build from arm64 to intel (likely on an Apple Silicon mac).
v8_snapshot_toolchain =
@@ -119,7 +139,13 @@ assert(v8_snapshot_toolchain != "",
# We reuse the snapshot toolchain for building torque and other generators to
# avoid building v8_libbase on the host more than once. On mips with big endian,
# the snapshot toolchain is the target toolchain and, hence, can't be used.
-v8_generator_toolchain = v8_snapshot_toolchain
+# When using Wine for cross-compilation, mksnapshot is built as a Windows .exe
+# but other generators (torque, etc.) must still be host-native binaries.
+if (v8_win_cross_compile_using_wine && is_win && host_os != "win") {
+ v8_generator_toolchain = host_toolchain
+} else {
+ v8_generator_toolchain = v8_snapshot_toolchain
+}
if (host_cpu == "x64" && v8_current_cpu == "mips64") {
v8_generator_toolchain = "//build/toolchain/linux:clang_x64"
}

View File

@@ -9,11 +9,7 @@ elif [ "`uname`" == "Darwin" ]; then
BUILD_TYPE="mas"
fi
elif [ "`uname`" == "Linux" ]; then
if [ "$ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN" == "1" ]; then
BUILD_TYPE="win"
else
BUILD_TYPE="linux"
fi
BUILD_TYPE="linux"
else
echo "Unsupported platform"
exit 1

View File

@@ -18,7 +18,6 @@
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/image_converter.h"
#include "shell/common/gin_converters/optional_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "ui/base/models/image_model.h"
@@ -28,6 +27,7 @@
namespace gin {
using SharingItem = electron::ElectronMenuModel::SharingItem;
using Badge = electron::ElectronMenuModel::Badge;
template <>
struct Converter<SharingItem> {
@@ -44,6 +44,28 @@ struct Converter<SharingItem> {
}
};
template <>
struct Converter<Badge> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
Badge* out) {
gin_helper::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
std::string type_str;
if (dict.Get("type", &type_str)) {
out->type = base::UTF8ToUTF16(type_str);
} else {
out->type = u"none";
}
dict.GetOptional("count", &(out->count));
dict.GetOptional("content", &(out->content));
return true;
}
};
} // namespace gin
#endif
@@ -252,6 +274,21 @@ void Menu::SetCustomType(int index, const std::u16string& customType) {
model_->SetCustomType(index, customType);
}
#if BUILDFLAG(IS_MAC)
void Menu::SetBadge(int index, const gin_helper::Dictionary& badge_dict) {
ElectronMenuModel::Badge badge;
std::string type_str;
if (badge_dict.Get("type", &type_str)) {
badge.type = base::UTF8ToUTF16(type_str);
} else {
badge.type = u"none";
}
badge_dict.GetOptional("count", &badge.count);
badge_dict.GetOptional("content", &badge.content);
model_->SetBadge(index, std::move(badge));
}
#endif
void Menu::Clear() {
model_->Clear();
}
@@ -292,8 +329,12 @@ void Menu::FillObjectTemplate(v8::Isolate* isolate,
.SetMethod("setToolTip", &Menu::SetToolTip)
.SetMethod("setRole", &Menu::SetRole)
.SetMethod("setCustomType", &Menu::SetCustomType)
#if BUILDFLAG(IS_MAC)
.SetMethod("setBadge", &Menu::SetBadge)
#endif
.SetMethod("clear", &Menu::Clear)
.SetMethod("getItemCount", &Menu::GetItemCount)
.SetMethod("getIndexOfCommandId", &Menu::GetIndexOfCommandId)
.SetMethod("popupAt", &Menu::PopupAt)
.SetMethod("closePopupAt", &Menu::ClosePopupAt)
.SetMethod("_getAcceleratorTextAt", &Menu::GetAcceleratorTextAtForTesting)

View File

@@ -13,6 +13,7 @@
#include "shell/browser/event_emitter_mixin.h"
#include "shell/browser/ui/electron_menu_model.h"
#include "shell/common/gin_helper/constructible.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/self_keep_alive.h"
#include "ui/base/mojom/menu_source_type.mojom-forward.h"
@@ -128,6 +129,9 @@ class Menu : public gin::Wrappable<Menu>,
void SetToolTip(int index, const std::u16string& toolTip);
void SetRole(int index, const std::u16string& role);
void SetCustomType(int index, const std::u16string& customType);
#if BUILDFLAG(IS_MAC)
void SetBadge(int index, const gin_helper::Dictionary& badge);
#endif
void Clear();
int GetIndexOfCommandId(int command_id) const;
int GetItemCount() const;

View File

@@ -550,8 +550,7 @@ gin::WrapperInfo Session::kWrapperInfo = {{gin::kEmbedderNativeGin},
Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
: isolate_(isolate),
network_emulation_token_(base::UnguessableToken::Create()),
browser_context_{
raw_ref<ElectronBrowserContext>::from_ptr(browser_context)} {
browser_context_{browser_context} {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
data->AddDisposeObserver(this);
// Observe DownloadManager to get download notifications.
@@ -584,16 +583,21 @@ Session::~Session() {
}
void Session::Dispose() {
if (keep_alive_) {
browser_context()->GetDownloadManager()->RemoveObserver(this);
if (!keep_alive_)
return;
ElectronBrowserContext* const browser_context = this->browser_context();
if (!browser_context)
return;
browser_context->GetDownloadManager()->RemoveObserver(this);
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
if (auto* service =
SpellcheckServiceFactory::GetForContext(browser_context())) {
service->SetHunspellObserver(nullptr);
}
#endif
if (auto* service =
SpellcheckServiceFactory::GetForContext(browser_context)) {
service->SetHunspellObserver(nullptr);
}
#endif
}
void Session::OnDownloadCreated(content::DownloadManager* manager,
@@ -1875,6 +1879,7 @@ void Session::OnBeforeMicrotasksRunnerDispose(v8::Isolate* isolate) {
data->RemoveDisposeObserver(this);
Dispose();
weak_factory_.Invalidate();
browser_context_ = nullptr;
keep_alive_.Clear();
}

View File

@@ -10,7 +10,6 @@
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "content/public/browser/download_manager.h"
@@ -103,8 +102,8 @@ class Session final : public gin::Wrappable<Session>,
Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context);
~Session() override;
ElectronBrowserContext* browser_context() const {
return &browser_context_.get();
[[nodiscard]] ElectronBrowserContext* browser_context() const {
return browser_context_;
}
// gin::Wrappable
@@ -225,7 +224,7 @@ class Session final : public gin::Wrappable<Session>,
// The client id to enable the network throttler.
base::UnguessableToken network_emulation_token_;
const raw_ref<ElectronBrowserContext> browser_context_;
raw_ptr<ElectronBrowserContext> browser_context_;
gin::WeakCellFactory<Session> weak_factory_{this};

View File

@@ -910,14 +910,15 @@ WebContents::WebContents(v8::Isolate* isolate,
session = Session::FromPartition(isolate, "");
}
session_ = session;
ElectronBrowserContext* const browser_context = session->browser_context();
DCHECK(browser_context != nullptr);
std::unique_ptr<content::WebContents> web_contents;
if (is_guest()) {
scoped_refptr<content::SiteInstance> site_instance =
content::SiteInstance::CreateForURL(session->browser_context(),
content::SiteInstance::CreateForURL(browser_context,
GURL("chrome-guest://fake-host"));
content::WebContents::CreateParams params(session->browser_context(),
site_instance);
content::WebContents::CreateParams params{browser_context, site_instance};
guest_delegate_ =
std::make_unique<WebViewGuestDelegate>(embedder_->web_contents(), this);
params.guest_delegate = guest_delegate_.get();
@@ -945,7 +946,7 @@ WebContents::WebContents(v8::Isolate* isolate,
SkColor bc = ParseCSSColor(background_color_str).value_or(SK_ColorWHITE);
bool transparent = bc == SK_ColorTRANSPARENT;
content::WebContents::CreateParams params(session->browser_context());
content::WebContents::CreateParams params{browser_context};
auto* view = new OffScreenWebContentsView(
transparent, offscreen_use_shared_texture_,
offscreen_shared_texture_pixel_format_, offscreen_device_scale_factor_,
@@ -956,13 +957,13 @@ WebContents::WebContents(v8::Isolate* isolate,
web_contents = content::WebContents::Create(params);
view->SetWebContents(web_contents.get());
} else {
content::WebContents::CreateParams params(session->browser_context());
content::WebContents::CreateParams params{browser_context};
params.initially_hidden = !initially_shown;
web_contents = content::WebContents::Create(params);
}
InitWithSessionAndOptions(isolate, std::move(web_contents),
session->browser_context(), options);
InitWithSessionAndOptions(isolate, std::move(web_contents), browser_context,
options);
}
void WebContents::InitZoomController(content::WebContents* web_contents,

View File

@@ -626,6 +626,9 @@ void ElectronBrowserMainParts::PostMainMessageLoopRun() {
#if BUILDFLAG(IS_LINUX)
ui::OzonePlatform::GetInstance()->PostMainMessageLoopRun();
#endif
browser_.reset();
js_env_.reset();
}
#if !BUILDFLAG(IS_MAC)

View File

@@ -116,10 +116,6 @@ class FileSystemAccessPermissionContext
content::GlobalRenderFrameHostId frame_id,
EntriesAllowedByEnterprisePolicyCallback callback) override;
enum class Access { kRead, kWrite, kReadWrite };
enum class RequestType { kNewPermission, kRestorePermissions };
void RevokeActiveGrants(const url::Origin& origin,
const base::FilePath& file_path = base::FilePath());

View File

@@ -117,6 +117,30 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
return result;
}
// Convert a Badge to an NSMenuItemBadge.
NSMenuItemBadge* CreateBadge(const electron::ElectronMenuModel::Badge& badge)
API_AVAILABLE(macos(14.0)) {
NSString* badgeType = base::SysUTF16ToNSString(badge.type);
if ([badgeType isEqualToString:@"alerts"]) {
if (badge.count.has_value())
return [NSMenuItemBadge alertsWithCount:badge.count.value()];
} else if ([badgeType isEqualToString:@"updates"]) {
if (badge.count.has_value())
return [NSMenuItemBadge updatesWithCount:badge.count.value()];
} else if ([badgeType isEqualToString:@"new-items"]) {
if (badge.count.has_value())
return [NSMenuItemBadge newItemsWithCount:badge.count.value()];
} else if ([badgeType isEqualToString:@"none"]) {
if (badge.content.has_value()) {
NSString* content = base::SysUTF8ToNSString(badge.content.value());
return [[NSMenuItemBadge alloc] initWithString:content];
}
}
return nil;
}
} // namespace
// This class stores a base::WeakPtr<electron::ElectronMenuModel> as an
@@ -341,14 +365,12 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
electron::ElectronMenuModel::ItemType type = model->GetTypeAt(index);
std::u16string customType = model->GetCustomTypeAt(index);
// The sectionHeaderWithTitle menu item is only available in macOS 14.0+.
if (@available(macOS 14, *)) {
if (customType == u"header") {
item = [NSMenuItem sectionHeaderWithTitle:label];
}
}
// If the menu item has an icon, set it.
ui::ImageModel icon = model->GetIconAt(index);
if (icon.IsImage())
item.image = icon.GetImage().ToNSImage();
@@ -356,6 +378,15 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
std::u16string toolTip = model->GetToolTipAt(index);
item.toolTip = base::SysUTF16ToNSString(toolTip);
if (@available(macOS 14, *)) {
electron::ElectronMenuModel::Badge badge;
if (model->GetBadgeAt(index, &badge)) {
NSMenuItemBadge* nsBadge = CreateBadge(badge);
if (nsBadge)
item.badge = nsBadge;
}
}
if (role == u"services") {
std::u16string title = u"Services";
NSString* sub_label = l10n_util::FixUpWindowsStyleLabel(title);
@@ -517,6 +548,15 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
} else {
item.image = nil;
}
if (@available(macOS 14, *)) {
electron::ElectronMenuModel::Badge badge;
if (model->GetBadgeAt(index, &badge)) {
item.badge = CreateBadge(badge);
} else {
item.badge = nil;
}
}
}
- (void)refreshMenuTree:(NSMenu*)menu {

View File

@@ -12,6 +12,15 @@ namespace electron {
ElectronMenuModel::SharingItem::SharingItem() = default;
ElectronMenuModel::SharingItem::SharingItem(SharingItem&&) = default;
ElectronMenuModel::SharingItem::~SharingItem() = default;
ElectronMenuModel::Badge::Badge() = default;
ElectronMenuModel::Badge::Badge(Badge&&) = default;
ElectronMenuModel::Badge::Badge(const Badge&) = default;
ElectronMenuModel::Badge& ElectronMenuModel::Badge::operator=(const Badge&) =
default;
ElectronMenuModel::Badge& ElectronMenuModel::Badge::operator=(Badge&&) =
default;
ElectronMenuModel::Badge::~Badge() = default;
#endif
bool ElectronMenuModel::Delegate::GetAcceleratorForCommandId(
@@ -115,6 +124,21 @@ bool ElectronMenuModel::GetSharingItemAt(size_t index,
void ElectronMenuModel::SetSharingItem(SharingItem item) {
sharing_item_.emplace(std::move(item));
}
void ElectronMenuModel::SetBadge(size_t index, Badge badge) {
int command_id = GetCommandIdAt(index);
badges_[command_id] = std::move(badge);
}
bool ElectronMenuModel::GetBadgeAt(size_t index, Badge* badge) const {
int command_id = GetCommandIdAt(index);
const auto iter = badges_.find(command_id);
if (iter != badges_.end()) {
*badge = iter->second;
return true;
}
return false;
}
#endif
void ElectronMenuModel::MenuWillClose() {

View File

@@ -33,6 +33,19 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
std::optional<std::vector<GURL>> urls;
std::optional<std::vector<base::FilePath>> file_paths;
};
struct Badge {
Badge();
Badge(Badge&&);
Badge(const Badge&);
Badge& operator=(const Badge&);
Badge& operator=(Badge&&);
~Badge();
std::u16string type; // "alerts", "updates", "new-items", or "none"
std::optional<int> count;
std::optional<std::string> content;
};
#endif
class Delegate : public ui::SimpleMenuModel::Delegate {
@@ -105,6 +118,9 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
return sharing_item_;
}
// Set/Get the Badge of a menu item.
void SetBadge(size_t index, Badge badge);
bool GetBadgeAt(size_t index, Badge* badge) const;
#endif
// ui::SimpleMenuModel:
@@ -123,6 +139,7 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
#if BUILDFLAG(IS_MAC)
std::optional<SharingItem> sharing_item_;
base::flat_map<int, Badge> badges_; // command id -> badge
#endif
base::flat_map<int, std::u16string> toolTips_; // command id -> tooltip

View File

@@ -705,8 +705,10 @@ gin_helper::Handle<SimpleURLLoaderWrapper> SimpleURLLoaderWrapper::Create(
else // default session
session = Session::FromPartition(args->isolate(), "");
}
if (session)
if (session) {
browser_context = session->browser_context();
DCHECK(browser_context != nullptr);
}
}
auto ret = gin_helper::CreateHandle(

View File

@@ -10,7 +10,7 @@
#include <vector>
#include "base/containers/span.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/memory_pressure_listener_registry.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
@@ -810,7 +810,7 @@ class WebFrameRenderer final
void ClearCache(v8::Isolate* isolate) {
blink::WebCache::Clear();
base::MemoryPressureListener::NotifyMemoryPressure(
base::MemoryPressureListenerRegistry::NotifyMemoryPressure(
base::MEMORY_PRESSURE_LEVEL_CRITICAL);
}

View File

@@ -59,7 +59,6 @@ const startServer = async () => {
function waitForCrash (): Promise<CrashInfo> {
return new Promise(resolve => {
emitter.once('crash', (crash) => {
console.log('crash fired', crash);
resolve(crash);
});
});
@@ -70,7 +69,6 @@ const startServer = async () => {
const fields = {} as Record<string, any>;
const files = {} as Record<string, Buffer>;
busboy.on('file', (fieldname, file) => {
console.log('Receiving file for :', fieldname);
const chunks = [] as Array<Buffer>;
file.on('data', (chunk) => {
chunks.push(chunk);
@@ -83,12 +81,10 @@ const startServer = async () => {
fields[fieldname] = val;
});
busboy.on('finish', () => {
console.log('On finish');
// breakpad id must be 16 hex digits.
const reportId = Math.random().toString(16).split('.')[1].padStart(16, '0');
res.end(reportId, async () => {
req.socket.destroy();
console.log('Emmitting crash:', fields);
emitter.emit('crash', { ...fields, ...files });
});
});
@@ -142,15 +138,10 @@ function waitForNewFileInDir (dir: string): Promise<string[]> {
ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_TESTS)('crashReporter module', function () {
describe('should send minidump', () => {
it('when renderer crashes', async () => {
console.log('starting server for renderer crash test');
const { port, waitForCrash } = await startServer();
console.log('Done starting server for renderer crash test');
runCrashApp('renderer', port);
console.log('Waiting for renderer crash');
const crash = await waitForCrash();
console.log('Renderer crash received');
checkCrash('renderer', crash);
console.log('Renderer crash checked');
expect(crash.mainProcessSpecific).to.be.undefined();
});

View File

@@ -1,5 +1,6 @@
import { expect } from 'chai';
import { once } from 'node:events';
import * as path from 'node:path';
import { ifdescribe, isTestingBindingAvailable, startRemoteControlApp } from './lib/spec-helpers';
@@ -39,6 +40,33 @@ describe('cpp heap', () => {
});
describe('session module', () => {
it('does not crash on exit with live session wrappers', async () => {
const rc = await startRemoteControlApp();
await rc.remotely(async () => {
const { app, session } = require('electron');
const sessions = [
session.defaultSession,
session.fromPartition('cppheap-exit'),
session.fromPartition('persist:cppheap-exit-persist')
];
// We want to test GC on shutdown, so add a global reference
// to these sessions to prevent pre-shutdown GC.
(globalThis as any).sessionRefs = sessions;
// We want to test CppGC-traced references during shutdown.
// The CppGC-managed cookies will do that; but since they're
// lazy-created, access them here to ensure they're live.
sessions.forEach(ses => ses.cookies);
setTimeout(() => app.quit());
});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(0);
});
it('should record as node in heap snapshot', async () => {
const { remotely } = await startRemoteControlApp(['--expose-internals']);
const result = await remotely(async (heap: string, snapshotHelper: string) => {

View File

@@ -166,6 +166,7 @@ declare namespace Electron {
commandsMap: Record<string, MenuItem>;
groupsMap: Record<string, MenuItem[]>;
getItemCount(): number;
getIndexOfCommandId(commandId: number): number;
popupAt(window: BaseWindow, frame: WebFrameMain | undefined, x: number, y: number, positioning: number, sourceType: Required<Electron.PopupOptions>['sourceType'], callback: () => void): void;
closePopupAt(id: number): void;
setSublabel(index: number, label: string): void;
@@ -173,6 +174,7 @@ declare namespace Electron {
setIcon(index: number, image: string | NativeImage): void;
setRole(index: number, role: string): void;
setCustomType(index: number, customType: string): void;
setBadge(index: number, badge: MenuItemBadge): void;
insertItem(index: number, commandId: number, label: string): void;
insertCheckItem(index: number, commandId: number, label: string): void;
insertRadioItem(index: number, commandId: number, label: string, groupId: number): void;