Compare commits

..

66 Commits

Author SHA1 Message Date
trop[bot]
7394591138 ci: use hermetic mac SDK for the release ffmpeg build (#50755)
* ci: use hermetic mac SDK for the release ffmpeg build

gn gen out/ffmpeg runs as a raw gn invocation, so it never receives the
mac_sdk_path arg that e build injects for out/Default. On macOS runners
that means out/Default builds against the hermetic build-tools SDK while
out/ffmpeg falls through to the runner's system Xcode SDK. Reuse the
value e build already wrote so both builds share the same sysroot.

Co-authored-by: Samuel Attard <sattard@anthropic.com>

* ci: copy hermetic SDK symlink into out/ffmpeg and rewrite path

mac_sdk_path must live under root_build_dir, so pointing out/ffmpeg at
//out/Default/... doesn't work. Copy the xcode_links symlink tree into
out/ffmpeg and rewrite the path. Gate on Darwin so Windows/Linux don't
run the sed/cp at all.

Co-authored-by: Samuel Attard <sattard@anthropic.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-04-06 21:35:50 -04:00
trop[bot]
d37b4f5d9f fix: enforce size constraints on window creation on Windows and Linux (#50753)
fix: enforce size constraints on window creation on Windows and Linux (#49906)

* enforce size constraints on window creation

* set constraints after resizing on init

* restore conditional centering

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Mitchell Cohen <mitch.cohen@me.com>
2026-04-06 18:40:05 -05:00
trop[bot]
6f1d53ae8f ci: make src-cache upload atomic (#50750)
ci: make src-cache upload atomic and sweep orphaned temp files

The checkout action's cp of the ~6GB zstd archive directly to the final
path on the cache share is non-atomic; an interrupted copy or a
concurrent reader produces zstd "Read error (39): premature end" on
restore, and the truncated file then satisfies the existence check so
no later run repairs it.

Upload to a run-unique *.tar.upload-<run_id>-<attempt> temp name on the
share and mv to the final path, discarding our temp if a concurrent run
got there first. A new clean-orphaned-cache-uploads workflow removes
temp files older than 4h every 4 hours.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
2026-04-06 22:39:35 +00:00
trop[bot]
fb150b2f17 docs: link menu type references (#50752)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: lilianakatrina684-a11y <lilianakatrina684@gmail.com>
2026-04-06 17:27:35 -04:00
Samuel Attard
c219f2c990 build: derive patches upstream-head ref from script path (41-x-y) (#50741)
build: derive patches upstream-head ref from script path (#50727)

* build: derive patches upstream-head ref from script path

gclient-new-workdir.py symlinks each repo's .git/refs back to the source
checkout, so the fixed refs/patches/upstream-head was shared across all
worktrees. Parallel `e sync` runs in different worktrees clobbered each
other's upstream-head, breaking `e patches` and check-patch-diff.

Suffix the ref with an md5 of the script directory so each worktree writes
a distinct ref into the shared refs dir. Fall back to the legacy ref name
in guess_base_commit so existing checkouts keep working until next sync.

* fixup: also write legacy upstream-head ref and note it in docs
2026-04-06 16:02:11 -04:00
Samuel Attard
3fa5280fde fix: re-enable MacWebContentsOcclusion with embedder window fix (#50715)
fix: re-enable MacWebContentsOcclusion with embedder window fix (#50579)

* fix: re-enable MacWebContentsOcclusion with embedder window fix

Replace the full revert of Chromium's MacWebContentsOcclusion cleanup
with a targeted patch that handles embedder windows shown after
WebContentsViewCocoa attachment. This lets us drop the feature flag
disable in feature_list.cc and re-enable upstream occlusion tracking.

Adds tests for show/hide event counts on macOS and visibility tracking
across multiple child WebContentsViews.

* test: drop show/hide event count assertion

The assertion that 'show' fires exactly once per w.show() call is not
an API guarantee - macOS can send multiple occlusion state
notifications during a single show() when other windows are on screen
(common on CI after hundreds of prior tests). The
visibilitychange-count test in api-web-contents-view-spec.ts covers
the actual invariant we care about.

* fix: ignore WebContentsOcclusionCheckerMac synthetic notifications in window delegate

On macOS 13.3-25.x, Chromium's occlusion checker enables manual
frame-intersection detection and posts synthetic
NSWindowDidChangeOcclusionStateNotification tagged with its class name
in userInfo. These fire when the checker's NSContainsRect heuristic
decides a window is covered by another window's frame, but the real
-[NSWindow occlusionState] hasn't changed.

Our delegate was treating these the same as real macOS notifications
and emitting show/hide events based on occlusionState, which was
unchanged - resulting in spurious duplicate show events when e.g.
Quick Look opened and its frame intersected the BrowserWindow.
2026-04-06 16:01:41 -04:00
electron-roller[bot]
45ad6b3525 chore: bump chromium to 146.0.7680.179 (41-x-y) (#50616)
* chore: bump chromium in DEPS to 146.0.7680.178

* chore: bump chromium in DEPS to 146.0.7680.179

* chore: fixup patch indices

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-04-06 15:03:38 -04:00
trop[bot]
26e20c7402 ci: use github mirror to get lint dependency versions (#50736)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-04-06 09:24:10 -07:00
Samuel Attard
ca1522385c chore: harden GitHub Actions against script injection patterns (#50708)
chore: harden GitHub Actions against script injection patterns (#50512)

* fix: harden GitHub Actions against script injection vulnerabilities

Replace direct ${{ }} expression interpolation in run: blocks with
environment variables to prevent script injection attacks. Changes:

- archaeologist-dig.yml: move clone_url, head.sha, base.ref to env vars
- non-maintainer-dependency-change.yml: move user.login to env var
- issue-unlabeled.yml: move toJSON(labels) to env var
- issue-labeled.yml: move issue.number to env var
- pipeline-electron-lint.yml: validate chromium_revision format
- cipd-install/action.yml: move all inputs to env vars and quote them
- set-chromium-cookie/action.yml: reference secrets via $ENV_VAR
- Add security comments to all 5 pull_request_target workflows

https://claude.ai/code/session_01UUWmLxn5hyyxrhK8rGxU2s

* fix: allow version strings in chromium_revision validation

The previous regex `^[a-f0-9]+$` only matched git SHAs but
chromium_revision is a version string like `148.0.7741.0`.
Broaden to `^[a-zA-Z0-9._-]+$` which still blocks shell
metacharacters.

https://claude.ai/code/session_01UUWmLxn5hyyxrhK8rGxU2s

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-06 10:04:05 -04:00
Samuel Attard
3d743a6ef7 build: replace npx with lockfile-pinned binaries (#50718)
build: replace npx with lockfile-pinned binaries (#50598)

* build: replace npx with lockfile-pinned binaries

- nan-spec-runner: reorder yarn install first, invoke nan node-gyp bin directly
- publish-to-npm: use host npm with E404 try/catch (closes existing TODO)
- upload-symbols: add @sentry/cli devDep, invoke from node_modules/.bin
- remove script/lib/npx.py (dead since #48243)

* build: bump @sentry/cli to 1.70.0 for arm support

* build: bump @sentry/cli to 1.72.0, skip CDN download on test jobs

@sentry/cli fetches its platform binary from Sentry CDN at postinstall.
Only upload-symbols.py (release pipeline) needs the binary; set
SENTRYCLI_SKIP_DOWNLOAD=1 in the two test-segment workflows that
call install-dependencies. The 64k variant uses pre-built artifacts
and does not install deps.
2026-04-06 10:02:18 -04:00
Samuel Attard
aafa96f929 ci: zstd-compress the src cache and drop the doubled win_toolchain (#50721)
ci: zstd-compress the src cache and drop the doubled win_toolchain (#50702)

* ci: shrink src cache and fix Windows tar cleanup

- Exclude platform-specific toolchains (llvm-build, rust-toolchain) from
  the src cache; all platforms now fetch them via fix-sync post-restore
- Exclude unused test data and benchmarks: blink/web_tests, jetstream,
  speedometer, catapult/tracing/test_data, swiftshader/tests/regres
- Fix Windows restore leaving the tarball on disk after extraction
  ($src_cache was scoped to the previous PowerShell step)
- Bump src-cache key v1 -> v2

* ci: fetch llvm/rust toolchains in gn-check and clang-tidy

These workflows restore the src cache but don't run fix-sync. Now that
llvm-build and rust-toolchain are excluded from the cache, they need to
download them directly — gn gen read_file()s both, and clang-tidy runs
the binary from llvm-build.

* ci: fetch clang-tidy package explicitly

update.py's default 'clang' package doesn't include the clang-tidy
binary; it ships as a separate package.

* ci: preserve blink/web_tests/BUILD.gn when stripping test data

//BUILD.gn references //third_party/blink/web_tests:wpt_tests as a
target label, so the BUILD.gn must exist for gn gen. The data = [...]
entries it declares are runtime-only and not existence-checked at gen
time, so the actual test directories can still be removed.

* ci: compress src cache with zstd and drop gclient sync -vv

The src cache was an uncompressed tar (~16GB after exclusions). Switch
to zstd -T0 --long=30 for ~4x smaller transfer and multi-threaded
compression. Decompress on restore:
- Linux/macOS: zstd -d -c | tar -xf -
- Windows: zstd -d to an intermediate .tar, then the existing 7z
  -snld20 extraction (preserves symlink handling)

All filename references updated .tar -> .tar.zst. -f added to the two
-o invocations so re-runs overwrite instead of failing.

Also drop -vv from gclient sync; default verbosity is sufficient.

* ci: keep .tar extension for src cache (zstd content inside)

The sas-sidecar that issues Azure SAS tokens validates filenames against
/^v[0-9]+-[a-z\-]+-[a-f0-9]+\.(tar|tgz)$/ and is not easily redeployed,
so keep the .tar extension and decode zstd on restore. Windows
decompresses to a distinct intermediate (src_cache.tar) so input and
output don't collide.

* ci: log NTFS 8.3/lastaccess/Defender state before Windows cache extract

Temporary diagnostics to see whether 8.3 short-name generation is the
cause of the ~20 min tar extraction.

* ci: revert src-cache exclusion additions

The new exclusions (web_tests contents, jetstream, speedometer,
catapult test_data, regres, llvm-build, rust-toolchain) caused siso/RBE
cache misses — even data-only deps are part of action input hashes.
Revert to the original exclusion list and drop the corresponding
toolchain-fetch plumbing. zstd compression, the Windows tar cleanup,
and the -vv removal remain.

* ci: drop win_toolchain from src cache; remove NTFS diagnostics

The Windows src cache includes 14.6GB of depot_tools/win_toolchain —
7.3GB of MSVC/SDK doubled because tar captures both the vs_files.ciopfs
backing store and the live ciopfs mount at vs_files/. Every Windows
cache consumer already re-fetches this via vs_toolchain.py update
--force (fix-sync for build/publish, inline for gn-check/clang-tidy),
so the cached copy is never used.

Diagnostics removed — CI confirmed 8dot3, last-access, and Defender are
all already off on the AKS Windows nodes.

* ci: unmount ciopfs vs_files before removing win_toolchain

vs_files is a live ciopfs mount during the win-targeted checkout; rm -rf
fails with EBUSY until it's unmounted.

* ci: skip win_toolchain download during checkout instead of removing after

fusermount isn't on the checkout container, so the ciopfs mount can't be
torn down before rm. Setting DEPOT_TOOLS_WIN_TOOLCHAIN=0 makes the
win_toolchain hook a no-op (vs_toolchain.py:525-527), so there's no
download and no mount. All Windows consumers re-fetch it post-restore
anyway. The rm -rf stays as a safety net.

* ci: also set ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN=0 for checkout sync

build.yml sets ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN=1 at the job level for
the Windows checkout, which makes e d inject DEPOT_TOOLS_WIN_TOOLCHAIN=1
and override the inline =0. Need both: the ELECTRON_ var stops e d from
overriding, the plain one stops vs_toolchain.py from defaulting to 1.

* ci: extract Windows src cache with piped tar instead of 7z

7z takes ~20 min to extract the ~1.1M-entry tar regardless of size —
~1ms per entry of header parsing and path handling, single-threaded,
well under the 75k IOPS / 1000 MBps the ephemeral disk can do. Switch
to the same zstd -d | tar -xf - pipe used on Linux/macOS (via Git Bash
tar). No intermediate src_cache.tar, download deleted after extract.

The -snld20 flag was working around 7z's own "dangerous symlink"
refusal; GNU tar extracts symlinks as-is so it shouldn't be needed.

* ci: keep depot_tools/win_toolchain scripts in src cache

The rm -rf removed get_toolchain_if_necessary.py (a depot_tools source
file), breaking vs_toolchain.py update --force on restore.
DEPOT_TOOLS_WIN_TOOLCHAIN=0 on the sync already prevents the vs_files
download, so the rm was only removing scripts.

* ci: split src cache into 4 parallel-extractable shards

Windows tar extraction is ~1ms/entry for ~1.2M entries (~20 min)
regardless of tool, well under the 75k IOPS / 1000 MBps the D16lds_v5
ephemeral disk can do. Tar is a sequential stream so the only way to
parallelize is to split at creation time.

Shards (balanced by entry count, ~220-360k each):
  a: src/third_party/blink
  b: src/third_party/{dawn,electron_node,tflite,devtools-frontend}
  c: src/third_party (rest)
  d: src (excluding third_party)

DEPSHASH is now the raw hash; shard files are
v2-src-cache-shard-{a..d}-${DEPSHASH}.tar (all pass the sas-sidecar
filename regex). sas-token is now a JSON keyed by shard letter. All
restore paths extract the four shards in parallel with per-PID wait so
a failed shard aborts the step.

* Revert "ci: split src cache into 4 parallel-extractable shards"

This reverts commit 970574998b.
2026-04-06 09:58:31 -04:00
trop[bot]
898e77a9ee ci: fetch clang-tidy package in fix-sync (#50726)
fix-sync re-downloads llvm-build on macOS/Windows with the base clang
and objdump packages, but not clang-tidy. A local gclient sync pulls
clang-tidy (checkout_clang_tidy=True in DEPS), so CI's llvm-build tree
diverges from a local one. siso hashes the toolchain as action input,
so cache-only local runs against the CI-populated RBE cache miss.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-04-06 07:55:23 +00:00
trop[bot]
e1bb3e7165 fix: defer Wrappable destruction in SecondWeakCallback to a posted task (#50694)
V8's second-pass weak callbacks run inside a
DisallowJavascriptExecutionScope: they may touch the V8 API but must
not invoke JS, directly or indirectly. Several Electron Wrappables
(WebContents in particular) emit JS events from their destructors,
so deleting synchronously inside SecondWeakCallback can crash with
"Invoke in DisallowJavascriptExecutionScope" when GC happens to
collect the JS wrapper during a foreground GC task — typically during
shutdown's uv_run drain after a leaked WebContentsView.

This was previously latent and timing-dependent (electron/electron#47420,
electron/electron#45416, podman-desktop/podman-desktop#12409). The
esbuild migration's keepNames option (which wraps every function/class
with an Object.defineProperty call) shifted heap layout enough to make
the spec/fixtures/crash-cases/webcontentsview-create-leak-exit case
reliably reproduce it on every run, giving a clean signal for the fix.

Both WrappableBase and DeprecatedWrappableBase SecondWeakCallback now
post the deletion via base::SequencedTaskRunner::GetCurrentDefault()
so the destructor (and any Emit it does) runs once V8 has left the GC
scope. Falls back to synchronous deletion if no task runner is
available (early/late process lifetime).

Fixes electron/electron#47420.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
2026-04-05 09:49:43 +00:00
trop[bot]
dbc7cbd000 fix: propagate requesting frame through sync permission checks (#50687)
WebContentsPermissionHelper::CheckPermission was hardcoding
GetPrimaryMainFrame() and deriving the requesting origin from
web_contents_->GetLastCommittedURL(), so the setPermissionCheckHandler
callback always received the top frame's origin and
details.isMainFrame/details.requestingUrl always reflected the main
frame, even when a cross-origin subframe with allow="serial" or
allow="camera; microphone" triggered the check.

Thread the requesting RenderFrameHost through CheckPermission,
CheckSerialAccessPermission, and CheckMediaAccessPermission so the
permission manager receives the real requesting frame. Update the
serial delegate and WebContents::CheckMediaAccessPermission callers to
pass the frame they already have.

Adds a regression test that loads a cross-origin iframe with
allow="camera; microphone", calls enumerateDevices() from within the
iframe, and asserts the permission check handler receives the iframe
origin for requestingOrigin, isMainFrame, and requestingUrl.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
2026-04-05 00:32:03 +00:00
trop[bot]
821b738db0 fix: resolve getFileHandle concurrent stalling by queuing callbacks (#50670)
fix: resolve getFileHandle concurrent stalling by queuing callbacks (#50597)

Previously, concurrent calls to FileSystemAccessPermissionContext::ConfirmSensitiveEntryAccess
for the same file path would silently discard the subsequent callbacks because
the internal callback map used a single callback per file path and std::map::try_emplace
would drop the callback if the key already existed. This caused Promises in JS
(e.g., dirHandle.getFileHandle()) to stall indefinitely.

This commit updates the callback map to hold a vector of callbacks, so all
concurrent requesters for the same filepath are grouped together and resolved
once the asynchronous blocklist check completes.

Notes: Fixed an issue where concurrent `getFileHandle` requests on the same path could stall indefinitely.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Kunal Dubey <21157775+xakep8@users.noreply.github.com>
2026-04-04 11:18:30 -05:00
trop[bot]
969741f9f8 fix: validate dock_state_ against allowlist before JS execution (#50666)
fix: validate dock_state_ against allowlist before JS execution

The dock_state_ member was concatenated directly into a JavaScript
string and executed via ExecuteJavaScript() in the DevTools context.

We should validate against the four known dock states and fall back
to "right" for any unrecognized value for safety

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-04-03 15:45:45 -05:00
trop[bot]
476a864388 feat: make Chrome extensions work on custom protocols (#50529)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Niklas Wenzel <dev@nikwen.de>
2026-04-02 20:09:56 -07:00
trop[bot]
65c5528d13 fix: don't force kFitToPrintableArea scaling when custom margins are set (#50652)
When silent printing with non-default margins (custom, no margins, or
printable area margins), the kFitToPrintableArea scaling option causes
double-marginalization: the custom margins define the content area, then
the scaling additionally fits content to the printer's printable area.

Only apply kFitToPrintableArea when using default margins in silent mode.
For non-default margins, use the same scaling as non-silent prints.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-04-02 17:17:41 -05:00
trop[bot]
81333d7c79 fix: glitchy rendering and maximize behavior with different GTK themes (#50645)
fix: glitchy rendering and maximize behavior with different GTK themes (#50550)

* fix glitchy rendering with different gtk themes especially when maximizing

* use actual insets, not restored insets

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Mitchell Cohen <mitch.cohen@me.com>
2026-04-02 22:42:40 +02:00
trop[bot]
fd56128f46 fix: remove menu update debug log (#50613)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: clavin <clavin@electronjs.org>
2026-04-01 22:52:31 +02:00
trop[bot]
75d8a239a0 fix: invoke print callback directly when no print job exists (#50604)
ShowInvalidPrinterSettingsError() called TerminatePrintJob(true),
but when no print_job_ had been created yet (e.g. settings validation
failed before a job could start), TerminatePrintJob bails out
immediately without reaching ReleasePrintJob() where the callback
is invoked. This left the CompletionCallback stuck in callback_
until WebContents destruction, causing webContents.print() to only
fire its callback when the application closed.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-04-01 10:21:34 +02:00
trop[bot]
e03cb79aa5 fix: prevent borders and smearing in transparent frameless/client frame windows on Linux (#50605)
fix the appearance of transparent frameless and client frame windows

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Mitchell Cohen <mitch.cohen@me.com>
2026-03-31 18:28:56 -05:00
John Kleinschmidt
78896775d9 ci: update actions to node24 (#50522)
ci: update actions to node24 (#50373)

* ci: update actions to node24

* chore: fixup actions/cache to 5.0.4 everywhere

(cherry picked from commit 639d3b99b7)
2026-03-31 15:26:31 +02:00
trop[bot]
40eb41656a ci: update nick-fields/retry to v4.0.0 (#50544)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-03-31 14:32:29 +02:00
Michaela Laurencin
5a69e80cac ci: add functionality for programmatic add/remove needs-signed-commits label (#50316) (#50587)
* remove comment based label removal

* ci: add functionality for programmatic add/remove needs-signed-commits label

* add new line to pull-request-opened-synchronized
2026-03-31 10:30:47 +02:00
trop[bot]
90decd4eaf fix: add missing HandleScope in contentTracing.getTraceBufferUsage() (#50594)
The `OnTraceBufferUsageAvailable` callback creates V8 handles via
`Dictionary::CreateEmpty()` before `promise.Resolve()` enters its
`SettleScope` (which provides a `HandleScope`). When the callback
fires asynchronously from a Mojo response (i.e. when a trace session
is active), there is no `HandleScope` on the stack, causing a fatal
V8 error: "Cannot create a handle without a HandleScope".

Add an explicit `v8::HandleScope` at the top of the callback, matching
the pattern used by the other contentTracing APIs which resolve their
promises through `SettleScope` or the static `ResolvePromise` helper.

Made-with: Cursor

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Alexey Kozy <alexey@anysphere.co>
2026-03-31 10:15:13 +02:00
trop[bot]
ba551d265c perf: enable V8 builtins PGO (#50574)
* build: enable V8 builtins PGO

Removes the gn arg that disabled V8 builtins profile-guided optimization
and adds a V8 patch to warn instead of abort when the builtin PGO profile
data does not match. Also strips the PGO-related flags from the generated
mksnapshot_args so they are not passed through to downstream mksnapshot
invocations.

Co-authored-by: Sam Attard <sattard@anthropic.com>

* docs: clarify Node.js async_hooks as reason for promise_hooks flag

Addresses review feedback: the v8_enable_javascript_promise_hooks flag
is set to support Node.js async_hooks, not used directly by Electron.

Co-authored-by: Sam Attard <sattard@anthropic.com>

* chore: update patches

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2026-03-30 19:44:54 +02:00
trop[bot]
24784ed024 refactor: improve input handling in FilePath gin converter (#50547)
refactor: improve input handling in file_path_converter

Properly handle paths containing ASCII control characters in the FilePath gin converter

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
2026-03-27 22:56:38 +00:00
trop[bot]
f49f6b1a29 docs: clarify allowed characters in protocol names (#50538)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Niklas Wenzel <dev@nikwen.de>
2026-03-27 09:58:46 -04:00
trop[bot]
c63e0d8b96 test: add interactive macOS dialog tests (#50528)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-27 08:20:36 -04:00
trop[bot]
33a81b40c2 fix: register PrintDialogLinuxFactory on Linux (#50486)
fix: register PrintDialogLinuxFactory on Linux

Chromium 145 refactored Linux print dialog creation to use a factory
pattern instead of directly calling LinuxUi::CreatePrintDialog().
Chrome registers this factory in
ChromeBrowserMainExtraPartsViewsLinux::ToolkitInitialized(), but
Electron did not, causing PrintingContextLinux::EnsurePrintDialog()
to leave print_dialog_ null on every call.

Without a dialog, UseDefaultSettings() and UpdatePrinterSettings()
return success but with empty/unprocessed settings, causing
PrintMsgPrintParamsIsValid() to fail. This broke both window.print()
(no dialog appears) and webContents.print() (callback stuck until
app close with "Invalid printer settings").

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-26 17:01:28 -04:00
trop[bot]
eb49ed962d fix: outdated execution path for COM activation (#50519)
* fix: outdated execution path

Co-authored-by: Jan Hannemann <jan.hannemann@outlook.com>

* fix: use stub exe when detected

Co-authored-by: Jan Hannemann <jan.hannemann@outlook.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jan Hannemann <jan.hannemann@outlook.com>
2026-03-26 20:32:47 +00:00
electron-roller[bot]
7e36ac67ce chore: bump chromium to 146.0.7680.166 (41-x-y) (#50458)
* chore: bump chromium in DEPS to 146.0.7680.164

* chore: update patches

* chore: bump chromium in DEPS to 146.0.7680.166

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2026-03-26 15:53:50 -04:00
trop[bot]
cbae32aac6 fix: [a11y] fire AXMenuOpened event when ARIA menu is added to DOM (#50506)
* fix: fire AXMenuOpened event when a visible ARIA menu instance is added to the DOM

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* fix: remove redundent FireMenuPopupEndForDeletedMenus

MENU_POPUP_END for deleted menus is already handled by
AXTreeManager::OnNodeWillBeDeleted, which
fires the event directly on the menu node before destruction.

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* chore: add feature flag (kDynamicMenuPopupEvents)

Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>

* chore: update patches

Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>

* chore: update patches after trop

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-03-26 09:54:27 -07:00
trop[bot]
880b1e08e7 refactor: remove dead named-window lookup from guest-window-manager (#50497)
The frameNamesToWindow map was a holdover from the BrowserWindowProxy
IPC shim. Since nativeWindowOpen became the only code path, Blink's
FrameTree::FindOrCreateFrameForNavigation resolves named window targets
directly in the renderer, scoped to the opener's browsing context
group. When a matching named window exists, Blink navigates it without
ever sending a CreateNewWindow IPC to the browser, so this map was
never consulted in the legitimate same-opener case.

The only time the map found a match was when two unrelated renderers
happened to use the same target name, in which case openGuestWindow
would short-circuit before consuming the guest WebContents that
Chromium had already created for the new window, leaking it.

Adds a test verifying Blink handles same-opener named-target reuse
end-to-end without any browser-side tracking.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
2026-03-26 11:46:12 -04:00
trop[bot]
aedea576da fix: hex-encode Windows notification icon temp filenames (#50483)
* fix: hex-encode Windows notification icon temp filenames

NotificationPresenterWin was using SHA1HashString(origin.spec()) directly
as the basename for the temporary PNG written for toast icons.

SHA1HashString returns raw digest bytes, so the generated filename could
contain invalid path characters on Windows. That caused WriteFile to fail
when saving notification icons, which left toast XML without the expected
icon path.

Hex-encode the digest before appending .png so the temporary filename is
filesystem-safe while keeping deterministic naming for a given origin.

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* Update shell/browser/notifications/win/notification_presenter_win.cc

Co-authored-by: Robo <hop2deep@gmail.com>

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-26 09:20:32 -04:00
trop[bot]
707541d9b2 fix: fall back to default DPI when GTK returns 0 on Linux (#50489)
GetDefaultPrinterDPI() creates a blank GtkPrintSettings and reads
its resolution, which returns 0 for uninitialized settings. With
DPI=0, SetPrintableAreaIfValid() computes a zero scale factor,
producing empty page dimensions that fail PrintMsgPrintParamsIsValid().

Fall back to kDefaultPdfDpi (72) when GTK returns 0, matching the
existing Windows fallback pattern when CreateDC fails.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-26 09:17:44 -04:00
trop[bot]
3dcb641a99 fix: crash calling OSR shared texture release() after texture GC'd (#50501)
The weak persistent tracking the OffscreenReleaseHolderMonitor was tied
to the texture object, but the release() closure holds a raw pointer to
the monitor via its v8::External data. If JS retained texture.release
while dropping the texture itself, the monitor would be freed on GC and
a later release() call would crash.

Track the release function instead of the texture object. Since the
texture holds release as a property, this keeps the monitor alive as
long as either is reachable.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
2026-03-26 00:49:57 -07:00
trop[bot]
878a763344 fix: crash in clipboard.readImage() on malformed image data (#50492)
gfx::PNGCodec::Decode() returns a null SkBitmap when it fails to decode
the clipboard contents as a PNG. Passing that null bitmap to
gfx::Image::CreateFrom1xBitmap() triggers a crash.

Return an empty gfx::Image instead, matching the existing null-check
pattern in skia_util.cc.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
2026-03-25 22:15:58 -07:00
trop[bot]
6a8d187105 feat: add accessibilityDisplayShouldDifferentiateWithoutColor on macOS (#50408)
feat: add nativeTheme.shouldDifferentiateWithoutColor on macOS

Adds nativeTheme.shouldDifferentiateWithoutColor on macOS that maps to
NSWorkspace.accessibilityDisplayShouldDifferentiateWithoutColor. If true,
the user has indicated that they prefer UI that differentiates items with
something other than color alone. This is useful for users with color
vision deficiency.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Robert Böhnke <robb@robb.is>
2026-03-25 15:53:31 -04:00
trop[bot]
29622930a0 feat: support notification priority on Windows (#50382)
* feat: support notification priority on Windows

Add Windows notifications support urgency/priority levels.
This maps the existing `urgency` option (previously Linux-only) to
Windows toast notification priorities:

- 'critical' maps to ToastNotificationPriority_High, which sorts the
  notification above default-priority items in Action Center.
- 'normal' and 'low' both map to ToastNotificationPriority_Default.

Note that on Windows, 'critical' priority does not prevent the toast
from being auto-dismissed. Users should additionally set `timeoutType`
to 'never' for that behavior.

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* chore: make linter happy

Co-authored-by: Charles Kerr <charles@charleskerr.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2026-03-25 15:49:00 -04:00
trop[bot]
8b9e721047 fix: don't re-parse URL unnecessarily when handling dialogs (#50399)
* fix: fallback to opaque URL when needed inside dialog callback

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

* refactor: remove additional URL parsing entirely when showing dialogs

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

* test: add crash test case for URL-less dialogs

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

* refactor: exit on events instead of on timeout for dialog crash test

Co-authored-by: Robo <hop2deep@gmail.com>

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

* style: make linter happy

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

* style: make linter actually happy

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

* fix: address failing `safeDialogs` tests

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Noah Gregory <noahmgregory@gmail.com>
2026-03-20 13:46:37 -04:00
trop[bot]
43bb93908c fix: correct utility process exit code on Windows (#50386)
* fix: correct utility process exit code on Windows

On Windows, process exit codes are 32-bit unsigned integers (DWORD).
When passed from Chromium to Electron as a signed int and then
implicitly converted to uint64_t, values with the high bit set
(e.g., NTSTATUS codes) undergo sign extension, producing incorrect
values.

Cast the exit code to uint32_t before widening to uint64_t to
prevent sign extension and preserve the original Windows exit code.

Fixes #49455

Co-authored-by: João Silva <joaomrsilva@tecnico.ulisboa.pt>

* fix: narrow HandleTermination and Shutdown to uint32_t, add tests

Co-authored-by: João Silva <joaomrsilva@tecnico.ulisboa.pt>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: João Silva <joaomrsilva@tecnico.ulisboa.pt>
2026-03-19 18:48:27 -07:00
trop[bot]
b0055e0500 fix: improved the appearance of shadows and borders on frameless windows on Wayland (#50213)
fix: improved the appearance of shadows and borders on frameless windows on Wayland (#50007)

* remove painting from linux frame layout

* use chromium csd strategy for frameless windows

* Apply suggestions from code review

Remove unneeded virtual methods



* removed inline destructors

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Mitchell Cohen <mitch.cohen@me.com>
2026-03-19 15:42:58 -04:00
trop[bot]
9a7381a328 ci: output build cache hit rate as GHA annotation (#50370)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2026-03-19 12:04:12 -04:00
trop[bot]
af3e0fca24 fix: always call the original impl in swizzled mousedown impls (#50354)
fix: always call the original implementation in swizzled mousedown implementations

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Noah Gregory <noahmgregory@gmail.com>
2026-03-18 20:11:50 -07:00
trop[bot]
99d879b52e chore: Respect HTTP(S) proxy env variable for Yarn (#50350)
Respect HTTP(S) proxy env variable for Yarn

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Filip Mösner <filip.mosner@seznam.cz>
2026-03-18 20:03:10 -07:00
electron-roller[bot]
3d8105ae7f chore: bump chromium to 146.0.7680.153 (41-x-y) (#50346)
* chore: bump chromium in DEPS to 146.0.7680.153

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2026-03-18 17:49:48 -04:00
trop[bot]
aba01d38dc fix: correctly track BaseWindow::IsActive() on MacOS (#50340)
fix: correctly set IsActive() in BaseWindow on MacOS

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Kyle Cutler <kycutler@microsoft.com>
2026-03-18 17:11:46 -04:00
trop[bot]
a0f01336a3 fix: ensure WebContents::WasShown runs when window is shown (#50343)
Avoids a freeze when failing to enter fullscreen on macOS.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Beutner <beutner.john@gmail.com>
2026-03-18 14:53:09 -04:00
trop[bot]
4a98b4e27e docs: fix markdown formatting in fuses.md (#50333)
* docs: fix markdown formatting in fuses.md

* Use bulleted list (was being run together on one line)
* Wrap ASCII diagram in code block

Co-authored-by: Ryan Zimmerman <ryan@exodus.io>

* docs: apply suggestions from code review

Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>

Co-authored-by: Ryan Zimmerman <ryan@exodus.io>

* docs: fix misapplied suggestion

Co-authored-by: Ryan Zimmerman <ryan@exodus.io>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Ryan Zimmerman <ryan@exodus.io>
2026-03-18 16:09:27 +01:00
trop[bot]
44bc2c8cef fix: user resizable transparent windows on win32 (#50298)
test: revert win32 frameless and transparent resizable expectations

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Justin Mayfield <tooker@gmail.com>
2026-03-17 09:46:52 +01:00
trop[bot]
4e9e7335bc test: fix esm issue in node-spec-runner (#50295)
Chromium added a top-level package.json in CL:7485999 that sets
the type to module and breaks commonjs tests run via
node-spec-runner.js. This commit temporarily changes the type to
commonjs while running the tests, then changes it back to module when done.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-16 15:44:40 -04:00
trop[bot]
cd88382756 fix: add ASAR support to additional copy methods (#50286)
* fix: add ASAR support for additional copy methods

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

* test: add tests for ASAR support for additional copy messages

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Noah Gregory <noahmgregory@gmail.com>
2026-03-16 13:56:00 -04:00
trop[bot]
105c5591d0 docs: update the example of webContents.setWindowOpenHandler to cla… (#50293)
docs: reorganize the comments for clarifying `webContents.setWindowOpenHandler` example

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: z0gSh1u <zx.cs@qq.com>
2026-03-16 12:53:05 -04:00
trop[bot]
90b3a2341d build: remove redundant bits of ncrypto node patch (#50279)
build: remove redundant ncrypto node patch

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-16 16:03:05 +01:00
electron-roller[bot]
21f9474f4f chore: bump chromium to 146.0.7680.80 (41-x-y) (#50262)
* chore: bump chromium in DEPS to 146.0.7680.80

* chore: fixup patch indices

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-16 15:18:28 +01:00
trop[bot]
c3e397ed2d fix: prefer browser runtime over node in DevTools HostRuntime detection (#50276)
Upstream DevTools' HostRuntime checks `IS_NODE` before `IS_BROWSER` when
selecting the platform runtime. In Electron, `process` is available in
renderer processes, so `IS_NODE` evaluates to `true` in the DevTools
context. This causes DevTools to dynamically import the Node.js platform
runtime, which uses `node:worker_threads`. DevTools Web Workers running
under the `devtools://` protocol cannot load Node.js built-in modules,
so the import fails and breaks features like the formatter worker.

Fix by swapping the check order to prefer `IS_BROWSER` when both are
true. This is safe because in pure Node.js environments (the only case
where the node runtime is needed), `window` and `self` are both
undefined, so `IS_BROWSER` is always `false` regardless of check order.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-16 12:55:46 +01:00
trop[bot]
d84dca2818 build: skip archiving patch conflict fix artifact (#50257)
The update-patches artifact is a single .patch file, so zipping it
is unnecessary overhead. With archive: false, gh run download fetches
the raw file directly without requiring a decompression step.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 18:51:49 -07:00
trop[bot]
bb20d0c352 docs: point pull requests guide to build tools (#50253)
* docs: point pull requests guide to build tools

Co-authored-by: Erick Zhao <erick@hotmail.ca>

* update for `--fork`

Co-authored-by: Erick Zhao <erick@hotmail.ca>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2026-03-13 16:16:09 -04:00
electron-roller[bot]
c66fc559b2 chore: bump chromium to 146.0.7680.76 (41-x-y) (#50244)
* chore: bump chromium in DEPS to 146.0.7680.76

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2026-03-13 15:00:47 -04:00
trop[bot]
76f34911f2 build: add patch conflict resolution workflow with CI artifacts (#50239)
ci: upload patch conflict fix as artifact in apply-patches

When patch-up.js cannot auto-push the 3-way-merged patch diff (e.g. on
fork PRs), the checkout action already writes patches/update-patches.patch
and tells the user to check CI artifacts — but nothing was uploading it.

This adds the missing upload-artifact step to the apply-patches job so
the resolved diff is available for download, and documents in CLAUDE.md
that pulling this artifact and applying it with `git am` is the fast
path for fixing patch conflicts on PR branches without a full local sync.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 10:10:48 +01:00
trop[bot]
5d381dd27e ci: update actions/cache to 5.0.3 (#50238)
chore: update actions/cache to 5.0.3

Needed due to https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-03-13 10:04:48 +01:00
Keeley Hammond
42d7f2783b chore: cherry-pick d5b0cb2acffe from v8 (#50233)
* chore: cherry-pick d5b0cb2acffe from v8

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2026-03-13 03:11:14 +00:00
Keeley Hammond
61b4c6b93e chore: cherry-pick 248acd90d9a3 from skia (#50234)
* chore: add skia patch dir

* chore: cherry-pick 248acd90d9a3 from skia

* chore: fix the dumbest comma, patch
2026-03-13 03:04:12 +00:00
John Kleinschmidt
b9ca21156b ci: add timeout to test step (#50206)
ci: add timeout to test step (#50186)

Additionally, take a screenshot on timeout so that we can debug why there is a hang
2026-03-12 14:40:26 +01:00
154 changed files with 3807 additions and 892 deletions

View File

@@ -125,6 +125,9 @@ runs:
fi
sed $SEDOPTION '/.*builtins-pgo/d' out/Default/mksnapshot_args
sed $SEDOPTION '/--turbo-profiling-input/d' out/Default/mksnapshot_args
sed $SEDOPTION '/--reorder-builtins/d' out/Default/mksnapshot_args
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
if [ "${{ inputs.target-platform }}" = "win" ]; then
cd out/Default
@@ -202,7 +205,17 @@ runs:
if: ${{ inputs.is-release == 'true' }}
run: |
cd src
gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true use_siso=true $GN_EXTRA_ARGS"
# Reuse the hermetic mac_sdk_path that `e build` wrote for out/Default so
# out/ffmpeg builds against the same SDK instead of the runner's system Xcode.
# The path has to live under root_build_dir, so copy the symlink tree and
# rewrite Default -> ffmpeg.
MAC_SDK_ARG=""
if [ "$(uname)" = "Darwin" ]; then
mkdir -p out/ffmpeg
cp -a out/Default/xcode_links out/ffmpeg/
MAC_SDK_ARG=$(sed -n 's|^\(mac_sdk_path = "//out/\)Default/|\1ffmpeg/|p' out/Default/args.gn)
fi
gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true use_siso=true $MAC_SDK_ARG $GN_EXTRA_ARGS"
e build --target electron:electron_ffmpeg_zip -C ../../out/ffmpeg
- name: Remove Clang problem matcher
shell: bash
@@ -271,12 +284,12 @@ runs:
run: ./src/electron/script/actions/move-artifacts.sh
- name: Upload Generated Artifacts ${{ inputs.step-suffix }}
if: always() && !cancelled()
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f #v7.0.0
with:
name: generated_artifacts_${{ env.ARTIFACT_KEY }}
path: ./generated_artifacts_${{ inputs.artifact-platform }}_${{ inputs.target-arch }}
- name: Upload Src Artifacts ${{ inputs.step-suffix }}
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f #v7.0.0
with:
name: src_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src_artifacts_${{ inputs.artifact-platform }}_${{ inputs.target-arch }}

View File

@@ -28,7 +28,7 @@ runs:
shell: bash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH="v1-src-cache-$(cat src/electron/.depshash)"
DEPSHASH="v2-src-cache-$(cat src/electron/.depshash)"
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_FILE=$DEPSHASH.tar" >> $GITHUB_ENV
if [ "${{ inputs.target-platform }}" = "win" ]; then
@@ -43,7 +43,7 @@ runs:
curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}&getAccountName=true" > sas-token
- name: Save SAS Key
if: ${{ inputs.generate-sas-token == 'true' }}
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: sas-token
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}
@@ -109,7 +109,7 @@ runs:
echo "target_os=['$TARGET_OS']" >> ./.gclient
fi
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags -vv
ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN=0 DEPOT_TOOLS_WIN_TOOLCHAIN=0 ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags
if [[ "${{ inputs.is-release }}" != "true" ]]; then
# Re-export all the patches to check if there were changes.
python3 src/electron/script/export_all_patches.py src/electron/patches/config.json
@@ -187,21 +187,35 @@ runs:
shell: bash
run: |
echo "Uncompressed src size: $(du -sh src | cut -f1 -d' ')"
tar -cf $CACHE_FILE src
# Named .tar but zstd-compressed; the sas-sidecar's filename allowlist
# only permits .tar/.tgz so we keep the extension and decode on restore.
tar -cf - src | zstd -T0 --long=30 -f -o $CACHE_FILE
echo "Compressed src to $(du -sh $CACHE_FILE | cut -f1 -d' ')"
cp ./$CACHE_FILE $CACHE_DRIVE/
- name: Persist Src Cache
if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }}
shell: bash
run: |
final_cache_path=$CACHE_DRIVE/$CACHE_FILE
# Upload to a run-unique temp name first so concurrent readers never
# observe a partially-written file, and an interrupted copy can't leave
# a truncated file at the final path. Orphaned temp files get swept by
# the clean-orphaned-cache-uploads workflow.
tmp_cache_path=$final_cache_path.upload-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}
echo "Uploading to temp path: $tmp_cache_path"
cp ./$CACHE_FILE $tmp_cache_path
echo "Using cache key: $DEPSHASH"
echo "Checking path: $final_cache_path"
if [ -f "$final_cache_path" ]; then
echo "Cache already persisted at $final_cache_path by a concurrent run; discarding ours"
rm -f $tmp_cache_path
else
mv -f $tmp_cache_path $final_cache_path
echo "Cache key persisted in $final_cache_path"
fi
if [ ! -f "$final_cache_path" ]; then
echo "Cache key not found"
exit 1
else
echo "Cache key persisted in $final_cache_path"
fi
- name: Wait for active SSH sessions
shell: bash

View File

@@ -22,30 +22,50 @@ runs:
steps:
- name: Delete wrong ${{ inputs.dependency }}
shell: bash
env:
CIPD_ROOT_PREFIX: ${{ inputs.cipd-root-prefix-path }}
INSTALLATION_DIR: ${{ inputs.installation-dir }}
run : |
rm -rf ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }}
rm -rf "${CIPD_ROOT_PREFIX}${INSTALLATION_DIR}"
- name: Create ensure file for ${{ inputs.dependency }}
if: ${{ inputs.dependency-version == '' }}
shell: bash
env:
PACKAGE: ${{ inputs.package }}
DEPS_FILE: ${{ inputs.deps-file }}
INSTALLATION_DIR: ${{ inputs.installation-dir }}
DEPENDENCY: ${{ inputs.dependency }}
run: |
echo '${{ inputs.package }}' `e d gclient getdep --deps-file=${{ inputs.deps-file }} -r '${{ inputs.installation-dir }}:${{ inputs.package }}'` > ${{ inputs.dependency }}_ensure_file
cat ${{ inputs.dependency }}_ensure_file
echo "$PACKAGE" $(e d gclient getdep --deps-file="$DEPS_FILE" -r "${INSTALLATION_DIR}:${PACKAGE}") > "${DEPENDENCY}_ensure_file"
cat "${DEPENDENCY}_ensure_file"
- name: Create ensure file for ${{ inputs.dependency }} from dependency-version
if: ${{ inputs.dependency-version != '' }}
shell: bash
env:
PACKAGE: ${{ inputs.package }}
DEPENDENCY_VERSION: ${{ inputs.dependency-version }}
DEPENDENCY: ${{ inputs.dependency }}
run: |
echo '${{ inputs.package }} ${{ inputs.dependency-version }}' > ${{ inputs.dependency }}_ensure_file
cat ${{ inputs.dependency }}_ensure_file
echo "$PACKAGE $DEPENDENCY_VERSION" > "${DEPENDENCY}_ensure_file"
cat "${DEPENDENCY}_ensure_file"
- name: CIPD installation of ${{ inputs.dependency }} (macOS)
if: ${{ inputs.target-platform != 'win' }}
shell: bash
env:
CIPD_ROOT_PREFIX: ${{ inputs.cipd-root-prefix-path }}
INSTALLATION_DIR: ${{ inputs.installation-dir }}
DEPENDENCY: ${{ inputs.dependency }}
run: |
echo "ensuring ${{ inputs.dependency }}"
e d cipd ensure --root ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} -ensure-file ${{ inputs.dependency }}_ensure_file
echo "ensuring $DEPENDENCY"
e d cipd ensure --root "${CIPD_ROOT_PREFIX}${INSTALLATION_DIR}" -ensure-file "${DEPENDENCY}_ensure_file"
- name: CIPD installation of ${{ inputs.dependency }} (Windows)
if: ${{ inputs.target-platform == 'win' }}
shell: powershell
env:
CIPD_ROOT_PREFIX: ${{ inputs.cipd-root-prefix-path }}
INSTALLATION_DIR: ${{ inputs.installation-dir }}
DEPENDENCY: ${{ inputs.dependency }}
run: |
echo "ensuring ${{ inputs.dependency }} on Windows"
e d cipd ensure --root ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} -ensure-file ${{ inputs.dependency }}_ensure_file
echo "ensuring $env:DEPENDENCY on Windows"
e d cipd ensure --root "$env:CIPD_ROOT_PREFIX$env:INSTALLATION_DIR" -ensure-file "$($env:DEPENDENCY)_ensure_file"

View File

@@ -27,6 +27,7 @@ runs:
python3 src/tools/clang/scripts/update.py
# Refs https://chromium-review.googlesource.com/c/chromium/src/+/6667681
python3 src/tools/clang/scripts/update.py --package objdump
python3 src/tools/clang/scripts/update.py --package clang-tidy
- name: Fix esbuild
if: ${{ inputs.target-platform != 'linux' }}
uses: ./src/electron/.github/actions/cipd-install

View File

@@ -7,7 +7,7 @@ runs:
shell: bash
id: yarn-cache-dir-path
run: echo "dir=$(node src/electron/script/yarn.js config get cacheFolder)" >> $GITHUB_OUTPUT
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}

View File

@@ -31,7 +31,7 @@ runs:
fi
mkdir temp-cache
tar -xf $cache_path -C temp-cache
zstd -d --long=30 -c $cache_path | tar -xf - -C temp-cache
echo "Unzipped cache is $(du -sh temp-cache/src | cut -f1)"
if [ -d "temp-cache/src" ]; then

View File

@@ -8,14 +8,14 @@ runs:
steps:
- name: Obtain SAS Key
continue-on-error: true
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: sas-token
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-1
enableCrossOsArchive: true
- name: Obtain SAS Key
continue-on-error: true
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: sas-token
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}
@@ -24,7 +24,7 @@ runs:
# The cache will always exist here as a result of the checkout job
# Either it was uploaded to Azure in the checkout job for this commit
# or it was uploaded in the checkout job for a previous commit.
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0
with:
timeout_minutes: 30
max_attempts: 3
@@ -61,9 +61,9 @@ runs:
echo "Cache is empty - exiting"
exit 1
fi
mkdir temp-cache
tar -xf $DEPSHASH.tar -C temp-cache
zstd -d --long=30 -c $DEPSHASH.tar | tar -xf - -C temp-cache
echo "Unzipped cache is $(du -sh temp-cache/src | cut -f1)"
if [ -d "temp-cache/src" ]; then
@@ -85,23 +85,21 @@ runs:
- name: Unzip and Ensure Src Cache (Windows)
if: ${{ inputs.target-platform == 'win' }}
shell: powershell
shell: bash
run: |
$src_cache = "$env:DEPSHASH.tar"
$cache_size = $(Get-Item $src_cache).length
Write-Host "Downloaded cache is $cache_size"
if ($cache_size -eq 0) {
Write-Host "Cache is empty - exiting"
echo "Downloaded cache is $(du -sh $DEPSHASH.tar | cut -f1)"
if [ `du $DEPSHASH.tar | cut -f1` = "0" ]; then
echo "Cache is empty - exiting"
exit 1
}
fi
$TEMP_DIR=New-Item -ItemType Directory -Path temp-cache
$TEMP_DIR_PATH = $TEMP_DIR.FullName
C:\ProgramData\Chocolatey\bin\7z.exe -y -snld20 x $src_cache -o"$TEMP_DIR_PATH"
mkdir temp-cache
zstd -d --long=30 -c $DEPSHASH.tar | tar -xf - -C temp-cache
rm -f $DEPSHASH.tar
- name: Move Src Cache (Windows)
if: ${{ inputs.target-platform == 'win' }}
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0
with:
timeout_minutes: 30
max_attempts: 3
@@ -112,9 +110,6 @@ runs:
Write-Host "Relocating Cache"
Remove-Item -Recurse -Force src
Move-Item temp-cache\src src
Write-Host "Deleting zip file"
Remove-Item -Force $src_cache
}
if (-Not (Test-Path "src\third_party\blink")) {
Write-Host "Cache was not correctly restored - exiting"

View File

@@ -7,7 +7,7 @@ runs:
if: ${{ runner.os != 'Windows' }}
shell: bash
run: |
if [[ -z "${{ env.CHROMIUM_GIT_COOKIE }}" ]]; then
if [[ -z "$CHROMIUM_GIT_COOKIE" ]]; then
echo "CHROMIUM_GIT_COOKIE is not set - cannot authenticate."
exit 0
fi
@@ -18,9 +18,7 @@ runs:
git config --global http.cookiefile ~/.gitcookies
tr , \\t <<\__END__ >>~/.gitcookies
${{ env.CHROMIUM_GIT_COOKIE }}
__END__
echo "$CHROMIUM_GIT_COOKIE" | tr , \\t >>~/.gitcookies
eval 'set -o history' 2>/dev/null || unsetopt HIST_IGNORE_SPACE 2>/dev/null
RESPONSE=$(curl -s -b ~/.gitcookies https://chromium-review.googlesource.com/a/accounts/self)
@@ -42,7 +40,7 @@ runs:
)
git config --global http.cookiefile "%USERPROFILE%\.gitcookies"
powershell -noprofile -nologo -command Write-Output "${{ env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}" >>"%USERPROFILE%\.gitcookies"
powershell -noprofile -nologo -command Write-Output $env:CHROMIUM_GIT_COOKIE_WINDOWS_STRING >>"%USERPROFILE%\.gitcookies"
curl -s -b "%USERPROFILE%\.gitcookies" https://chromium-review.googlesource.com/a/accounts/self > response.txt

View File

@@ -71,3 +71,11 @@ jobs:
uses: ./src/electron/.github/actions/checkout
with:
target-platform: linux
- name: Upload Patch Conflict Fix
if: ${{ failure() }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: update-patches
path: patches/update-patches.patch
if-no-files-found: ignore
archive: false

View File

@@ -21,17 +21,21 @@ jobs:
with:
node-version: 24.12.x
- name: Setting Up Dig Site
env:
CLONE_URL: ${{ github.event.pull_request.head.repo.clone_url }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
echo "remote: ${{ github.event.pull_request.head.repo.clone_url }}"
echo "sha ${{ github.event.pull_request.head.sha }}"
echo "base ref ${{ github.event.pull_request.base.ref }}"
git clone https://github.com/electron/electron.git electron
echo "remote: $CLONE_URL"
echo "sha $HEAD_SHA"
echo "base ref $BASE_REF"
git clone https://github.com/electron/electron.git electron
cd electron
mkdir -p artifacts
git remote add fork ${{ github.event.pull_request.head.repo.clone_url }} && git fetch fork
git checkout ${{ github.event.pull_request.head.sha }}
git merge-base origin/${{ github.event.pull_request.base.ref }} HEAD > .dig-old
echo ${{ github.event.pull_request.head.sha }} > .dig-new
git remote add fork "$CLONE_URL" && git fetch fork
git checkout "$HEAD_SHA"
git merge-base "origin/$BASE_REF" HEAD > .dig-old
echo "$HEAD_SHA" > .dig-new
cp .dig-old artifacts
- name: Generating Types for SHA in .dig-new

View File

@@ -431,3 +431,30 @@ jobs:
- name: GitHub Actions Jobs Done
run: |
echo "All GitHub Actions Jobs are done"
check-signed-commits:
name: Check signed commits in green PR
needs: gha-done
if: ${{ contains(github.event.pull_request.labels.*.name, 'needs-signed-commits')}}
runs-on: ubuntu-slim
permissions:
contents: read
pull-requests: write
steps:
- name: Check signed commits in PR
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
with:
comment: |
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
for all incoming PRs. To get your PR merged, please sign those commits
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
(`git push --force-with-lease`)
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
- name: Remove needs-signed-commits label
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR_URL --remove-label needs-signed-commits

View File

@@ -0,0 +1,32 @@
name: Clean Orphaned Cache Uploads
# Description:
# Sweeps orphaned in-flight upload temp files left on the src-cache volumes
# by checkout/action.yml when its cp-to-share step dies before the rename.
# A successful upload finishes in minutes, so anything older than 4h is dead.
on:
schedule:
- cron: "0 */4 * * *"
workflow_dispatch:
permissions: {}
jobs:
clean-orphaned-uploads:
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
- /mnt/win-cache:/mnt/win-cache
steps:
- name: Remove Orphaned Upload Temp Files
shell: bash
run: |
find /mnt/cross-instance-cache -maxdepth 1 -type f -name '*.tar.upload-*' -mmin +240 -print -delete
find /mnt/win-cache -maxdepth 1 -type f -name '*.tar.upload-*' -mmin +240 -print -delete

View File

@@ -61,9 +61,10 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: electron/electron
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
set -eo pipefail
COMMENT_COUNT=$(gh issue view ${{ github.event.issue.number }} --comments --json comments | jq '[ .comments[] | select(.author.login == "electron-issue-triage" or .authorAssociation == "OWNER" or .authorAssociation == "MEMBER") | select(.body | startswith("<!-- blocked/need-repro -->")) ] | length')
COMMENT_COUNT=$(gh issue view "$ISSUE_NUMBER" --comments --json comments | jq '[ .comments[] | select(.author.login == "electron-issue-triage" or .authorAssociation == "OWNER" or .authorAssociation == "MEMBER") | select(.body | startswith("<!-- blocked/need-repro -->")) ] | length')
if [[ $COMMENT_COUNT -eq 0 ]]; then
echo "SHOULD_COMMENT=1" >> "$GITHUB_OUTPUT"
fi

View File

@@ -16,9 +16,11 @@ jobs:
steps:
- name: Check for any blocked labels
id: check-for-blocked-labels
env:
LABELS_JSON: ${{ toJSON(github.event.issue.labels.*.name) }}
run: |
set -eo pipefail
BLOCKED_LABEL_COUNT=$(echo '${{ toJSON(github.event.issue.labels.*.name) }}' | jq '[ .[] | select(startswith("blocked/")) ] | length')
BLOCKED_LABEL_COUNT=$(echo "$LABELS_JSON" | jq '[ .[] | select(startswith("blocked/")) ] | length')
if [[ $BLOCKED_LABEL_COUNT -eq 0 ]]; then
echo "NOT_BLOCKED=1" >> "$GITHUB_OUTPUT"
fi

View File

@@ -10,6 +10,10 @@ on:
- '.yarn/**'
- '.yarnrc.yml'
# SECURITY: This workflow uses pull_request_target and has access to secrets.
# Do NOT checkout or run code from the PR head. All code execution must use
# the base branch only. Adding a ref to PR head would expose secrets to
# untrusted code.
permissions: {}
jobs:
@@ -45,5 +49,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
printf "<!-- disallowed-non-maintainer-change -->\n\nHello @${{ github.event.pull_request.user.login }}! It looks like this pull request touches one of our dependency or CI files, and per [our contribution policy](https://github.com/electron/electron/blob/main/CONTRIBUTING.md#dependencies-upgrades-policy) we do not accept these types of changes in PRs." | gh pr review $PR_URL -r --body-file=-
printf "<!-- disallowed-non-maintainer-change -->\n\nHello @${PR_AUTHOR}! It looks like this pull request touches one of our dependency or CI files, and per [our contribution policy](https://github.com/electron/electron/blob/main/CONTRIBUTING.md#dependencies-upgrades-policy) we do not accept these types of changes in PRs." | gh pr review $PR_URL -r --body-file=-

View File

@@ -35,7 +35,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AKS

View File

@@ -46,7 +46,11 @@ jobs:
shell: bash
run: |
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
gn_version="$(curl -sL -b ~/.gitcookies "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
if [[ ! "$chromium_revision" =~ ^[a-zA-Z0-9._-]+$ ]]; then
echo "::error::Invalid chromium_revision: $chromium_revision"
exit 1
fi
gn_version="$(curl -sL "https://raw.githubusercontent.com/chromium/chromium/refs/tags/${chromium_revision}/DEPS" | grep gn_version | head -n1 | cut -d\' -f4)"
cipd ensure -ensure-file - -root . <<-CIPD
\$ServiceURL https://chrome-infra-packages.appspot.com/
@@ -60,9 +64,13 @@ jobs:
shell: bash
run: |
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
if [[ ! "$chromium_revision" =~ ^[a-zA-Z0-9._-]+$ ]]; then
echo "::error::Invalid chromium_revision: $chromium_revision"
exit 1
fi
mkdir -p src/buildtools
curl -sL -b ~/.gitcookies "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > src/buildtools/DEPS
curl -sL "https://raw.githubusercontent.com/chromium/chromium/refs/tags/${chromium_revision}/buildtools/DEPS" > src/buildtools/DEPS
gclient sync --spec="solutions=[{'name':'src/buildtools','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':True},'managed':False}]"
- name: Add problem matchers

View File

@@ -151,7 +151,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy

View File

@@ -81,7 +81,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy

View File

@@ -160,7 +160,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy

View File

@@ -43,6 +43,8 @@ env:
ELECTRON_OUT_DIR: Default
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
# @sentry/cli is only needed by release upload-symbols.py; skip the ~17MB CDN download on test jobs
SENTRYCLI_SKIP_DOWNLOAD: 1
jobs:
test:
@@ -209,6 +211,7 @@ jobs:
- name: Run Electron Tests
shell: bash
timeout-minutes: 40
env:
MOCHA_REPORTER: mocha-multi-reporters
MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap
@@ -259,6 +262,19 @@ jobs:
fi
fi
- name: Take screenshot on timeout or cancellation
if: ${{ inputs.target-platform != 'linux' && (cancelled() || failure()) }}
shell: bash
run: |
screenshot_dir="src/electron/spec/artifacts"
mkdir -p "$screenshot_dir"
screenshot_file="$screenshot_dir/screenshot-timeout-$(date +%Y%m%d%H%M%S).png"
if [ "${{ inputs.target-platform }}" = "macos" ]; then
screencapture -x "$screenshot_file" || true
elif [ "${{ inputs.target-platform }}" = "win" ]; then
powershell -command "Add-Type -AssemblyName System.Windows.Forms; \$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds; \$bitmap = New-Object System.Drawing.Bitmap(\$screen.Width, \$screen.Height); \$graphics = [System.Drawing.Graphics]::FromImage(\$bitmap); \$graphics.CopyFromScreen(\$screen.Location, [System.Drawing.Point]::Empty, \$screen.Size); \$bitmap.Save('$screenshot_file')" || true
fi
- name: Upload Test results to Datadog
env:
DD_ENV: ci
@@ -274,8 +290,8 @@ jobs:
fi
if: always() && !cancelled()
- name: Upload Test Artifacts
if: always() && !cancelled()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f #v7.0.0
with:
name: test_artifacts_${{ env.ARTIFACT_KEY }}_${{ matrix.shard }}
path: src/electron/spec/artifacts

View File

@@ -36,6 +36,8 @@ env:
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
ELECTRON_OUT_DIR: Default
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
# @sentry/cli is only needed by release upload-symbols.py; skip the ~17MB CDN download on test jobs
SENTRYCLI_SKIP_DOWNLOAD: 1
jobs:
node-tests:

View File

@@ -4,6 +4,10 @@ on:
pull_request_target:
types: [labeled]
# SECURITY: This workflow uses pull_request_target and has access to secrets.
# Do NOT checkout or run code from the PR head. All code execution must use
# the base branch only. Adding a ref to PR head would expose secrets to
# untrusted code.
permissions: {}
jobs:

View File

@@ -0,0 +1,39 @@
name: Pull Request Opened/Synchronized
on:
pull_request_target:
types: [opened, synchronize]
# SECURITY: This workflow uses pull_request_target and has access to secrets.
# Do NOT checkout or run code from the PR head. All code execution must use
# the base branch only. Adding a ref to PR head would expose secrets to
# untrusted code.
permissions: {}
jobs:
check-signed-commits:
name: Check signed commits in PR
if: ${{ !contains(github.event.pull_request.labels.*.name, 'needs-signed-commits')}}
runs-on: ubuntu-slim
permissions:
contents: read
pull-requests: write
steps:
- name: Check signed commits in PR
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
with:
comment: |
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
for all incoming PRs. To get your PR merged, please sign those commits
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
(`git push --force-with-lease`)
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
- name: Add needs-signed-commits label
if: ${{ failure() }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR_URL --add-label needs-signed-commits

1
.gitignore vendored
View File

@@ -42,6 +42,7 @@ spec/.hash
# Generated native addon files
/spec/fixtures/native-addon/echo/build/
/spec/fixtures/native-addon/dialog-helper/build/
# If someone runs tsc this is where stuff will end up
ts-gen

View File

@@ -9,4 +9,8 @@ npmMinimalAgeGate: 10080
npmPreapprovedPackages:
- "@electron/*"
httpProxy: "${HTTP_PROXY:-}"
httpsProxy: "${HTTPS_PROXY:-}"
yarnPath: .yarn/releases/yarn-4.12.0.cjs

View File

@@ -127,6 +127,22 @@ patches/{target}/*.patch → [e sync --3] → target repo commits
2. Create a git commit
3. Run `e patches <target>` to export
**Fixing patch conflicts on an existing PR:**
If asked to fix a patch conflict on a branch that already has an open PR, check the PR's failed **Apply Patches** CI run for an `update-patches` artifact before running `e sync` locally. CI has already performed the 3-way merge and exported the resolved patch diff — applying it is much faster than a full local sync.
```bash
# Find the failed Apply Patches run for the PR and download the artifact
gh run list --repo electron/electron --branch <pr-branch> --workflow "Apply Patches" --limit 1
gh run download <run-id> --repo electron/electron --name update-patches
# Apply the CI-generated fix, then push
git am update-patches.patch
git push
```
If no artifact exists (e.g. the 3-way merge itself failed), fall back to `e sync --3` and resolve manually.
## Testing
**Test location:** `spec/` directory

2
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'146.0.7680.72',
'146.0.7680.179',
'node_version':
'v24.14.0',
'nan_version':

View File

@@ -51,9 +51,6 @@ is_cfi = false
use_qt5 = false
use_qt6 = false
# Disables the builtins PGO for V8
v8_builtins_profiling_log_file = ""
# https://chromium.googlesource.com/chromium/src/+/main/docs/dangling_ptr.md
# TODO(vertedinde): hunt down dangling pointers on Linux
enable_dangling_raw_ptr_checks = false

View File

@@ -245,6 +245,10 @@ static_library("chrome") {
"//chrome/browser/ui/views/dark_mode_manager_linux.cc",
"//chrome/browser/ui/views/dark_mode_manager_linux.h",
]
sources += [
"//chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.cc",
"//chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.h",
]
public_deps += [ "//components/dbus" ]
}

View File

@@ -44,8 +44,8 @@ See [`Menu`](menu.md) for examples.
menu items.
* `registerAccelerator` boolean (optional) _Linux_ _Windows_ - If false, the accelerator won't be registered
with the system, but it will still be displayed. Defaults to true.
* `sharingItem` SharingItem (optional) _macOS_ - The item to share when the `role` is `shareMenu`.
* `submenu` (MenuItemConstructorOptions[] | [Menu](menu.md)) (optional) - Should be specified
* `sharingItem` [SharingItem](structures/sharing-item.md) (optional) _macOS_ - The item to share when the `role` is `shareMenu`.
* `submenu` ([MenuItemConstructorOptions](#new-menuitemoptions)[] | [Menu](menu.md)) (optional) - Should be specified
for `submenu` type menu items. If `submenu` is specified, the `type: 'submenu'` can be omitted.
If the value is not a [`Menu`](menu.md) then it will be automatically converted to one using
`Menu.buildFromTemplate`.
@@ -89,7 +89,7 @@ A `Function` that is fired when the MenuItem receives a click event.
It can be called with `menuItem.click(event, focusedWindow, focusedWebContents)`.
* `event` [KeyboardEvent](structures/keyboard-event.md)
* `focusedWindow` [BaseWindow](browser-window.md)
* `focusedWindow` [BaseWindow](base-window.md)
* `focusedWebContents` [WebContents](web-contents.md)
#### `menuItem.submenu`
@@ -110,11 +110,11 @@ A `string` (optional) indicating the item's role, if set. Can be `undo`, `redo`,
#### `menuItem.accelerator`
An `Accelerator | null` indicating the item's accelerator, if set.
An [`Accelerator | null`](../tutorial/keyboard-shortcuts.md#accelerators) indicating the item's accelerator, if set.
#### `menuItem.userAccelerator` _Readonly_ _macOS_
An `Accelerator | null` indicating the item's [user-assigned accelerator](https://developer.apple.com/documentation/appkit/nsmenuitem/1514850-userkeyequivalent?language=objc) for the menu item.
An [`Accelerator | null`](../tutorial/keyboard-shortcuts.md#accelerators) indicating the item's [user-assigned accelerator](https://developer.apple.com/documentation/appkit/nsmenuitem/1514850-userkeyequivalent?language=objc) for the menu item.
> [!NOTE]
> This property is only initialized after the `MenuItem` has been added to a `Menu`. Either via `Menu.buildFromTemplate` or via `Menu.append()/insert()`. Accessing before initialization will just return `null`.
@@ -170,7 +170,7 @@ This property can be dynamically changed.
#### `menuItem.sharingItem` _macOS_
A `SharingItem` indicating the item to share when the `role` is `shareMenu`.
A [`SharingItem`](structures/sharing-item.md) indicating the item to share when the `role` is `shareMenu`.
This property can be dynamically changed.

View File

@@ -70,7 +70,7 @@ for more information on macOS' native actions.
#### `Menu.buildFromTemplate(template)`
- `template` (MenuItemConstructorOptions | [MenuItem](menu-item.md))[]
- `template` ([MenuItemConstructorOptions](menu-item.md#new-menuitemoptions) | [MenuItem](menu-item.md))[]
Returns [`Menu`](menu.md)
@@ -162,7 +162,7 @@ Emitted when a popup is closed either manually or with `menu.closePopup()`.
#### `menu.items`
A `MenuItem[]` array containing the menu's items.
A [`MenuItem[]`](menu-item.md) array containing the menu's items.
Each `Menu` consists of multiple [`MenuItem`](menu-item.md) instances and each `MenuItem`
can nest a `Menu` into its `submenu` property.

View File

@@ -84,3 +84,7 @@ Currently, Windows high contrast is the only system setting that triggers forced
### `nativeTheme.prefersReducedTransparency` _Readonly_
A `boolean` that indicates whether the user has chosen via system accessibility settings to reduce transparency at the OS level.
### `nativeTheme.shouldDifferentiateWithoutColor` _macOS_ _Readonly_
A `boolean` that indicates whether the user prefers UI that differentiates items using something other than color alone (e.g. shapes or labels). This maps to [NSWorkspace.accessibilityDisplayShouldDifferentiateWithoutColor](https://developer.apple.com/documentation/appkit/nsworkspace/accessibilitydisplayshoulddifferentiatewithoutcolor).

View File

@@ -42,11 +42,15 @@ Returns `boolean` - Whether or not desktop notifications are supported on the cu
* `timeoutType` string (optional) _Linux_ _Windows_ - The timeout duration of the notification. Can be 'default' or 'never'.
* `replyPlaceholder` string (optional) _macOS_ - The placeholder to write in the inline reply input field.
* `sound` string (optional) _macOS_ - The name of the sound file to play when the notification is shown.
* `urgency` string (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
* `urgency` string (optional) _Linux_ _Windows_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
* `actions` [NotificationAction[]](structures/notification-action.md) (optional) _macOS_ - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation.
* `closeButtonText` string (optional) _macOS_ - A custom title for the close button of an alert. An empty string will cause the default localized text to be used.
* `toastXml` string (optional) _Windows_ - A custom description of the Notification on Windows superseding all properties above. Provides full customization of design and behavior of the notification.
> [!NOTE]
> On Windows, `urgency` type 'critical' sorts the notification higher in Action Center (above default priority notifications), but does not prevent auto-dismissal. To prevent auto-dismissal, you should also set
> `timeoutType` to 'never'.
### Instance Events
Objects created with `new Notification` emit the following events:

View File

@@ -56,6 +56,15 @@ app.whenReady().then(() => {
})
```
## Protocol names
[RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) defines what a valid
protocol name is:
> Scheme names consist of a sequence of characters beginning with a letter and followed
> by any combination of letters, digits, plus ("+"), period ("."), or hyphen ("-").
> Although schemes are case-insensitive, the canonical form is lowercase […].
## Methods
The `protocol` module has the following methods:

View File

@@ -11,3 +11,5 @@
* `stream` boolean (optional) - Default false.
* `codeCache` boolean (optional) - Enable V8 code cache for the scheme, only
works when `standard` is also set to true. Default false.
* `allowExtensions` boolean (optional) - Allow Chrome extensions to be used
on pages served over this protocol. Default false.

View File

@@ -1485,6 +1485,11 @@ mainWindow.webContents.setWindowOpenHandler((details) => {
const browserView = new BrowserView(options)
mainWindow.addBrowserView(browserView)
browserView.setBounds({ x: 0, y: 0, width: 640, height: 480 })
// For `background-tab` disposition (e.g., when middle-clicking or ctrl/cmd-clicking a link),
// `options.webContents` is undefined because its creation can be deferred. So load the URL manually.
if (details.disposition === 'background-tab') {
browserView.webContents.loadURL(details.url)
}
return browserView.webContents
}
}

View File

@@ -79,7 +79,7 @@ $ ../../electron/script/git-import-patches ../../electron/patches/node
$ ../../electron/script/git-export-patches -o ../../electron/patches/node
```
Note that `git-import-patches` will mark the commit that was `HEAD` when it was run as `refs/patches/upstream-head`. This lets you keep track of which commits are from Electron patches (those that come after `refs/patches/upstream-head`) and which commits are in upstream (those before `refs/patches/upstream-head`).
Note that `git-import-patches` will mark the commit that was `HEAD` when it was run as `refs/patches/upstream-head` (and a checkout-specific `refs/patches/upstream-head-<hash>` so that gclient worktrees sharing a `.git/refs` directory don't clobber each other). This lets you keep track of which commits are from Electron patches (those that come after `refs/patches/upstream-head`) and which commits are in upstream (those before `refs/patches/upstream-head`).
#### Resolving conflicts

View File

@@ -21,24 +21,33 @@
### Step 1: Fork
Fork the project [on GitHub](https://github.com/electron/electron) and clone your fork
locally.
```sh
$ git clone git@github.com:username/electron.git
$ cd electron
$ git remote add upstream https://github.com/electron/electron.git
$ git fetch upstream
```
Fork Electron's [GitHub repository](https://github.com/electron/electron).
### Step 2: Build
Build steps and dependencies differ slightly depending on your operating system.
See these detailed guides on building Electron locally:
We recommend using [`@electron/build-tools`](https://github.com/electron/build-tools) to build
Electron itself.
* [Building on macOS](build-instructions-macos.md)
* [Building on Linux](build-instructions-linux.md)
* [Building on Windows](build-instructions-windows.md)
```sh
# Install build-tools package globally:
npm install -g @electron/build-tools
# Run the init script where you want to clone the project and point it to your fork:
e init --fork my-org/electron --bootstrap testing
```
This will create a new `electron` folder in your working directory and initialize the project.
Once the build completes, navigate to `electron/src/electron`, where your fork is actually cloned.
> [!IMPORTANT]
> Your Electron project has a complex folder structure with nested repositories.
> See the [Build Instructions](./build-instructions-gn.md) docs for detailed Build Tools
> usage instructions (e.g. how to sync dependencies or how to recompile the binary)
> and platform-specific notices.
There, you should have two `remote` URLs in git:
* `origin` will point to `electron/electron`
* `fork` will point to your fork (`my-org/electron`)
Once you've built the project locally, you're ready to start making changes!
@@ -48,7 +57,7 @@ To keep your development environment organized, create local branches to
hold your work. These should be branched directly off of the `main` branch.
```sh
$ git checkout -b my-branch -t upstream/main
git checkout -b my-branch
```
## Making Changes
@@ -60,7 +69,7 @@ changes to either the C/C++ code in the `shell/` folder,
the JavaScript code in the `lib/` folder, the documentation in `docs/api/`
or tests in the `spec/` folder.
Please be sure to run `npm run lint` from time to time on any code changes
Please be sure to run `yarn lint` from time to time on any code changes
to ensure that they follow the project's code style.
See [coding style](coding-style.md) for
@@ -75,8 +84,8 @@ across multiple commits. There is no limit to the number of commits in a
pull request.
```sh
$ git add my/changed/files
$ git commit
git add my/changed/files
git commit
```
Note that multiple commits get squashed when they are landed.
@@ -138,8 +147,8 @@ Once you have committed your changes, it is a good idea to use `git rebase`
(not `git merge`) to synchronize your work with the main repository.
```sh
$ git fetch upstream
$ git rebase upstream/main
git fetch origin
git rebase origin/main
```
This ensures that your working branch has the latest changes from `electron/electron`
@@ -156,7 +165,7 @@ Before submitting your changes in a pull request, always run the full
test suite. To run the tests:
```sh
$ npm run test
yarn test
```
Make sure the linter does not report any issues and that all tests pass.
@@ -165,7 +174,7 @@ Please do not submit patches that fail either check.
If you are updating tests and want to run a single spec to check it:
```sh
$ npm run test -match=menu
yarn test -match=menu
```
The above would only run spec modules matching `menu`, which is useful for
@@ -179,7 +188,7 @@ begin the process of opening a pull request by pushing your working branch
to your fork on GitHub.
```sh
$ git push origin my-branch
git push fork my-branch
```
### Step 9: Opening the Pull Request
@@ -203,9 +212,9 @@ branch, add a new commit with those changes, and push those to your fork.
GitHub will automatically update the pull request.
```sh
$ git add my/changed/files
$ git commit
$ git push origin my-branch
git add my/changed/files
git commit
git push fork my-branch
```
There are a number of more advanced mechanisms for managing commits using
@@ -213,8 +222,8 @@ There are a number of more advanced mechanisms for managing commits using
Feel free to post a comment in the pull request to ping reviewers if you are
awaiting an answer on something. If you encounter words or acronyms that
seem unfamiliar, refer to this
[glossary](https://sites.google.com/a/chromium.org/dev/glossary).
seem unfamiliar, refer to the
[Chromium glossary](https://sites.google.com/a/chromium.org/dev/glossary).
#### Approval and Request Changes Workflow

View File

@@ -146,13 +146,15 @@ The extra privileges granted to the `file://` protocol by this fuse are incomple
The `wasmTrapHandlers` fuse controls whether V8 will use signal handlers to trap Out of Bounds memory
access from WebAssembly. The feature works by surrounding the WebAssembly memory with large guard regions
and then installing a signal handler that traps attempt to access memory in the guard region. The feature
is only supported on the following 64-bit systems.
is only supported on the following 64-bit systems:
Linux. MacOS, Windows - x86_64
Linux, MacOS - aarch64
* Linux, macOS, Windows - x86_64
* Linux, macOS - aarch64
```text
| Guard Pages | WASM heap | Guard Pages |
|-----8GB-----| |-----8GB-----|
```
When the fuse is disabled V8 will use explicit bound checks in the generated WebAssembly code to ensure
memory safety. However, this method has some downsides

View File

@@ -782,8 +782,7 @@ WebContents.prototype._init = function () {
const originCounts = new Map<string, number>();
const openDialogs = new Set<AbortController>();
this.on('-run-dialog', async (info, callback) => {
const originUrl = new URL(info.frame.url);
const origin = originUrl.protocol === 'file:' ? originUrl.href : originUrl.origin;
const origin = info.frame.origin === 'file://' ? info.frame.url : info.frame.origin;
if ((originCounts.get(origin) ?? 0) < 0) return callback(false, '');
const prefs = this.getLastWebPreferences();

View File

@@ -17,11 +17,6 @@ export type WindowOpenArgs = {
features: string,
}
const frameNamesToWindow = new Map<string, WebContents>();
const registerFrameNameToGuestWindow = (name: string, webContents: WebContents) => frameNamesToWindow.set(name, webContents);
const unregisterFrameName = (name: string) => frameNamesToWindow.delete(name);
const getGuestWebContentsByFrameName = (name: string) => frameNamesToWindow.get(name);
/**
* `openGuestWindow` is called to create and setup event handling for the new
* window.
@@ -47,20 +42,6 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
...overrideBrowserWindowOptions
};
// To spec, subsequent window.open calls with the same frame name (`target` in
// spec parlance) will reuse the previous window.
// https://html.spec.whatwg.org/multipage/window-object.html#apis-for-creating-and-navigating-browsing-contexts-by-name
const existingWebContents = getGuestWebContentsByFrameName(frameName);
if (existingWebContents) {
if (existingWebContents.isDestroyed()) {
// FIXME(t57ser): The webContents is destroyed for some reason, unregister the frame name
unregisterFrameName(frameName);
} else {
existingWebContents.loadURL(url);
return;
}
}
if (createWindow) {
const webContents = createWindow({
webContents: guest,
@@ -72,7 +53,7 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
throw new Error('Invalid webContents. Created window should be connected to webContents passed with options object.');
}
handleWindowLifecycleEvents({ embedder, frameName, guest, outlivesOpener });
handleWindowLifecycleEvents({ embedder, guest, outlivesOpener });
}
return;
@@ -96,7 +77,7 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
});
}
handleWindowLifecycleEvents({ embedder, frameName, guest: window.webContents, outlivesOpener });
handleWindowLifecycleEvents({ embedder, guest: window.webContents, outlivesOpener });
embedder.emit('did-create-window', window, { url, frameName, options: browserWindowOptions, disposition, referrer, postData });
}
@@ -107,10 +88,9 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
* too is the guest destroyed; this is Electron convention and isn't based in
* browser behavior.
*/
const handleWindowLifecycleEvents = function ({ embedder, guest, frameName, outlivesOpener }: {
const handleWindowLifecycleEvents = function ({ embedder, guest, outlivesOpener }: {
embedder: WebContents,
guest: WebContents,
frameName: string,
outlivesOpener: boolean
}) {
const closedByEmbedder = function () {
@@ -128,13 +108,6 @@ const handleWindowLifecycleEvents = function ({ embedder, guest, frameName, outl
embedder.once('current-render-view-deleted' as any, closedByEmbedder);
}
guest.once('destroyed', closedByUser);
if (frameName) {
registerFrameNameToGuestWindow(frameName, guest);
guest.once('destroyed', function () {
unregisterFrameName(frameName);
});
}
};
// Security options that child windows will always inherit from parent windows

View File

@@ -1232,6 +1232,8 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
// has filesystem caching.
overrideAPI(fs, 'copyFile');
overrideAPISync(fs, 'copyFileSync');
overrideAPI(fs, 'cp');
overrideAPISync(fs, 'cpSync');
overrideAPI(fs, 'open');
overrideAPISync(process, 'dlopen', 1);

View File

@@ -14,6 +14,7 @@
"@electron/typescript-definitions": "^9.1.5",
"@octokit/rest": "^20.1.2",
"@primer/octicons": "^10.0.0",
"@sentry/cli": "1.72.0",
"@types/minimist": "^1.2.5",
"@types/node": "^24.9.0",
"@types/semver": "^7.5.8",
@@ -155,6 +156,9 @@
"spec/fixtures/native-addon/*"
],
"dependenciesMeta": {
"@sentry/cli": {
"built": true
},
"abstract-socket": {
"built": true
}

View File

@@ -121,7 +121,7 @@ build_disable_thin_lto_mac.patch
feat_corner_smoothing_css_rule_and_blink_painting.patch
build_add_public_config_simdutf_config.patch
fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch
revert_code_health_clean_up_stale_macwebcontentsocclusion.patch
fix_handle_embedder_windows_shown_after_webcontentsviewcocoa_attach.patch
feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch
feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch
fix_win32_synchronous_spellcheck.patch
@@ -147,3 +147,6 @@ fix_update_dbus_signal_signature_for_xdg_globalshortcuts_portal.patch
fix_set_correct_app_id_on_linux.patch
fix_pass_trigger_for_global_shortcuts_on_wayland.patch
feat_plumb_node_integration_in_worker_through_workersettings.patch
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
extensions_return_early_from_urlpattern_isvalidscheme.patch
feat_allow_enabling_extensions_on_custom_protocols.patch

View File

@@ -33,10 +33,10 @@ index 4a742db71f62f9ac891ceeb0604ca0b99d1d89c1..2c5af6482e2b6905552a05b16d3df0a4
"//base",
"//build:branding_buildflags",
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2fc3a991d89093ff9139eb09d74123197155caff..0862aa96c2a7b496338ac0593f84fcfa21f25572 100644
index a2a14349d40ce34831ab063cd5eb55cd5085c814..1a861ff7867f19935178c8368a9a720230fee026 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4749,7 +4749,7 @@ static_library("browser") {
@@ -4751,7 +4751,7 @@ static_library("browser") {
]
}
@@ -46,10 +46,10 @@ index 2fc3a991d89093ff9139eb09d74123197155caff..0862aa96c2a7b496338ac0593f84fcfa
# than here in :chrome_dll.
deps += [ "//chrome:packed_resources_integrity_header" ]
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7d5a246787bc3cc3bcb883aa78121d3d3f124780..b5de35620bc636d5e1d0d5770d898f564843bcef 100644
index 40ea51f97470e2b86f8d2d373ea99a2a71ad185e..db6a2291ce77d89c8e28a1435336fd939e436906 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7728,9 +7728,12 @@ test("unit_tests") {
@@ -7731,9 +7731,12 @@ test("unit_tests") {
"//chrome/notification_helper",
]
@@ -63,7 +63,7 @@ index 7d5a246787bc3cc3bcb883aa78121d3d3f124780..b5de35620bc636d5e1d0d5770d898f56
"//chrome//services/util_win:unit_tests",
"//chrome/app:chrome_dll_resources",
"//chrome/app:win_unit_tests",
@@ -8698,6 +8701,10 @@ test("unit_tests") {
@@ -8703,6 +8706,10 @@ test("unit_tests") {
"../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc",
]
@@ -74,7 +74,7 @@ index 7d5a246787bc3cc3bcb883aa78121d3d3f124780..b5de35620bc636d5e1d0d5770d898f56
sources += [
# The importer code is not used on Android.
"../common/importer/firefox_importer_utils_unittest.cc",
@@ -8755,7 +8762,6 @@ test("unit_tests") {
@@ -8760,7 +8767,6 @@ test("unit_tests") {
# TODO(crbug.com/417513088): Maybe merge with the non-android `deps` declaration above?
deps += [
"../browser/screen_ai:screen_ai_install_state",

View File

@@ -9,10 +9,10 @@ potentially prevent a window from being created.
TODO(loc): this patch is currently broken.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 46368e70af175d8d0ab0fb5a36d258e48270371e..8d7be769a6c76650ae999338578215dcd324c199 100644
index 2d8a70f5fc0f6c2dc2a7587b7bc2e43dbcee8f0e..a87bd09d7a12c5f003488792843cd1807ee1e30f 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -9990,6 +9990,7 @@ void RenderFrameHostImpl::CreateNewWindow(
@@ -9997,6 +9997,7 @@ void RenderFrameHostImpl::CreateNewWindow(
last_committed_origin_, params->window_container_type,
params->target_url, params->referrer.To<Referrer>(),
params->frame_name, params->disposition, *params->features,

View File

@@ -43,7 +43,7 @@ index 21d5ab99800c0830cc31ec4ebb24e3f05cd904d8..3f8f514519d6e4a0abe3690f5df35de8
// When the enterprise policy is not set, use finch/feature flag choice.
return base::FeatureList::IsEnabled(chrome_pdf::features::kPdfXfaSupport);
diff --git a/chrome/browser/pdf/pdf_extension_util.cc b/chrome/browser/pdf/pdf_extension_util.cc
index 83bc44f0c1928b9023efa54bfb57bed69d77484a..9c79f96931a0b2a05d98191ea8eb31a3a01818fc 100644
index 24ca200ac662028d45180b21c3d79f2a4b96636e..b35025f7a06cae964858452c8f9e96655e34c47a 100644
--- a/chrome/browser/pdf/pdf_extension_util.cc
+++ b/chrome/browser/pdf/pdf_extension_util.cc
@@ -259,10 +259,13 @@ bool IsPrintingEnabled(content::BrowserContext* context) {

View File

@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Tue, 31 Mar 2026 00:11:27 +0200
Subject: [Extensions] Return early from URLPattern::IsValidScheme()
|scheme| will match at most one entry in |kValidSchemes|. No need to
iterate through the remaining ones.
Change-Id: I1f37383faccaddc775faabb797aea2851d93382f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7639311
Commit-Queue: Andrea Orru <andreaorru@chromium.org>
Reviewed-by: Andrea Orru <andreaorru@chromium.org>
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1594934}
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
index 4054af728030306c5473f9a47e580595596768a0..d4328ca22fdeefd3dca88bfe959dfb849705b109 100644
--- a/extensions/common/url_pattern.cc
+++ b/extensions/common/url_pattern.cc
@@ -396,8 +396,8 @@ bool URLPattern::IsValidScheme(std::string_view scheme) const {
}
for (size_t i = 0; i < std::size(kValidSchemes); ++i) {
- if (scheme == kValidSchemes[i] && (valid_schemes_ & kValidSchemeMasks[i])) {
- return true;
+ if (scheme == kValidSchemes[i]) {
+ return valid_schemes_ & kValidSchemeMasks[i];
}
}

View File

@@ -0,0 +1,164 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Wed, 25 Feb 2026 16:24:03 +0100
Subject: feat: allow enabling extensions on custom protocols
This allows us to use Chrome extensions on custom protocols.
The patch can't really be upstreamed, unfortunately, because there are
other URLPattern functions that we don't patch that Chrome needs.
Patching those properly would require replacing the bitmask logic in
URLPattern with a more flexible solution. This would be a larger effort
and Chromium might reject it for performance reasons.
See: https://source.chromium.org/chromium/chromium/src/+/main:extensions/common/url_pattern.h;l=53-74;drc=50dbcddad2f8e36ddfcec21d4551f389df425c37
This patch makes it work in the context of Electron.
diff --git a/extensions/browser/api/content_settings/content_settings_helpers.cc b/extensions/browser/api/content_settings/content_settings_helpers.cc
index 34fa528a82f03891c89b3bb95bc9d2a135ee5f36..f88041554b828215a32dbb4aadcc73df40e6d8c2 100644
--- a/extensions/browser/api/content_settings/content_settings_helpers.cc
+++ b/extensions/browser/api/content_settings/content_settings_helpers.cc
@@ -37,7 +37,7 @@ ContentSettingsPattern ParseExtensionPattern(const std::string& pattern_str,
std::string* error) {
const int kAllowedSchemes =
URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
- URLPattern::SCHEME_FILE;
+ URLPattern::SCHEME_FILE | URLPattern::SCHEME_ELECTRON_CUSTOM_PROTOCOLS;
URLPattern url_pattern(kAllowedSchemes);
URLPattern::ParseResult result = url_pattern.Parse(pattern_str);
if (result != URLPattern::ParseResult::kSuccess) {
diff --git a/extensions/browser/api/web_request/extension_web_request_event_router.h b/extensions/browser/api/web_request/extension_web_request_event_router.h
index 3de9285a548c1812783e90e76417b060dea612af..fad75693a26a139695f822e8b7567b0d38bb53cc 100644
--- a/extensions/browser/api/web_request/extension_web_request_event_router.h
+++ b/extensions/browser/api/web_request/extension_web_request_event_router.h
@@ -52,7 +52,8 @@ inline constexpr int kWebRequestFilterValidSchemes =
URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
URLPattern::SCHEME_FTP | URLPattern::SCHEME_FILE |
URLPattern::SCHEME_EXTENSION | URLPattern::SCHEME_WS |
- URLPattern::SCHEME_WSS | URLPattern::SCHEME_UUID_IN_PACKAGE;
+ URLPattern::SCHEME_WSS | URLPattern::SCHEME_UUID_IN_PACKAGE |
+ URLPattern::SCHEME_ELECTRON_CUSTOM_PROTOCOLS;
class WebRequestEventRouter : public KeyedService {
public:
diff --git a/extensions/common/extension.cc b/extensions/common/extension.cc
index 25f6860b1db1fecba457c06ecff9263efa4a6a8a..bc7e9834d2ad3f7abd8a58ee49f6a7f447915754 100644
--- a/extensions/common/extension.cc
+++ b/extensions/common/extension.cc
@@ -219,7 +219,8 @@ const int Extension::kValidHostPermissionSchemes =
URLPattern::SCHEME_CHROMEUI | URLPattern::SCHEME_HTTP |
URLPattern::SCHEME_HTTPS | URLPattern::SCHEME_FILE |
URLPattern::SCHEME_FTP | URLPattern::SCHEME_WS | URLPattern::SCHEME_WSS |
- URLPattern::SCHEME_UUID_IN_PACKAGE;
+ URLPattern::SCHEME_UUID_IN_PACKAGE |
+ URLPattern::SCHEME_ELECTRON_CUSTOM_PROTOCOLS;
//
// Extension
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
index d4328ca22fdeefd3dca88bfe959dfb849705b109..ba24e788d4a2e467d24f6369e2d93ea3b4a0c9d7 100644
--- a/extensions/common/url_pattern.cc
+++ b/extensions/common/url_pattern.cc
@@ -140,6 +140,11 @@ bool URLPattern::IsValidSchemeForExtensions(std::string_view scheme) {
return true;
}
}
+ for (auto& extension_scheme : url::GetExtensionSchemes()) {
+ if (scheme == extension_scheme) {
+ return true;
+ }
+ }
return false;
}
@@ -401,6 +406,14 @@ bool URLPattern::IsValidScheme(std::string_view scheme) const {
}
}
+ if (valid_schemes_ & URLPattern::SCHEME_ELECTRON_CUSTOM_PROTOCOLS) {
+ for (auto& extension_scheme : url::GetExtensionSchemes()) {
+ if (scheme == extension_scheme) {
+ return true;
+ }
+ }
+ }
+
return false;
}
diff --git a/extensions/common/url_pattern.h b/extensions/common/url_pattern.h
index 4d09251b0160644d86682ad3db7c41b50f360e6f..8a626e14eff2d58d8218a7b0df820c6c0522b00f 100644
--- a/extensions/common/url_pattern.h
+++ b/extensions/common/url_pattern.h
@@ -64,6 +64,9 @@ class URLPattern {
SCHEME_DATA = 1 << 9,
SCHEME_UUID_IN_PACKAGE = 1 << 10,
+ // Represents the schemes returned by url::GetExtensionSchemes().
+ SCHEME_ELECTRON_CUSTOM_PROTOCOLS = 1 << 11,
+
// IMPORTANT!
// SCHEME_ALL will match every scheme, including chrome://, chrome-
// extension://, about:, etc. Because this has lots of security
diff --git a/extensions/common/user_script.cc b/extensions/common/user_script.cc
index f680ef4d31d580a285abe51387e3df043d4458f1..afde40d56d7874aa04ea2b1d881e5cab79fd7661 100644
--- a/extensions/common/user_script.cc
+++ b/extensions/common/user_script.cc
@@ -69,7 +69,8 @@ enum {
kValidUserScriptSchemes = URLPattern::SCHEME_CHROMEUI |
URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
URLPattern::SCHEME_FILE | URLPattern::SCHEME_FTP |
- URLPattern::SCHEME_UUID_IN_PACKAGE
+ URLPattern::SCHEME_UUID_IN_PACKAGE |
+ URLPattern::SCHEME_ELECTRON_CUSTOM_PROTOCOLS
};
// static
diff --git a/url/url_util.cc b/url/url_util.cc
index 21cfd314fd52727e4b07de880bd80a0bec7407d5..cb90ea396122d8f5dc27e6d9ffc36cc0abedb97d 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -134,6 +134,9 @@ struct SchemeRegistry {
// Embedder schemes that have V8 code cache enabled in js and wasm scripts.
std::vector<std::string> code_cache_schemes = {};
+ // Embedder schemes on which Chrome extensions can be used.
+ std::vector<std::string> extension_schemes = {};
+
// Schemes with a predefined default custom handler.
std::vector<SchemeWithHandler> predefined_handler_schemes;
@@ -678,6 +681,15 @@ const std::vector<std::string>& GetCodeCacheSchemes() {
return GetSchemeRegistry().code_cache_schemes;
}
+void AddExtensionScheme(std::string_view new_scheme) {
+ DoAddScheme(new_scheme,
+ &GetSchemeRegistryWithoutLocking()->extension_schemes);
+}
+
+const std::vector<std::string>& GetExtensionSchemes() {
+ return GetSchemeRegistry().extension_schemes;
+}
+
void AddPredefinedHandlerScheme(std::string_view new_scheme,
std::string_view handler) {
DoAddSchemeWithHandler(
diff --git a/url/url_util.h b/url/url_util.h
index f965c1dbd47781748d3091209d140a128ca7192f..b6d42ae86e4cf981ae00e96a0f296ada50c90d29 100644
--- a/url/url_util.h
+++ b/url/url_util.h
@@ -124,6 +124,11 @@ COMPONENT_EXPORT(URL) const std::vector<std::string>& GetEmptyDocumentSchemes();
COMPONENT_EXPORT(URL) void AddCodeCacheScheme(std::string_view new_scheme);
COMPONENT_EXPORT(URL) const std::vector<std::string>& GetCodeCacheSchemes();
+// Adds an application-defined scheme to the list of schemes on which Chrome
+// extensions can be used.
+COMPONENT_EXPORT(URL) void AddExtensionScheme(std::string_view new_scheme);
+COMPONENT_EXPORT(URL) const std::vector<std::string>& GetExtensionSchemes();
+
// Adds a scheme with a predefined default handler.
//
// This pair of strings must be normalized protocol handler parameters as

View File

@@ -313,7 +313,7 @@ index 18f283e625101318ee14b50e6e765dfd1c9a1a44..44a3a55974c9e4b9e715574075f25661
auto DrawAsSinglePath = [&]() {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 70a7e2a5203d3cdddbad7eecca28d65945522fed..35751435ebe8205a5c9d73bed0422ccbe61ab8b4 100644
index 640a50a1af53f0771da02de73d70a94c973aa624..f981c8dc7492872f296e01cd64692859671be5d5 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -214,6 +214,10 @@

View File

@@ -28,7 +28,7 @@ The patch should be removed in favor of either:
Upstream bug https://bugs.chromium.org/p/chromium/issues/detail?id=1081397.
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 5b79df01e0a5ee81919ebed7d689e430fe7fe305..b11808a69483f4cbcc56d90cc6161984df90c1e4 100644
index 7d101d40116bf743f940f32ba4c9b507aa9a235b..2aa1584fd451fb15ec6084fb0c19724e6c63e0e3 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -11666,6 +11666,11 @@ url::Origin NavigationRequest::GetOriginForURLLoaderFactoryUnchecked() {

View File

@@ -0,0 +1,95 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Keeley Hammond <khammond@slack-corp.com>
Date: Thu, 19 Mar 2026 00:34:37 -0700
Subject: fix: fire MENU_POPUP_START for dynamically created ARIA menus
When an ARIA menu element is dynamically created (e.g. via appendChild)
rather than being shown by toggling visibility, the AXMenuOpened event
was not fired. The OnIgnoredChanged path handles the visibility toggle
case, but OnAtomicUpdateFinished did not fire MENU_POPUP_START for
newly created menu nodes.
Previous attempts to fix this (crbug.com/1254875) were reverted because
they fired the event too eagerly in OnNodeCreated (before the tree was
fully formed) and without filtering, causing regressions with screen
readers on pages that misused role="menu".
This fix addresses both issues:
1. Fires MENU_POPUP_START in OnAtomicUpdateFinished (after the tree
update is complete) rather than in OnNodeCreated.
2. Only fires if the menu has at least one menuitem child, filtering
out false positives from misused role="menu" elements.
MENU_POPUP_END for deleted menus is already handled by
AXTreeManager::OnNodeWillBeDeleted, which fires the event directly
on the menu node before destruction.
The change is behind the DynamicMenuPopupEvents feature flag, disabled
by default, to allow stabilization before enabling by default. Enable
with --enable-features=DynamicMenuPopupEvents.
This patch can be removed when a CL containing the fix is accepted
into Chromium.
Bug: 40794596
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index 5e0d7a48b4a039db67b5cc6b7e86103739702b40..517fb5e9904f3907de177e172c76328910bb7333 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -4,6 +4,7 @@
#include "ui/accessibility/ax_event_generator.h"
+#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_event.h"
@@ -12,6 +13,12 @@
namespace ui {
+// Feature flag for firing MENU_POPUP_START for dynamically created ARIA menus.
+// Disabled by default to allow stabilization before enabling globally.
+BASE_FEATURE(kDynamicMenuPopupEvents,
+ "DynamicMenuPopupEvents",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
namespace {
bool HasEvent(const std::set<AXEventGenerator::EventParams>& node_events,
@@ -914,12 +921,31 @@ void AXEventGenerator::OnAtomicUpdateFinished(
/*new_value*/ true);
}
- if (IsAlert(change.node->GetRole()))
+ if (IsAlert(change.node->GetRole())) {
AddEvent(change.node, Event::ALERT);
- else if (change.node->data().IsActiveLiveRegionRoot())
+ } else if (change.node->data().IsActiveLiveRegionRoot()) {
AddEvent(change.node, Event::LIVE_REGION_CREATED);
- else if (change.node->data().IsContainedInActiveLiveRegion())
+ } else if (change.node->data().IsContainedInActiveLiveRegion()) {
FireLiveRegionEvents(change.node, /* is_removal */ false);
+ }
+
+ // Fire MENU_POPUP_START when a menu is dynamically created (e.g. via
+ // appendChild). The OnIgnoredChanged path handles menus that already exist
+ // in the DOM and are shown/hidden. This handles the case where the menu
+ // element itself is created on the fly.
+ // Only fire if the menu has at least one menuitem child, to avoid false
+ // positives from elements that misuse role="menu".
+ if (base::FeatureList::IsEnabled(kDynamicMenuPopupEvents) &&
+ change.node->GetRole() == ax::mojom::Role::kMenu &&
+ !change.node->IsInvisibleOrIgnored()) {
+ for (auto iter = change.node->UnignoredChildrenBegin();
+ iter != change.node->UnignoredChildrenEnd(); ++iter) {
+ if (IsMenuItem(iter->GetRole())) {
+ AddEvent(change.node, Event::MENU_POPUP_START);
+ break;
+ }
+ }
+ }
}
FireActiveDescendantEvents();

View File

@@ -0,0 +1,81 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <sattard@anthropic.com>
Date: Mon, 30 Mar 2026 03:05:40 -0700
Subject: fix: handle embedder windows shown after WebContentsViewCocoa attach
The occlusion checker assumes windows are shown before or at the same
time as a WebContentsViewCocoa is attached. Embedders like Electron
support creating a window hidden, attaching web contents, and showing
later. This breaks three assumptions:
1. updateWebContentsVisibility only checks -[NSWindow isOccluded], which
defaults to NO for never-shown windows, so viewDidMoveToWindow
incorrectly reports kVisible for hidden windows.
2. windowChangedOcclusionState: only responds to checker-originated
notifications, but setOccluded: early-returns when isOccluded doesn't
change. A hidden window's isOccluded is NO and stays NO after show(),
so no checker notification fires on show and the view never updates
to kVisible.
3. performOcclusionStateUpdates iterates orderedWindows and marks
not-yet-shown windows as occluded (their occlusionState lacks the
Visible bit), which stops painting before first show.
Fix by also checking occlusionState in updateWebContentsVisibility,
responding to macOS-originated notifications in
windowChangedOcclusionState:, and skipping non-visible windows in
performOcclusionStateUpdates.
This patch can be removed if the changes are upstreamed to Chromium.
diff --git a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
index a5570988c3721d9f6bd05c402a7658d3af6f2c2c..54aaffde30c14a27068f89b6de6123abd6ea0660 100644
--- a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
+++ b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
@@ -400,9 +400,11 @@ - (void)performOcclusionStateUpdates {
for (NSWindow* window in windowsFromFrontToBack) {
// The fullscreen transition causes spurious occlusion notifications.
// See https://crbug.com/1081229 . Also, ignore windows that don't have
- // web contentses.
+ // web contentses, and windows that aren't visible (embedders like
+ // Electron may create windows hidden with web contents already attached;
+ // marking these as occluded would stop painting before first show).
if (window == _windowReceivingFullscreenTransitionNotifications ||
- ![window containsWebContentsViewCocoa])
+ ![window isVisible] || ![window containsWebContentsViewCocoa])
continue;
[window setOccluded:[self isWindowOccluded:window
diff --git a/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm b/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
index 1ef2c9052262eccdbc40030746a858b7f30ac469..34708d45274f95b5f35cdefad98ad4a1c3c28e1c 100644
--- a/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
+++ b/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
@@ -477,7 +477,8 @@ - (void)updateWebContentsVisibility {
Visibility visibility = Visibility::kVisible;
if ([self isHiddenOrHasHiddenAncestor] || ![self window])
visibility = Visibility::kHidden;
- else if ([[self window] isOccluded])
+ else if ([[self window] isOccluded] ||
+ !([[self window] occlusionState] & NSWindowOcclusionStateVisible))
visibility = Visibility::kOccluded;
[self updateWebContentsVisibility:visibility];
@@ -521,11 +522,12 @@ - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
}
- (void)windowChangedOcclusionState:(NSNotification*)aNotification {
- // Only respond to occlusion notifications sent by the occlusion checker.
- NSDictionary* userInfo = [aNotification userInfo];
- NSString* occlusionCheckerKey = [WebContentsOcclusionCheckerMac className];
- if (userInfo[occlusionCheckerKey] != nil)
- [self updateWebContentsVisibility];
+ // Respond to occlusion notifications from both macOS and the occlusion
+ // checker. Embedders (e.g. Electron) may attach a WebContentsViewCocoa to
+ // a window that has not yet been shown; macOS will notify us when the
+ // window's occlusion state changes, but the occlusion checker will not
+ // because -[NSWindow isOccluded] remains NO before and after show.
+ [self updateWebContentsVisibility];
}
- (void)viewDidMoveToWindow {

View File

@@ -17,7 +17,7 @@ Revert "Reland "Port net::CookieCryptoDelegate to os_crypt async""
This reverts commit f01b115c7e21a09cc762f65bf7fd9c6ea9d9d0f8.
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 0862aa96c2a7b496338ac0593f84fcfa21f25572..aed5a316bd3d97df715f779273ae4c283cd29c92 100644
index 1a861ff7867f19935178c8368a9a720230fee026..b1ca947122f4ea715be18a0fd4e75b30fffc5a3c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -714,6 +714,8 @@ static_library("browser") {

View File

@@ -1189,7 +1189,7 @@ index a1068589ad844518038ee7bc15a3de9bc5cba525..1ff781c49f086ec8015c7d3c44567dbe
} // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 8575983261c7b57fc85097edb94a8e6f306974f9..aae50b6830450baf27f2834a8187540d7ff6eb35 100644
index d368b2481156bb79c6e74c8b09a828eb2fa2d44c..07cbf495717714d71d977a8820e08050c3062526 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -700,6 +700,7 @@ static_library("test_support") {
@@ -1217,7 +1217,7 @@ index 8575983261c7b57fc85097edb94a8e6f306974f9..aae50b6830450baf27f2834a8187540d
]
if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) {
@@ -3411,6 +3415,7 @@ test("content_unittests") {
@@ -3412,6 +3416,7 @@ test("content_unittests") {
"//ui/shell_dialogs",
"//ui/webui:test_support",
"//url",

View File

@@ -68,7 +68,7 @@ index f91857eb0b6ad385721b8224100de26dfdd7dd8d..45e8766fcb8d46d8edc3bf8d21d3f826
: PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3;
}
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a705b5d994 100644
index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f0c49c1e8 100644
--- a/chrome/browser/printing/print_view_manager_base.cc
+++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -80,6 +80,20 @@ namespace printing {
@@ -326,14 +326,23 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
ReleasePrinterQuery();
}
@@ -851,15 +886,24 @@ void PrintViewManagerBase::RemoveTestObserver(TestObserver& observer) {
@@ -851,15 +886,33 @@ void PrintViewManagerBase::RemoveTestObserver(TestObserver& observer) {
test_observers_.RemoveObserver(&observer);
}
+void PrintViewManagerBase::ShowInvalidPrinterSettingsError() {
+ if (!callback_.is_null()) {
+ printing_status_ = PrintStatus::kInvalid;
+ TerminatePrintJob(true);
+ if (print_job_) {
+ TerminatePrintJob(true);
+ } else {
+ // No print job was created, so TerminatePrintJob would bail out
+ // without ever calling ReleasePrintJob (where the callback is
+ // invoked). Fire the callback directly to avoid leaking it until
+ // WebContents destruction.
+ std::move(callback_).Run(false,
+ PrintReasonFromPrintStatus(printing_status_));
+ }
+ }
+}
+
@@ -351,7 +360,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
}
void PrintViewManagerBase::RenderFrameDeleted(
@@ -901,13 +945,14 @@ void PrintViewManagerBase::SystemDialogCancelled() {
@@ -901,13 +954,14 @@ void PrintViewManagerBase::SystemDialogCancelled() {
// System dialog was cancelled. Clean up the print job and notify the
// BackgroundPrintingManager.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -367,7 +376,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
}
void PrintViewManagerBase::OnDocDone(int job_id, PrintedDocument* document) {
@@ -921,18 +966,26 @@ void PrintViewManagerBase::OnJobDone() {
@@ -921,18 +975,26 @@ void PrintViewManagerBase::OnJobDone() {
// Printing is done, we don't need it anymore.
// print_job_->is_job_pending() may still be true, depending on the order
// of object registration.
@@ -396,7 +405,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
TerminatePrintJob(true);
}
@@ -942,7 +995,7 @@ bool PrintViewManagerBase::RenderAllMissingPagesNow() {
@@ -942,7 +1004,7 @@ bool PrintViewManagerBase::RenderAllMissingPagesNow() {
// Is the document already complete?
if (print_job_->document() && print_job_->document()->IsComplete()) {
@@ -405,7 +414,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
return true;
}
@@ -995,7 +1048,10 @@ bool PrintViewManagerBase::SetupNewPrintJob(
@@ -995,7 +1057,10 @@ bool PrintViewManagerBase::SetupNewPrintJob(
// Disconnect the current `print_job_`.
auto weak_this = weak_ptr_factory_.GetWeakPtr();
@@ -417,7 +426,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
if (!weak_this)
return false;
@@ -1015,7 +1071,7 @@ bool PrintViewManagerBase::SetupNewPrintJob(
@@ -1015,7 +1080,7 @@ bool PrintViewManagerBase::SetupNewPrintJob(
#endif
print_job_->AddObserver(*this);
@@ -426,7 +435,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
return true;
}
@@ -1073,7 +1129,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
@@ -1073,7 +1138,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
// Ensure that any residual registration of printing client is released.
// This might be necessary in some abnormal cases, such as the associated
// render process having terminated.
@@ -435,7 +444,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
if (!analyzing_content_) {
UnregisterSystemPrintClient();
}
@@ -1083,6 +1139,11 @@ void PrintViewManagerBase::ReleasePrintJob() {
@@ -1083,6 +1148,11 @@ void PrintViewManagerBase::ReleasePrintJob() {
}
#endif
@@ -447,7 +456,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
if (!print_job_)
return;
@@ -1090,7 +1151,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
@@ -1090,7 +1160,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
// printing_rfh_ should only ever point to a RenderFrameHost with a live
// RenderFrame.
DCHECK(rfh->IsRenderFrameLive());
@@ -456,7 +465,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
}
print_job_->RemoveObserver(*this);
@@ -1132,7 +1193,7 @@ bool PrintViewManagerBase::RunInnerMessageLoop() {
@@ -1132,7 +1202,7 @@ bool PrintViewManagerBase::RunInnerMessageLoop() {
}
bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
@@ -465,7 +474,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
return true;
if (!cookie) {
@@ -1155,7 +1216,7 @@ bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
@@ -1155,7 +1225,7 @@ bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
return false;
}
@@ -474,7 +483,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
// Don't start printing if enterprise checks are being performed to check if
// printing is allowed, or if content analysis is going to take place right
// before starting `print_job_`.
@@ -1286,6 +1347,8 @@ void PrintViewManagerBase::CompleteScriptedPrint(
@@ -1286,6 +1356,8 @@ void PrintViewManagerBase::CompleteScriptedPrint(
auto callback_wrapper = base::BindOnce(
&PrintViewManagerBase::ScriptedPrintReply, weak_ptr_factory_.GetWeakPtr(),
std::move(callback), render_process_host->GetDeprecatedID());
@@ -483,7 +492,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..eb76ee91743236d05c3a70a54d5345a7
std::unique_ptr<PrinterQuery> printer_query =
queue()->PopPrinterQuery(params->cookie);
if (!printer_query)
@@ -1296,10 +1359,10 @@ void PrintViewManagerBase::CompleteScriptedPrint(
@@ -1296,10 +1368,10 @@ void PrintViewManagerBase::CompleteScriptedPrint(
params->expected_pages_count, params->has_selection, params->margin_type,
params->is_scripted, !render_process_host->IsPdf(),
base::BindOnce(&OnDidScriptedPrint, queue_, std::move(printer_query),
@@ -666,7 +675,7 @@ index ac2f719be566020d9f41364560c12e6d6d0fe3d8..16d758a6936f66148a196761cfb875f6
PrintingFailed(int32 cookie, PrintFailureReason reason);
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 60b5e83a8bc1ed07970be4cdfdc19962698bd754..1320f3b10b07b2cee90f39f406604176c7575796 100644
index 60b5e83a8bc1ed07970be4cdfdc19962698bd754..dd83b6cfb6e3f916e60f50402014cd931a4d8850 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -54,6 +54,7 @@
@@ -790,7 +799,7 @@ index 60b5e83a8bc1ed07970be4cdfdc19962698bd754..1320f3b10b07b2cee90f39f406604176
// Check if `this` is still valid.
if (!self)
return;
@@ -2394,29 +2415,43 @@ void PrintRenderFrameHelper::IPCProcessed() {
@@ -2394,29 +2415,47 @@ void PrintRenderFrameHelper::IPCProcessed() {
}
bool PrintRenderFrameHelper::InitPrintSettings(blink::WebLocalFrame* frame,
@@ -826,8 +835,12 @@ index 60b5e83a8bc1ed07970be4cdfdc19962698bd754..1320f3b10b07b2cee90f39f406604176
- : mojom::PrintScalingOption::kSourceSize;
- RecordDebugEvent(settings.params->printed_doc_type ==
+ bool silent = new_settings.FindBool("silent").value_or(false);
+ if (silent) {
+ settings->params->print_scaling_option = mojom::PrintScalingOption::kFitToPrintableArea;
+ int margins_type = new_settings.FindInt(kSettingMarginsType)
+ .value_or(static_cast<int>(mojom::MarginType::kDefaultMargins));
+ if (silent &&
+ margins_type == static_cast<int>(mojom::MarginType::kDefaultMargins)) {
+ settings->params->print_scaling_option =
+ mojom::PrintScalingOption::kFitToPrintableArea;
+ } else {
+ settings->params->print_scaling_option =
+ center_on_paper ? mojom::PrintScalingOption::kCenterShrinkToFitPaper

View File

@@ -1,279 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: David Sanders <dsanders11@ucsbalum.com>
Date: Wed, 8 Jan 2025 23:53:27 -0800
Subject: Revert "Code Health: Clean up stale MacWebContentsOcclusion"
Chrome has removed this WebContentsOcclusion feature flag upstream,
which is now causing our visibility tests to break. This patch
restores the legacy occlusion behavior to ensure the roll can continue
while we debug the issue.
This patch can be removed when the root cause because the visibility
specs failing on MacOS only is debugged and fixed. It should be removed
before Electron 35's stable date.
Refs: https://chromium-review.googlesource.com/c/chromium/src/+/6078344
This partially (leaves the removal of the feature flag) reverts
ef865130abd5539e7bce12308659b19980368f12.
diff --git a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
index 04c7635cc093d9d676869383670a8f2199f14ac6..52d76e804e47ab0b56016d26262d6d67cbc00875 100644
--- a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
+++ b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
@@ -11,6 +11,8 @@
#include "base/metrics/field_trial_params.h"
#import "content/app_shim_remote_cocoa/web_contents_view_cocoa.h"
+extern CONTENT_EXPORT const base::FeatureParam<bool>
+ kEnhancedWindowOcclusionDetection;
extern CONTENT_EXPORT const base::FeatureParam<bool>
kDisplaySleepAndAppHideDetection;
diff --git a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
index a5570988c3721d9f6bd05c402a7658d3af6f2c2c..0a2dba6aa2d48bc39d2a55c8b4d6606744c10ca7 100644
--- a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
+++ b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
@@ -14,9 +14,16 @@
#include "base/mac/mac_util.h"
#include "base/metrics/field_trial_params.h"
#include "base/no_destructor.h"
+#include "content/common/features.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
+using features::kMacWebContentsOcclusion;
+
+// Experiment features.
+const base::FeatureParam<bool> kEnhancedWindowOcclusionDetection{
+ &kMacWebContentsOcclusion, "EnhancedWindowOcclusionDetection", false};
+
namespace {
NSString* const kWindowDidChangePositionInWindowList =
@@ -125,7 +132,8 @@ - (void)dealloc {
- (BOOL)isManualOcclusionDetectionEnabled {
return [WebContentsOcclusionCheckerMac
- manualOcclusionDetectionSupportedForCurrentMacOSVersion];
+ manualOcclusionDetectionSupportedForCurrentMacOSVersion] &&
+ kEnhancedWindowOcclusionDetection.Get();
}
// Alternative implementation of orderWindow:relativeTo:. Replaces
diff --git a/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm b/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
index 1ef2c9052262eccdbc40030746a858b7f30ac469..c7101b0d71826b05f61bfe0e74429d922769e792 100644
--- a/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
+++ b/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
@@ -15,6 +15,7 @@
#import "content/app_shim_remote_cocoa/web_drag_source_mac.h"
#import "content/browser/web_contents/web_contents_view_mac.h"
#import "content/browser/web_contents/web_drag_dest_mac.h"
+#include "content/common/features.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "ui/base/clipboard/clipboard_constants.h"
@@ -27,6 +28,7 @@
#include "ui/resources/grit/ui_resources.h"
using content::DropData;
+using features::kMacWebContentsOcclusion;
using remote_cocoa::mojom::DraggingInfo;
using remote_cocoa::mojom::SelectionDirection;
@@ -122,12 +124,15 @@ @implementation WebContentsViewCocoa {
WebDragSource* __strong _dragSource;
NSDragOperation _dragOperation;
+ BOOL _inFullScreenTransition;
BOOL _willSetWebContentsOccludedAfterDelay;
}
+ (void)initialize {
- // Create the WebContentsOcclusionCheckerMac shared instance.
- [WebContentsOcclusionCheckerMac sharedInstance];
+ if (base::FeatureList::IsEnabled(kMacWebContentsOcclusion)) {
+ // Create the WebContentsOcclusionCheckerMac shared instance.
+ [WebContentsOcclusionCheckerMac sharedInstance];
+ }
}
- (instancetype)initWithViewsHostableView:(ui::ViewsHostableView*)v {
@@ -438,6 +443,7 @@ - (void)updateWebContentsVisibility:
(remote_cocoa::mojom::Visibility)visibility {
using remote_cocoa::mojom::Visibility;
+ DCHECK(base::FeatureList::IsEnabled(kMacWebContentsOcclusion));
if (!_host)
return;
@@ -483,6 +489,20 @@ - (void)updateWebContentsVisibility {
[self updateWebContentsVisibility:visibility];
}
+- (void)legacyUpdateWebContentsVisibility {
+ using remote_cocoa::mojom::Visibility;
+ if (!_host || _inFullScreenTransition)
+ return;
+ Visibility visibility = Visibility::kVisible;
+ if ([self isHiddenOrHasHiddenAncestor] || ![self window])
+ visibility = Visibility::kHidden;
+ else if ([[self window] occlusionState] & NSWindowOcclusionStateVisible)
+ visibility = Visibility::kVisible;
+ else
+ visibility = Visibility::kOccluded;
+ _host->OnWindowVisibilityChanged(visibility);
+}
+
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
// Subviews do not participate in auto layout unless the the size this view
// changes. This allows RenderWidgetHostViewMac::SetBounds(..) to select a
@@ -505,11 +525,39 @@ - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
NSWindow* oldWindow = [self window];
+ if (base::FeatureList::IsEnabled(kMacWebContentsOcclusion)) {
+ if (oldWindow) {
+ [notificationCenter
+ removeObserver:self
+ name:NSWindowDidChangeOcclusionStateNotification
+ object:oldWindow];
+ }
+
+ if (newWindow) {
+ [notificationCenter
+ addObserver:self
+ selector:@selector(windowChangedOcclusionState:)
+ name:NSWindowDidChangeOcclusionStateNotification
+ object:newWindow];
+ }
+
+ return;
+ }
+
+ _inFullScreenTransition = NO;
if (oldWindow) {
- [notificationCenter
- removeObserver:self
- name:NSWindowDidChangeOcclusionStateNotification
- object:oldWindow];
+ NSArray* notificationsToRemove = @[
+ NSWindowDidChangeOcclusionStateNotification,
+ NSWindowWillEnterFullScreenNotification,
+ NSWindowDidEnterFullScreenNotification,
+ NSWindowWillExitFullScreenNotification,
+ NSWindowDidExitFullScreenNotification
+ ];
+ for (NSString* notificationName in notificationsToRemove) {
+ [notificationCenter removeObserver:self
+ name:notificationName
+ object:oldWindow];
+ }
}
if (newWindow) {
@@ -517,26 +565,66 @@ - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
selector:@selector(windowChangedOcclusionState:)
name:NSWindowDidChangeOcclusionStateNotification
object:newWindow];
+ // The fullscreen transition causes spurious occlusion notifications.
+ // See https://crbug.com/1081229
+ [notificationCenter addObserver:self
+ selector:@selector(fullscreenTransitionStarted:)
+ name:NSWindowWillEnterFullScreenNotification
+ object:newWindow];
+ [notificationCenter addObserver:self
+ selector:@selector(fullscreenTransitionComplete:)
+ name:NSWindowDidEnterFullScreenNotification
+ object:newWindow];
+ [notificationCenter addObserver:self
+ selector:@selector(fullscreenTransitionStarted:)
+ name:NSWindowWillExitFullScreenNotification
+ object:newWindow];
+ [notificationCenter addObserver:self
+ selector:@selector(fullscreenTransitionComplete:)
+ name:NSWindowDidExitFullScreenNotification
+ object:newWindow];
}
}
- (void)windowChangedOcclusionState:(NSNotification*)aNotification {
- // Only respond to occlusion notifications sent by the occlusion checker.
- NSDictionary* userInfo = [aNotification userInfo];
- NSString* occlusionCheckerKey = [WebContentsOcclusionCheckerMac className];
- if (userInfo[occlusionCheckerKey] != nil)
- [self updateWebContentsVisibility];
+ if (!base::FeatureList::IsEnabled(kMacWebContentsOcclusion)) {
+ [self legacyUpdateWebContentsVisibility];
+ return;
+ }
+}
+
+- (void)fullscreenTransitionStarted:(NSNotification*)notification {
+ _inFullScreenTransition = YES;
+}
+
+- (void)fullscreenTransitionComplete:(NSNotification*)notification {
+ _inFullScreenTransition = NO;
}
- (void)viewDidMoveToWindow {
+ if (!base::FeatureList::IsEnabled(kMacWebContentsOcclusion)) {
+ [self legacyUpdateWebContentsVisibility];
+ return;
+ }
+
[self updateWebContentsVisibility];
}
- (void)viewDidHide {
+ if (!base::FeatureList::IsEnabled(kMacWebContentsOcclusion)) {
+ [self legacyUpdateWebContentsVisibility];
+ return;
+ }
+
[self updateWebContentsVisibility];
}
- (void)viewDidUnhide {
+ if (!base::FeatureList::IsEnabled(kMacWebContentsOcclusion)) {
+ [self legacyUpdateWebContentsVisibility];
+ return;
+ }
+
[self updateWebContentsVisibility];
}
diff --git a/content/common/features.cc b/content/common/features.cc
index ee2fa2cd950a6c5cddc5904ee7a4656a18b9d73d..10a1e1e14777b61f6c42266f6f085bd47b759efd 100644
--- a/content/common/features.cc
+++ b/content/common/features.cc
@@ -364,6 +364,14 @@ BASE_FEATURE(kInterestGroupUpdateIfOlderThan, base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kIOSurfaceCapturer, base::FEATURE_ENABLED_BY_DEFAULT);
#endif
+// Feature that controls whether WebContentsOcclusionChecker should handle
+// occlusion notifications.
+#if BUILDFLAG(IS_MAC)
+BASE_FEATURE(kMacWebContentsOcclusion,
+ "MacWebContentsOcclusion",
+ base::FEATURE_ENABLED_BY_DEFAULT);
+#endif
+
// When enabled, child process will not terminate itself when IPC is reset.
BASE_FEATURE(kKeepChildProcessAfterIPCReset, base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/content/common/features.h b/content/common/features.h
index 24443780a7196b40096f44826232f77eaab68ffa..9164f2cf39542525ef2c30f572c7d0b557473f5d 100644
--- a/content/common/features.h
+++ b/content/common/features.h
@@ -140,6 +140,9 @@ CONTENT_EXPORT BASE_DECLARE_FEATURE(kInterestGroupUpdateIfOlderThan);
#if BUILDFLAG(IS_MAC)
CONTENT_EXPORT BASE_DECLARE_FEATURE(kIOSurfaceCapturer);
#endif
+#if BUILDFLAG(IS_MAC)
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kMacWebContentsOcclusion);
+#endif
CONTENT_EXPORT BASE_DECLARE_FEATURE(kKeepChildProcessAfterIPCReset);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kLocalNetworkAccessForWorkers);

View File

@@ -10,10 +10,10 @@ on Windows. We should refactor our code so that this patch isn't
necessary.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index eecdbe8a279ef1a7d9aed4f5496e871d54092e0f..16ff3ffbe8300534cef76f857284ef92ad0a88f6 100644
index d17637a54208450504d071a3f10c20668cfbe76d..f3ffc975d794f356d9a83837fd977e758b726501 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -27059,6 +27059,21 @@
@@ -27095,6 +27095,21 @@
]
}
],

View File

@@ -15,7 +15,7 @@ Note that we also need to manually update embedder's
`api::WebContents::IsFullscreenForTabOrPending` value.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 8d7be769a6c76650ae999338578215dcd324c199..3e8021289c00ec6b15457b17173dfed386eac2fe 100644
index a87bd09d7a12c5f003488792843cd1807ee1e30f..b38240fd422163f09bfb8d4b40213a1940a72acd 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -9097,6 +9097,17 @@ void RenderFrameHostImpl::EnterFullscreen(

View File

@@ -1 +1,3 @@
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
fix_prefer_browser_runtime_over_node_in_hostruntime_detection.patch
feat_allow_enabling_extension_panels_on_custom_protocols.patch

View File

@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Wed, 25 Feb 2026 16:23:07 +0100
Subject: feat: allow enabling extension panels on custom protocols
This allows us to show Chrome extension panels on pages served over
custom protocols.
diff --git a/front_end/core/root/Runtime.ts b/front_end/core/root/Runtime.ts
index d2c063a85e0fcd1e658be6709dd55dbcda5601a6..0dd32e1468296484976034d47ebb8b8632fa6835 100644
--- a/front_end/core/root/Runtime.ts
+++ b/front_end/core/root/Runtime.ts
@@ -637,6 +637,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
* or guest mode, rather than a "normal" profile.
*/
isOffTheRecord: boolean,
+ devToolsExtensionSchemes: readonly string[],
devToolsEnableOriginBoundCookies: HostConfigEnableOriginBoundCookies,
devToolsAnimationStylesInStylesTab: HostConfigAnimationStylesInStylesTab,
thirdPartyCookieControls: HostConfigThirdPartyCookieControls,
diff --git a/front_end/panels/common/ExtensionServer.ts b/front_end/panels/common/ExtensionServer.ts
index 0a5ec620b135b128013d6ddbb5299f9a5813f122..0f8c04bb5c02c9f1ee3af785a60a2450c47fff58 100644
--- a/front_end/panels/common/ExtensionServer.ts
+++ b/front_end/panels/common/ExtensionServer.ts
@@ -12,6 +12,7 @@ import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Platform from '../../core/platform/platform.js';
import * as _ProtocolClient from '../../core/protocol_client/protocol_client.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
+import * as Root from '../../core/root/root.js';
import * as SDK from '../../core/sdk/sdk.js';
import type * as Protocol from '../../generated/protocol.js';
import * as Bindings from '../../models/bindings/bindings.js';
@@ -1607,7 +1608,8 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
return false;
}
- if (!kPermittedSchemes.includes(parsedURL.protocol)) {
+ if (!kPermittedSchemes.includes(parsedURL.protocol) &&
+ !Root.Runtime.hostConfig.devToolsExtensionSchemes?.includes(parsedURL.protocol)) {
return false;
}

View File

@@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Thu, 12 Mar 2026 17:03:29 +0100
Subject: fix: prefer browser runtime over node in HostRuntime detection
In Electron, the `process` global is available in renderer processes,
including the DevTools renderer. This causes the IS_NODE check to pass,
leading DevTools to attempt importing the Node.js platform runtime
(which uses `node:worker_threads`). However, DevTools Web Workers
running under the `devtools://` protocol don't have access to Node.js
built-in modules, resulting in a failed dynamic import.
Fix by checking IS_BROWSER first, since DevTools always runs in a
browser-like environment. The Node.js runtime is only needed when
DevTools runs under pure Node.js (e.g., CLI tooling or testing).
diff --git a/front_end/core/platform/HostRuntime.ts b/front_end/core/platform/HostRuntime.ts
index 91adba7c966a9c4c0e5315d2cfee07f8f622b731..16822b8d4ea74a4ffd6870e5e95948d75918f5d2 100644
--- a/front_end/core/platform/HostRuntime.ts
+++ b/front_end/core/platform/HostRuntime.ts
@@ -14,12 +14,12 @@ export const IS_BROWSER =
typeof window !== 'undefined' || (typeof self !== 'undefined' && typeof self.postMessage === 'function');
export const HOST_RUNTIME = await (async(): Promise<Api.HostRuntime.HostRuntime> => {
- if (IS_NODE) {
- return (await import('./node/node.js')).HostRuntime.HOST_RUNTIME;
- }
if (IS_BROWSER) {
return (await import('./browser/browser.js')).HostRuntime.HOST_RUNTIME;
}
+ if (IS_NODE) {
+ return (await import('./node/node.js')).HostRuntime.HOST_RUNTIME;
+ }
throw new Error('Unknown runtime!');
})();

View File

@@ -17,7 +17,7 @@ Upstreams:
- https://github.com/nodejs/node/pull/39136
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
index 461819ce0fa732048e4365c40a86ef55d984c35f..fa55c980a9c4f373723a867fd41276d67b0b9413 100644
index 461819ce0fa732048e4365c40a86ef55d984c35f..f1c85e94cf526d0255f47c003664680d26413ec3 100644
--- a/deps/ncrypto/ncrypto.cc
+++ b/deps/ncrypto/ncrypto.cc
@@ -11,6 +11,7 @@
@@ -28,38 +28,6 @@ index 461819ce0fa732048e4365c40a86ef55d984c35f..fa55c980a9c4f373723a867fd41276d6
#if OPENSSL_VERSION_MAJOR >= 3
#include <openssl/core_names.h>
#include <openssl/params.h>
@@ -1130,7 +1131,9 @@ int64_t X509View::getValidToTime() const {
return tp;
#else
struct tm tp;
- ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
+#ifndef OPENSSL_IS_BORINGSSL
+ ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
+#endif
return PortableTimeGM(&tp);
#endif
}
@@ -1142,7 +1145,9 @@ int64_t X509View::getValidFromTime() const {
return tp;
#else
struct tm tp;
+#ifndef OPENSSL_IS_BORINGSSL
ASN1_TIME_to_tm(X509_get0_notBefore(cert_), &tp);
+#endif
return PortableTimeGM(&tp);
#endif
}
@@ -2886,10 +2891,6 @@ std::optional<uint32_t> SSLPointer::verifyPeerCertificate() const {
const char* SSLPointer::getClientHelloAlpn() const {
if (ssl_ == nullptr) return {};
#ifndef OPENSSL_IS_BORINGSSL
- const unsigned char* buf;
- size_t len;
- size_t rem;
-
if (!SSL_client_hello_get0_ext(
get(),
TLSEXT_TYPE_application_layer_protocol_negotiation,
@@ -3090,9 +3091,11 @@ const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm);
const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap);
const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap);

View File

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

View File

@@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sam Attard <sattard@anthropic.com>
Date: Sun, 22 Mar 2026 10:51:26 +0000
Subject: build: warn instead of abort on builtin PGO profile mismatch
Electron sets v8_enable_javascript_promise_hooks = true to support
Node.js async_hooks (see node/src/env.cc SetPromiseHooks usage:
https://github.com/nodejs/node/blob/abff716eaccd0c4f4949d1315cb057a45979649d/src/env.cc#L223-L236).
This flag adds conditional branches to builtins-microtask-queue-gen.cc
and promise-misc.tq, changing the control-flow graph hash of several
Promise/async builtins. This invalidates V8's pre-generated PGO profile
for those builtins (built with Chrome defaults where the flag is off).
Rather than disabling builtins PGO entirely, warn and skip mismatched
builtins so all other builtins still benefit from PGO.
diff --git a/BUILD.gn b/BUILD.gn
index 15de2179a0e5ce50d5c659a9d15a920c50124c3e..9fb3a69450bdcab42c2571e8b1f57c4f3c283d9a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2764,9 +2764,11 @@ template("run_mksnapshot") {
"--turbo-profiling-input",
rebase_path(v8_builtins_profiling_log_file, root_build_dir),
- # Replace this with --warn-about-builtin-profile-data to see the full
- # list of builtins with incompatible profiles.
- "--abort-on-bad-builtin-profile-data",
+ # Electron: Use warn instead of abort so that builtins whose control
+ # flow is changed by Electron's build flags (e.g. RunMicrotasks via
+ # v8_enable_javascript_promise_hooks) are skipped rather than failing
+ # the build. All other builtins still receive PGO.
+ "--warn-about-builtin-profile-data",
]
if (!v8_enable_builtins_profiling && v8_enable_builtins_reordering) {

View File

@@ -32,7 +32,8 @@ async function main () {
}));
const hitRate = stats.CacheHit / (stats.Remote + stats.CacheHit + stats.LocalFallback);
console.log(`Effective cache hit rate: ${(hitRate * 100).toFixed(2)}%`);
const messagePrefix = process.env.GITHUB_ACTIONS ? '::notice title=Build Stats::' : '';
console.log(`${messagePrefix}Effective cache hit rate: ${(hitRate * 100).toFixed(2)}%`);
if (uploadStats) {
if (!process.env.DD_API_KEY) {

View File

@@ -6,6 +6,7 @@ Everything here should be project agnostic: it shouldn't rely on project's
structure, or make assumptions about the passed arguments or calls' outcomes.
"""
import hashlib
import io
import os
import posixpath
@@ -18,7 +19,14 @@ sys.path.append(SCRIPT_DIR)
from patches import PATCH_FILENAME_PREFIX, is_patch_location_line
UPSTREAM_HEAD='refs/patches/upstream-head'
# In gclient-new-workdir worktrees, .git/refs is symlinked back to the source
# checkout, so a single fixed ref name would be shared (and clobbered) across
# worktrees. Derive a per-checkout suffix from this script's absolute path so
# each worktree records its own upstream head in the shared refs directory.
_LEGACY_UPSTREAM_HEAD = 'refs/patches/upstream-head'
UPSTREAM_HEAD = (
_LEGACY_UPSTREAM_HEAD + '-' + hashlib.md5(SCRIPT_DIR.encode()).hexdigest()[:8]
)
def is_repo_root(path):
path_exists = os.path.exists(path)
@@ -83,6 +91,8 @@ def import_patches(repo, ref=UPSTREAM_HEAD, **kwargs):
"""same as am(), but we save the upstream HEAD so we can refer to it when we
later export patches"""
update_ref(repo=repo, ref=ref, newvalue='HEAD')
if ref != _LEGACY_UPSTREAM_HEAD:
update_ref(repo=repo, ref=_LEGACY_UPSTREAM_HEAD, newvalue='HEAD')
am(repo=repo, **kwargs)
@@ -102,19 +112,21 @@ def get_commit_count(repo, commit_range):
def guess_base_commit(repo, ref):
"""Guess which commit the patches might be based on"""
try:
upstream_head = get_commit_for_ref(repo, ref)
num_commits = get_commit_count(repo, upstream_head + '..')
return [upstream_head, num_commits]
except subprocess.CalledProcessError:
args = [
'git',
'-C',
repo,
'describe',
'--tags',
]
return subprocess.check_output(args).decode('utf-8').rsplit('-', 2)[0:2]
for candidate in (ref, _LEGACY_UPSTREAM_HEAD):
try:
upstream_head = get_commit_for_ref(repo, candidate)
num_commits = get_commit_count(repo, upstream_head + '..')
return [upstream_head, num_commits]
except subprocess.CalledProcessError:
continue
args = [
'git',
'-C',
repo,
'describe',
'--tags',
]
return subprocess.check_output(args).decode('utf-8').rsplit('-', 2)[0:2]
def format_patch(repo, since):

View File

@@ -1,21 +0,0 @@
import os
import subprocess
import sys
def npx(*npx_args):
npx_env = os.environ.copy()
npx_env['npm_config_yes'] = 'true'
call_args = [__get_executable_name()] + list(npx_args)
subprocess.check_call(call_args, env=npx_env)
def __get_executable_name():
executable = 'npx'
if sys.platform == 'win32':
executable += '.cmd'
return executable
if __name__ == '__main__':
npx(*sys.argv[1:])

View File

@@ -6,7 +6,7 @@ const path = require('node:path');
const BASE = path.resolve(__dirname, '../..');
const NAN_DIR = path.resolve(BASE, 'third_party', 'nan');
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
const NODE_GYP_BIN = path.join(NAN_DIR, 'node_modules', 'node-gyp', 'bin', 'node-gyp.js');
const utils = require('./lib/utils');
const { YARN_SCRIPT_PATH } = require('./yarn');
@@ -19,14 +19,6 @@ const args = minimist(process.argv.slice(2), {
string: ['only']
});
const getNodeGypVersion = () => {
const nanPackageJSONPath = path.join(NAN_DIR, 'package.json');
const nanPackageJSON = JSON.parse(fs.readFileSync(nanPackageJSONPath, 'utf8'));
const { devDependencies } = nanPackageJSON;
const nodeGypVersion = devDependencies['node-gyp'];
return nodeGypVersion || 'latest';
};
async function main () {
const outDir = utils.getOutDir({ shouldLog: true });
const nodeDir = path.resolve(BASE, 'out', outDir, 'gen', 'node_headers');
@@ -34,8 +26,7 @@ async function main () {
npm_config_msvs_version: '2022',
...process.env,
npm_config_nodedir: nodeDir,
npm_config_arch: process.env.NPM_CONFIG_ARCH,
npm_config_yes: 'true'
npm_config_arch: process.env.NPM_CONFIG_ARCH
};
const clangDir = path.resolve(BASE, 'third_party', 'llvm-build', 'Release+Asserts', 'bin');
@@ -105,30 +96,26 @@ async function main () {
env.LDFLAGS = ldflags;
}
const nodeGypVersion = getNodeGypVersion();
const { status: buildStatus, signal } = cp.spawnSync(NPX_CMD, [`node-gyp@${nodeGypVersion}`, 'rebuild', '--verbose', '--directory', 'test', '-j', 'max'], {
const { status: installStatus, signal: installSignal } = cp.spawnSync(process.execPath, [YARN_SCRIPT_PATH, 'install'], {
env,
cwd: NAN_DIR,
stdio: 'inherit',
shell: process.platform === 'win32'
stdio: 'inherit'
});
if (installStatus !== 0 || installSignal != null) {
console.error('Failed to install nan node_modules');
return process.exit(installStatus !== 0 ? installStatus : installSignal);
}
const { status: buildStatus, signal } = cp.spawnSync(process.execPath, [NODE_GYP_BIN, 'rebuild', '--verbose', '--directory', 'test', '-j', 'max'], {
env,
cwd: NAN_DIR,
stdio: 'inherit'
});
if (buildStatus !== 0 || signal != null) {
console.error('Failed to build nan test modules');
return process.exit(buildStatus !== 0 ? buildStatus : signal);
}
const { status: installStatus, signal: installSignal } = cp.spawnSync(process.execPath, [YARN_SCRIPT_PATH, 'install'], {
env,
cwd: NAN_DIR,
stdio: 'inherit',
shell: process.platform === 'win32'
});
if (installStatus !== 0 || installSignal != null) {
console.error('Failed to install nan node_modules');
return process.exit(installStatus !== 0 ? installStatus : installSignal);
}
const onlyTests = args.only?.split(',');
const DISABLED_TESTS = new Set([

View File

@@ -14,6 +14,7 @@ const args = minimist(process.argv.slice(2), {
const BASE = path.resolve(__dirname, '../..');
const ROOT_PACKAGE_JSON = path.resolve(BASE, 'package.json');
const NODE_DIR = path.resolve(BASE, 'third_party', 'electron_node');
const JUNIT_DIR = args.jUnitDir ? path.resolve(args.jUnitDir) : null;
const TAP_FILE_NAME = 'test.tap';
@@ -38,6 +39,18 @@ const defaultOptions = [
'-J'
];
// The root package.json is ESM, which breaks the test runner.
// Temporarily change it to CommonJS while running the tests, then
// change it back when done.
const resetPackageJson = ({ useESM }) => {
// This won't always exist in CI.
if (!fs.existsSync(ROOT_PACKAGE_JSON)) { return; }
const packageJson = JSON.parse(fs.readFileSync(ROOT_PACKAGE_JSON, 'utf-8'));
packageJson.type = useESM ? 'module' : 'commonjs';
fs.writeFileSync(ROOT_PACKAGE_JSON, JSON.stringify(packageJson, null, 2) + '\n');
};
const getCustomOptions = () => {
let customOptions = ['tools/test.py'];
@@ -79,6 +92,8 @@ async function main () {
const options = args.default ? defaultOptions : getCustomOptions();
resetPackageJson({ useESM: false });
const testChild = cp.spawn('python3', options, {
env: {
...process.env,
@@ -88,7 +103,10 @@ async function main () {
cwd: NODE_DIR,
stdio: 'inherit'
});
testChild.on('exit', (testCode) => {
resetPackageJson({ useESM: true });
if (JUNIT_DIR) {
fs.mkdirSync(JUNIT_DIR);
const converterStream = require('tap-xunit')();

View File

@@ -212,10 +212,15 @@ new Promise<string>((resolve, reject) => {
});
})
.then((tarballPath) => {
// TODO: Remove NPX
const existingVersionJSON = childProcess.execSync(`npx npm@7 view ${rootPackageJson.name}@${currentElectronVersion} --json`).toString('utf-8');
// It's possible this is a re-run and we already have published the package, if not we just publish like normal
if (!existingVersionJSON) {
let versionAlreadyPublished = false;
try {
childProcess.execSync(`npm view ${rootPackageJson.name}@${currentElectronVersion} --json`, { stdio: 'pipe' });
versionAlreadyPublished = true;
} catch (e: any) {
if (!e.stdout?.toString().includes('E404')) throw e;
}
if (!versionAlreadyPublished) {
childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag} --otp=${process.env.ELECTRON_NPM_OTP}`);
}
})

View File

@@ -31,9 +31,9 @@ PDB_LIST = [
PDB_LIST += glob.glob(os.path.join(RELEASE_DIR, '*.dll.pdb'))
NPX_CMD = "npx"
SENTRY_CLI = os.path.join(ELECTRON_DIR, 'node_modules', '.bin', 'sentry-cli')
if sys.platform == "win32":
NPX_CMD += ".cmd"
SENTRY_CLI += ".cmd"
def main():
@@ -48,11 +48,8 @@ def main():
for symbol_file in files:
print("Generating Sentry src bundle for: " + symbol_file)
npx_env = os.environ.copy()
npx_env['npm_config_yes'] = 'true'
subprocess.check_output([
NPX_CMD, '@sentry/cli@1.62.0', 'difutil', 'bundle-sources',
symbol_file], env=npx_env)
SENTRY_CLI, 'difutil', 'bundle-sources', symbol_file])
files += glob.glob(SYMBOLS_DIR + '/*/*/*.src.zip')

View File

@@ -317,6 +317,12 @@ void BaseWindow::OnWindowSheetEnd() {
Emit("sheet-end");
}
void BaseWindow::OnWindowIsKeyChanged(bool is_key) {
#if BUILDFLAG(IS_MAC)
window()->SetActive(is_key);
#endif
}
void BaseWindow::OnWindowEnterHtmlFullScreen() {
Emit("enter-html-full-screen");
}

View File

@@ -85,6 +85,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
void OnWindowRotateGesture(float rotation) override;
void OnWindowSheetBegin() override;
void OnWindowSheetEnd() override;
void OnWindowIsKeyChanged(bool is_key) override;
void OnWindowEnterFullScreen() override;
void OnWindowLeaveFullScreen() override;
void OnWindowEnterHtmlFullScreen() override;

View File

@@ -280,16 +280,22 @@ v8::Local<v8::Value> BrowserWindow::GetWebContents(v8::Isolate* isolate) {
}
void BrowserWindow::OnWindowShow() {
if (!web_contents_shown_) {
web_contents()->WasShown();
web_contents_shown_ = true;
}
BaseWindow::OnWindowShow();
}
void BrowserWindow::OnWindowHide() {
web_contents()->WasOccluded();
web_contents_shown_ = false;
BaseWindow::OnWindowHide();
}
void BrowserWindow::Show() {
web_contents()->WasShown();
web_contents_shown_ = true;
BaseWindow::Show();
}
@@ -298,6 +304,7 @@ void BrowserWindow::ShowInactive() {
if (IsModal())
return;
web_contents()->WasShown();
web_contents_shown_ = true;
BaseWindow::ShowInactive();
}

View File

@@ -80,6 +80,7 @@ class BrowserWindow : public BaseWindow,
// Helpers.
v8::Global<v8::Value> web_contents_;
bool web_contents_shown_ = false;
v8::Global<v8::Value> web_contents_view_;
base::WeakPtr<api::WebContents> api_web_contents_;

View File

@@ -151,7 +151,10 @@ void OnTraceBufferUsageAvailable(
gin_helper::Promise<gin_helper::Dictionary> promise,
float percent_full,
size_t approximate_count) {
auto dict = gin_helper::Dictionary::CreateEmpty(promise.isolate());
v8::Isolate* isolate = promise.isolate();
v8::HandleScope handle_scope(isolate);
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
dict.Set("percentage", percent_full);
dict.Set("value", approximate_count);

View File

@@ -147,7 +147,12 @@ gin::ObjectTemplateBuilder NativeTheme::GetObjectTemplateBuilder(
&NativeTheme::ShouldUseInvertedColorScheme)
.SetProperty("inForcedColorsMode", &NativeTheme::InForcedColorsMode)
.SetProperty("prefersReducedTransparency",
&NativeTheme::GetPrefersReducedTransparency);
&NativeTheme::GetPrefersReducedTransparency)
#if BUILDFLAG(IS_MAC)
.SetProperty("shouldDifferentiateWithoutColor",
&NativeTheme::ShouldDifferentiateWithoutColor)
#endif
;
}
const char* NativeTheme::GetTypeName() {

View File

@@ -56,6 +56,9 @@ class NativeTheme final : public gin_helper::DeprecatedWrappable<NativeTheme>,
bool ShouldUseInvertedColorScheme();
bool InForcedColorsMode();
bool GetPrefersReducedTransparency();
#if BUILDFLAG(IS_MAC)
bool ShouldDifferentiateWithoutColor();
#endif
// ui::NativeThemeObserver:
void OnNativeThemeUpdated(ui::NativeTheme* theme) override;

View File

@@ -26,4 +26,9 @@ void NativeTheme::UpdateMacOSAppearanceForOverrideValue(
[[NSApplication sharedApplication] setAppearance:new_appearance];
}
bool NativeTheme::ShouldDifferentiateWithoutColor() {
return [[NSWorkspace sharedWorkspace]
accessibilityDisplayShouldDifferentiateWithoutColor];
}
} // namespace electron::api

View File

@@ -38,6 +38,7 @@ struct SchemeOptions {
bool corsEnabled = false;
bool stream = false;
bool codeCache = false;
bool allowExtensions = false;
};
struct CustomScheme {
@@ -70,6 +71,7 @@ struct Converter<CustomScheme> {
opt.Get("corsEnabled", &(out->options.corsEnabled));
opt.Get("stream", &(out->options.stream));
opt.Get("codeCache", &(out->options.codeCache));
opt.Get("allowExtensions", &(out->options.allowExtensions));
}
return true;
}
@@ -124,7 +126,7 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
}
std::vector<std::string> secure_schemes, cspbypassing_schemes, fetch_schemes,
service_worker_schemes, cors_schemes;
service_worker_schemes, cors_schemes, extension_schemes;
for (const auto& custom_scheme : custom_schemes) {
// Register scheme to privileged list (https, wss, data, chrome-extension)
if (custom_scheme.options.standard) {
@@ -160,6 +162,10 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
GetCodeCacheSchemes().push_back(custom_scheme.scheme);
url::AddCodeCacheScheme(custom_scheme.scheme.c_str());
}
if (custom_scheme.options.allowExtensions) {
extension_schemes.push_back(custom_scheme.scheme);
url::AddExtensionScheme(custom_scheme.scheme.c_str());
}
}
const auto AppendSchemesToCmdLine = [](const std::string_view switch_name,
@@ -179,6 +185,8 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
AppendSchemesToCmdLine(electron::switches::kFetchSchemes, fetch_schemes);
AppendSchemesToCmdLine(electron::switches::kServiceWorkerSchemes,
service_worker_schemes);
AppendSchemesToCmdLine(electron::switches::kExtensionSchemes,
extension_schemes);
AppendSchemesToCmdLine(electron::switches::kStandardSchemes,
GetStandardSchemes());
AppendSchemesToCmdLine(electron::switches::kStreamingSchemes,

View File

@@ -259,7 +259,7 @@ void UtilityProcessWrapper::OnServiceProcessLaunch(
EmitWithoutEvent("spawn");
}
void UtilityProcessWrapper::HandleTermination(uint64_t exit_code) {
void UtilityProcessWrapper::HandleTermination(uint32_t exit_code) {
// HandleTermination is called from multiple callsites,
// we need to ensure we only process it for the first callsite.
if (terminated_)
@@ -327,7 +327,7 @@ void UtilityProcessWrapper::CloseConnectorPort() {
}
}
void UtilityProcessWrapper::Shutdown(uint64_t exit_code) {
void UtilityProcessWrapper::Shutdown(uint32_t exit_code) {
node_service_remote_.reset();
HandleTermination(exit_code);
}

View File

@@ -57,7 +57,7 @@ class UtilityProcessWrapper final
static gin_helper::Handle<UtilityProcessWrapper> Create(gin::Arguments* args);
static raw_ptr<UtilityProcessWrapper> FromProcessId(base::ProcessId pid);
void Shutdown(uint64_t exit_code);
void Shutdown(uint32_t exit_code);
// gin_helper::Wrappable
static gin::DeprecatedWrapperInfo kWrapperInfo;
@@ -77,7 +77,7 @@ class UtilityProcessWrapper final
void OnServiceProcessLaunch(const base::Process& process);
void CloseConnectorPort();
void HandleTermination(uint64_t exit_code);
void HandleTermination(uint32_t exit_code);
void PostMessage(gin::Arguments* args);
bool Kill();

View File

@@ -1739,7 +1739,8 @@ bool WebContents::CheckMediaAccessPermission(
content::WebContents::FromRenderFrameHost(render_frame_host);
auto* permission_helper =
WebContentsPermissionHelper::FromWebContents(web_contents);
return permission_helper->CheckMediaAccessPermission(security_origin, type);
return permission_helper->CheckMediaAccessPermission(render_frame_host,
security_origin, type);
}
void WebContents::RequestMediaAccessPermission(

View File

@@ -555,7 +555,7 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
if (process_type == ::switches::kUtilityProcess ||
process_type == ::switches::kRendererProcess) {
// Copy following switches to child process.
static constexpr std::array<const char*, 10U> kCommonSwitchNames = {
static constexpr std::array<const char*, 11U> kCommonSwitchNames = {
switches::kStandardSchemes.c_str(),
switches::kEnableSandbox.c_str(),
switches::kSecureSchemes.c_str(),
@@ -565,7 +565,8 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
switches::kServiceWorkerSchemes.c_str(),
switches::kStreamingSchemes.c_str(),
switches::kNoStdioInit.c_str(),
switches::kCodeCacheSchemes.c_str()};
switches::kCodeCacheSchemes.c_str(),
switches::kExtensionSchemes.c_str()};
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
kCommonSwitchNames);
if (process_type == ::switches::kUtilityProcess ||

View File

@@ -127,6 +127,10 @@
#include "shell/common/plugin_info.h"
#endif // BUILDFLAG(ENABLE_PLUGINS)
#if BUILDFLAG(ENABLE_PRINTING)
#include "components/printing/common/print_dialog_linux_factory.h"
#endif
namespace electron {
namespace {
@@ -415,6 +419,10 @@ void ElectronBrowserMainParts::ToolkitInitialized() {
ui::LinuxUi::SetInstance(linux_ui);
#if BUILDFLAG(ENABLE_PRINTING)
print_dialog_factory_ = std::make_unique<printing::PrintDialogLinuxFactory>();
#endif
// Cursor theme changes are tracked by LinuxUI (via a CursorThemeManager
// implementation). Start observing them once it's initialized.
ui::CursorFactory::GetInstance()->ObserveThemeChanges();

View File

@@ -14,8 +14,13 @@
#include "content/public/browser/browser_main_parts.h"
#include "electron/buildflags/buildflags.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "printing/buildflags/buildflags.h"
#include "services/device/public/mojom/geolocation_control.mojom.h"
#if BUILDFLAG(ENABLE_PRINTING)
#include "printing/printing_context_linux.h"
#endif
class BrowserProcessImpl;
class IconManager;
@@ -179,6 +184,11 @@ class ElectronBrowserMainParts : public content::BrowserMainParts {
std::unique_ptr<display::ScopedNativeScreen> screen_;
#endif
#if BUILDFLAG(ENABLE_PRINTING)
std::unique_ptr<printing::PrintingContextLinux::PrintDialogFactory>
print_dialog_factory_;
#endif
static ElectronBrowserMainParts* self_;
};

View File

@@ -87,13 +87,6 @@ void InitializeFeatureList() {
std::string(",") + sandbox::policy::features::kNetworkServiceSandbox.name;
#endif
#if BUILDFLAG(IS_MAC)
disable_features +=
// MacWebContentsOcclusion is causing some odd visibility
// issues with multiple web contents
std::string(",") + features::kMacWebContentsOcclusion.name;
#endif
#if BUILDFLAG(ENABLE_PDF_VIEWER)
// Enable window.showSaveFilePicker api for saving pdf files.
// Refs https://issues.chromium.org/issues/373852607

View File

@@ -697,7 +697,11 @@ void FileSystemAccessPermissionContext::ConfirmSensitiveEntryAccess(
content::GlobalRenderFrameHostId frame_id,
base::OnceCallback<void(SensitiveEntryResult)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
callback_map_.try_emplace(path_info.path, std::move(callback));
auto [it, inserted] = callback_map_.try_emplace(path_info.path);
it->second.push_back(std::move(callback));
if (!inserted)
return;
auto after_blocklist_check_callback = base::BindOnce(
&FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist,
@@ -769,8 +773,11 @@ void FileSystemAccessPermissionContext::PerformAfterWriteChecks(
void FileSystemAccessPermissionContext::RunRestrictedPathCallback(
const base::FilePath& file_path,
SensitiveEntryResult result) {
if (auto val = callback_map_.extract(file_path))
std::move(val.mapped()).Run(result);
if (auto val = callback_map_.extract(file_path)) {
for (auto& callback : val.mapped()) {
std::move(callback).Run(result);
}
}
}
void FileSystemAccessPermissionContext::OnRestrictedPathResult(

View File

@@ -196,7 +196,8 @@ class FileSystemAccessPermissionContext
std::map<url::Origin, base::DictValue> id_pathinfo_map_;
std::map<base::FilePath, base::OnceCallback<void(SensitiveEntryResult)>>
std::map<base::FilePath,
std::vector<base::OnceCallback<void(SensitiveEntryResult)>>>
callback_map_;
std::unique_ptr<ChromeFileSystemAccessPermissionContext::BlockPathRules>

View File

@@ -136,24 +136,10 @@ NativeWindow::~NativeWindow() {
void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) {
// Setup window from options.
if (int x, y; options.Get(options::kX, &x) && options.Get(options::kY, &y)) {
SetPosition(gfx::Point{x, y});
#if BUILDFLAG(IS_WIN)
// FIXME(felixrieseberg): Dirty, dirty workaround for
// https://github.com/electron/electron/issues/10862
// Somehow, we need to call `SetBounds` twice to get
// usable results. The root cause is still unknown.
SetPosition(gfx::Point{x, y});
#endif
} else if (bool center; options.Get(options::kCenter, &center) && center) {
Center();
}
const bool use_content_size =
options.ValueOrDefault(options::kUseContentSize, false);
// On Linux and Window we may already have maximum size defined.
// On Linux and Windows we may already have minimum and maximum size defined.
extensions::SizeConstraints size_constraints(
use_content_size ? GetContentSizeConstraints() : GetSizeConstraints());
@@ -180,10 +166,32 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) {
size_constraints.set_maximum_size(gfx::Size(max_width, max_height));
if (use_content_size) {
gfx::Size clamped = size_constraints.ClampSize(GetContentSize());
if (clamped != GetContentSize()) {
SetContentSize(clamped);
}
SetContentSizeConstraints(size_constraints);
} else {
gfx::Size clamped = size_constraints.ClampSize(GetSize());
if (clamped != GetSize()) {
SetSize(clamped);
}
SetSizeConstraints(size_constraints);
}
if (int x, y; options.Get(options::kX, &x) && options.Get(options::kY, &y)) {
SetPosition(gfx::Point{x, y});
#if BUILDFLAG(IS_WIN)
// FIXME(felixrieseberg): Dirty, dirty workaround for
// https://github.com/electron/electron/issues/10862
// Somehow, we need to call `SetBounds` twice to get
// usable results. The root cause is still unknown.
SetPosition(gfx::Point{x, y});
#endif
} else if (bool center; options.Get(options::kCenter, &center) && center) {
Center();
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
if (bool val; options.Get(options::kClosable, &val))
SetClosable(val);

View File

@@ -1017,17 +1017,13 @@ void NativeWindowViews::MoveTop() {
bool NativeWindowViews::CanResize() const {
#if BUILDFLAG(IS_WIN)
return resizable_ && thick_frame_;
return has_frame() ? resizable_ && thick_frame_ : resizable_;
#else
return resizable_;
#endif
}
bool NativeWindowViews::IsResizable() const {
#if BUILDFLAG(IS_WIN)
if (has_frame())
return ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME;
#endif
return CanResize();
}

View File

@@ -45,7 +45,7 @@ struct NotificationOptions {
std::u16string timeout_type;
std::u16string reply_placeholder;
std::u16string sound;
std::u16string urgency; // Linux
std::u16string urgency; // Linux/Windows
std::vector<NotificationAction> actions;
std::u16string close_button_text;
std::u16string toast_xml;

View File

@@ -72,7 +72,8 @@ std::wstring NotificationPresenterWin::SaveIconToFilesystem(
std::string filename;
if (origin.is_valid()) {
filename = base::SHA1HashString(origin.spec()) + ".png";
const auto hash = base::SHA1HashString(origin.spec());
filename = base::HexEncode(hash) + ".png";
} else {
const int64_t now_usec = base::Time::Now().since_origin().InMicroseconds();
filename = base::NumberToString(now_usec) + ".png";

View File

@@ -96,6 +96,21 @@ std::wstring GetExecutablePath() {
return std::wstring(path, len);
}
// Installers sometimes put the running app in a versioned subfolder and ship a
// stub with the same filename one directory up. Point the Start Menu shortcut
// at the stub when it exists so toast activation and updates keep a stable
// launch path.
std::wstring GetShortcutTargetPath(const std::wstring& exe_path) {
if (exe_path.empty())
return L"";
base::FilePath exe_fp(exe_path);
base::FilePath stub_candidate =
exe_fp.DirName().DirName().Append(exe_fp.BaseName());
if (base::PathExists(stub_candidate))
return stub_candidate.value();
return exe_path;
}
void EnsureCLSIDRegistry() {
std::wstring exe = GetExecutablePath();
if (exe.empty())
@@ -116,7 +131,10 @@ void EnsureCLSIDRegistry() {
server_key.WriteValue(nullptr, exe.c_str());
}
bool ExistingShortcutValid(const base::FilePath& lnk_path, PCWSTR aumid) {
bool ExistingShortcutValid(const base::FilePath& lnk_path,
PCWSTR aumid,
const std::wstring& expected_target_path,
const std::wstring& expected_working_dir) {
if (!base::PathExists(lnk_path))
return false;
Microsoft::WRL::ComPtr<IShellLink> existing;
@@ -128,6 +146,31 @@ bool ExistingShortcutValid(const base::FilePath& lnk_path, PCWSTR aumid) {
FAILED(pf->Load(lnk_path.value().c_str(), STGM_READ))) {
return false;
}
// After an auto-update the .lnk may still have the correct AUMID/CLSID but
// point at an old install path; treat that as invalid so we rewrite it.
wchar_t target_path[MAX_PATH];
if (FAILED(existing->GetPath(target_path, MAX_PATH, nullptr, SLGP_RAWPATH)))
return false;
if (base::FilePath::CompareIgnoreCase(
base::FilePath(expected_target_path).value(),
base::FilePath(target_path).value()) != 0) {
return false;
}
wchar_t work_dir[MAX_PATH];
work_dir[0] = L'\0';
if (FAILED(existing->GetWorkingDirectory(work_dir, MAX_PATH)))
return false;
base::FilePath expected_cwd =
base::FilePath(expected_working_dir).NormalizePathSeparators();
base::FilePath actual_cwd =
base::FilePath(work_dir).NormalizePathSeparators();
if (base::FilePath::CompareIgnoreCase(expected_cwd.value(),
actual_cwd.value()) != 0) {
return false;
}
Microsoft::WRL::ComPtr<IPropertyStore> store;
if (FAILED(existing.As(&store)))
return false;
@@ -157,6 +200,7 @@ void EnsureShortcut() {
std::wstring exe = GetExecutablePath();
if (exe.empty())
return;
std::wstring shortcut_target = GetShortcutTargetPath(exe);
PWSTR programs_path = nullptr;
if (FAILED(
@@ -195,18 +239,20 @@ void EnsureShortcut() {
}
}
if (ExistingShortcutValid(lnk_path, aumid))
const std::wstring expected_working_dir =
base::FilePath(exe).DirName().value();
if (ExistingShortcutValid(lnk_path, aumid, shortcut_target,
expected_working_dir))
return;
Microsoft::WRL::ComPtr<IShellLink> shell_link;
if (FAILED(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&shell_link))))
return;
shell_link->SetPath(exe.c_str());
shell_link->SetPath(shortcut_target.c_str());
shell_link->SetArguments(L"");
shell_link->SetDescription(product_name.c_str());
shell_link->SetWorkingDirectory(
base::FilePath(exe).DirName().value().c_str());
shell_link->SetWorkingDirectory(expected_working_dir.c_str());
Microsoft::WRL::ComPtr<IPropertyStore> prop_store;
if (SUCCEEDED(shell_link.As(&prop_store))) {

View File

@@ -280,8 +280,9 @@ void WindowsToastNotification::CreateToastNotificationOnBackgroundThread(
// Continue to create the toast notification
ComPtr<ABI::Windows::UI::Notifications::IToastNotification>
toast_notification;
if (!CreateToastNotification(toast_xml, notification_id, weak_notification,
ui_task_runner, &toast_notification)) {
if (!CreateToastNotification(toast_xml, options, notification_id,
weak_notification, ui_task_runner,
&toast_notification)) {
return; // Error already posted to UI thread
}
@@ -349,6 +350,7 @@ bool WindowsToastNotification::CreateToastXmlDocument(
// returns the created notification via out parameter.
bool WindowsToastNotification::CreateToastNotification(
ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> toast_xml,
const NotificationOptions& options,
const std::string& notification_id,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
@@ -416,6 +418,27 @@ bool WindowsToastNotification::CreateToastNotification(
return false;
}
ComPtr<winui::Notifications::IToastNotification4> toast4;
hr = (*toast_notification)->QueryInterface(IID_PPV_ARGS(&toast4));
if (SUCCEEDED(hr)) {
winui::Notifications::ToastNotificationPriority priority =
winui::Notifications::ToastNotificationPriority::
ToastNotificationPriority_Default;
if (options.urgency == u"critical") {
priority = winui::Notifications::ToastNotificationPriority::
ToastNotificationPriority_High;
}
hr = toast4->put_Priority(priority);
if (FAILED(hr)) {
std::string err = base::StrCat({"WinAPI: Setting priority failed, ERROR ",
FailureResultToString(hr)});
DebugLog(err);
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
return false;
}
}
return true;
}

View File

@@ -94,6 +94,7 @@ class WindowsToastNotification : public Notification {
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
static bool CreateToastNotification(
ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> toast_xml,
const NotificationOptions& options,
const std::string& notification_id,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,

View File

@@ -59,6 +59,8 @@ gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name) {
GtkPrintSettings* print_settings = gtk_print_settings_new();
int dpi = gtk_print_settings_get_resolution(print_settings);
g_object_unref(print_settings);
if (dpi <= 0)
dpi = printing::kDefaultPdfDpi;
return {dpi, dpi};
#endif
}

View File

@@ -51,8 +51,7 @@ bool ElectronSerialDelegate::CanRequestPortPermission(
auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
auto* permission_helper =
WebContentsPermissionHelper::FromWebContents(web_contents);
return permission_helper->CheckSerialAccessPermission(
frame->GetLastCommittedOrigin());
return permission_helper->CheckSerialAccessPermission(frame);
}
bool ElectronSerialDelegate::HasPortPermission(

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