Compare commits

..

163 Commits

Author SHA1 Message Date
Michaela Laurencin
7f7a20e3c9 remove decorateURL from default_app 2026-04-09 11:25:21 -04:00
Shelley Vohr
7245c6a3f0 ci: re-check signed commits on every PR synchronize (#50811)
The needs-signed-commits label was previously added by the lightweight
synchronize workflow but only removed by a job in build.yml gated on
`gha-done`, which requires every macOS/Linux/Windows build to finish
green. That made label removal both slow (waits on the full pipeline)
and fragile (any unrelated build failure leaves the label pinned even
after commits are properly signed).

Drop the `if` guard on the synchronize job so it re-evaluates signing
on every push, and add a removal step that runs on success when the
label is present. Force-pushing signed commits now clears the label as
soon as the check completes, with no dependency on the build pipeline.
2026-04-09 11:02:01 -04:00
Charles Kerr
b484b0bde9 fix: fix inset and stop using gfx::ToFlooredRectDeprecated() (#50809)
fix: fix inset and stop using ToFlooredRectDeprecated()
2026-04-09 09:55:11 -05:00
Charles Kerr
6c8a910232 refactor: remove unnecessary raw_ptr SavePageHandler::web_contents_ (#50810)
refactor: remove unnecessary field raw_ptr<content::WebContents> SavePageHandler::web_contents_
2026-04-09 09:54:44 -05:00
Noah Gregory
cc3d4f5f58 fix: PDF support when site isolation trials disabled (#50689)
* fix: use proper OOPIF PDF check in `StreamsPrivateAPI`

* fix: add `ShouldEnableSubframeZoom` override to `ElectronBrowserClient` for upstream parity

* fix: add `MaybeOverrideLocalURLCrossOriginEmbedderPolicy` override to `ElectronBrowserClient` for upstream parity

* fix: add `DoesSiteRequireDedicatedProcess` override to `ElectronBrowserClient` for upstream parity

* style: move `DoesSiteRequireDedicatedProcess` to correct override section
2026-04-09 15:35:26 +02:00
Shelley Vohr
b711ce7b04 chore: remove window enlargement revert patch (#50612)
* chore: remove window enlargement revert patch

Chromium removed the `window_enlargement_` system from
DesktopWindowTreeHostWin (1771dbae), which was a workaround for an AMD
driver bug from 2013 (crbug.com/286609) where translucent HWNDs smaller
than 64x64 caused graphical glitches. Chromium confirmed this is no
longer needed and shipped the removal.

This removes the revert patch and all Electron-side code that depended
on the `kEnableTransparentHwndEnlargement` feature flag, including the
`GetExpandedWindowSize` helper and max size constraint expansion in
`NativeWindow::GetContentMaximumSize`.

* test: remove obsolete <64x64 transparent window test

The test was added in 2018 (#12904) to verify the AMD driver
workaround that artificially enlarged translucent HWNDs smaller than
64x64 (crbug.com/286609). The workaround set the real HWND to 64x64
and subtracted a stored window_enlargement_ from every client/window
bounds query, so getContentSize() reported the originally-requested
size even though the actual HWND was larger.

With both the Chromium window_enlargement_ system and Electron's
GetExpandedWindowSize gone, setContentSize on a transparent
thickFrame window calls SetWindowPos directly. WS_THICKFRAME windows
are subject to DefWindowProc's MINMAXINFO.ptMinTrackSize clamp on
programmatic resizes (Chromium's OnGetMinMaxInfo ends with
SetMsgHandled(FALSE), so DefWindowProc overwrites the zeroed
min-track with system defaults), which on Windows Server 2025
floors at 32x39 — hence the failing [32, 39] vs [30, 30].

The removed feature_list.cc comment explicitly flagged this test as
the blocker for retiring kEnableTransparentHwndEnlargement, so
delete it alongside the workaround it was validating.
2026-04-09 15:34:10 +02:00
Alexey
adf9a6e303 fix: restore std::deque for dynamic crash key storage (#50795)
#47171 migrated `std::deque` to `base::circular_deque` in
`shell/common/crash_keys.cc`. However, `CrashKeyString` wraps a
`crashpad::Annotation` that holds self-referential pointers and
registers itself in a process-global linked list. `circular_deque`
relocates elements on growth (via `VectorBuffer::MoveConstructRange`),
leaving those pointers dangling — causing missing crash keys or a hung
crashpad handler (especially on macOS). The `base/containers/README.md`
warns: "Since `base::deque` does not have stable iterators and it will
move the objects it contains, it may not be appropriate for all uses."

Reverts to `std::deque`, whose block-based layout never relocates
existing elements. Adds a regression test that registers 50 dynamic
crash keys and verifies they all survive a renderer crash.

Notes: Fixed crash keys being lost and the crash reporter hanging on
macOS when many dynamic crash keys were registered.

Made-with: Cursor
2026-04-09 10:50:32 +02:00
Calvin
6744293e96 fix: account for extraSize in aspect ratio min/max clamping on macOS (#50794)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 10:50:17 +02:00
dependabot[bot]
0d3342debf build(deps-dev): bump @xmldom/xmldom from 0.8.11 to 0.8.12 in the npm_and_yarn group across 1 directory (#50824)
build(deps-dev): bump @xmldom/xmldom

Bumps the npm_and_yarn group with 1 update in the / directory: [@xmldom/xmldom](https://github.com/xmldom/xmldom).


Updates `@xmldom/xmldom` from 0.8.11 to 0.8.12
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.8.11...0.8.12)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  dependency-version: 0.8.12
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 09:54:13 +02:00
Calvin
157cdac4b9 test: use shared get_out_dir() in generate_node_headers.py (#50828)
The local get_out_dir() defaulted to 'Testing' instead of 'Default',
causing e test to fail when using a non-Testing build config. Replace
it with the canonical version from script/lib/util.py.
2026-04-09 09:52:14 +02:00
Shelley Vohr
4dfada86ce fix: menu items not cleaned up after rebuild (#50806)
Menu was holding a SelfKeepAlive to itself from construction, so any
Menu that was never opened (e.g. an application menu replaced before
being shown) stayed pinned in cppgc forever. Repeated calls to
Menu.setApplicationMenu leaked every prior Menu along with its model
and items.

Restore the original Pin/Unpin lifecycle: start keep_alive_ empty and
only assign `this` in OnMenuWillShow. OnMenuWillClose already clears
it.
2026-04-09 11:56:39 +09:00
Kanishk Ranjan
df81a1d4ac test: add desktopCapturer icon validation (#50261)
* chore: testing of desktopCapturer can run on arm

* fix: DesktopMediaListCaptureThread crash

Fixed a crash when Windows calls ::CoCreateInstance() in the
DesktopMediaListCaptureThread before COM is initialized.

* test: added test for desktopCapturer fetchWindowIcons

* chore: updating Chromium patch hash

---------

Co-authored-by: Charles Kerr <charles@charleskerr.com>
2026-04-08 14:56:27 -04:00
Shelley Vohr
c3e3958668 fix: devtools re-attaches on open when previously detached (#50807)
PR #50646 added a dock state allowlist in SetDockState() that collapsed any
non-matching value to "right". WebContents::OpenDevTools passes an empty
string when no `mode` option is given, which is the sentinel LoadCompleted()
uses to restore `currentDockState` from prefs. The allowlist clobbered that
sentinel to "right", so previously-undocked devtools would flash detached
and then snap back to the right dock.

Preserve the empty string through SetDockState() so the pref-restore path
runs; still reject any non-empty invalid value to keep the JS-injection
guard from #50646 intact.
2026-04-08 13:36:47 -04:00
electron-roller[bot]
afd5fb4a60 chore: bump chromium to 148.0.7778.0 (main) (#50769)
* chore: bump chromium in DEPS to 148.0.7776.0

* chore: bump chromium in DEPS to 148.0.7778.0

* fix(patch): buffered_data_source_host_impl include added upstream

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7712714

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

* fix(patch): ASan process info callback added upstream

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7724018

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

* fix(patch): ServiceProcessHost per-instance observer migration

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7700794

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

* fix(patch): FSA BlockPath factory method refactor

Upstream refactored BlockPath initialization to use factory methods
(CreateRelative, CreateAbsolute, CreateSuffix) and a switch statement.
Updated the exposed code in the header to match.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7665590

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

* fix(patch): service process tracker per-instance observer refactor

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7700794

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

* chore: update patches (trivial only)

* 7723958: Rename blink::WebString::FromUTF16() to FromUtf16()

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7723958

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

* fixup! fix(patch): ASan process info callback added upstream

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Samuel Maddock <samuelmaddock@electronjs.org>
Co-authored-by: Claude <svc-devxp-claude@slack-corp.com>
2026-04-08 13:34:24 -04:00
Charles Kerr
8679522922 chore: iwyu commonly-included headers in shell/ (#50778)
* chore: iwyu in shell/browser/api/electron_api_web_contents.h

* chore: iwyu in shell/browser/browser.h

* chore: iwyu in shell/browser/javascript_environment.h

* chore: iwyu in shell/common/gin_hhelper/function_template.h

* chore: do not include node_includes.h if we are not using it

* chore: fix transitive include
2026-04-08 09:33:42 -05:00
David Sanders
0828de3ccd ci: include .obj checksums when calculating object change rate (#50772) 2026-04-08 14:57:40 +02:00
Michaela Laurencin
6b5a4ff66c ci: allow ai-pr label without comment (#50792) 2026-04-08 13:09:23 +02:00
Charles Kerr
ca28023d4d chore: remove unused enum classes (#50782)
chore: remove unused FileSystemAccessPermissionContext::Access enum class

chore: remove unused FileSystemAccessPermissionContext::RequestType enum class

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

* fix: address code review feedback
2026-04-08 15:04:29 +09:00
Charles Kerr
7eccea1315 refactor: remove use of deprecated class base::MemoryPressureListener (#50763) 2026-04-07 20:11:02 -05:00
Shelley Vohr
2e74ad2c68 feat: add setSuspended and isSuspended to globalShortcut (#50425)
Adds the ability to temporarily suspend and resume global shortcut
handling via `globalShortcut.setSuspended()` and query the current
state via `globalShortcut.isSuspended()`. When suspended, registered
shortcuts stop listening and new registrations are rejected. When
resumed, previously registered shortcuts are automatically restored.
2026-04-07 15:21:43 +02:00
dependabot[bot]
9ba299afff build(deps-dev): bump @octokit/rest from 20.1.2 to 22.0.1 (#50759)
Bumps [@octokit/rest](https://github.com/octokit/rest.js) from 20.1.2 to 22.0.1.
- [Release notes](https://github.com/octokit/rest.js/releases)
- [Commits](https://github.com/octokit/rest.js/compare/v20.1.2...v22.0.1)

---
updated-dependencies:
- dependency-name: "@octokit/rest"
  dependency-version: 22.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 12:52:52 +02:00
Charles Kerr
6df2228ea0 refactor: remove more unused menu api (#50661)
* chore: do not expose menu.isItemCheckedAt() to JS

Not used, documented, or typed. Added in dae98fa43f.

* chore: do not expose menu.isEnabledAt() to JS

Nto used, documented, or typed. Added in dae98fa43f.

* chore: do not expose menu.isVisibleAt() to JS

Not used, documented, or typed. Added in dae98fa43f.

* chore: remove unused undocumented API `getOjectHash`

Not used, documented, or typed. Added in ddad3e4846.

Appears to never have been used.
2026-04-07 10:05:12 +02:00
Charles Kerr
a29674e4cf fix: dangling raw_ptr JavascriptEnvironment::isolate_ (#50738) 2026-04-07 10:03:11 +02:00
David Sanders
81dd0f42e1 ci: don't set needs review status on PR that isn't open (#50762) 2026-04-06 23:12:53 -07:00
electron-roller[bot]
6aaf490aa5 chore: bump chromium to 148.0.7768.0 (main) (#50599)
* chore: bump chromium in DEPS to 148.0.7765.0

* chore: bump chromium in DEPS to 148.0.7766.0

* fix(patch-conflict): update packed_resources dep name after upstream rename

Upstream renamed //chrome:packed_resources_integrity_header to
//chrome:packed_resources. Updated the patch to guard the new dependency
name with !is_electron_build while preserving the same intent.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7714543

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(patch-conflict): update code_cache_host_impl.cc for upstream includes and TODO

Upstream added #include <stdint.h> and a TODO comment in
code_cache_host_impl.cc which conflicted with the Electron code cache
custom schemes patch. Resolved by keeping both upstream additions and
the Electron ProcessLockURLIsCodeCacheScheme function.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7615151

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: update patch hunk headers

Co-Authored-By: Claude <noreply@anthropic.com>

* 7700837: update RecordContentToVisibleTimeRequest from mojom to native struct

Upstream typemapped RecordContentToVisibleTimeRequest from a Mojo
struct to a native C++ struct. Updated OSR virtual method signatures
from blink::mojom::RecordContentToVisibleTimeRequestPtr to
std::optional<blink::RecordContentToVisibleTimeRequest> and
blink::RecordContentToVisibleTimeRequest to match.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7700837

Co-Authored-By: Claude <noreply@anthropic.com>

* 7714579: update WebString::FromASCII to FromUTF8

Upstream renamed blink::WebString::FromASCII to FromAscii. Updated
Electron's usage to FromUTF8 which is equivalent for ASCII scheme
strings and avoids a dependency on the renamed method. Also fixed
blink::String::FromUTF8 to use the String constructor directly.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7714579

Co-Authored-By: Claude <noreply@anthropic.com>

* 7696480: add stream_info dep after StreamInfo extraction

Upstream extracted extensions::StreamInfo from PdfViewerStreamManager
to a standalone class in extensions/browser/mime_handler/stream_info.h.
Added the new target as a dependency since Electron's streams_private
and pdf_viewer_private APIs use PdfViewerStreamManager which now
depends on the separate StreamInfo target.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7696480

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: bump chromium in DEPS to 148.0.7768.0

* fix(patch-conflict): update PiP patch for new toggle_mute_button in overlay window

Upstream added a toggle_mute_button to the live caption dialog controls
in VideoOverlayWindowViews::SetLiveCaptionDialogVisibility. Extended the
existing #if 0 guard to include the new button handling since Electron
disables live caption dialog functionality.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7682308

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(patch-conflict): update packed_resource_integrity patch after upstream dep removal

Upstream removed the deps += [ "//chrome:packed_resources" ] line from
the if (!is_win) block in chrome/browser/BUILD.gn. The Electron patch
no longer needs to guard this dep with !is_electron_build in this
location since the dep was already relocated by an earlier upstream CL.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7714543

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(patch-conflict): update WebSocket throttling revert for DisconnectWebSocketOnBFCache guard

Upstream added a DisconnectWebSocketOnBFCacheEnabled() runtime feature
check that wraps the WebSocket BFCache feature registration. Updated the
Electron revert patch to place the kAllowAggressiveThrottlingWithWebSocket
ternary inside the new conditional guard.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7698838

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(patch-conflict): update SCContentSharingPicker patch for upstream native picker refactor

Upstream added is_native_picker and filter_ based native picker session
validation to ScreenCaptureKitDeviceMac. Electron's patch uses its own
native picker approach (active_streams_ counter + direct SCContentSharingPicker
API), so marked the new upstream parameters as [[maybe_unused]] and kept
Electron's implementation.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7713560

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: update patch hunk headers

Co-Authored-By: Claude <noreply@anthropic.com>

* 7708800: update StartDragging signature to use RenderFrameHost

Upstream refactored StartDragging to take a RenderFrameHost& instead of
separate source_origin and source_rwh parameters. Updated
OffScreenWebContentsView to match the new signature and derive the
RenderWidgetHostImpl from the RenderFrameHost internally.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7708800

Co-Authored-By: Claude <noreply@anthropic.com>

* 7682308: add toggle_mute_button to chromium_src build sources

Upstream added a ToggleMuteButton to the PiP overlay window controls.
Added the new toggle_mute_button.cc/h source files to Electron's
chromium_src/BUILD.gn to resolve linker errors.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/7682308

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: update patches after main rebase

* fixup! 7708800: update StartDragging signature to use RenderFrameHost

fix linting

* 7705541: [trap-handler] Track individual Wasm memories | https://chromium-review.googlesource.com/c/v8/v8/+/7705541

Moved the SetUpWebAssemblyTrapHandler() call to before the V8 isolate is created

* fixup! fix utility process tests

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
2026-04-06 20:38:46 -07:00
Samuel Attard
b8f25c4ced fix: resolve target bundle path once at start of install (#50745)
Resolve the Squirrel.Mac target bundle URL to a canonical path once at the
top of -[SQRLInstaller installRequest:] and use it for every step of the
install chain, rejecting requests whose path is not already canonical. When
running with elevated privileges, additionally require the target to be the
application bundle that contains the installer. SQRLUpdater now writes the
resolved bundle URL so the serialized request is canonical by construction.
2026-04-06 22:10:52 -04:00
Samuel Attard
9fafc81e88 ci: use hermetic mac SDK for the release ffmpeg build (#50746)
* 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.

* 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.
2026-04-06 18:26:38 -04:00
Mitchell Cohen
4d05010945 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
2026-04-06 16:38:23 -04:00
LiRongWan
c3189e9886 docs: link menu type references (#50414)
* docs: link menu type references

* docs: trigger CI re-run for signed commits verification
2026-04-06 16:36:06 -04:00
Samuel Attard
983ebdd6de ci: make src-cache upload atomic (#50743)
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.
2026-04-06 16:04:49 -04:00
dependabot[bot]
b9c08ef9c2 build(deps): bump @electron/get from 2.0.3 to 4.0.3 in /npm (#50553)
Bumps [@electron/get](https://github.com/electron/get) from 2.0.3 to 4.0.3.
- [Release notes](https://github.com/electron/get/releases)
- [Commits](https://github.com/electron/get/compare/v2.0.3...v4.0.3)

---
updated-dependencies:
- dependency-name: "@electron/get"
  dependency-version: 4.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-06 10:32:35 -07:00
Samuel Attard
9f3cc9122c 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 09:42:08 -07:00
John Kleinschmidt
e66e4ca02c ci: use github mirror to get lint dependency versions (#50733) 2026-04-06 09:14:01 -07:00
Samuel Attard
6ed3198ba8 build: migrate from eslint to oxlint (#50691)
Consolidates the root .eslintrc.json and five nested configs (build,
script, docs, default_app, spec) into a single .oxlintrc.json at the
repo root. script/lint.js now shells out to the oxlint binary from
node_modules/.bin instead of using the ESLint Node API, and emits
GitHub Actions annotations directly via --format=github in CI
(replacing the deleted eslint-stylish problem matcher).

Oxlint has no markdown processor, so the ESLint-based lint of JS code
blocks in docs/**/*.md is replaced with an inline regex check for bare
Node.js builtin imports. This preserves the rule docs/.eslintrc.json
was originally added for in #42113; the rest of the standard ruleset on
docs code blocks was already being enforced in parallel by
lint-roller-markdown-standard.
2026-04-06 09:05:13 -07:00
Samuel Attard
903e65e048 ci: fetch clang-tidy package in fix-sync (#50704)
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.
2026-04-05 22:31:30 -07:00
Samuel Attard
fef2fd2941 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-05 17:56:03 -07:00
Charles Kerr
4d8fd31e5f refactor: replace calls to NotifyAccessibilityEventDeprecated() (#50662) 2026-04-05 10:41:57 -05:00
Charles Kerr
96486a4102 refactor: remove raw_ptr<content::StoragePartition> from ServiceWorkerContext and ServiceWorkerKey (#50663)
This removes two `raw_ptr<context::StoragePartition>` instances.

These pointers were used to build a ServiceWorkerMain* lookup key.
The key was built from [version_id, raw_ptr<StoragePartition>].
Unfortunately these keys could be dangling on shutdown.

This PR now uses stable, immutable fields for building the key:
[version_id, BrowserContext::UniqueId(), context::StoragePartitionConfig].
context::StoragePartitionConfig is a unique lookup key for StoragePartition
within a BrowserContext.
2026-04-05 10:41:35 -05:00
Samuel Attard
3f8238b92c fix: defer Wrappable destruction in SecondWeakCallback to a posted task (#50688)
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.
2026-04-05 07:38:08 +00:00
Charles Kerr
64c5440eec fix: dangling raw_ptr MicrotasksRunner::isolate_ (#50676) 2026-04-04 23:03:14 -05:00
Samuel Attard
30cf60a935 fix: propagate requesting frame through sync permission checks (#50679)
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.
2026-04-04 15:59:25 -07:00
Charles Kerr
ec30e4cdae refactor: remove unused field ServiceWorkerMain.start_worker_promise_ (#50674)
added in a467d068 but never used.
2026-04-04 14:22:48 -05:00
Samuel Attard
40033db422 chore: resolve dependabot security alerts (#50680) 2026-04-04 11:56:48 -07:00
Samuel Attard
c3d441cf7d ci: add Datadog metrics to clean-src-cache job (#50642)
* ci: add Datadog metrics to clean-src-cache job

Report free space (before/after cleanup), space freed, and total space
for both cross-instance-cache and win-cache volumes to Datadog, matching
the pattern used in the macOS disk cleanup workflow.

https://claude.ai/code/session_013bpDsZLrFDpWMiARNFH4z9

* ci: use awk instead of bc, add workflow_dispatch trigger

- Replace bc with awk for KB-to-GB conversion since bc may not be
  available in the container image
- Add workflow_dispatch trigger for manual testing

https://claude.ai/code/session_013bpDsZLrFDpWMiARNFH4z9

* ci: remove workflow_dispatch, handled in another PR

https://claude.ai/code/session_013bpDsZLrFDpWMiARNFH4z9

* ci: move DD_API_KEY to job-level env for if-condition

The step-level env is not available when GitHub evaluates the step's
if expression, so env.DD_API_KEY was always empty. Move it to
job-level env so the conditional works correctly.

https://claude.ai/code/session_013bpDsZLrFDpWMiARNFH4z9

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-03 22:17:29 -07:00
Charles Kerr
14583d22e6 refactor: remove use of deprecated API base::GetProc() (#50650)
* refactor: replace deprecated API base::GetProcId() in web_frame_main

* refactor: replace deprecated API base::GetProcId() in web_contents

* refactor: replace deprecated API base::GetProcId() in a11y ui

* refactor: frame.osProcessId now returns 0 instead of -1 for invalid processes.

This is consistent with WebContents.getOSProcessId
2026-04-03 15:48:41 -05:00
Charles Kerr
68bfe49120 refactor: remove never-used JS API (#50649)
* chore: do not expose v8Util.getObjectHash() to JS

Not used, documented, or typed. Added in ddad3e4846.

* chore: do not expose DownloadItem.isDone() to JS

Not used, documented, or typed. Added in dcad25c98c.

* chore: do not expose BrowserWindow.isWebViewFocused() to JS

Not used, documented, or typed. Added in a949e9542d.
2026-04-03 13:04:27 -05:00
Kunal Dubey
2c6332a7d6 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.
2026-04-03 18:04:55 +02:00
Bohdan Tkachenko
ddc1bd9553 fix: forward activation token from libnotify on notification click (#50568)
* feat: forward activation token from libnotify notification clicks

When a notification action is clicked on Linux, retrieve the activation
token from libnotify (if available) via dlsym and set it using
`base::nix::SetActivationToken()`. This enables proper window focus
handling under Wayland, where the compositor requires a valid activation
token to grant focus to the application.

The `notify_notification_get_activation_token` symbol is resolved at
runtime to maintain compatibility with older libnotify versions that
do not expose this API.

* refactor: simplify libnotify soname loading and activation token lookup

Replace the chained Load() calls with a loop over a constexpr array of
sonames, and inline the lazy EnsureActivationTokenFunc() into
Initialize() since it is only called once and the library handle is
already known at that point.
2026-04-03 10:40:36 -05:00
Shelley Vohr
12109371d3 fix: validate dock_state_ against allowlist before JS execution (#50646)
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
2026-04-03 10:39:16 -05:00
Charles Kerr
69891d04bf test: improve cookie changed event coverage (#50655)
test: add tests for cookie changed overwrite and inserted

test: add tests for cookie changed inserted-no-value-change-overwrite

test: add tests for cookie changed expired-overwrite
2026-04-03 10:26:17 -05:00
David Sanders
188813e206 ci: fix pulling previous object checksums (#50635)
* ci: fix pulling previous object checksums

* chore: fix artifact finding

* chore: skip unpack

* refactor: dawidd6/action-download-artifact can't handle non-archived artifacts

Assisted-by: Claude Opus 4.6

* refactor: use Octokit in standalone script

Assisted-by: Claude Opus 4.6
2026-04-03 04:52:50 +00:00
Charles Kerr
8b768b8211 chore: stop exposing unused menu methods to JS (#50634)
* chore: remove unused undocumented API `menu.worksWhenHiddenAt()`.

Not used, documented, or typed. Added by 544d8a423c.

* chore: remove unused undocumented API `menu.getCommandIdAt()

Not used, documented, or typed. Added by dae98fa43f.

* chore: do not expose `menu.getIndexOfCommandId()` to JS

Added by dae98fa43f but not documented, typed, or used by JS code.

The C++ method is used by other shell code, but not in JS.

* chore: remove unused undocumented API `menu.getLabelAt()`

Not used, documented, or typed. Added by dae98fa43f.

* chore: remove unused undocumented API `menu.getToolTipAt()`

Not used, documented, or typed. Added by 06d48514c6.

* chore: remove unused undocumented API `menu.getSubLabelAt()`

Not used, documented, or typed. Added by dae98fa43f.
2026-04-02 22:04:18 -05:00
Mitchell Cohen
82b97ddf5b ci: run BrowserWindow test spec on Wayland (#50572)
add browserwindow test spec for wayland
2026-04-02 17:19:23 -05:00
Charles Kerr
16f408a502 chore: remove declaration for nonexistent method WebContents._getPrintersAsync() (#50628)
chore: remove declaration for nonexistent method WebContents._getPrintersAsync()

added in 8f51d3e1 but never implemented / never used
2026-04-02 15:29:10 -05:00
Michaela Laurencin
246aa63910 ci: correct contributing link and add link to ai tool policy (#50632)
* ci: correct contributing link and add link to ai tool policy

* add missing bracket
2026-04-02 13:54:13 -05:00
Shelley Vohr
230f02faf2 fix: don't force kFitToPrintableArea scaling when custom margins are set (#50615)
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.
2026-04-02 20:41:21 +02:00
Charles Kerr
1362d7b94d refactor: remove unused internal method WebContents.equal() (#50626)
refactor: remove unused internal method WebContents.equal()

last use removed in Feb 2021 @ 51bb0ad36d
2026-04-02 12:46:39 -05:00
Mitchell Cohen
877fe479b5 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
2026-04-02 09:52:27 -05:00
Shelley Vohr
f41438ff73 fix: prefill native print dialog options on macOS with OOP printing (#50600)
Chromium enabled out-of-process (OOP) printing by default on macOS in
https://chromium-review.googlesource.com/c/chromium/src/+/6032774. This
broke webContents.print() option prefilling (e.g. copies, collate,
duplex) in two ways:

1. ScriptedPrint() silently aborted because RegisterSystemPrintClient()
   was only called from GetDefaultPrintSettings(), but Electron's flow
   calls UpdatePrintSettings() instead when options are provided.

2. PrinterQueryOop::UpdatePrintSettings() sends settings to the remote
   PrintBackend service, but on macOS the native dialog runs in-browser
   using the local PrintingContextMac::print_info_, which was never
   updated with the user's requested settings.

Fix by registering the system print client in UpdatePrintSettings() and
applying cached settings to the local printing context before showing
the in-browser system print dialog.
2026-04-02 16:06:35 +02:00
Shelley Vohr
c6e201c965 build: allow clearing src & cross mnt cache via dispatch (#50638) 2026-04-02 10:01:08 +00:00
Niklas Wenzel
156a4e610c fix: extension service workers not starting beyond first app launch (#50611)
* fix: extension service worker not starting beyond first app launch

* fix: set preference only for extensions with service workers
2026-04-02 10:02:06 +02:00
Charles Kerr
81f8fc1880 refactor: remove unused internal method contents.canGoToIndex() (#50606)
refactor: remove unused internal method contents.canGoToIndex()

refactor: make WebContents::CanGoToIndex() private

The JS binding has been unused since 2021-04-27 #28839 0a1b26b1
2026-04-01 22:37:41 +02:00
Charles Kerr
343d6e5f3f test: add tests for navigationHistory.goToIndex() (#50607)
test: add tests for navigationHistory.goToIndex()
2026-04-01 22:37:19 +02:00
Asish Kumar
e7080835f1 docs: add destroy method to native addon tutorials to prevent hang on quit (#50561)
Native addons that hold persistent references to callbacks, emitters,
and threadsafe functions prevent Electron from quitting cleanly since
Electron 40.5.0 due to changes in Node.js shutdown behavior. This adds
a `destroy()` method to all four native code tutorials (Swift macOS,
Obj-C macOS, C++ Linux, C++ Win32) that releases these resources and
must be called before app quit.

The destroy method resets callback and emitter references and aborts the
threadsafe function, allowing the addon's destructor to run properly.
An [!IMPORTANT] note is added to each tutorial's JavaScript wrapper
section explaining when and why to call destroy().

Fixes #50457

Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
2026-04-01 13:13:09 -05:00
LiRongWan
7c1a6f7e95 docs: recommend subdirectory for userData to avoid Chromium conflicts (#50563)
Fixes #45414

Storing files directly in the userData root can cause naming conflicts
with Chromium's own subdirectories (Cache, GPUCache, Local Storage, etc.).
Added a recommendation to use a subdirectory such as
path.join(app.getPath('userData'), 'my-app-data') instead.

Notes: no-notes
2026-04-01 09:54:08 -05:00
Calvin
22ac2b13fb fix: remove menu update debug log (#50608) 2026-04-01 17:06:26 +09:00
Samuel Attard
a8acb96608 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-03-31 20:23:43 +00:00
Mitchell Cohen
97773bf50c fix: prevent borders and smearing in transparent frameless/client frame windows on Linux (#50541)
fix the appearance of transparent frameless and client frame windows
2026-03-31 11:24:10 -05:00
Shelley Vohr
1e0846749b fix: invoke print callback directly when no print job exists (#50431)
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.
2026-03-31 11:01:59 -05:00
electron-roller[bot]
8cd766ff53 chore: bump chromium to 148.0.7763.0 (main) (#50582) 2026-03-31 10:16:35 +02:00
dependabot[bot]
e5b20a11d2 build(deps): bump github/codeql-action from 4.34.1 to 4.35.1 (#50590)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.34.1 to 4.35.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](3869755554...c10b8064de)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-31 10:15:41 +02:00
Alexey
e0bd4ffc39 fix: add missing HandleScope in contentTracing.getTraceBufferUsage() (#50556)
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
2026-03-31 10:21:43 +09:00
Samuel Attard
bbbcae1a12 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-03-30 14:13:00 -07:00
Samuel Attard
3e1666be08 chore: remove dead C++ code from shell/ (#50513)
Removes unreferenced code found via codebase sweep. Each category below may
indicate a missing feature rather than truly-unused code — see PR description.

Dead class (1):
  ElectronNavigationUIData — never instantiated; ElectronBrowserClient uses
  upstream ExtensionNavigationUIData directly

Unused methods (7):
  CertificateManagerModel: ImportUserCert, ImportCACerts, ImportServerCert,
    Delete, is_user_db_available (only PKCS12 path is used)
  AutofillDriverFactory::AddDriverForFrame + CreationCallback type
  ZoomLevelDelegate::SetDefaultZoomLevelPref
  gtk_util: GetOpenLabel, GetSaveLabel

Unused members (2):
  AutofillPopup::selected_index_
  InspectableWebContents::synced_setting_names_

Declaration fixes (6):
  menu_util.h: BuildMenuItemWithImage signature corrected (GtkWidget* → gfx::Image&)
  win_frame_view.h: GetReadableFeatureColor (impl removed, decl left behind)
  frameless_view.h: friend class NativeWindowsViews (typo, class does not exist)
  Forward decls: WebDialogHelper, ChromeContentRendererClient,
    ElectronNativeWindowObserver, ValueStoreFactory
2026-03-30 10:36:00 -07:00
electron-roller[bot]
a06b49aca1 chore: bump chromium to 148.0.7759.0 (main) (#50515)
* chore: bump chromium in DEPS to 148.0.7755.0

* chore: bump chromium in DEPS to 148.0.7756.0

* chore: update patches

* 7698536: Wire up experiment arms for Glic summarize pdf button.

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

* 7695602: Include gperf to sources for iOS builds

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

* 7671200: Expose IgnoreDuplicateNavs in WebView

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

* chore: bump chromium in DEPS to 148.0.7758.0

* chore: update patches

* 7701873: Allow running completion callbacks directly in CommitPresentedFrameToCA() on Mac

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

* 7697732: Enhance diagnostic logging for ScreenCaptureKit errors on macOS

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

* 7698176: Disallow cookies with empty name and ambiguous value

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

* 7607319: Code Health: Use span in base::HexEncode

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

* chore: bump chromium in DEPS to 148.0.7759.0

* chore: update patches

* 7696478: [extensions] Move StreamContainer to extensions/browser/mime_handler/

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

* 7656748: Fixed controlled frame fullscreen crash

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

* chore: update patches

* fixup! 7696478: [extensions] Move StreamContainer to extensions/browser/mime_handler/

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2026-03-30 10:32:35 -07:00
Keeley Hammond
d318893aa0 fix: fix devtools patch type error on release builds (#50551)
fix: fix devtools types
2026-03-27 22:40:51 +00:00
Keeley Hammond
f133e2f775 refactor: improve input handling in FilePath gin converter (#50540)
refactor: improve input handling in file_path_converter

Properly handle paths containing ASCII control characters in the FilePath gin converter
2026-03-27 14:30:58 -04:00
John Kleinschmidt
b44b9ba316 ci: update nick-fields/retry to v4.0.0 (#50521) 2026-03-27 13:44:06 -04:00
dependabot[bot]
d5e4429724 build(deps-dev): bump @datadog/datadog-ci from 4.1.2 to 5.9.1 (#50407)
Bumps [@datadog/datadog-ci](https://github.com/DataDog/datadog-ci/tree/HEAD/packages/datadog-ci) from 4.1.2 to 5.9.1.
- [Release notes](https://github.com/DataDog/datadog-ci/releases)
- [Commits](https://github.com/DataDog/datadog-ci/commits/v5.9.1/packages/datadog-ci)

---
updated-dependencies:
- dependency-name: "@datadog/datadog-ci"
  dependency-version: 5.9.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-27 13:29:15 -04:00
John Kleinschmidt
8f11366f50 ci: don't request review for PRs in draft or WIP (#50539) 2026-03-27 13:27:52 -04:00
electron-roller[bot]
0dabcfdec4 chore: bump node to v24.14.1 (main) (#50480)
* chore: bump node in DEPS to v24.14.1

* 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-27 13:26:58 -04:00
Hichem
b4460a05da docs: Document known issue with dock.hide() method (#50476)
* Document known issue for dock.hide() method

Added a note about a known issue with dock.hide() method.

* Adjust workaround time for dock.hide() method

Updated workaround time for dock.hide() known issue.

* Fix known issue timing for dock.hide() workaround

Updated the workaround time in the known issue section for dock.hide() to 1000ms.

* Adjust workaround delay for dock.hide() method

Updated workaround time for dock.hide() known issue.
2026-03-27 10:00:04 -04:00
Niklas Wenzel
0a1ea1f028 docs: clarify allowed characters in protocol names (#50411) 2026-03-27 09:39:16 -04:00
Mitchell Cohen
b41ec6586a fix: correct linux zygote process titles (#50509)
* fix: correct linux zygote process titles

* pass argv on mac as well

* lint
2026-03-27 08:24:05 -04:00
Niklas Wenzel
4eff8f20f2 feat: make Chrome extensions work on custom protocols (#49951) 2026-03-26 20:00:51 -04:00
Shelley Vohr
8cb61e8b9b test: add interactive macOS dialog tests (#50363) 2026-03-26 17:06:03 -04:00
WofWca
b9731b89dc docs: update Notification support info (#50364)
This is a follow-up to
74fd10450f
(https://github.com/electron/electron/pull/48132).
The support for these has been added for Windows,
but not all documentation has been updated accordingly

Co-authored-by: Charles Kerr <charles@charleskerr.com>
2026-03-26 17:04:34 -04:00
dependabot[bot]
d64e1146dd build(deps): bump actions/download-artifact from 7.0.0 to 8.0.1 (#50444)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.1.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-26 16:17:53 -04:00
Jan Hannemann
ae6b219545 fix: outdated execution path for COM activation (#50471)
* fix: outdated execution path

* fix: use stub exe when detected
2026-03-26 18:21:58 +00:00
electron-roller[bot]
c44d60cfe4 chore: bump chromium to 148.0.7751.0 (main) (#50427)
* chore: bump chromium in DEPS to 148.0.7749.0

* chore: bump chromium in DEPS to 148.0.7751.0

* chore: update patches

* 7681299: Introduce OccludedWidgetInputProtector to track always-on-top widgets

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

* 7685453: chrome://accessibility: Don't AllowJavascript() in async calls

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

* 7665878: Prefer browser runtime over Node.js in HostRuntime detection

Refs https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7665878

* 7674037: Rename the bookmark-related interfaces of the Clipboard class to URL.

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

* 7621713: Migrate ServiceWorker framework to ChildProcessId

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

* 7680500: Migrate ServiceWorkerHost to ChildProcessId

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

* chore: update roller commit message lint script to handle devtools CLs

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2026-03-26 10:18:24 -04:00
Samuel Attard
9928c7d828 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-03-26 14:02:50 +00:00
David Sanders
f5bc6f7949 ci: fix variable name when downloading previous object checkusms (#50510) 2026-03-26 09:31:30 -04:00
Keeley Hammond
a839fb94aa fix: [a11y] fire AXMenuOpened event when ARIA menu is added to DOM (#50377)
* fix: fire AXMenuOpened event when a visible ARIA menu instance is added to the DOM

* 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.

* chore: add feature flag (kDynamicMenuPopupEvents)

* chore: update patches
2026-03-25 21:33:49 +00:00
Michaela Laurencin
2e2c56adde ci: add functionality for programmatic add/remove needs-signed-commits label (#50316)
* 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-25 15:38:44 -04:00
Samuel Attard
678adeaf7c fix: crash calling OSR shared texture release() after texture GC'd (#50473)
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.
2026-03-25 10:48:41 -07:00
Samuel Attard
1d14694dec refactor: remove dead named-window lookup from guest-window-manager (#50474)
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.
2026-03-25 10:48:30 -07:00
Samuel Attard
a48f03fb8d fix: crash in clipboard.readImage() on malformed image data (#50475)
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.
2026-03-25 10:47:00 -07:00
Shelley Vohr
f6b43cb0ef fix: fall back to default DPI when GTK returns 0 on Linux (#50453)
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.
2026-03-25 12:37:40 -05:00
Shelley Vohr
7451d560ba fix: register PrintDialogLinuxFactory on Linux (#50430)
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").
2026-03-25 12:37:03 -05:00
Damglador
27edd6e21c fix: pulseaudio stream and icon names (#49270)
Use platform_util::GetXdgAppId() with fallback to argv0 as PA_PROP_APPLICATION_ICON_NAME.
Use electron::GetPossiblyOverriddenApplicationName()
to set environment variable "ELECTRON_PA_APP_NAME" in audio_service.cc,
to use it in pulse_util.cc for setting input/output pa_context name.

This replaces hard-codded kBrowserDisplayName that was used for PA_PROP_APPLICATION_ICON_NAME,
and PRODUCT_STRING that was used for pa_context names.

This is done to make audio streams recognizable in tools like qpwgrapth and general audio managers,
instead of having 20 "Chromium" outputs and "Chromium input" inputs, that are actually coming from
completely different applications.
2026-03-25 12:25:44 -05:00
Shelley Vohr
ec3a18d438 fix: hex-encode Windows notification icon temp filenames (#50454)
* 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.

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

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

---------

Co-authored-by: Robo <hop2deep@gmail.com>
2026-03-25 09:29:58 -07:00
Samuel Attard
02d4101ca3 chore: remove redundant chromium patches (#50463)
- export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch:
  gin::V8Platform::GetPageAllocator() is now exported upstream via the
  public v8::Platform interface, so we no longer need to patch gin to
  expose a custom accessor. Update javascript_environment.cc to use the
  upstream API instead.

- fix_getcursorscreenpoint_wrongly_returns_0_0.patch: this fix has
  landed upstream in Chromium and is no longer needed as a local patch.
2026-03-24 17:21:13 -07:00
Keeley Hammond
fdaba4c6b0 chore: add CODEOWNERS for .claude folder (#50434)
Add wg-infra as code owners for the .claude folder to protect
Claude Code configuration files from unauthorized modifications.

https://claude.ai/code/session_01YK2mEzC3DLrhqbcXW9jwUr

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-24 15:39:35 -07:00
Robo
542ff828ab refactor: SafeV8Function to be backed by cppgc (#50397)
* refactor: SafeV8Function to be backed by cppgc

* spec: focus renderer before attempting paste

* spec: remove listeners to prevent leak on failed tests
2026-03-24 16:59:32 -05:00
pranjal-ogg
4371a4dceb docs: add cold-start deep link handling example (#49142)
docs: handle cold-start deep links on Windows/Linux

add a check for `process.argv` in the `app.whenReady()` callback to handle deep links when the application is cold-started on Windows and Linux.
2026-03-24 13:28:53 -05:00
dependabot[bot]
60f4b07723 build(deps): bump actions-cool/issues-helper from 3.7.6 to 3.8.0 (#50446)
Bumps [actions-cool/issues-helper](https://github.com/actions-cool/issues-helper) from 3.7.6 to 3.8.0.
- [Release notes](https://github.com/actions-cool/issues-helper/releases)
- [Changelog](https://github.com/actions-cool/issues-helper/blob/main/CHANGELOG.md)
- [Commits](71b62d7da7...200c78641d)

---
updated-dependencies:
- dependency-name: actions-cool/issues-helper
  dependency-version: 3.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 13:28:30 -05:00
dependabot[bot]
f282bec8ef build(deps): bump github/codeql-action from 4.33.0 to 4.34.1 (#50447)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.33.0 to 4.34.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](b1bff81932...3869755554)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 13:28:12 -05:00
dependabot[bot]
cef388de3d build(deps): bump actions/github-script from 7.0.1 to 8.0.0 (#50445)
Bumps [actions/github-script](https://github.com/actions/github-script) from 7.0.1 to 8.0.0.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7.0.1...ed597411d8f924073f98dfc5c65a23a2325f34cd)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 09:54:56 -05:00
Anirudh Sevugan
1828690467 fix: deprecate ELECTRON_SKIP_BINARY_DOWNLOAD env (#50406)
* fix: remove ELECTRON_SKIP_BINARY_DOWNLOAD

it is redundant as of electron v42
its purpose was to skip the binary download for post install script
but as of electron v42, post install script is gone
and replaced with a lazy download

it was also slated for removal in [this comment](https://github.com/electron/rfcs/pull/22#issuecomment-3387307743)

* docs: remove ELECTRON_SKIP_BINARY_DOWNLOAD section

the env is redundant as of electron v42
so docs don't have to mention it anymore

* docs: add ELECTRON_SKIP_BINARY_DOWNLOAD to breaking changes
2026-03-24 09:42:15 -04:00
David Sanders
f4c4cd14ac ci: upload object change stats to Datadog (#50390)
* ci: upload object change stats to Datadog

Assisted-by: Claude Opus 4.6

* ci: bump actions/upload-artifact version

* chore: only output new object count if non-zero

* chore: skip object change tracking on ASan builds

* chore: handle pull requests as well

* chore: always set chromium-version-changed

* chore: remove npx usage
2026-03-23 18:51:02 -07:00
dependabot[bot]
3db3996102 build(deps): bump dsanders11/project-actions from 1.7.0 to 2.0.0 (#50448)
Bumps [dsanders11/project-actions](https://github.com/dsanders11/project-actions) from 1.7.0 to 2.0.0.
- [Release notes](https://github.com/dsanders11/project-actions/releases)
- [Commits](2134fe7cc7...5767984408)

---
updated-dependencies:
- dependency-name: dsanders11/project-actions
  dependency-version: 2.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-23 21:42:24 -04:00
Samuel Attard
dbcf0fb5f0 fix: lazily initialize safeStorage async encryptor (#50419)
* fix: lazily initialize safeStorage async encryptor

The SafeStorage constructor previously registered a browser observer that
called os_crypt_async()->GetInstance() on app-ready. Because ESM named
imports (import { x } from 'electron') eagerly evaluate all electron
module getters, simply importing electron in an ESM entrypoint would
construct SafeStorage and touch the OS keychain on app-ready, even when
safeStorage was never used.

This showed up as a macOS CI hang: the esm-spec import-meta fixture
triggers a keychain access prompt that blocks the test runner until
timeout.

Now the async encryptor is requested lazily on the first call to
encryptStringAsync, decryptStringAsync, or isAsyncEncryptionAvailable.
isAsyncEncryptionAvailable now returns a Promise that resolves once
initialization completes, matching what the docs already stated.

* chore: lint

* fix: add HandleScope in OnOsCryptReady for pending operations

OnOsCryptReady fires asynchronously from a posted task without an active
V8 HandleScope. Previously this was harmless because eager init meant the
pending queues were always empty when it fired. With lazy init, operations
queue up first, then the callback processes them and needs to create V8
handles (Buffer::Copy, Dictionary::CreateEmpty, Promise::Resolve).
2026-03-23 10:47:14 -07:00
Samuel Attard
29750dda08 build: enable V8 builtins PGO (#50416)
* 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.

* 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.
2026-03-23 11:54:43 -04:00
electron-roller[bot]
6df6ec5f09 chore: bump chromium to 148.0.7741.0 (main) (#50336)
* chore: bump chromium in DEPS to 148.0.7739.0

* chore: bump chromium in DEPS to 148.0.7740.0

* 7654582: Delete obsolete kEnableServiceWorkersForChromeScheme feature flag.

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

* chore: fixup patch indices

* chore: fixup patch indices

* 7664982: Move SharedModuleService to //extensions

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

* chore: bump chromium in DEPS to 148.0.7741.0

* chore: fixup patch indices

* 7666060: [CodeHealth] Replace `RequestPermissions` with `RequestPermissionsFromCurrentDocument`

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

---------

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-23 10:23:55 +01:00
Samuel Attard
882a6b2cf9 build: speed up apply_all_patches by ~60% (#50417)
git am rewrites the index 2-3x per patch. In Chromium (~500K files,
70MB index) this dominated wall time: ~67 of 73 seconds were spent
rehashing and rewriting the index ~300 times for 150 patches.

- Add index.skipHash=true to skip recomputing the trailing SHA over
  the full index on every write
- Force index v4 before am so path-prefix compression roughly halves
  the on-disk index size (70MB -> 40MB)
- Disable core.fsync and gc.auto during am since a crashed apply is
  just re-run from a clean reset
- Apply patch targets in parallel (capped at ncpu-2); Chromium still
  dominates but this hides node/nan/etc behind it. Falls back to
  sequential on roller/ branches where conflict output needs to be
  readable.
- Prefix each output line with the target name so parallel output is
  attributable

Measured on a 13-target config with 238 total patches: 73s -> 28s.
2026-03-23 09:49:48 +01:00
Samuel Attard
b8fa540fd3 fix: use fresh LazyNow for OnEndWorkItemImpl to fix TimeKeeper DCHECK (#50418) 2026-03-22 19:54:31 -07:00
Robb Böhnke
dee8f5a0ff feat: add accessibilityDisplayShouldDifferentiateWithoutColor on macOS (#49912)
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.
2026-03-20 19:51:23 -04:00
dependabot[bot]
32f8e2ce45 build(deps-dev): bump eslint-plugin-n from 16.6.2 to 17.24.0 (#50310)
* build(deps-dev): bump eslint-plugin-n from 16.6.2 to 17.24.0

Bumps [eslint-plugin-n](https://github.com/eslint-community/eslint-plugin-n) from 16.6.2 to 17.24.0.
- [Release notes](https://github.com/eslint-community/eslint-plugin-n/releases)
- [Changelog](https://github.com/eslint-community/eslint-plugin-n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint-community/eslint-plugin-n/compare/16.6.2...v17.24.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-n
  dependency-version: 17.24.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: disable errors we still need these

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-20 17:38:44 -04:00
Anirudh Sevugan
4e6324e00b docs: remove postinstall lifecycle warning (#50359)
* docs: change postinstall lifecycle to changed from electron v42

* docs: remove postinstall lifecycle warning
2026-03-20 17:37:53 -04:00
Shelley Vohr
7f21d31498 feat: use Downloads folder as default path for file dialogs (#49868)
* fix: use Downloads folder as default path for file dialogs

Co-authored-by: Sourav Bera <sbera987654321@gmail.com>

* chore: improve breaking change description

---------

Co-authored-by: Sourav Bera <sbera987654321@gmail.com>
2026-03-20 17:28:53 -04:00
John Kleinschmidt
639d3b99b7 ci: update actions to node24 (#50373)
* ci: update actions to node24

* chore: fixup actions/cache to 5.0.4 everywhere
2026-03-20 15:33:48 -04:00
dodola
0c7bde54d4 feat: add copyVideoFrameAt and saveVideoFrameAs methods on webContents (#48149)
* feat: add copyVideoFrameAt and saveVideoFrameAs Method on Webcontent

chore: change the description of savevideoframe api

chore: add the description of the restrictive elements for using the APIs.

move to webframemain

fixed mediaPlayerAction to kSaveVideoFrameAs

Update spec/api-web-frame-main-spec.ts

Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>

Update spec/api-web-frame-main-spec.ts

Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>

fixed clipboard tests for video frame copying

fixed test for copying video frame to clipboard. check video loaded before copy video frame in test.

chore: try non-proprietary video format

Revert "chore: try non-proprietary video format"

This reverts commit ef085f88a1af53b6408a7af695cc60b8681398cf.

fix: format video as file url

* test: skip webFrameMain.copyVideoFrameAt on win32 CI due Chromium DCHECK
2026-03-20 15:32:09 -04:00
Noah Gregory
8a0c20431c fix: don't re-parse URL unnecessarily when handling dialogs (#50062)
* fix: fallback to opaque URL when needed inside dialog callback

* refactor: remove additional URL parsing entirely when showing dialogs

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

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

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

* style: make linter happy

* style: make linter actually happy

* fix: address failing `safeDialogs` tests

---------

Co-authored-by: Robo <hop2deep@gmail.com>
2026-03-20 09:27:59 -04:00
dependabot[bot]
72797d7b42 build(deps): bump flatted from 3.2.7 to 3.4.1 in the npm_and_yarn group across 1 directory (#50376)
build(deps): bump flatted in the npm_and_yarn group across 1 directory

Bumps the npm_and_yarn group with 1 update in the / directory: [flatted](https://github.com/WebReflection/flatted).


Updates `flatted` from 3.2.7 to 3.4.1
- [Commits](https://github.com/WebReflection/flatted/compare/v3.2.7...v3.4.1)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 19:18:55 -07:00
João Silva
9ccc752a43 fix: correct utility process exit code on Windows (#50256)
* 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

* fix: narrow HandleTermination and Shutdown to uint32_t, add tests
2026-03-19 16:58:14 -04:00
Calvin
6993eb3c78 chore: wordsmith non-maintainer dependency change bot message (#50345)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 15:44:59 -04:00
Shelley Vohr
d9649f9e16 feat: support notification priority on Windows (#50225)
* 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.

* chore: make linter happy

---------

Co-authored-by: Charles Kerr <charles@charleskerr.com>
2026-03-19 15:44:19 -04:00
John Kleinschmidt
5b2b9cdeff ci: don't run auto close for certain circumstances (#50372) 2026-03-19 14:48:09 -04:00
Shelley Vohr
e31a95b15f chore: remove macos hittest workaround patch (#50330)
build: remove macos hittest workaround patch

CL:6574464 changed BridgedContentView::hitTest: to use GetHitTestResult(), which
returns kRootView for any non-null, non-NativeViewHost view — causing
BridgedContentView to absorb all web content mouse events. In BrowserWindow,
content_view_ sits in front of the sibling WebContentsView and covers the full
client area, so it was always found first, breaking all loadURL page interaction.

Fix this by installing a ContentViewTargeterDelegate on content_view_ in
NativeWindowMac::SetContentView that returns nullptr (instead of the view itself)
when no children cover the target point. This makes GetHitTestResult return kOther,
allowing hitTest: to fall through to [super hitTest:] and find
RenderWidgetHostViewCocoa. This also removes the now-unnecessary chromium
partial-revert patch that worked around the same issue.
2026-03-19 11:04:17 -04:00
David Sanders
1ad832a4c1 ci: output build cache hit rate as GHA annotation (#50361) 2026-03-19 09:41:32 -04:00
John Kleinschmidt
8e077a09f3 ci: only run auto close on PRs targeting main branch (#50357) 2026-03-19 00:19:25 +00:00
John Kleinschmidt
95f0d8156b ci: test linux 64k (#49961) 2026-03-18 19:14:49 -04:00
Noah Gregory
b881f86c8f fix: always call the original impl in swizzled mousedown impls (#50096)
fix: always call the original implementation in swizzled mousedown implementations
2026-03-18 17:54:43 -04:00
John Kleinschmidt
5959ecc3ee ci: auto close PRs that do not fill out the required template (#50348) 2026-03-18 17:52:03 -04:00
John Kleinschmidt
a6a44692dc chore: Respect HTTP(S) proxy env variable for Yarn (#50322)
Respect HTTP(S) proxy env variable for Yarn

Co-authored-by: Filip Mösner <filip.mosner@seznam.cz>
2026-03-18 17:13:05 -04:00
John Beutner
12ea28c23e fix: ensure WebContents::WasShown runs when window is shown (#49421)
Avoids a freeze when failing to enter fullscreen on macOS.
2026-03-18 12:34:11 -04:00
Kyle Cutler
ade684dc35 fix: correctly track BaseWindow::IsActive() on MacOS (#49460)
fix: correctly set IsActive() in BaseWindow on MacOS
2026-03-18 11:47:19 -04:00
electron-roller[bot]
4ec6923898 chore: bump chromium to 148.0.7738.0 (main) (#50323)
* chore: bump chromium in DEPS to 148.0.7738.0

* chore: fixup patch indices

* 7664509: Migrate ServiceWorkerInfo to ChildProcessId

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

---------

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-18 11:45:24 -04:00
Ryan Zimmerman
e86cd9da96 docs: fix markdown formatting in fuses.md (#50318)
* docs: fix markdown formatting in fuses.md

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

* docs: apply suggestions from code review

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

* docs: fix misapplied suggestion

---------

Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2026-03-18 10:47:52 -04:00
reito
d6db1a27af feat: add nv12 osr format support. (#49799)
* feat: add nv12 osr output format.

* feat: add nv12 osr output format.
2026-03-17 17:14:01 -04:00
Shelley Vohr
76331f0564 refactor: replace CHILD_PLUGIN with CHILD_EMBEDDER_FIRST on macOS (#50278)
refactor: replace CHILD_PLUGIN with CHILD_EMBEDDER_FIRST on macOS

Chromium removed upstream support for child plugin processes without
library validation in https://crbug.com/461717105, which we patched
back via feat_restore_macos_child_plugin_process.patch.

Chromium's CHILD_EMBEDDER_FIRST mechanism already provides the right
extensibility point for this: values > CHILD_EMBEDDER_FIRST are reserved
for embedders and resolved via ContentBrowserClient::GetChildProcessSuffix().
Chrome itself uses this pattern for its Alerts helper process.

This commit replaces the Chromium patch with an Electron-native
implementation.
2026-03-17 14:41:15 -04:00
electron-roller[bot]
7cb6a737a9 chore: bump chromium to 148.0.7737.0 (main) (#50277)
* chore: bump chromium in DEPS to 148.0.7734.0

* chore: fixup patch indices

* chore: bump chromium in DEPS to 148.0.7736.0

* chore: fixup patch indices

* chore: bump chromium in DEPS to 148.0.7737.0

* chore: fixup patch indices

* 7666125: Migrate ServiceWorkerContext to ChildProcessId

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

---------

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-17 13:30:32 -04:00
dependabot[bot]
3659b97563 build(deps): bump dorny/paths-filter from 3.0.2 to 4.0.1 (#50306)
Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from 3.0.2 to 4.0.1.
- [Release notes](https://github.com/dorny/paths-filter/releases)
- [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md)
- [Commits](de90cc6fb3...fbd0ab8f3e)

---
updated-dependencies:
- dependency-name: dorny/paths-filter
  dependency-version: 4.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-17 12:30:14 -04:00
John Kleinschmidt
7d72eb809e ci: update test timeout to 60 minutes (#50305) 2026-03-17 10:06:42 -04:00
dependabot[bot]
8ba0ae7fa8 build(deps): bump actions/download-artifact from 8.0.0 to 8.0.1 (#50309)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](70fc10c6e5...3e5f45b2cf)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-17 14:42:11 +01:00
David Sanders
36350d78d0 chore: add missing timers-shim.ts to filenames.auto.gni (#50311) 2026-03-17 09:46:15 +01:00
dependabot[bot]
9b80324d7f build(deps): bump github/codeql-action from 4.32.6 to 4.33.0 (#50308)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.32.6 to 4.33.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](0d579ffd05...b1bff81932)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-16 22:15:03 -07:00
dependabot[bot]
a549c56faa build(deps): bump slackapi/slack-github-action from 2.1.1 to 3.0.1 (#50307)
Bumps [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action) from 2.1.1 to 3.0.1.
- [Release notes](https://github.com/slackapi/slack-github-action/releases)
- [Commits](91efab103c...af78098f53)

---
updated-dependencies:
- dependency-name: slackapi/slack-github-action
  dependency-version: 3.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-16 22:14:43 -07:00
Keeley Hammond
958278c273 feat: add id and groupId options to macOS notifications (#50097)
* feat: add custom `id` property to Notification API (macOS only)

* feat: add `groupId` property to Notification API (macOS). Notifications with the same groupId will be visually grouped together in Notification Center

* fix: move validation to construction time, add empty string check, remove setters

* docs: clarify id/group id properties, make instance properties read-only

* test: update tests to reflect read-only properties
2026-03-16 21:24:29 +01:00
Shelley Vohr
b7e9bbed0c fix: restore sdk_inputs cross-toolchain deps for macOS (#50297)
fix: restore sdk_inputs cross-toolchain deps for macOS

The change in CL:7652975 restricted sdk_inputs public_deps
to iOS only, to avoid setting up Xcode symlinks for the Linux
toolchain when cross-building chrome/linux on Mac. However, this
also broke cross-arch macOS builds (e.g. ffmpeg with target_cpu=x64)
where the mig target in the clang_arm64 toolchain depends on
sdk_inputs from the default clang_x64 toolchain.

Add target_os == \"mac\" alongside the existing iOS check to preserve
the original intent while restoring the cross-toolchain dependency
for macOS builds.
2026-03-16 20:12:38 +00:00
Justin Mayfield
eec3fe967e fix: user resizable transparent windows on win32 (#49428)
test: revert win32 frameless and transparent resizable expectations
2026-03-16 15:31:07 -04:00
David Sanders
01714757e3 ci: ignore test timeouts in audit (#50259) 2026-03-16 14:33:37 -04:00
Shelley Vohr
ffad67222d test: fix esm issue in node-spec-runner (#50289)
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.
2026-03-16 12:55:03 -04:00
ZHUO Xu
078586fab0 docs: update the example of webContents.setWindowOpenHandler to cla… (#49379)
docs: reorganize the comments for clarifying `webContents.setWindowOpenHandler` example
2026-03-16 12:12:52 -04:00
Noah Gregory
a561dd97a6 fix: add ASAR support to additional copy methods (#50226)
* fix: add ASAR support for additional copy methods

* test: add tests for ASAR support for additional copy messages
2026-03-16 14:36:48 +01:00
Shelley Vohr
b9cbcde600 build: remove redundant bits of ncrypto node patch (#50252)
build: remove redundant ncrypto node patch
2026-03-16 12:13:47 +01:00
electron-roller[bot]
36b0709942 chore: bump chromium to 148.0.7733.0 (main) (#50197)
* chore: bump chromium in DEPS to 147.0.7727.2

* chore: bump chromium in DEPS to 148.0.7728.0

* chore: bump chromium in DEPS to 148.0.7729.0

* chore: bump chromium in DEPS to 148.0.7730.0

* chore: bump chromium in DEPS to 148.0.7732.0

* chore: update WrappablePointerTag patch
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7641766

* chore: update custom protocol patch for removed code
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7653454

* chore: update patches

* fix: cleanup removed CHILD_PLUGIN code
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7653455

* fix: move from int to ChildProcessId
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7621912

* fix: update extensions CreateTab signature
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7644389

* fix: draggable hit region test interface update for mac windows
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7655245

* chore: bump chromium in DEPS to 148.0.7733.0

* feat: restore macos child plugin process
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7653455

* fixup! chore: merge main

* chore: update patches

* fix: replace clipboard IsFormatAvailable with async GetAllAvailableFormats
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7631097

Async API pending RFC https://github.com/electron/rfcs/pull/19

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Samuel Maddock <samuelmaddock@electronjs.org>
2026-03-16 10:55:06 +01:00
Shelley Vohr
cf84efbbb9 fix: prefer browser runtime over node in DevTools HostRuntime detection (#50241)
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.
2026-03-16 10:29:35 +01:00
David Sanders
58cd1aba10 ci: fix unsupported major in release board automation (#50260) 2026-03-14 15:34:50 -07:00
dependabot[bot]
26a3a8679a build(deps-dev): bump folder-hash from 4.1.1 to 4.1.2 (#50258)
Bumps [folder-hash](https://github.com/marc136/node-folder-hash) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/marc136/node-folder-hash/releases)
- [Changelog](https://github.com/marc136/node-folder-hash/blob/v5/CHANGELOG.md)
- [Commits](https://github.com/marc136/node-folder-hash/compare/v4.1.1...v4.1.2)

---
updated-dependencies:
- dependency-name: folder-hash
  dependency-version: 4.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 22:46:05 -07:00
John Kleinschmidt
a1e4c260ea ci: create PR triage project automation (#50248)
* ci: create PR triage project automation

* chore: use ubuntu-slim

---------

Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2026-03-14 02:35:29 +00:00
154 changed files with 3240 additions and 3919 deletions

View File

@@ -35,7 +35,7 @@
"ms-vscode.cpptools",
"mutantdino.resourcemonitor",
"dsanders11.vscode-electron-build-tools",
"dbaeumer.vscode-eslint",
"oxc.oxc-vscode",
"shakram02.bash-beautify",
"marshallofsound.gnls-electron"
],

View File

@@ -1,79 +0,0 @@
{
"root": true,
"extends": "standard",
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"env": {
"browser": true
},
"rules": {
"semi": ["error", "always"],
"no-var": "error",
"no-unused-vars": "off",
"guard-for-in": "error",
"@typescript-eslint/no-unused-vars": ["error", {
"vars": "all",
"args": "after-used",
"ignoreRestSiblings": true
}],
"prefer-const": ["error", {
"destructuring": "all"
}],
"n/no-callback-literal": "off",
"import/newline-after-import": "error",
"import/order": ["error", {
"alphabetize": {
"order": "asc"
},
"newlines-between": "always",
"pathGroups": [
{
"pattern": "@electron/internal/**",
"group": "external",
"position": "before"
},
{
"pattern": "@electron/**",
"group": "external",
"position": "before"
},
{
"pattern": "{electron,electron/**}",
"group": "external",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": [],
"distinctGroup": true,
"groups": [
"external",
"builtin",
["sibling", "parent"],
"index",
"type"
]
}]
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"overrides": [
{
"files": "*.ts",
"rules": {
"no-undef": "off",
"no-redeclare": "off",
"@typescript-eslint/no-redeclare": ["error"],
"no-use-before-define": "off"
}
},
{
"files": "*.d.ts",
"rules": {
"no-useless-constructor": "off",
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}

1
.github/CODEOWNERS vendored
View File

@@ -19,6 +19,7 @@ DEPS @electron/wg-upgrades
/lib/renderer/security-warnings.ts @electron/wg-security
# Infra WG
/.claude/ @electron/wg-infra
/.github/actions/ @electron/wg-infra
/.github/workflows/*-publish.yml @electron/wg-infra
/.github/workflows/build.yml @electron/wg-infra

View File

@@ -5,6 +5,8 @@ Thank you for your Pull Request. Please provide a description above and review
the requirements below.
Contributors guide: https://github.com/electron/electron/blob/main/CONTRIBUTING.md
NOTE: PRS submitted without this template will be automatically closed.
-->
#### Checklist

View File

@@ -47,6 +47,16 @@ runs:
- name: Add Clang problem matcher
shell: bash
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
- name: Download previous object checksums
shell: bash
if: ${{ (github.event_name == 'push' || github.event_name == 'pull_request') && inputs.is-asan != 'true' }}
env:
GITHUB_TOKEN: ${{ github.token }}
ARTIFACT_NAME: object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json
SEARCH_BRANCH: ${{ case(github.event_name == 'push', github.ref_name, github.event.pull_request.base.ref) }}
REPO: ${{ github.repository }}
OUTPUT_PATH: src/previous-object-checksums.json
run: node src/electron/.github/actions/build-electron/download-previous-object-checksums.mjs
- name: Build Electron ${{ inputs.step-suffix }}
if: ${{ inputs.target-platform != 'win' }}
shell: bash
@@ -72,12 +82,17 @@ runs:
cp out/Default/.ninja_log out/electron_ninja_log
node electron/script/check-symlinks.js
# Upload build stats to Datadog
if ! [ -z $DD_API_KEY ]; then
npx node electron/script/build-stats.mjs out/Default/siso.INFO --upload-stats || true
# Build stats and object checksums
BUILD_STATS_ARGS="out/Default/siso.INFO --out-dir out/Default --output-object-checksums object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json"
if [ -f previous-object-checksums.json ]; then
BUILD_STATS_ARGS="$BUILD_STATS_ARGS --input-object-checksums previous-object-checksums.json"
fi
if ! [ -z "$DD_API_KEY" ]; then
BUILD_STATS_ARGS="$BUILD_STATS_ARGS --upload-stats"
else
echo "Skipping build-stats.mjs upload because DD_API_KEY is not set"
fi
node electron/script/build-stats.mjs $BUILD_STATS_ARGS || true
- name: Build Electron (Windows) ${{ inputs.step-suffix }}
if: ${{ inputs.target-platform == 'win' }}
shell: powershell
@@ -95,16 +110,21 @@ runs:
Copy-Item out\Default\.ninja_log out\electron_ninja_log
node electron\script\check-symlinks.js
# Upload build stats to Datadog
# Build stats and object checksums
$statsArgs = @("out\Default\siso.exe.INFO", "--out-dir", "out\Default", "--output-object-checksums", "object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json")
if (Test-Path previous-object-checksums.json) {
$statsArgs += @("--input-object-checksums", "previous-object-checksums.json")
}
if ($env:DD_API_KEY) {
try {
npx node electron\script\build-stats.mjs out\Default\siso.exe.INFO --upload-stats ; $LASTEXITCODE = 0
} catch {
Write-Host "Build stats upload failed, continuing..."
}
$statsArgs += "--upload-stats"
} else {
Write-Host "Skipping build-stats.mjs upload because DD_API_KEY is not set"
}
try {
& node electron\script\build-stats.mjs @statsArgs ; $LASTEXITCODE = 0
} catch {
Write-Host "Build stats failed, continuing..."
}
- name: Verify dist.zip ${{ inputs.step-suffix }}
shell: bash
run: |
@@ -302,3 +322,10 @@ runs:
with:
name: out_gen_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src/out/Default/gen
- name: Upload Object Checksums ${{ inputs.step-suffix }}
if: ${{ always() && !cancelled() && inputs.is-asan != 'true' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: object_checksums_${{ inputs.artifact-platform }}_${{ inputs.target-arch }}
path: ./src/object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json
archive: false

View File

@@ -0,0 +1,82 @@
import { Octokit } from '@octokit/rest';
import { writeFileSync } from 'node:fs';
const token = process.env.GITHUB_TOKEN;
const repo = process.env.REPO;
const artifactName = process.env.ARTIFACT_NAME;
const branch = process.env.SEARCH_BRANCH;
const outputPath = process.env.OUTPUT_PATH;
const required = { GITHUB_TOKEN: token, REPO: repo, ARTIFACT_NAME: artifactName, SEARCH_BRANCH: branch, OUTPUT_PATH: outputPath };
const missing = Object.entries(required).filter(([, v]) => !v).map(([k]) => k);
if (missing.length > 0) {
console.error(`Missing required environment variables: ${missing.join(', ')}`);
process.exit(1);
}
const [owner, repoName] = repo.split('/');
const octokit = new Octokit({ auth: token });
async function main () {
console.log(`Searching for artifact '${artifactName}' on branch '${branch}'...`);
// Resolve the "Build" workflow name to an ID, mirroring how `gh run list --workflow` works
// under the hood (it uses /repos/{owner}/{repo}/actions/workflows/{id}/runs).
const { data: workflows } = await octokit.actions.listRepoWorkflows({ owner, repo: repoName });
const buildWorkflow = workflows.workflows.find((w) => w.name === 'Build');
if (!buildWorkflow) {
console.log('Could not find "Build" workflow, continuing without previous checksums');
return;
}
const { data: runs } = await octokit.actions.listWorkflowRuns({
owner,
repo: repoName,
workflow_id: buildWorkflow.id,
branch,
status: 'completed',
event: 'push',
per_page: 20,
exclude_pull_requests: true
});
for (const run of runs.workflow_runs) {
const { data: artifacts } = await octokit.actions.listWorkflowRunArtifacts({
owner,
repo: repoName,
run_id: run.id,
name: artifactName
});
if (artifacts.artifacts.length > 0) {
const artifact = artifacts.artifacts[0];
console.log(`Found artifact in run ${run.id} (artifact ID: ${artifact.id}), downloading...`);
// Non-archived artifacts are still downloaded from the /zip endpoint
const response = await octokit.actions.downloadArtifact({
owner,
repo: repoName,
artifact_id: artifact.id,
archive_format: 'zip'
});
if (response.headers['content-type'] !== 'application/json') {
console.error(`Unexpected content type for artifact download: ${response.headers['content-type']}`);
console.error('Expected application/json, continuing without previous checksums');
return;
}
writeFileSync(outputPath, JSON.stringify(response.data));
console.log('Downloaded previous object checksums successfully');
return;
}
}
console.log(`No previous object checksums found in last ${runs.workflow_runs.length} runs, continuing without them`);
}
main().catch((err) => {
console.error('Failed to download previous object checksums, continuing without them:', err.message);
process.exit(0);
});

View File

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

View File

@@ -1,22 +0,0 @@
{
"problemMatcher": [
{
"owner": "eslint-stylish",
"pattern": [
{
"regexp": "^\\s*([^\\s].*)$",
"file": 1
},
{
"regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
"line": 1,
"column": 2,
"severity": 3,
"message": 4,
"code": 5,
"loop": true
}
]
}
]
}

View File

@@ -26,7 +26,7 @@ jobs:
# Use dorny/paths-filter instead of the path filter under the on: pull_request: block
# so that the output can be used to conditionally run the apply-patches job, which lets
# the job be marked as a required status check (conditional skip counts as a success).
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |

View File

@@ -86,6 +86,7 @@ jobs:
!message.startsWith("Response status code does not indicate success") &&
!message.startsWith("The hosted runner lost communication with the server") &&
!message.startsWith("Dependabot encountered an error performing the update") &&
!message.startsWith("The action 'Run Electron Tests' has timed out") &&
!/Unable to make request/.test(message) &&
!/The requested URL returned error/.test(message),
)
@@ -154,7 +155,7 @@ jobs:
await core.summary.write();
- name: Send Slack message if errors
if: ${{ always() && steps.audit-errors.outputs.errorsFound && github.ref == 'refs/heads/main' }}
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1
with:
payload: |
link: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"

View File

@@ -31,8 +31,8 @@ jobs:
else
echo "Not a release branch: $BRANCH_NAME"
fi
- name: Determine Unsupported Major Version
id: determine-unsupported-major
- name: Determine Next Unsupported Major Version
id: determine-next-unsupported-major
if: ${{ steps.check-major-version.outputs.MAJOR }}
env:
MAJOR: ${{ steps.check-major-version.outputs.MAJOR }}
@@ -50,26 +50,27 @@ jobs:
# Find the oldest version where eolDate >= stableDate of the new major
# This gives us the oldest supported version when the new major goes stable
UNSUPPORTED_MAJOR=$(echo "$SCHEDULE" | jq -r --arg stableDate "$STABLE_DATE" '
NEXT_UNSUPPORTED_MAJOR=$(echo "$SCHEDULE" | jq -r --arg stableDate "$STABLE_DATE" '
[.[] | select(.eolDate != null and .eolDate >= $stableDate)] | sort_by(.version | split(".")[0] | tonumber) | first | .version | split(".")[0]
')
if [[ -z "$UNSUPPORTED_MAJOR" || "$UNSUPPORTED_MAJOR" == "null" ]]; then
if [[ -z "$NEXT_UNSUPPORTED_MAJOR" || "$NEXT_UNSUPPORTED_MAJOR" == "null" ]]; then
echo "Could not determine oldest supported version"
exit 1
fi
echo "SCHEDULE=$SCHEDULE" >> "$GITHUB_OUTPUT"
echo "UNSUPPORTED_MAJOR=$UNSUPPORTED_MAJOR" >> "$GITHUB_OUTPUT"
echo "NEXT_UNSUPPORTED_MAJOR=$NEXT_UNSUPPORTED_MAJOR" >> "$GITHUB_OUTPUT"
- name: New Release Branch Tasks
if: ${{ steps.check-major-version.outputs.MAJOR }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: electron/electron
MAJOR: ${{ steps.check-major-version.outputs.MAJOR }}
UNSUPPORTED_MAJOR: ${{ steps.determine-unsupported-major.outputs.UNSUPPORTED_MAJOR }}
NEXT_UNSUPPORTED_MAJOR: ${{ steps.determine-next-unsupported-major.outputs.NEXT_UNSUPPORTED_MAJOR }}
run: |
PREVIOUS_MAJOR=$((MAJOR - 1))
UNSUPPORTED_MAJOR=$((NEXT_UNSUPPORTED_MAJOR - 1))
# Create new labels
gh label create $MAJOR-x-y --color 8d9ee8 || true
@@ -108,8 +109,8 @@ jobs:
id: generate-project-metadata
env:
MAJOR: ${{ steps.check-major-version.outputs.MAJOR }}
UNSUPPORTED_MAJOR: ${{ steps.determine-unsupported-major.outputs.UNSUPPORTED_MAJOR }}
SCHEDULE: ${{ steps.determine-unsupported-major.outputs.SCHEDULE }}
NEXT_UNSUPPORTED_MAJOR: ${{ steps.determine-next-unsupported-major.outputs.NEXT_UNSUPPORTED_MAJOR }}
SCHEDULE: ${{ steps.determine-next-unsupported-major.outputs.SCHEDULE }}
with:
script: |
const schedule = JSON.parse(process.env.SCHEDULE)
@@ -144,7 +145,7 @@ jobs:
major,
"next-major": nextMajor,
"prev-major": prevMajor,
"ending-support-major": parseInt(process.env.UNSUPPORTED_MAJOR),
"ending-support-major": parseInt(process.env.NEXT_UNSUPPORTED_MAJOR),
"beta-date": betaDate,
"beta-prep-week": betaPrepWeek.toISOString().split('T')[0],
"beta-prep-week-end": betaPrepWeekEnd.toISOString().split('T')[0],
@@ -156,7 +157,7 @@ jobs:
}))
- name: Create Release Project Board
if: ${{ steps.check-major-version.outputs.MAJOR }}
uses: dsanders11/project-actions/copy-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/copy-project@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
id: create-release-board
with:
drafts: true
@@ -176,7 +177,7 @@ jobs:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
- name: Find Previous Release Project Board
if: ${{ steps.check-major-version.outputs.MAJOR }}
uses: dsanders11/project-actions/find-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/find-project@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
id: find-prev-release-board
with:
fail-if-project-not-found: false
@@ -184,7 +185,7 @@ jobs:
token: ${{ steps.generate-token.outputs.token }}
- name: Close Previous Release Project Board
if: ${{ steps.find-prev-release-board.outputs.number }}
uses: dsanders11/project-actions/close-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/close-project@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
project-number: ${{ steps.find-prev-release-board.outputs.number }}
token: ${{ steps.generate-token.outputs.token }}

View File

@@ -61,7 +61,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
@@ -442,34 +442,7 @@ jobs:
contents: read
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, windows-x64, windows-x86, windows-arm64]
if: always() && github.repository == 'electron/electron' && !contains(needs.*.result, 'failure')
steps:
steps:
- 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

@@ -7,6 +7,7 @@ name: Clean Source Cache
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions: {}
@@ -16,6 +17,8 @@ jobs:
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
env:
DD_API_KEY: ${{ secrets.DD_API_KEY }}
container:
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
options: --user root
@@ -23,12 +26,130 @@ jobs:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
- /mnt/win-cache:/mnt/win-cache
steps:
- name: Get Disk Space Before Cleanup
id: disk-before
shell: bash
run: |
echo "Disk space before cleanup:"
df -h /mnt/cross-instance-cache
df -h /mnt/win-cache
CROSS_FREE_BEFORE=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $4}')
CROSS_TOTAL=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $2}')
WIN_FREE_BEFORE=$(df -k /mnt/win-cache | tail -1 | awk '{print $4}')
WIN_TOTAL=$(df -k /mnt/win-cache | tail -1 | awk '{print $2}')
echo "cross_free_kb=$CROSS_FREE_BEFORE" >> $GITHUB_OUTPUT
echo "cross_total_kb=$CROSS_TOTAL" >> $GITHUB_OUTPUT
echo "win_free_kb=$WIN_FREE_BEFORE" >> $GITHUB_OUTPUT
echo "win_total_kb=$WIN_TOTAL" >> $GITHUB_OUTPUT
- name: Cleanup Source Cache
shell: bash
run: |
df -h /mnt/cross-instance-cache
find /mnt/cross-instance-cache -type f -mtime +15 -delete
find /mnt/win-cache -type f -mtime +15 -delete
- name: Get Disk Space After Cleanup
id: disk-after
shell: bash
run: |
echo "Disk space after cleanup:"
df -h /mnt/cross-instance-cache
df -h /mnt/win-cache
find /mnt/win-cache -type f -mtime +15 -delete
df -h /mnt/win-cache
CROSS_FREE_AFTER=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $4}')
WIN_FREE_AFTER=$(df -k /mnt/win-cache | tail -1 | awk '{print $4}')
echo "cross_free_kb=$CROSS_FREE_AFTER" >> $GITHUB_OUTPUT
echo "win_free_kb=$WIN_FREE_AFTER" >> $GITHUB_OUTPUT
- name: Log Disk Space to Datadog
if: ${{ env.DD_API_KEY != '' }}
shell: bash
env:
CROSS_FREE_BEFORE: ${{ steps.disk-before.outputs.cross_free_kb }}
CROSS_FREE_AFTER: ${{ steps.disk-after.outputs.cross_free_kb }}
CROSS_TOTAL: ${{ steps.disk-before.outputs.cross_total_kb }}
WIN_FREE_BEFORE: ${{ steps.disk-before.outputs.win_free_kb }}
WIN_FREE_AFTER: ${{ steps.disk-after.outputs.win_free_kb }}
WIN_TOTAL: ${{ steps.disk-before.outputs.win_total_kb }}
run: |
TIMESTAMP=$(date +%s)
CROSS_FREE_BEFORE_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_FREE_BEFORE / 1024 / 1024}")
CROSS_FREE_AFTER_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_FREE_AFTER / 1024 / 1024}")
CROSS_FREED_GB=$(awk "BEGIN {printf \"%.2f\", ($CROSS_FREE_AFTER - $CROSS_FREE_BEFORE) / 1024 / 1024}")
CROSS_TOTAL_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_TOTAL / 1024 / 1024}")
WIN_FREE_BEFORE_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_FREE_BEFORE / 1024 / 1024}")
WIN_FREE_AFTER_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_FREE_AFTER / 1024 / 1024}")
WIN_FREED_GB=$(awk "BEGIN {printf \"%.2f\", ($WIN_FREE_AFTER - $WIN_FREE_BEFORE) / 1024 / 1024}")
WIN_TOTAL_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_TOTAL / 1024 / 1024}")
echo "cross-instance-cache: free before=${CROSS_FREE_BEFORE_GB}GB, after=${CROSS_FREE_AFTER_GB}GB, freed=${CROSS_FREED_GB}GB, total=${CROSS_TOTAL_GB}GB"
echo "win-cache: free before=${WIN_FREE_BEFORE_GB}GB, after=${WIN_FREE_AFTER_GB}GB, freed=${WIN_FREED_GB}GB, total=${WIN_TOTAL_GB}GB"
curl -s -X POST "https://api.datadoghq.com/api/v2/series" \
-H "Content-Type: application/json" \
-H "DD-API-KEY: ${DD_API_KEY}" \
-d @- << EOF
{
"series": [
{
"metric": "electron.src_cache.disk.free_space_before_cleanup_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREE_BEFORE_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:cross-instance-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.free_space_after_cleanup_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREE_AFTER_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:cross-instance-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.space_freed_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREED_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:cross-instance-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.total_space_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_TOTAL_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:cross-instance-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.free_space_before_cleanup_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREE_BEFORE_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:win-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.free_space_after_cleanup_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREE_AFTER_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:win-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.space_freed_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREED_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:win-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.total_space_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_TOTAL_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:win-cache", "platform:linux"]
}
]
}
EOF
echo "Disk space metrics logged to Datadog"

View File

@@ -21,7 +21,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Set status
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 90
@@ -42,7 +42,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Set status
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 90
@@ -76,7 +76,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
- name: Create comment
if: ${{ steps.check-for-comment.outputs.SHOULD_COMMENT }}
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
with:
actions: 'create-comment'
token: ${{ steps.generate-token.outputs.token }}

View File

@@ -20,7 +20,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Add to Issue Triage
uses: dsanders11/project-actions/add-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/add-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
field: Reporter
field-value: ${{ github.event.issue.user.login }}
@@ -146,7 +146,7 @@ jobs:
}
- name: Create unsupported major comment
if: ${{ steps.add-labels.outputs.unsupportedMajor }}
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
with:
actions: 'create-comment'
token: ${{ steps.generate-token.outputs.token }}

View File

@@ -20,7 +20,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Remove from issue triage
uses: dsanders11/project-actions/delete-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/delete-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 90

View File

@@ -33,7 +33,7 @@ jobs:
org: electron
- name: Set status
if: ${{ steps.check-for-blocked-labels.outputs.NOT_BLOCKED }}
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 90

View File

@@ -51,4 +51,21 @@ jobs:
PR_URL: ${{ github.event.pull_request.html_url }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
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=-
cat <<'REVIEW_EOF' | sed "s/%AUTHOR%/$PR_AUTHOR/g" | gh pr review $PR_URL -r --body-file=-
<!-- disallowed-non-maintainer-change -->
Hello @%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.
To move this PR forward, please:
1. Revert the dependency/CI file changes from your branch. (e.g. `yarn.lock`, `.yarn/`, `.yarnrc.yml`, `.github/workflows/`, `.github/actions/`)
2. Ensure your branch [allows maintainer commits](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so a maintainer can push the necessary dependency changes on your behalf.
3. Leave a comment letting reviewers know the dependency change is still needed.
<details>
<summary>For maintainers</summary>
To land this PR, push a verified commit to the contributor's branch with the required dependency/CI changes, then dismiss this review.
</details>
REVIEW_EOF

View File

@@ -76,7 +76,6 @@ jobs:
- name: Add problem matchers
shell: bash
run: |
echo "::add-matcher::src/electron/.github/problem-matchers/eslint-stylish.json"
echo "::add-matcher::src/electron/.github/problem-matchers/markdownlint.json"
- name: Run Lint
shell: bash

View File

@@ -126,7 +126,7 @@ jobs:
cd src/electron
git pack-refs
- name: Download Out Gen Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: out_gen_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src/out/${{ env.ELECTRON_OUT_DIR }}/gen

View File

@@ -43,7 +43,7 @@ jobs:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Download Generated Artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: generated_artifacts_linux_arm64
path: ./generated_artifacts_linux_arm64

View File

@@ -175,12 +175,12 @@ jobs:
echo "DISABLE_CRASH_REPORTER_TESTS=true" >> $GITHUB_ENV
echo "IS_ASAN=true" >> $GITHUB_ENV
- name: Download Generated Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: generated_artifacts_${{ env.ARTIFACT_KEY }}
path: ./generated_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}
- name: Download Src Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: src_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}

View File

@@ -67,12 +67,12 @@ jobs:
- name: Install Dependencies
uses: ./src/electron/.github/actions/install-dependencies
- name: Download Generated Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
path: ./generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
- name: Download Src Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: src_artifacts_linux_${{ env.TARGET_ARCH }}
path: ./src_artifacts_linux_${{ env.TARGET_ARCH }}
@@ -123,12 +123,12 @@ jobs:
- name: Install Dependencies
uses: ./src/electron/.github/actions/install-dependencies
- name: Download Generated Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
path: ./generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
- name: Download Src Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: src_artifacts_linux_${{ env.TARGET_ARCH }}
path: ./src_artifacts_linux_${{ env.TARGET_ARCH }}

58
.github/workflows/pr-template-check.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: PR Template Check
on:
pull_request_target:
types: [opened, ready_for_review]
# 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-pr-template:
if: ${{ github.event.pull_request.head.repo.fork && !github.event.pull_request.draft && !startsWith(github.head_ref, 'roller/') }}
name: Check PR Template
runs-on: ubuntu-slim
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
sparse-checkout: .github/PULL_REQUEST_TEMPLATE.md
sparse-checkout-cone-mode: false
- name: Check for required sections
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const fs = require('fs');
const template = fs.readFileSync('.github/PULL_REQUEST_TEMPLATE.md', 'utf8');
const requiredSections = [...template.matchAll(/^(#{1,4} .+)$/gm)].map(
(m) => m[1],
);
if (requiredSections.length === 0) {
console.log('No heading sections found in PR template');
return;
}
const body = context.payload.pull_request.body || '';
const missingSections = requiredSections.filter(
(section) => !body.includes(section),
);
if (missingSections.length > 0) {
const list = missingSections.map((s) => `- \`${s}\``).join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `This PR was automatically closed because the PR template was not properly filled out. The following required sections are missing:\n\n${list}\n\nPlease update your PR description to include all required sections and reopen the PR.`,
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
state: 'closed',
});
}

View File

@@ -0,0 +1,46 @@
name: PR Triage Automation
on:
pull_request_target:
types: [synchronize, review_requested]
issue_comment:
types: [created]
# 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:
set-needs-review:
name: Set status to Needs Review
if: >-
(github.event_name == 'pull_request_target'
&& github.event.pull_request.state == 'open'
&& github.event.pull_request.draft != true
&& !contains(github.event.pull_request.labels.*.name, 'wip ⚒')
&& (github.event.action == 'synchronize' || github.event.action == 'review_requested'))
|| (github.event_name == 'issue_comment'
&& github.event.issue.pull_request
&& github.event.issue.state == 'open'
&& !contains(github.event.issue.labels.*.name, 'wip ⚒')
&& github.event.comment.user.login == github.event.issue.user.login)
runs-on: ubuntu-slim
permissions:
contents: read
steps:
- name: Generate GitHub App token
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Set status to Needs Review
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 118
field: Status
field-value: 🌀 Needs Review
fail-if-item-not-found: false

View File

@@ -18,7 +18,7 @@ jobs:
permissions: {}
steps:
- name: Trigger Slack workflow
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1
with:
webhook: ${{ secrets.BACKPORT_REQUESTED_SLACK_WEBHOOK_URL }}
webhook-type: webhook-trigger
@@ -42,7 +42,7 @@ jobs:
creds: ${{ secrets.RELEASE_BOARD_GH_APP_CREDS }}
org: electron
- name: Set status
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 94
@@ -50,7 +50,7 @@ jobs:
field-value: ✅ Reviewed
pull-request-labeled-ai-pr:
name: ai-pr label added
if: github.event.label.name == 'ai-pr'
if: github.event.label.name == 'ai-pr' && github.event.pull_request.state != 'closed'
runs-on: ubuntu-latest
permissions: {}
steps:
@@ -60,7 +60,7 @@ jobs:
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
- name: Create comment
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
with:
actions: 'create-comment'
token: ${{ steps.generate-token.outputs.token }}
@@ -72,7 +72,7 @@ jobs:
Hello @${{ github.event.pull_request.user.login }}. Due to the high amount of AI spam PRs we receive, if a PR is detected to be majority AI-generated without disclosure and untested, we will automatically close the PR.
We welcome the use of AI tools, as long as the PR meets our quality standards and has clearly been built and tested. If you believe your PR was closed in error, we welcome you to resubmit. However, please read our [CONTRIBUTING.md](http://contributing.md/) carefully before reopening. Thanks for your contribution.
We welcome the use of AI tools, as long as the PR meets our quality standards and has clearly been built and tested. If you believe your PR was closed in error, we welcome you to resubmit. However, please read our [CONTRIBUTING.md](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) and [AI Tool Policy](https://github.com/electron/governance/blob/main/policy/ai.md) carefully before reopening. Thanks for your contribution.
- name: Close the pull request
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}

View File

@@ -13,7 +13,6 @@ 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
@@ -23,9 +22,9 @@ jobs:
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
⚠️ 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).
@@ -37,3 +36,11 @@ jobs:
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR_URL --add-label needs-signed-commits
- name: Remove needs-signed-commits label
if: ${{ success() && contains(github.event.pull_request.labels.*.name, 'needs-signed-commits') }}
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

@@ -51,6 +51,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v3.29.5
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
with:
sarif_file: results.sarif

View File

@@ -29,7 +29,7 @@ jobs:
PROJECT_NUMBER=$(gh project list --owner electron --format json | jq -r '.projects | map(select(.title | test("^[0-9]+-x-y$"))) | max_by(.number) | .number')
echo "PROJECT_NUMBER=$PROJECT_NUMBER" >> "$GITHUB_OUTPUT"
- name: Update Completed Stable Prep Items
uses: dsanders11/project-actions/completed-by@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
uses: dsanders11/project-actions/completed-by@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
with:
field: Prep Status
field-value: ✅ Complete

329
.oxlintrc.json Normal file
View File

@@ -0,0 +1,329 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": [
"typescript",
"import",
"node",
"promise",
"unicorn"
],
"jsPlugins": [
{
"name": "no-only-tests",
"specifier": "./script/lint-plugins/no-only-tests.mjs"
}
],
"categories": {
"correctness": "off"
},
"options": {
"typeAware": false
},
"env": {
"builtin": true,
"browser": true
},
"ignorePatterns": [
".github/workflows/node_modules",
"spec/node_modules",
"spec/fixtures/native-addon",
"shell/browser/resources/win/resource.h",
"shell/common/node_includes.h",
"spec/fixtures/pages/jquery-3.6.0.min.js"
],
"rules": {
"no-var": "error",
"accessor-pairs": [
"error",
{
"setWithoutGet": true,
"enforceForClassMembers": true
}
],
"array-callback-return": [
"error",
{
"allowImplicit": false,
"checkForEach": false
}
],
"constructor-super": "error",
"curly": [
"error",
"multi-line"
],
"default-case-last": "error",
"eqeqeq": [
"error",
"always",
{
"null": "ignore"
}
],
"new-cap": [
"error",
{
"newIsCap": true,
"capIsNew": false,
"properties": true
}
],
"no-array-constructor": "error",
"no-async-promise-executor": "error",
"no-caller": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-class-members": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-useless-backreference": "error",
"no-empty": [
"error",
{
"allowEmptyCatch": true
}
],
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-eval": "error",
"no-ex-assign": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-boolean-cast": "error",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-import-assign": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-iterator": "error",
"no-labels": [
"error",
{
"allowLoop": false,
"allowSwitch": false
}
],
"no-lone-blocks": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-prototype-builtins": "error",
"no-useless-catch": "error",
"no-useless-constructor": "error",
"no-use-before-define": [
"error",
{
"functions": false,
"classes": false,
"variables": false
}
],
"no-multi-str": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-obj-calls": "error",
"no-proto": "error",
"no-redeclare": [
"error"
],
"no-regex-spaces": "error",
"no-return-assign": [
"error",
"except-parens"
],
"no-self-assign": [
"error",
{
"props": true
}
],
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-template-curly-in-string": "error",
"no-this-before-super": "error",
"no-throw-literal": "error",
"no-unexpected-multiline": "error",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": [
"error",
{
"defaultAssignment": false
}
],
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unused-vars": [
"error",
{
"vars": "all",
"args": "after-used",
"argsIgnorePattern": "^_",
"ignoreRestSiblings": true
}
],
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-escape": "error",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-void": "error",
"no-with": "error",
"prefer-const": [
"error",
{
"destructuring": "all"
}
],
"prefer-promise-reject-errors": "error",
"symbol-description": "error",
"unicode-bom": [
"error",
"never"
],
"use-isnan": [
"error",
{
"enforceForSwitchCase": true,
"enforceForIndexOf": true
}
],
"valid-typeof": [
"error",
{
"requireStringLiterals": true
}
],
"yoda": [
"error",
"never"
],
"import/export": "error",
"import/first": "error",
"import/no-absolute-path": [
"error",
{
"esmodule": true,
"commonjs": true,
"amd": false
}
],
"import/no-duplicates": "error",
"import/no-named-default": "error",
"import/no-webpack-loader-syntax": "error",
"promise/param-names": "error",
"guard-for-in": "error",
"node/handle-callback-err": [
"error",
"^(err|error)$"
],
"node/no-exports-assign": "error",
"node/no-new-require": "error",
"node/no-path-concat": "error"
},
"overrides": [
{
"files": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
"rules": {
"no-use-before-define": "off"
}
},
{
"files": ["lib/browser/**", "lib/utility/**"],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": ["electron", "electron/renderer"],
"patterns": [
"./*",
"../*",
"@electron/internal/isolated_renderer/*",
"@electron/internal/renderer/*",
"@electron/internal/sandboxed_worker/*",
"@electron/internal/worker/*"
]
}
]
}
},
{
"files": [
"lib/renderer/**",
"lib/worker/**",
"lib/preload_realm/**",
"lib/sandboxed_renderer/**",
"lib/isolated_renderer/**"
],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": ["electron", "electron/main"],
"patterns": ["./*", "../*", "@electron/internal/browser/*"]
}
]
}
},
{
"files": ["lib/common/**"],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": ["electron", "electron/main", "electron/renderer"],
"patterns": [
"./*",
"../*",
"@electron/internal/browser/*",
"@electron/internal/isolated_renderer/*",
"@electron/internal/renderer/*",
"@electron/internal/sandboxed_worker/*",
"@electron/internal/worker/*"
]
}
]
}
},
{
"files": [
"build/**",
"script/**",
"docs/**",
"default_app/**",
"spec/**"
],
"rules": {
"unicorn/prefer-node-protocol": "error"
}
},
{
"files": ["spec/**/*.ts", "spec/**/*.js", "spec/**/*.mjs"],
"rules": {
"no-only-tests/no-only-tests": "error"
}
},
{
"files": ["**/*.d.ts"],
"rules": {
"no-useless-constructor": "off",
"no-unused-vars": "off"
}
}
]
}

View File

@@ -1,5 +1,14 @@
# Electron Development Guide
## Running node_modules binaries
**Never use `npx`.** It is considered dangerous because it can silently fetch and execute arbitrary packages from the registry. Always run binaries through one of these safer mechanisms instead:
1. **Preferred** — spawn the executable directly from `node_modules/.bin/<tool>` (or the platform equivalent on Windows). This is what `script/lint.js` does for `oxlint`.
2. **Acceptable** — invoke via `yarn <tool>` or `yarn run <tool>`, which resolves to the locally installed version without the registry fallback that `npx` performs.
This rule applies to shell commands you run yourself and to any scripts you author or modify in this repo.
## Project Overview
Electron is a framework for building cross-platform desktop applications using web technologies. It embeds Chromium for rendering and Node.js for backend functionality.
@@ -201,12 +210,13 @@ gh label list --repo electron/electron --search target/ --json name,color --jq '
## Code Style
**C++:** Follows Chromium style, enforced by clang-format
**TypeScript/JavaScript:** ESLint configuration in `.eslintrc.json`
**TypeScript/JavaScript:** [oxlint](https://oxc.rs/docs/guide/usage/linter) configuration in `.oxlintrc.json`
**Linting:**
```bash
npm run lint # Run all linters
npm run lint:js # Run oxlint over all JS/TS/MJS sources
npm run lint:clang-format # C++ formatting
npm run lint:api-history # Validate API history YAML blocks in docs
```

View File

@@ -1,8 +0,0 @@
{
"plugins": [
"import"
],
"rules": {
"import/enforce-node-protocol-usage": ["error", "always"]
}
}

View File

@@ -1,8 +0,0 @@
{
"plugins": [
"import"
],
"rules": {
"import/enforce-node-protocol-usage": ["error", "always"]
}
}

View File

@@ -1,5 +1,5 @@
import { shell } from 'electron/common';
import { app, dialog, BrowserWindow, ipcMain, Menu } from 'electron/main';
import { app, dialog, BrowserWindow, ipcMain } from 'electron/main';
import * as path from 'node:path';
import * as url from 'node:url';
@@ -11,60 +11,6 @@ app.on('window-all-closed', () => {
app.quit();
});
const isMac = process.platform === 'darwin';
app.whenReady().then(() => {
const helpMenu: Electron.MenuItemConstructorOptions = {
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
await shell.openExternal('https://electronjs.org');
}
},
{
label: 'Documentation',
click: async () => {
const version = process.versions.electron;
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
}
},
{
label: 'Community Discussions',
click: async () => {
await shell.openExternal('https://discord.gg/electronjs');
}
},
{
label: 'Search Issues',
click: async () => {
await shell.openExternal('https://github.com/electron/electron/issues');
}
}
]
};
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
const template: Electron.MenuItemConstructorOptions[] = [
...(isMac ? [macAppMenu] : []),
{ role: 'fileMenu' },
{ role: 'editMenu' },
{ role: 'viewMenu' },
{ role: 'windowMenu' },
helpMenu
];
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
});
function decorateURL (url: string) {
// safely add `?utm_source=default_app
const parsedUrl = new URL(url);
parsedUrl.searchParams.append('utm_source', 'default_app');
return parsedUrl.toString();
}
// Find the shortest path to the electron binary
const absoluteElectronPath = process.execPath;
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath);
@@ -116,7 +62,7 @@ async function createWindow (backgroundColor?: string) {
mainWindow.on('ready-to-show', () => mainWindow!.show());
mainWindow.webContents.setWindowOpenHandler(details => {
shell.openExternal(decorateURL(details.url));
shell.openExternal(details.url);
return { action: 'deny' };
});

View File

@@ -1,35 +0,0 @@
{
"extends": "standard",
"plugins": [
"import",
"markdown"
],
"overrides": [
{
"files": ["*.md", "**/*.md"],
"processor": "markdown/markdown"
}
],
"rules": {
"@typescript-eslint/no-unused-vars": "off",
"import/order": ["error", {
"alphabetize": {
"order": "asc"
},
"newlines-between": "always",
"pathGroups": [
{
"pattern": "{electron,electron/**}",
"group": "builtin",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": []
}],
"n/no-callback-literal": "off",
"no-undef": "off",
"no-unused-expressions": "off",
"no-unused-vars": "off",
"import/enforce-node-protocol-usage": ["error", "always"]
}
}

View File

@@ -28,7 +28,10 @@ added:
* `window` [BaseWindow](base-window.md) (optional)
* `options` Object
* `title` string (optional)
* `defaultPath` string (optional)
* `defaultPath` string (optional) - Absolute directory path, absolute file
path, or file name to use by default. If not provided, the dialog will
default to the user's Downloads folder, or their home directory if Downloads
doesn't exist.
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
left empty the default label will be used.
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
@@ -109,7 +112,10 @@ changes:
* `window` [BaseWindow](base-window.md) (optional)
* `options` Object
* `title` string (optional)
* `defaultPath` string (optional)
* `defaultPath` string (optional) - Absolute directory path, absolute file
path, or file name to use by default. If not provided, the dialog will
default to the user's Downloads folder, or their home directory if Downloads
doesn't exist.
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
left empty the default label will be used.
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
@@ -198,7 +204,9 @@ added:
* `options` Object
* `title` string (optional) - The dialog title. Cannot be displayed on some _Linux_ desktop environments.
* `defaultPath` string (optional) - Absolute directory path, absolute file
path, or file name to use by default.
path, or file name to use by default. If not provided, the dialog will
default to the user's Downloads folder, or their home directory if Downloads
doesn't exist.
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
left empty the default label will be used.
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
@@ -238,7 +246,9 @@ changes:
* `options` Object
* `title` string (optional) - The dialog title. Cannot be displayed on some _Linux_ desktop environments.
* `defaultPath` string (optional) - Absolute directory path, absolute file
path, or file name to use by default.
path, or file name to use by default. If not provided, the dialog will
default to the user's Downloads folder, or their home directory if Downloads
doesn't exist.
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
left empty the default label will be used.
* `filters` [FileFilter[]](structures/file-filter.md) (optional)

View File

@@ -56,6 +56,9 @@ Returns `string` - The badge string of the dock.
Hides the dock icon.
> [!IMPORTANT]
> **Known issue:** Calling `dock.hide()` within one second of a previous call will have no effect. As a workaround, ensure at least one second has elapsed between calls — for example, by deferring with a `setTimeout` of 1100ms or more after a previous call.
#### `dock.show()` _macOS_
Returns `Promise<void>` - Resolves when the dock icon is shown.

View File

@@ -46,7 +46,7 @@ this has the additional effect of removing the menu bar from the window.
> [!NOTE]
> The default menu will be created automatically if the app does not set one.
> It contains standard items such as `File`, `Edit`, `View`, and `Window`.
> It contains standard items such as `File`, `Edit`, `View`, `Window` and `Help`.
#### `Menu.getApplicationMenu()`

View File

@@ -59,7 +59,12 @@ On Windows, returns true once the app has emitted the `ready` event.
### `safeStorage.isAsyncEncryptionAvailable()`
Returns `Promise<Boolean>` - Whether encryption is available for asynchronous safeStorage operations.
Returns `Promise<boolean>` - Resolves with whether encryption is available for
asynchronous safeStorage operations.
The asynchronous encryptor is initialized lazily the first time this method,
`encryptStringAsync`, or `decryptStringAsync` is called after the app is ready.
The returned promise resolves once initialization completes.
### `safeStorage.encryptString(plainText)`

View File

@@ -94,6 +94,7 @@
The actual output pixel format and color space of the texture should refer to [`OffscreenSharedTexture`](../structures/offscreen-shared-texture.md) object in the `paint` event.
* `argb` - The requested output texture format is 8-bit unorm RGBA, with SRGB SDR color space.
* `rgbaf16` - The requested output texture format is 16-bit float RGBA, with scRGB HDR color space.
* `nv12` - The requested output texture format is 12bpp with Y plane followed by a 2x2 interleaved UV plane, with REC709 color space.
* `deviceScaleFactor` number (optional) _Experimental_ - The device scale factor of the offscreen rendering output. If not set, will use `1` as default.
* `contextIsolation` boolean (optional) - Whether to run Electron APIs and
the specified `preload` script in a separate JavaScript context. Defaults

View File

@@ -1585,6 +1585,20 @@ Centers the current text selection in web page.
Copy the image at the given position to the clipboard.
#### `contents.copyVideoFrameAt(x, y)`
* `x` Integer
* `y` Integer
When executed on a video media element, copies the frame at (x, y) to the clipboard.
#### `contents.saveVideoFrameAs(x, y)`
* `x` Integer
* `y` Integer
When executed on a video media element, shows a save dialog and saves the frame at (x, y) to disk.
#### `contents.paste()`
Executes the editing command `paste` in web page.

View File

@@ -175,6 +175,20 @@ app.on('web-contents-created', (_, webContents) => {
})
```
#### `frame.copyVideoFrameAt(x, y)`
* `x` Integer
* `y` Integer
When executed on a video media element, copies the frame at (x, y) to the clipboard.
#### `frame.saveVideoFrameAs(x, y)`
* `x` Integer
* `y` Integer
When executed on a video media element, shows a save dialog and saves the frame at (x, y) to disk.
### Instance Properties
#### `frame.ipc` _Readonly_

View File

@@ -12,6 +12,34 @@ This document uses the following convention to categorize breaking changes:
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
## Planned Breaking API Changes (43.0)
### Behavior Changed: Dialog methods default to Downloads directory
The `defaultPath` option for the following methods now defaults to the user's Downloads folder (or their home directory if Downloads doesn't exist) when not explicitly provided:
* `dialog.showOpenDialog`
* `dialog.showOpenDialogSync`
* `dialog.showSaveDialog`
* `dialog.showSaveDialogSync`
Previously, when no `defaultPath` was provided, the underlying OS file dialog would determine the initial directory — typically remembering the last directory the user navigated to, or falling back to an OS-specific default. Now, Electron explicitly sets the initial directory to Downloads, which also means the OS will no longer track and restore the last-used directory between dialog invocations.
To preserve the old behavior, you can track the last-used directory yourself and pass it as `defaultPath`:
```js
const path = require('node:path')
let lastUsedPath
const result = await dialog.showOpenDialog({
defaultPath: lastUsedPath
})
if (!result.canceled && result.filePaths.length > 0) {
lastUsedPath = path.dirname(result.filePaths[0])
}
```
## Planned Breaking API Changes (42.0)
### Behavior Changed: macOS notifications now use `UNNotification` API

View File

@@ -3,7 +3,7 @@
These are the style guidelines for coding in Electron.
You can run `npm run lint` to show any style issues detected by `cpplint` and
`eslint`.
`oxlint`.
## General Code

View File

@@ -87,6 +87,13 @@ if (!gotTheLock) {
// Create mainWindow, load the rest of the app, etc...
app.whenReady().then(() => {
createWindow()
// Check for deep link on cold start
if (process.argv.length >= 2) {
const lastArg = process.argv[process.argv.length - 1]
if (lastArg.startsWith('electron-fiddle://')) {
dialog.showErrorBox('Welcome Back', `You arrived from: ${lastArg}`)
}
}
})
}
```

View File

@@ -179,6 +179,7 @@ auto_filenames = {
"lib/common/define-properties.ts",
"lib/common/deprecate.ts",
"lib/common/ipc-messages.ts",
"lib/common/timers-shim.ts",
"lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts",

View File

@@ -793,8 +793,6 @@ filenames = {
"shell/browser/extensions/electron_kiosk_delegate.h",
"shell/browser/extensions/electron_messaging_delegate.cc",
"shell/browser/extensions/electron_messaging_delegate.h",
"shell/browser/extensions/electron_navigation_ui_data.cc",
"shell/browser/extensions/electron_navigation_ui_data.h",
"shell/browser/extensions/electron_process_manager_delegate.cc",
"shell/browser/extensions/electron_process_manager_delegate.h",
"shell/common/extensions/electron_extensions_api_provider.cc",

View File

@@ -1,21 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/renderer"
],
"patterns": [
"./*",
"../*",
"@electron/internal/isolated_renderer/*",
"@electron/internal/renderer/*",
"@electron/internal/sandboxed_worker/*",
"@electron/internal/worker/*"
]
}
]
}
}

View File

@@ -73,6 +73,7 @@ export async function getSources (args: Electron.SourcesOptions) {
capturer._onerror = (error: string) => {
stopRunning();
// eslint-disable-next-line prefer-promise-reject-errors
reject(error);
};

View File

@@ -437,6 +437,14 @@ WebContents.prototype.loadURL = function (url, options) {
return p;
};
WebContents.prototype.copyVideoFrameAt = function (x: number, y: number) {
this.mainFrame.copyVideoFrameAt(x, y);
};
WebContents.prototype.saveVideoFrameAs = function (x: number, y: number) {
this.mainFrame.saveVideoFrameAs(x, y);
};
WebContents.prototype.setWindowOpenHandler = function (handler: (details: Electron.HandlerDetails) => Electron.WindowOpenHandlerResponse) {
this._windowOpenHandler = handler;
};

View File

@@ -1,4 +1,5 @@
import { Menu } from 'electron/main';
import { shell } from 'electron/common';
import { app, Menu } from 'electron/main';
const isMac = process.platform === 'darwin';
@@ -11,13 +12,47 @@ export const setApplicationMenuWasSet = () => {
export const setDefaultApplicationMenu = () => {
if (applicationMenuWasSet) return;
const helpMenu: Electron.MenuItemConstructorOptions = {
role: 'help',
submenu: app.isPackaged
? []
: [
{
label: 'Learn More',
click: async () => {
await shell.openExternal('https://electronjs.org');
}
},
{
label: 'Documentation',
click: async () => {
const version = process.versions.electron;
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
}
},
{
label: 'Community Discussions',
click: async () => {
await shell.openExternal('https://discord.gg/electronjs');
}
},
{
label: 'Search Issues',
click: async () => {
await shell.openExternal('https://github.com/electron/electron/issues');
}
}
]
};
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
const template: Electron.MenuItemConstructorOptions[] = [
...(isMac ? [macAppMenu] : []),
{ role: 'fileMenu' },
{ role: 'editMenu' },
{ role: 'viewMenu' },
{ role: 'windowMenu' }
{ role: 'windowMenu' },
helpMenu
];
const menu = Menu.buildFromTemplate(template);

View File

@@ -1,23 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/main",
"electron/renderer"
],
"patterns": [
"./*",
"../*",
"@electron/internal/browser/*",
"@electron/internal/isolated_renderer/*",
"@electron/internal/renderer/*",
"@electron/internal/sandboxed_worker/*",
"@electron/internal/worker/*"
]
}
]
}
}

View File

@@ -1,18 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/main"
],
"patterns": [
"./*",
"../*",
"@electron/internal/browser/*"
]
}
]
}
}

View File

@@ -1,18 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/main"
],
"patterns": [
"./*",
"../*",
"@electron/internal/browser/*"
]
}
]
}
}

View File

@@ -1,18 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/main"
],
"patterns": [
"./*",
"../*",
"@electron/internal/browser/*"
]
}
]
}
}

View File

@@ -124,7 +124,9 @@ if (nodeIntegration) {
delete (global as any).setImmediate;
delete (global as any).clearImmediate;
delete (global as any).global;
// eslint-disable-next-line n/no-deprecated-api
delete (global as any).root;
// eslint-disable-next-line n/no-deprecated-api
delete (global as any).GLOBAL;
});
}

View File

@@ -1,18 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/main"
],
"patterns": [
"./*",
"../*",
"@electron/internal/browser/*"
]
}
]
}
}

View File

@@ -90,6 +90,7 @@ export function executeSandboxedPreloadScripts (context: PreloadContext, preload
if (contents) {
runPreloadScript(context, contents);
} else if (error) {
// eslint-disable-next-line no-throw-literal
throw error;
}
} catch (error) {

View File

@@ -1,21 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/renderer"
],
"patterns": [
"./*",
"../*",
"@electron/internal/isolated_renderer/*",
"@electron/internal/renderer/*",
"@electron/internal/sandboxed_worker/*",
"@electron/internal/worker/*"
]
}
]
}
}

View File

@@ -1,18 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
"electron",
"electron/main"
],
"patterns": [
"./*",
"../*",
"@electron/internal/browser/*"
]
}
]
}
}

View File

@@ -6,7 +6,7 @@
"install-electron": "install.js"
},
"dependencies": {
"@electron/get": "^2.0.0",
"@electron/get": "^4.0.3",
"@types/node": "^24.9.0",
"extract-zip": "^2.0.1"
},

View File

@@ -5,7 +5,7 @@
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {
"@azure/storage-blob": "^12.28.0",
"@datadog/datadog-ci": "^4.1.2",
"@datadog/datadog-ci": "^5.9.1",
"@electron/asar": "^4.0.1",
"@electron/docs-parser": "^2.0.0",
"@electron/fiddle-core": "^1.3.4",
@@ -13,7 +13,7 @@
"@electron/lint-roller": "^3.2.0",
"@electron/typescript-definitions": "^9.1.5",
"@hurdlegroup/robotjs": "^0.12.3",
"@octokit/rest": "^20.1.2",
"@octokit/rest": "^22.0.1",
"@primer/octicons": "^10.0.0",
"@sentry/cli": "1.72.0",
"@types/minimist": "^1.2.5",
@@ -21,22 +21,12 @@
"@types/semver": "^7.5.8",
"@types/stream-json": "^1.7.8",
"@types/temp": "^0.9.4",
"@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.7.0",
"@xmldom/xmldom": "^0.8.11",
"@xmldom/xmldom": "^0.8.12",
"buffer": "^6.0.3",
"chalk": "^4.1.0",
"check-for-leaks": "^1.2.1",
"eslint": "^8.57.1",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-markdown": "^5.1.0",
"eslint-plugin-mocha": "^10.5.0",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.6.0",
"events": "^3.2.0",
"folder-hash": "^4.1.1",
"folder-hash": "^4.1.2",
"got": "^11.8.5",
"husky": "^9.1.7",
"lint-staged": "^16.1.0",
@@ -44,6 +34,7 @@
"minimist": "^1.2.8",
"node-gyp": "^11.4.2",
"null-loader": "^4.0.1",
"oxlint": "^1.57.0",
"pre-flight": "^2.0.0",
"process": "^0.11.10",
"semver": "^7.6.3",

View File

@@ -125,7 +125,6 @@ feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch
fix_win32_synchronous_spellcheck.patch
chore_grandfather_in_electron_views_and_delegates.patch
refactor_patch_electron_permissiontypes_into_blink.patch
revert_views_remove_desktopwindowtreehostwin_window_enlargement.patch
fix_add_macos_memory_query_fallback_to_avoid_crash.patch
fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch
feat_add_support_for_embedder_snapshot_validation.patch
@@ -146,5 +145,8 @@ 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_restore_sdk_inputs_cross-toolchain_deps_for_macos.patch
fix_use_fresh_lazynow_for_onendworkitemimpl_after_didruntask.patch
fix_pulseaudio_stream_and_icon_names.patch
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
feat_allow_enabling_extensions_on_custom_protocols.patch
fix_initialize_com_on_desktopmedialistcapturethread_on_windows.patch

View File

@@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Charles Kerr <charles@charleskerr.com>
Date: Tue, 24 Mar 2026 16:33:20 -0500
Subject: fix: initialize COM on DesktopMediaListCaptureThread on Windows
On Windows, the DesktopMediaListCaptureThread runs with a UI message
pump that doesn't initialize COM. When WebRTC's window capturer
enumerates windows, interacting with UWP/Metro windows causes twinapi.dll
to internally call ::CoCreateInstance(), which hits Chromium's
DCheckedCoCreateInstance hook and triggers a crash.
This patch fixes the crash by ensuring COM is initialized on the
capture thread by calling `init_com_with_mta(false)`.
diff --git a/chrome/browser/media/webrtc/native_desktop_media_list.cc b/chrome/browser/media/webrtc/native_desktop_media_list.cc
index 9a8ebb4edfb92d9fe28ae4b87463a68547ea1ab3..13446d9849c54f1bfe515c3db4d69dd181ec6d39 100644
--- a/chrome/browser/media/webrtc/native_desktop_media_list.cc
+++ b/chrome/browser/media/webrtc/native_desktop_media_list.cc
@@ -786,6 +786,13 @@ NativeDesktopMediaList::NativeDesktopMediaList(
base::MessagePumpType thread_type = base::MessagePumpType::UI;
#else
base::MessagePumpType thread_type = base::MessagePumpType::DEFAULT;
+#endif
+#if BUILDFLAG(IS_WIN)
+ // On Windows, window enumeration via webrtc::DesktopCapturer may interact
+ // with UWP/Metro windows through twinapi.dll, which internally calls
+ // CoCreateInstance. Initialize COM on this thread to prevent crashes in
+ // Chromium's DCheckedCoCreateInstance hook.
+ thread_.init_com_with_mta(false);
#endif
thread_.StartWithOptions(base::Thread::Options(thread_type, 0));

View File

@@ -0,0 +1,120 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Damglador <vse.stopchanskyi@gmail.com>
Date: Fri, 26 Dec 2025 21:26:43 +0100
Subject: fix: pulseaudio stream and icon names
Use platform_util::GetXdgAppId() with fallback to argv0 as PA_PROP_APPLICATION_ICON_NAME.
Use electron::GetPossiblyOverriddenApplicationName()
to set environment variable "ELECTRON_PA_APP_NAME" in audio_service.cc,
to use it in pulse_util.cc for setting input/output pa_context name.
This replaces hard-codded kBrowserDisplayName that was used for PA_PROP_APPLICATION_ICON_NAME,
and PRODUCT_STRING that was used for pa_context names.
This is done to make audio streams recognizable in tools like qpwgrapth and general audio managers,
instead of having 20 "Chromium" outputs and "Chromium input" inputs, that are actually coming from
completely different applications.
This patch can be removed when upstream starts using AudioManager::SetGlobalAppName()
for all pa_context names (and when actually works with AudioServiceOutOfProcess).
diff --git a/content/browser/audio/audio_service.cc b/content/browser/audio/audio_service.cc
index 70615782c50d18606c3baa42a223e54f8619bc07..fb67e69f9ff46b432236b46913a1b10dd8302887 100644
--- a/content/browser/audio/audio_service.cc
+++ b/content/browser/audio/audio_service.cc
@@ -29,6 +29,9 @@
#include "services/audio/public/mojom/audio_service.mojom.h"
#include "services/audio/service.h"
#include "services/audio/service_factory.h"
+#if BUILDFLAG(IS_LINUX)
+#include "electron/shell/common/application_info.h"
+#endif
#if BUILDFLAG(ENABLE_PASSTHROUGH_AUDIO_CODECS) && BUILDFLAG(IS_WIN)
#define PASS_EDID_ON_COMMAND_LINE 1
@@ -109,6 +112,10 @@ void LaunchAudioServiceOutOfProcess(
mojo::PendingReceiver<audio::mojom::AudioService> receiver,
uint32_t codec_bitmask) {
std::vector<std::string> switches;
+#if BUILDFLAG(IS_LINUX)
+ // Set ELECTRON_PA_APP_NAME variable for pulse_util to grab and set pa_context name
+ setenv("ELECTRON_PA_APP_NAME", electron::GetPossiblyOverriddenApplicationName().c_str(), 1);
+#endif
#if BUILDFLAG(IS_MAC)
// On Mac, the audio service requires a CFRunLoop provided by a
// UI MessageLoop type, to run AVFoundation and CoreAudio code.
diff --git a/media/audio/pulse/pulse_util.cc b/media/audio/pulse/pulse_util.cc
index a08e42a464a3894cbf2b8e3cf8a320a33423b719..e5d69506e1585710a2540c91ca51cba7a4692575 100644
--- a/media/audio/pulse/pulse_util.cc
+++ b/media/audio/pulse/pulse_util.cc
@@ -10,6 +10,7 @@
#include <memory>
#include <type_traits>
+#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
@@ -20,6 +21,7 @@
#include "build/branding_buildflags.h"
#include "media/audio/audio_device_description.h"
#include "media/base/audio_timestamp_helper.h"
+#include "electron/shell/common/platform_util.h"
#if defined(DLOPEN_PULSEAUDIO)
#include "media/audio/pulse/pulse_stubs.h"
@@ -36,10 +38,8 @@ namespace pulse {
namespace {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-constexpr char kBrowserDisplayName[] = "google-chrome";
#define PRODUCT_STRING "Google Chrome"
#else
-constexpr char kBrowserDisplayName[] = "chromium-browser";
#define PRODUCT_STRING "Chromium"
#endif
@@ -236,7 +236,7 @@ bool InitPulse(pa_threaded_mainloop** mainloop, pa_context** context) {
pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop);
pa_context* pa_context =
- pa_context_new(pa_mainloop_api, PRODUCT_STRING " input");
+ pa_context_new(pa_mainloop_api, getenv("ELECTRON_PA_APP_NAME"));
if (!pa_context) {
pa_threaded_mainloop_free(pa_mainloop);
return false;
@@ -464,8 +464,11 @@ bool CreateInputStream(pa_threaded_mainloop* mainloop,
// Create a new recording stream and
// tells PulseAudio what the stream icon should be.
ScopedPropertyList property_list;
+ const std::string cmd_name =
+ base::CommandLine::ForCurrentProcess()->GetProgram().BaseName().value();
+ const std::string app_id = platform_util::GetXdgAppId().value_or(cmd_name);
pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME,
- kBrowserDisplayName);
+ app_id.c_str());
*stream = pa_stream_new_with_proplist(context, "RecordStream",
&sample_specifications, map,
property_list.get());
@@ -526,7 +529,7 @@ bool CreateOutputStream(raw_ptr<pa_threaded_mainloop>* mainloop,
pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(*mainloop);
*context = pa_context_new(
- pa_mainloop_api, app_name.empty() ? PRODUCT_STRING : app_name.c_str());
+ pa_mainloop_api, getenv("ELECTRON_PA_APP_NAME"));
RETURN_ON_FAILURE(*context, "Failed to create PulseAudio context.");
// A state callback must be set before calling pa_threaded_mainloop_lock() or
@@ -574,8 +577,11 @@ bool CreateOutputStream(raw_ptr<pa_threaded_mainloop>* mainloop,
// Open playback stream and
// tell PulseAudio what the stream icon should be.
ScopedPropertyList property_list;
+ const std::string cmd_name =
+ base::CommandLine::ForCurrentProcess()->GetProgram().BaseName().value();
+ const std::string app_id = platform_util::GetXdgAppId().value_or(cmd_name);
pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME,
- kBrowserDisplayName);
+ app_id.c_str());
*stream = pa_stream_new_with_proplist(
*context, "Playback", &sample_specifications, map, property_list.get());
RETURN_ON_FAILURE(*stream, "failed to create PA playback stream");

View File

@@ -8,10 +8,10 @@ such as the background turning black when maximizing the window and
dynamic background material settings not taking effect.
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index d1e06b675b19226cf3b78e1aada8d8f2d684fada..ce810555b8501797643987916a728cad8f5adaa5 100644
index e4da40256ce94d6a0896792a8ef2faa18e1fa5d2..3a5833fcc018f32e86c0a95a42937fb9ac6c5a40 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -184,6 +184,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) {
@@ -167,6 +167,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) {
}
}
@@ -23,7 +23,7 @@ index d1e06b675b19226cf3b78e1aada8d8f2d684fada..ce810555b8501797643987916a728cad
void DesktopWindowTreeHostWin::Init(const Widget::InitParams& params) {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index a40bd9f25fa07a553c011cf19f155f8158f4ae5f..ae2baec731b5fcd8be97f2177d23b860d67ab8bc 100644
index 27322ef34edf3fa8bfbd20b1baddcaf3b7555618..b8d1fa863fd05ebc3ab8ac5ef8c4d81361ce45fe 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -93,6 +93,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin

View File

@@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sam Attard <sattard@anthropic.com>
Date: Sun, 22 Mar 2026 19:21:45 +0000
Subject: fix: use fresh LazyNow for OnEndWorkItemImpl after DidRunTask
DidRunTask can cache the LazyNow early (via RecordTaskEnd in
BrowserUIThreadScheduler::OnTaskCompleted) before running task observers.
If a task observer's DidProcessTask triggers nested pump activity (nested
RunLoop, sync IPC, etc.), TimeKeeper's last_phase_end_ advances past the
cached value. The subsequent OnEndWorkItemImpl then computes a negative
delta and hits DCHECK(!delta.is_negative()) in RecordTimeInPhase.
Using a fresh LazyNow for OnEndWorkItemImpl samples the time after all
observers have run, which matches the existing comment's intent that
microtasks are extensions of the RunTask and the work item ends after them.
This is upstreamable: the bug exists whenever any TaskObserver::DidProcessTask
triggers nested pump activity, which is not forbidden by the contract.
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index bb09c99ea0b37a139440d0fe98c7f2f5e9c147e0..d27c34f8090ff54d20d8339c0ad56d37d6d61ab2 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -481,15 +481,22 @@ std::optional<WakeUp> ThreadControllerWithMessagePumpImpl::DoWorkImpl(
// `PendingTask` reference dangling.
selected_task.reset();
- LazyNow lazy_now_after_run_task(time_source_);
- main_thread_only().task_source->DidRunTask(lazy_now_after_run_task);
+ {
+ LazyNow lazy_now_did_run_task(time_source_);
+ main_thread_only().task_source->DidRunTask(lazy_now_did_run_task);
+ }
// End the work item scope after DidRunTask() as it can process microtasks
- // (which are extensions of the RunTask).
+ // (which are extensions of the RunTask). Use a fresh LazyNow here because
+ // DidRunTask may cache the LazyNow (via RecordTaskEnd) before running task
+ // observers, and those observers may trigger nested pump activity that
+ // advances TimeKeeper's last_phase_end_ past the cached value, resulting
+ // in a negative delta in RecordTimeInPhase.
+ LazyNow lazy_now_after_run_task(time_source_);
OnEndWorkItemImpl(lazy_now_after_run_task, run_depth);
- // If DidRunTask() read the clock (lazy_now_after_run_task.has_value()) or
- // if |batch_duration| > 0, store the clock value in `recent_time` so it can
- // be reused by SelectNextTask() at the next loop iteration.
+ // If OnEndWorkItemImpl() read the clock (lazy_now_after_run_task.has_value())
+ // or if |batch_duration| > 0, store the clock value in `recent_time` so it
+ // can be reused by SelectNextTask() at the next loop iteration.
if (lazy_now_after_run_task.has_value() || !batch_duration.is_zero()) {
recent_time = lazy_now_after_run_task.Now();
} else {

View File

@@ -1,285 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: John Kleinschmidt <jkleinsc@electronjs.org>
Date: Sat, 14 Jun 2025 16:21:07 -0400
Subject: Revert "[views] Remove DesktopWindowTreeHostWin::window_enlargement_"
This reverts commit 1771dbae6961e7bb7c22bbc6c77f84d90ef2be46.
Electron needs this patch to allow windows smaller than 64x64
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 988866d79a5d1dbd366ebdbff0e8eb2c0c498168..5761ac48be0a64618be0a94606149dd944e46e27 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -22626,6 +22626,21 @@
]
}
],
+ "TransparentHwndEnlargement": [
+ {
+ "platforms": [
+ "windows"
+ ],
+ "experiments": [
+ {
+ "name": "DisableTransparentHwndEnlargement",
+ "disable_features": [
+ "EnableTransparentHwndEnlargement"
+ ]
+ }
+ ]
+ }
+ ],
"TransportSecurityFileWriterScheduleAndroid": [
{
"platforms": [
diff --git a/ui/views/views_features.cc b/ui/views/views_features.cc
index 47077e16870889ef8f8c8b2adf58015bd5aff7fa..ba59e6e1609e61579bf49aca095490b083d72051 100644
--- a/ui/views/views_features.cc
+++ b/ui/views/views_features.cc
@@ -30,6 +30,14 @@ BASE_FEATURE(kEnableInputProtection, base::FEATURE_DISABLED_BY_DEFAULT);
// crbug.com/370856871.
BASE_FEATURE(kEnableTouchDragCursorSync, base::FEATURE_ENABLED_BY_DEFAULT);
+// Enables enlargement of HWNDs to a minimum size of 64x64 to handle reported
+// graphical glitches on certain hardware.
+// TODO(crbug.com/401996981): Remove this once enlargement is confirmed to no
+// longer be needed.
+BASE_FEATURE(kEnableTransparentHwndEnlargement,
+ "EnableTransparentHwndEnlargement",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
// Used to enable keyboard-accessible tooltips in Views UI, as opposed
// to kKeyboardAccessibleTooltip in //ui/base/ui_base_features.cc.
BASE_FEATURE(kKeyboardAccessibleTooltipInViews,
diff --git a/ui/views/views_features.h b/ui/views/views_features.h
index 888a16fb6213eceb131ae636dc643d7f2d5bcad9..6077412165081cd4abeaf0b061feb2f795ee8131 100644
--- a/ui/views/views_features.h
+++ b/ui/views/views_features.h
@@ -15,6 +15,7 @@ namespace views::features {
VIEWS_EXPORT BASE_DECLARE_FEATURE(kApplyInitialUrlToWebContents);
VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableInputProtection);
VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableTouchDragCursorSync);
+VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableTransparentHwndEnlargement);
VIEWS_EXPORT BASE_DECLARE_FEATURE(kKeyboardAccessibleTooltipInViews);
} // namespace views::features
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index e4da40256ce94d6a0896792a8ef2faa18e1fa5d2..d1e06b675b19226cf3b78e1aada8d8f2d684fada 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -85,6 +85,23 @@ namespace {
// This constant controls how many pixels wide that border is.
const int kMouseCaptureRegionBorder = 5;
+gfx::Size GetExpandedWindowSize(bool is_translucent, gfx::Size size) {
+ if (!base::FeatureList::IsEnabled(
+ features::kEnableTransparentHwndEnlargement) ||
+ !is_translucent) {
+ return size;
+ }
+
+ // Some AMD drivers can't display windows that are less than 64x64 pixels,
+ // so expand them to be at least that size. http://crbug.com/286609
+ gfx::Size expanded(std::max(size.width(), 64), std::max(size.height(), 64));
+ return expanded;
+}
+
+void InsetBottomRight(gfx::Rect* rect, const gfx::Vector2d& vector) {
+ rect->Inset(gfx::Insets::TLBR(0, 0, vector.y(), vector.x()));
+}
+
// Updates the cursor clip region. Used for mouse locking.
void UpdateMouseLockRegion(aura::Window* window, bool locked) {
if (!locked) {
@@ -342,9 +359,14 @@ bool DesktopWindowTreeHostWin::IsVisible() const {
}
void DesktopWindowTreeHostWin::SetSize(const gfx::Size& size) {
- const gfx::Size size_in_pixels =
+ gfx::Size size_in_pixels =
display::win::GetScreenWin()->DIPToScreenSize(GetHWND(), size);
- message_handler_->SetSize(size_in_pixels);
+ gfx::Size expanded =
+ GetExpandedWindowSize(message_handler_->is_translucent(), size_in_pixels);
+ window_enlargement_ =
+ gfx::Vector2d(expanded.width() - size_in_pixels.width(),
+ expanded.height() - size_in_pixels.height());
+ message_handler_->SetSize(expanded);
}
void DesktopWindowTreeHostWin::StackAbove(aura::Window* window) {
@@ -359,30 +381,40 @@ void DesktopWindowTreeHostWin::StackAtTop() {
}
void DesktopWindowTreeHostWin::CenterWindow(const gfx::Size& size) {
- const gfx::Size size_in_pixels =
+ gfx::Size size_in_pixels =
display::win::GetScreenWin()->DIPToScreenSize(GetHWND(), size);
- message_handler_->CenterWindow(size_in_pixels);
+ gfx::Size expanded_size;
+ expanded_size =
+ GetExpandedWindowSize(message_handler_->is_translucent(), size_in_pixels);
+ window_enlargement_ =
+ gfx::Vector2d(expanded_size.width() - size_in_pixels.width(),
+ expanded_size.height() - size_in_pixels.height());
+ message_handler_->CenterWindow(expanded_size);
}
void DesktopWindowTreeHostWin::GetWindowPlacement(
gfx::Rect* bounds,
ui::mojom::WindowShowState* show_state) const {
message_handler_->GetWindowPlacement(bounds, show_state);
+ InsetBottomRight(bounds, window_enlargement_);
*bounds = display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), *bounds);
}
gfx::Rect DesktopWindowTreeHostWin::GetWindowBoundsInScreen() const {
gfx::Rect pixel_bounds = message_handler_->GetWindowBoundsInScreen();
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
}
gfx::Rect DesktopWindowTreeHostWin::GetClientAreaBoundsInScreen() const {
gfx::Rect pixel_bounds = message_handler_->GetClientAreaBoundsInScreen();
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
}
gfx::Rect DesktopWindowTreeHostWin::GetRestoredBounds() const {
gfx::Rect pixel_bounds = message_handler_->GetRestoredBounds();
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
}
@@ -701,37 +733,44 @@ void DesktopWindowTreeHostWin::HideImpl() {
// other get/set methods work in DIP.
gfx::Rect DesktopWindowTreeHostWin::GetBoundsInPixels() const {
- const gfx::Rect bounds_px(message_handler_->GetClientAreaBounds());
+ gfx::Rect bounds(message_handler_->GetClientAreaBounds());
// If the window bounds were expanded we need to return the original bounds
// To achieve this we do the reverse of the expansion, i.e. add the
// window_expansion_top_left_delta_ to the origin and subtract the
// window_expansion_bottom_right_delta_ from the width and height.
- const gfx::Rect without_expansion_bounds_px(
- bounds_px.x() + window_expansion_top_left_delta_.x(),
- bounds_px.y() + window_expansion_top_left_delta_.y(),
- bounds_px.width() - window_expansion_bottom_right_delta_.x(),
- bounds_px.height() - window_expansion_bottom_right_delta_.y());
- return without_expansion_bounds_px;
+ gfx::Rect without_expansion(
+ bounds.x() + window_expansion_top_left_delta_.x(),
+ bounds.y() + window_expansion_top_left_delta_.y(),
+ bounds.width() - window_expansion_bottom_right_delta_.x() -
+ window_enlargement_.x(),
+ bounds.height() - window_expansion_bottom_right_delta_.y() -
+ window_enlargement_.y());
+ return without_expansion;
}
-void DesktopWindowTreeHostWin::SetBoundsInPixels(
- const gfx::Rect& bounds_in_pixels) {
+void DesktopWindowTreeHostWin::SetBoundsInPixels(const gfx::Rect& bounds) {
// If the window bounds have to be expanded we need to subtract the
// window_expansion_top_left_delta_ from the origin and add the
// window_expansion_bottom_right_delta_ to the width and height
- const gfx::Size old_content_size_px = GetBoundsInPixels().size();
-
- const gfx::Rect expanded_bounds_px(
- bounds_in_pixels.x() - window_expansion_top_left_delta_.x(),
- bounds_in_pixels.y() - window_expansion_top_left_delta_.y(),
- bounds_in_pixels.width() + window_expansion_bottom_right_delta_.x(),
- bounds_in_pixels.height() + window_expansion_bottom_right_delta_.y());
-
- // When `expanded_bounds_px` causes the window to be moved to a display with a
+ gfx::Size old_content_size = GetBoundsInPixels().size();
+
+ gfx::Rect expanded(
+ bounds.x() - window_expansion_top_left_delta_.x(),
+ bounds.y() - window_expansion_top_left_delta_.y(),
+ bounds.width() + window_expansion_bottom_right_delta_.x(),
+ bounds.height() + window_expansion_bottom_right_delta_.y());
+
+ gfx::Rect new_expanded(
+ expanded.origin(),
+ GetExpandedWindowSize(message_handler_->is_translucent(),
+ expanded.size()));
+ window_enlargement_ =
+ gfx::Vector2d(new_expanded.width() - expanded.width(),
+ new_expanded.height() - expanded.height());
+ // When |new_expanded| causes the window to be moved to a display with a
// different DSF, HWNDMessageHandler::OnDpiChanged() will be called and the
// window size will be scaled automatically.
- message_handler_->SetBounds(expanded_bounds_px,
- old_content_size_px != bounds_in_pixels.size());
+ message_handler_->SetBounds(new_expanded, old_content_size != bounds.size());
}
gfx::Rect
@@ -943,18 +982,26 @@ int DesktopWindowTreeHostWin::GetNonClientComponent(
void DesktopWindowTreeHostWin::GetWindowMask(const gfx::Size& size_px,
SkPath* path) {
- Widget* widget = GetWidget();
- if (!widget || !widget->non_client_view()) {
- return;
- }
+ // Request the window mask for hwnd of `size_px`. The hwnd size must be
+ // adjusted by `window_enlargement` to return to the client-expected window
+ // size (see crbug.com/41047830).
+ const gfx::Size adjusted_size_in_px =
+ size_px - gfx::Size(window_enlargement_.x(), window_enlargement_.y());
- widget->non_client_view()->GetWindowMask(
- display::win::GetScreenWin()->ScreenToDIPSize(GetHWND(), size_px), path);
- // Convert path in DIPs to pixels.
- if (!path->isEmpty()) {
- const float scale =
- display::win::GetScreenWin()->GetScaleFactorForHWND(GetHWND());
- *path = path->makeTransform(SkMatrix::Scale(scale, scale));
+ if (Widget* widget = GetWidget(); widget && widget->non_client_view()) {
+ widget->non_client_view()->GetWindowMask(
+ display::win::GetScreenWin()->ScreenToDIPSize(GetHWND(),
+ adjusted_size_in_px),
+ path);
+ // Convert path in DIPs to pixels.
+ if (!path->isEmpty()) {
+ const float scale =
+ display::win::GetScreenWin()->GetScaleFactorForHWND(GetHWND());
+ *path = path->makeTransform(SkMatrix::Scale(scale, scale));
+ }
+ } else if (!window_enlargement_.IsZero()) {
+ *path = SkPath::Rect(SkRect::MakeXYWH(0, 0, adjusted_size_in_px.width(),
+ adjusted_size_in_px.height()));
}
}
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index 27322ef34edf3fa8bfbd20b1baddcaf3b7555618..a40bd9f25fa07a553c011cf19f155f8158f4ae5f 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -175,7 +175,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
void ShowImpl() override;
void HideImpl() override;
gfx::Rect GetBoundsInPixels() const override;
- void SetBoundsInPixels(const gfx::Rect& bounds_in_pixels) override;
+ void SetBoundsInPixels(const gfx::Rect& bounds) override;
gfx::Rect GetBoundsInAcceleratedWidgetPixelCoordinates() override;
gfx::Point GetLocationOnScreenInPixels() const override;
void SetCapture() override;
@@ -330,6 +330,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
gfx::Vector2d window_expansion_top_left_delta_;
gfx::Vector2d window_expansion_bottom_right_delta_;
+ // Windows are enlarged to be at least 64x64 pixels, so keep track of the
+ // extra added here.
+ // TODO(crbug.com/401996981): This is likely no longer necessary and should be
+ // removed.
+ gfx::Vector2d window_enlargement_;
+
// Whether the window close should be converted to a hide, and then actually
// closed on the completion of the hide animation. This is cached because
// the property is set on the contained window which has a shorter lifetime.

View File

@@ -1,8 +0,0 @@
{
"plugins": [
"import"
],
"rules": {
"import/enforce-node-protocol-usage": ["error", "always"]
}
}

View File

@@ -1,13 +1,16 @@
#!/usr/bin/env python3
import argparse
import concurrent.futures
import json
import os
import subprocess
import warnings
from lib import git
from lib.patches import patch_from_dir
ELECTRON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
THREEWAY = "ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES" in os.environ
def apply_patches(target):
@@ -19,14 +22,43 @@ def apply_patches(target):
git.import_patches(
committer_email="scripts@electron",
committer_name="Electron Scripts",
output_prefix=f'[{os.path.basename(patch_dir)}] ',
patch_data=patch_from_dir(patch_dir),
repo=repo,
threeway=THREEWAY,
)
def is_roller_branch():
try:
branch = subprocess.check_output(
['git', '-C', ELECTRON_DIR, 'rev-parse', '--abbrev-ref', 'HEAD'],
stderr=subprocess.DEVNULL,
).decode('utf-8').strip()
return branch.startswith('roller/')
except subprocess.CalledProcessError:
return False
def apply_config(config):
for target in config:
apply_patches(target)
# Targets are independent git repos, so apply in parallel. The work is
# subprocess-bound (git am), so threads are sufficient. On roller/
# branches, patch conflicts are expected and interleaved failure output
# from multiple repos is hard to read, so force sequential there.
if is_roller_branch():
max_workers = 1
else:
max_workers = max(1, (os.cpu_count() or 4) - 2)
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as pool:
futures = {pool.submit(apply_patches, t): t for t in config}
failed = []
for f in concurrent.futures.as_completed(futures):
try:
f.result()
except Exception as e: # pylint: disable=broad-except
failed.append((futures[f].get('repo'), e))
if failed:
for repo, e in failed:
print(f'ERROR applying patches to {repo}: {e}')
raise failed[0][1]
def parse_args():
parser = argparse.ArgumentParser(description='Apply Electron patches')

View File

@@ -1,22 +1,143 @@
import { createHash } from 'node:crypto';
import * as fs from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { parseArgs } from 'node:util';
import { getChromiumVersionFromDEPS } from './lib/utils.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ELECTRON_DIR = resolve(__dirname, '..');
function getCommonTags () {
const tags = [];
if (process.env.TARGET_ARCH) tags.push(`target-arch:${process.env.TARGET_ARCH}`);
if (process.env.TARGET_PLATFORM) tags.push(`target-platform:${process.env.TARGET_PLATFORM}`);
if (process.env.GITHUB_HEAD_REF) {
// Will be set in pull requests
tags.push(`branch:${process.env.GITHUB_HEAD_REF}`);
} else if (process.env.GITHUB_REF_NAME) {
// Will be set for release branches
tags.push(`branch:${process.env.GITHUB_REF_NAME}`);
}
return tags;
}
async function uploadSeriesToDatadog (series) {
await fetch('https://api.datadoghq.com/api/v2/series', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'DD-API-KEY': process.env.DD_API_KEY
},
body: JSON.stringify({ series })
});
}
async function uploadCacheHitRateStats (hitRate, stats) {
const timestamp = Math.round(new Date().getTime() / 1000);
const tags = getCommonTags();
const series = [
{
metric: 'electron.build.effective-cache-hit-rate',
points: [{ timestamp, value: (hitRate * 100).toFixed(2) }],
type: 3, // GAUGE
unit: 'percent',
tags
}
];
// Add all raw stats as individual metrics
for (const [key, value] of Object.entries(stats)) {
series.push({
metric: `electron.build.stats.${key.toLowerCase()}`,
points: [{ timestamp, value }],
type: 1, // COUNT
tags
});
}
await uploadSeriesToDatadog(series);
}
async function uploadObjectChangeStats (stats) {
const timestamp = Math.round(new Date().getTime() / 1000);
const tags = getCommonTags();
if (stats['previous-chromium-version']) tags.push(`previous-chromium-version:${stats['previous-chromium-version']}`);
if (stats['chromium-version']) tags.push(`chromium-version:${stats['chromium-version']}`);
if (stats['previous-chromium-version'] && stats['chromium-version']) {
tags.push(`chromium-version-changed:${stats['previous-chromium-version'] !== stats['chromium-version']}`);
}
const series = [
{
metric: 'electron.build.object-change-rate',
points: [{ timestamp, value: (stats['change-rate'] * 100).toFixed(2) }],
type: 3, // GAUGE
unit: 'percent',
tags
},
{
metric: 'electron.build.object-change-size',
points: [{ timestamp, value: stats['change-size'] }],
type: 1, // COUNT
unit: 'byte',
tags
},
{
metric: 'electron.build.new-object-count',
points: [{ timestamp, value: stats['new-object-count'] }],
type: 1, // COUNT
unit: 'count',
tags
}
];
await uploadSeriesToDatadog(series);
}
async function main () {
const { positionals: [filename], values: { 'upload-stats': uploadStats } } = parseArgs({
const { positionals: [filename], values } = parseArgs({
allowPositionals: true,
options: {
'upload-stats': {
type: 'boolean',
default: false
},
'out-dir': {
type: 'string'
},
'input-object-checksums': {
type: 'string'
},
'output-object-checksums': {
type: 'string'
}
}
});
const {
'upload-stats': uploadStats,
'out-dir': outDir,
'input-object-checksums': inputObjectChecksums,
'output-object-checksums': outputObjectChecksums
} = values;
if (!filename) {
throw new Error('filename is required (should be a siso.INFO file)');
}
if ((inputObjectChecksums || outputObjectChecksums) && !outDir) {
throw new Error('--out-dir is required when using --input-object-checksums or --output-object-checksums');
} else if (outDir && (!inputObjectChecksums && !outputObjectChecksums)) {
throw new Error('--out-dir only makes sense with --input-object-checksums or --output-object-checksums');
}
const log = await fs.readFile(filename, 'utf-8');
// We expect to find a line which looks like stats=build.Stats{..., CacheHit:39008, Local:4778, Remote:0, LocalFallback:0, ...}
@@ -33,55 +154,84 @@ async function main () {
const hitRate = stats.CacheHit / (stats.Remote + stats.CacheHit + stats.LocalFallback);
const messagePrefix = process.env.GITHUB_ACTIONS ? '::notice title=Build Stats::' : '';
console.log(`${messagePrefix}Effective cache hit rate: ${(hitRate * 100).toFixed(2)}%`);
const objectChangeStats = {};
if (inputObjectChecksums || outputObjectChecksums) {
const depsContent = await fs.readFile(resolve(ELECTRON_DIR, 'DEPS'), 'utf8');
const currentVersion = getChromiumVersionFromDEPS(depsContent);
// Calculate the SHA256 for each object file under `outDir`
const files = await fs.readdir(outDir, { encoding: 'utf8', recursive: true });
const objectFiles = files.filter(file => file.endsWith('.o') || file.endsWith('.obj'));
const checksums = {};
for (const file of objectFiles) {
const content = await fs.readFile(resolve(outDir, file));
checksums[file] = createHash('sha256').update(content).digest('hex');
}
if (outputObjectChecksums) {
const outputData = {
chromiumVersion: currentVersion,
checksums
};
await fs.writeFile(outputObjectChecksums, JSON.stringify(outputData, null, 2));
}
if (inputObjectChecksums) {
const inputData = JSON.parse(await fs.readFile(inputObjectChecksums, 'utf8'));
const inputFiles = Object.keys(inputData.checksums);
let changedCount = 0;
let newObjectCount = 0;
let changedSize = 0;
// Count changed files (only those present in both input and current)
for (const file of inputFiles) {
if (!(file in checksums)) continue; // Skip deleted files
if (inputData.checksums[file] !== checksums[file]) {
changedCount++;
const stat = await fs.stat(resolve(outDir, file));
changedSize += stat.size;
}
}
// Count new files (in current but not in input)
for (const file of Object.keys(checksums)) {
if (!(file in inputData.checksums)) {
newObjectCount++;
const stat = await fs.stat(resolve(outDir, file));
changedSize += stat.size;
}
}
const changeRate = inputFiles.length > 0 ? changedCount / inputFiles.length : 0;
console.log(`${messagePrefix}Object change rate: ${(changeRate * 100).toFixed(2)}%`);
if (newObjectCount > 0) {
console.log(`${messagePrefix}New object count: ${newObjectCount}`);
}
console.log(`${messagePrefix}Cumulative changed object sizes: ${changedSize.toLocaleString()} bytes`);
objectChangeStats['change-rate'] = changeRate;
objectChangeStats['change-size'] = changedSize;
objectChangeStats['new-object-count'] = newObjectCount;
objectChangeStats['previous-chromium-version'] = inputData.chromiumVersion;
objectChangeStats['chromium-version'] = currentVersion;
}
}
if (uploadStats) {
if (!process.env.DD_API_KEY) {
throw new Error('DD_API_KEY is not set');
}
const timestamp = Math.round(new Date().getTime() / 1000);
await uploadCacheHitRateStats(hitRate, stats);
const tags = [];
if (process.env.TARGET_ARCH) tags.push(`target-arch:${process.env.TARGET_ARCH}`);
if (process.env.TARGET_PLATFORM) tags.push(`target-platform:${process.env.TARGET_PLATFORM}`);
if (process.env.GITHUB_HEAD_REF) {
// Will be set in pull requests
tags.push(`branch:${process.env.GITHUB_HEAD_REF}`);
} else if (process.env.GITHUB_REF_NAME) {
// Will be set for release branches
tags.push(`branch:${process.env.GITHUB_REF_NAME}`);
if (Object.keys(objectChangeStats).length > 0) {
await uploadObjectChangeStats(objectChangeStats);
}
const series = [
{
metric: 'electron.build.effective-cache-hit-rate',
points: [{ timestamp, value: (hitRate * 100).toFixed(2) }],
type: 3, // GAUGE
unit: 'percent',
tags
}
];
// Add all raw stats as individual metrics
for (const [key, value] of Object.entries(stats)) {
series.push({
metric: `electron.build.stats.${key.toLowerCase()}`,
points: [{ timestamp, value }],
type: 1, // COUNT
tags
});
}
await fetch('https://api.datadoghq.com/api/v2/series', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'DD-API-KEY': process.env.DD_API_KEY
},
body: JSON.stringify({ series })
});
}
}

View File

@@ -13,6 +13,7 @@ import posixpath
import re
import subprocess
import sys
import threading
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(SCRIPT_DIR)
@@ -59,7 +60,8 @@ def get_repo_root(path):
def am(repo, patch_data, threeway=False, directory=None, exclude=None,
committer_name=None, committer_email=None, keep_cr=True):
committer_name=None, committer_email=None, keep_cr=True,
output_prefix=None):
# --keep-non-patch prevents stripping leading bracketed strings on the subject line
args = ['--keep-non-patch']
if threeway:
@@ -80,9 +82,42 @@ def am(repo, patch_data, threeway=False, directory=None, exclude=None,
if committer_email is not None:
root_args += ['-c', 'user.email=' + committer_email]
root_args += ['-c', 'commit.gpgsign=false']
# git am rewrites the index 2-3x per patch. In large repos (Chromium's
# index is ~70MB / ~500K files) this dominates wall time. skipHash
# avoids recomputing the trailing SHA over the full index on every
# write, and index v4 roughly halves the on-disk size via path prefix
# compression. Also skip per-object fsync and auto-gc since a crashed
# apply is simply re-run from a clean reset.
root_args += [
'-c', 'index.skipHash=true',
'-c', 'index.version=4',
'-c', 'core.fsync=none',
'-c', 'gc.auto=0',
]
command = ['git'] + root_args + ['am'] + args
with subprocess.Popen(command, stdin=subprocess.PIPE) as proc:
proc.communicate(patch_data.encode('utf-8'))
popen_kwargs = {'stdin': subprocess.PIPE}
if output_prefix is not None:
popen_kwargs['stdout'] = subprocess.PIPE
popen_kwargs['stderr'] = subprocess.STDOUT
with subprocess.Popen(command, **popen_kwargs) as proc:
def feed_stdin():
proc.stdin.write(patch_data.encode('utf-8'))
proc.stdin.close()
if output_prefix is not None:
writer = threading.Thread(target=feed_stdin)
writer.start()
for line in proc.stdout:
try:
sys.stdout.write(
f'{output_prefix}{line.decode("utf-8", "replace")}')
sys.stdout.flush()
except BrokenPipeError:
pass
writer.join()
proc.wait()
else:
feed_stdin()
proc.wait()
if proc.returncode != 0:
raise RuntimeError(f"Command {command} returned {proc.returncode}")
@@ -93,6 +128,12 @@ def import_patches(repo, ref=UPSTREAM_HEAD, **kwargs):
update_ref(repo=repo, ref=ref, newvalue='HEAD')
if ref != _LEGACY_UPSTREAM_HEAD:
update_ref(repo=repo, ref=_LEGACY_UPSTREAM_HEAD, newvalue='HEAD')
# Upgrade to index v4 before applying so every intermediate index write
# during am benefits from path-prefix compression (roughly halves index
# size in large repos).
subprocess.call(
['git', '-C', repo, 'update-index', '--index-version', '4'],
stderr=subprocess.DEVNULL)
am(repo=repo, **kwargs)

View File

@@ -8,6 +8,8 @@ const path = require('node:path');
const ELECTRON_DIR = path.resolve(__dirname, '..', '..');
const SRC_DIR = path.resolve(ELECTRON_DIR, '..');
const CHROMIUM_VERSION_DEPS_REGEX = /chromium_version':\n +'(.+?)',/m;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const pass = chalk.green('✓');
const fail = chalk.red('✗');
@@ -162,10 +164,15 @@ function compareVersions (v1, v2) {
return 0;
}
function getChromiumVersionFromDEPS (depsContent) {
return CHROMIUM_VERSION_DEPS_REGEX.exec(depsContent)?.[1] ?? null;
}
module.exports = {
chunkFilenames,
compareVersions,
findMatchingFiles,
getChromiumVersionFromDEPS,
getCurrentBranch,
getElectronExec,
getOutDir,

View File

@@ -0,0 +1,27 @@
const BLOCK_NAMES = new Set(['describe', 'it', 'context', 'test', 'specify', 'suite']);
export default {
meta: { name: 'no-only-tests' },
rules: {
'no-only-tests': {
meta: { type: 'problem' },
create (context) {
return {
MemberExpression (node) {
if (
node.property.type === 'Identifier' &&
node.property.name === 'only' &&
node.object.type === 'Identifier' &&
BLOCK_NAMES.has(node.object.name)
) {
context.report({
node: node.property,
message: `${node.object.name}.only not permitted`
});
}
}
};
}
}
}
};

View File

@@ -5,12 +5,11 @@ import * as fs from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { compareVersions } from './lib/utils.js';
import { compareVersions, getChromiumVersionFromDEPS } from './lib/utils.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ELECTRON_DIR = resolve(__dirname, '..');
const DEPS_REGEX = /chromium_version':\n +'(.+?)',/m;
const CL_REGEX = /https:\/\/chromium-review\.googlesource\.com\/c\/(chromium\/src|devtools\/devtools-frontend|v8\/v8)\/\+\/(\d+)(#\S+)?/g;
const ROLLER_BRANCH_PATTERN = /^roller\/chromium\/(.+)$/;
@@ -140,12 +139,12 @@ async function main () {
cwd: ELECTRON_DIR,
encoding: 'utf8'
});
baseVersion = DEPS_REGEX.exec(baseDepsContent)?.[1] ?? null;
baseVersion = getChromiumVersionFromDEPS(baseDepsContent);
} catch {
// baseVersion remains null
}
const depsContent = await fs.readFile(resolve(ELECTRON_DIR, 'DEPS'), 'utf8');
const newVersion = DEPS_REGEX.exec(depsContent)?.[1] ?? null;
const newVersion = getChromiumVersionFromDEPS(depsContent);
if (!baseVersion || !newVersion) {
console.error('Could not determine Chromium version range');

View File

@@ -1,10 +1,8 @@
#!/usr/bin/env node
const { ESLint } = require('eslint');
const minimist = require('minimist');
const childProcess = require('node:child_process');
const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');
@@ -72,22 +70,31 @@ function spawnAndCheckExitCode (cmd, args, opts) {
}
}
async function runEslint (eslint, filenames, { fix, verbose }) {
const formatter = await eslint.loadFormatter();
let successCount = 0;
const results = await eslint.lintFiles(filenames);
for (const result of results) {
successCount += result.errorCount === 0 ? 1 : 0;
if (verbose && result.errorCount === 0 && result.warningCount === 0) {
console.log(`${result.filePath}: no errors or warnings`);
function runOxlint (filenames, { fix } = {}) {
const oxlintBin = path.join(
ELECTRON_ROOT,
'node_modules',
'.bin',
IS_WINDOWS ? 'oxlint.cmd' : 'oxlint'
);
const args = [];
if (fix) args.push('--fix');
// Emit GitHub Actions annotations directly when running in CI so errors
// surface inline on the PR without a separate problem matcher.
if (process.env.GITHUB_ACTIONS === 'true') {
args.push('--format=github');
}
for (const chunk of chunkFilenames(filenames)) {
const result = childProcess.spawnSync(oxlintBin, [...args, '--', ...chunk], {
stdio: 'inherit',
shell: IS_WINDOWS,
cwd: ELECTRON_ROOT
});
if (result.status !== 0) {
return false;
}
}
console.log(formatter.format(results));
if (fix) {
await ESLint.outputFixes(results);
}
return successCount === filenames.length;
return true;
}
function cpplint (args) {
@@ -155,15 +162,7 @@ const LINTERS = [{
ignoreRoots: ['.github/workflows/node_modules', 'spec/node_modules', 'spec/fixtures/native-addon'],
test: filename => filename.endsWith('.js') || filename.endsWith('.ts') || filename.endsWith('.mjs'),
run: async (opts, filenames) => {
const eslint = new ESLint({
// Do not use the lint cache on CI builds
cache: !process.env.CI,
cacheLocation: `node_modules/.eslintcache.${crypto.createHash('md5').update(fs.readFileSync(__filename)).digest('hex')}`,
extensions: ['.js', '.ts'],
fix: opts.fix,
resolvePluginsRelativeTo: ELECTRON_ROOT
});
const clean = await runEslint(eslint, filenames, { fix: opts.fix, verbose: opts.verbose });
const clean = runOxlint(filenames, { fix: opts.fix });
if (!clean) {
console.error('Linting had errors');
process.exit(1);
@@ -295,13 +294,56 @@ const LINTERS = [{
// Run the remaining checks only in docs
const docs = filenames.filter(filename => path.dirname(filename).split(path.sep)[0] === 'docs');
// Node.js builtin modules that should be imported with the `node:` protocol
// in docs code blocks. This mirrors what the old docs/.eslintrc.json
// enforced via `import/enforce-node-protocol-usage` (added in #42113,
// originally as `unicorn/prefer-node-protocol`).
const NODE_BUILTINS = new Set([
'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console',
'constants', 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'domain',
'events', 'fs', 'http', 'http2', 'https', 'inspector', 'module', 'net',
'os', 'path', 'perf_hooks', 'process', 'punycode', 'querystring',
'readline', 'repl', 'stream', 'string_decoder', 'sys', 'timers', 'tls',
'trace_events', 'tty', 'url', 'util', 'v8', 'vm', 'wasi',
'worker_threads', 'zlib'
]);
const NODE_IMPORT_RE = /(?:require\s*\(\s*|from\s+|import\s*\(\s*)['"]([^'"/]+)(?:\/[^'"]*)?['"]/g;
const BREAKING_CHANGES_MD = path.join('docs', 'breaking-changes.md');
// Strip line and block comments from a code snippet so the import check
// does not flag bare-specifier examples that appear inside `// ...` or
// `/* ... */` explanations. This is a conservative textual strip — it is
// not a full JS parser, but it is good enough for docs code blocks and
// matches the behavior of the AST-based rule it replaced.
const stripComments = (source) => source
.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '))
.replace(/(^|[^:])\/\/[^\n]*/g, (_m, prefix) => prefix);
for (const filename of docs) {
const contents = fs.readFileSync(filename, 'utf8');
const codeBlocks = await getCodeBlocks(contents);
const skipNodeProtocolCheck = path.normalize(filename).endsWith(BREAKING_CHANGES_MD);
for (const codeBlock of codeBlocks) {
const line = codeBlock.position.start.line;
// Check for bare Node.js builtin imports in JS/TS/fiddle code blocks.
if (!skipNodeProtocolCheck && codeBlock.lang && ['js', 'ts', 'javascript', 'typescript', 'fiddle'].includes(codeBlock.lang.toLowerCase())) {
const blockLines = stripComments(codeBlock.value).split('\n');
for (let i = 0; i < blockLines.length; i++) {
NODE_IMPORT_RE.lastIndex = 0;
let match;
while ((match = NODE_IMPORT_RE.exec(blockLines[i])) !== null) {
const mod = match[1];
if (NODE_BUILTINS.has(mod)) {
console.log(`${filename}:${line + 1 + i} Use 'node:${mod}' instead of bare '${mod}' in code blocks`);
errors = true;
}
}
}
}
if (codeBlock.lang) {
// Enforce all lowercase language identifiers
if (codeBlock.lang.toLowerCase() !== codeBlock.lang) {
@@ -368,26 +410,6 @@ const LINTERS = [{
}
}
const eslint = new ESLint({
// Do not use the lint cache on CI builds
cache: !process.env.CI,
cacheLocation: `node_modules/.eslintcache.${crypto.createHash('md5').update(fs.readFileSync(__filename)).digest('hex')}`,
fix: opts.fix,
overrideConfigFile: path.join(ELECTRON_ROOT, 'docs', '.eslintrc.json'),
resolvePluginsRelativeTo: ELECTRON_ROOT
});
const clean = await runEslint(
eslint,
docs.filter(
// TODO(dsanders11): Once we move to newer ESLint and the flat config,
// switch to using `ignorePatterns` and `warnIgnore: false` instead of
// explicitly filtering out this file that we don't want to lint
(filename) => !filename.endsWith('docs/breaking-changes.md')
),
{ fix: opts.fix, verbose: opts.verbose }
);
errors ||= !clean;
if (errors) {
process.exit(1);
}

View File

@@ -8,7 +8,9 @@ from pathlib import Path
SRC_DIR = Path(__file__).resolve().parents[3]
sys.path.append(os.path.join(SRC_DIR, 'third_party/electron_node/tools'))
sys.path.append(str(Path(__file__).resolve().parents[1])) # electron/script/
from lib.util import get_out_dir
import install
class LoadPythonDictionaryError(Exception):
@@ -31,13 +33,6 @@ def LoadPythonDictionary(path):
)
return file_data
def get_out_dir():
out_dir = 'Testing'
override = os.environ.get('ELECTRON_OUT_DIR')
if override is not None:
out_dir = override
return os.path.join(SRC_DIR, 'out', out_dir)
if __name__ == '__main__':
node_root_dir = os.path.join(SRC_DIR, 'third_party/electron_node')
out = {}

View File

@@ -77,6 +77,7 @@
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/error_thrower.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/language_util.h"
#include "shell/common/node_includes.h"
#include "shell/common/options_switches.h"

View File

@@ -8,7 +8,6 @@
#include "base/path_service.h"
#include "shell/common/electron_paths.h"
#include "shell/common/node_includes.h"
#include "shell/common/process_util.h"
#include "shell/common/thread_restrictions.h"

View File

@@ -19,7 +19,6 @@
#include "shell/browser/api/electron_api_web_frame_main.h"
#include "shell/browser/native_window.h"
#include "shell/common/keyboard_util.h"
#include "shell/common/node_includes.h"
#include "ui/base/cocoa/menu_utils.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"

View File

@@ -54,17 +54,9 @@ gin_helper::Handle<SafeStorage> SafeStorage::Create(v8::Isolate* isolate) {
return gin_helper::CreateHandle(isolate, new SafeStorage(isolate));
}
SafeStorage::SafeStorage(v8::Isolate* isolate) {
if (electron::Browser::Get()->is_ready()) {
OnFinishLaunching({});
} else {
Browser::Get()->AddObserver(this);
}
}
SafeStorage::SafeStorage(v8::Isolate* isolate) {}
SafeStorage::~SafeStorage() {
Browser::Get()->RemoveObserver(this);
}
SafeStorage::~SafeStorage() = default;
gin::ObjectTemplateBuilder SafeStorage::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
@@ -85,7 +77,11 @@ gin::ObjectTemplateBuilder SafeStorage::GetObjectTemplateBuilder(
;
}
void SafeStorage::OnFinishLaunching(base::DictValue launch_info) {
void SafeStorage::EnsureAsyncEncryptorRequested() {
DCHECK(electron::Browser::Get()->is_ready());
if (encryptor_requested_)
return;
encryptor_requested_ = true;
g_browser_process->os_crypt_async()->GetInstance(
base::BindOnce(&SafeStorage::OnOsCryptReady, base::Unretained(this)),
os_crypt_async::Encryptor::Option::kEncryptSyncCompat);
@@ -95,13 +91,21 @@ void SafeStorage::OnOsCryptReady(os_crypt_async::Encryptor encryptor) {
encryptor_ = std::move(encryptor);
is_available_ = true;
// This callback may fire from a posted task without an active V8 scope.
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
for (auto& pending : pending_availability_checks_) {
pending.Resolve(true);
}
pending_availability_checks_.clear();
for (auto& pending : pending_encrypts_) {
std::string ciphertext;
bool encrypted = encryptor_->EncryptString(pending.plaintext, &ciphertext);
if (encrypted) {
pending.promise.Resolve(
electron::Buffer::Copy(pending.promise.isolate(), ciphertext)
.ToLocalChecked());
electron::Buffer::Copy(isolate, ciphertext).ToLocalChecked());
} else {
pending.promise.RejectWithErrorMessage(
"Error while encrypting the text provided to "
@@ -117,8 +121,6 @@ void SafeStorage::OnOsCryptReady(os_crypt_async::Encryptor encryptor) {
encryptor_->DecryptString(pending.ciphertext, &plaintext, &flags);
if (decrypted) {
v8::Isolate* isolate = pending.promise.isolate();
v8::HandleScope handle_scope(isolate);
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
dict.Set("shouldReEncrypt", flags.should_reencrypt);
@@ -155,16 +157,33 @@ bool SafeStorage::IsEncryptionAvailable() {
#endif
}
bool SafeStorage::IsAsyncEncryptionAvailable() {
if (!electron::Browser::Get()->is_ready())
return false;
v8::Local<v8::Promise> SafeStorage::IsAsyncEncryptionAvailable(
v8::Isolate* isolate) {
gin_helper::Promise<bool> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
if (!electron::Browser::Get()->is_ready()) {
promise.Resolve(false);
return handle;
}
#if BUILDFLAG(IS_LINUX)
return is_available_ || (use_password_v10_ &&
static_cast<BrowserProcessImpl*>(g_browser_process)
->linux_storage_backend() == "basic_text");
#else
return is_available_;
if (use_password_v10_ && static_cast<BrowserProcessImpl*>(g_browser_process)
->linux_storage_backend() == "basic_text") {
promise.Resolve(true);
return handle;
}
#endif
EnsureAsyncEncryptorRequested();
if (is_available_) {
promise.Resolve(true);
return handle;
}
pending_availability_checks_.push_back(std::move(promise));
return handle;
}
void SafeStorage::SetUsePasswordV10(bool use) {
@@ -270,6 +289,8 @@ v8::Local<v8::Promise> SafeStorage::encryptStringAsync(
return handle;
}
EnsureAsyncEncryptorRequested();
if (is_available_) {
std::string ciphertext;
bool encrypted = encryptor_->EncryptString(plaintext, &ciphertext);
@@ -318,6 +339,8 @@ v8::Local<v8::Promise> SafeStorage::decryptStringAsync(
return handle;
}
EnsureAsyncEncryptorRequested();
if (is_available_) {
std::string plaintext;
os_crypt_async::Encryptor::DecryptFlags flags;

View File

@@ -10,7 +10,6 @@
#include "build/build_config.h"
#include "components/os_crypt/async/common/encryptor.h"
#include "shell/browser/browser_observer.h"
#include "shell/browser/event_emitter_mixin.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/promise.h"
@@ -37,8 +36,7 @@ class Handle;
namespace electron::api {
class SafeStorage final : public gin_helper::DeprecatedWrappable<SafeStorage>,
public gin_helper::EventEmitterMixin<SafeStorage>,
private BrowserObserver {
public gin_helper::EventEmitterMixin<SafeStorage> {
public:
static gin_helper::Handle<SafeStorage> Create(v8::Isolate* isolate);
@@ -57,14 +55,16 @@ class SafeStorage final : public gin_helper::DeprecatedWrappable<SafeStorage>,
~SafeStorage() override;
private:
// BrowserObserver:
void OnFinishLaunching(base::DictValue launch_info) override;
// Lazily request the async encryptor on first use. ESM named imports
// eagerly evaluate all electron module getters, so requesting in the
// constructor would touch the OS keychain even when safeStorage is unused.
void EnsureAsyncEncryptorRequested();
void OnOsCryptReady(os_crypt_async::Encryptor encryptor);
bool IsEncryptionAvailable();
bool IsAsyncEncryptionAvailable();
v8::Local<v8::Promise> IsAsyncEncryptionAvailable(v8::Isolate* isolate);
void SetUsePasswordV10(bool use);
@@ -85,6 +85,7 @@ class SafeStorage final : public gin_helper::DeprecatedWrappable<SafeStorage>,
bool use_password_v10_ = false;
bool encryptor_requested_ = false;
bool is_available_ = false;
std::optional<os_crypt_async::Encryptor> encryptor_;
@@ -114,6 +115,8 @@ class SafeStorage final : public gin_helper::DeprecatedWrappable<SafeStorage>,
std::string ciphertext;
};
std::vector<PendingDecrypt> pending_decrypts_;
std::vector<gin_helper::Promise<bool>> pending_availability_checks_;
};
} // namespace electron::api

View File

@@ -26,7 +26,6 @@
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/node_includes.h"
#include "shell/common/process_util.h"
#include "skia/ext/skia_utils_mac.h"

View File

@@ -47,11 +47,13 @@
#include "content/browser/renderer_host/render_widget_host_impl.h" // nogncheck
#include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck
#include "content/browser/web_contents/web_contents_impl.h" // nogncheck
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/desktop_media_id.h"
#include "content/public/browser/desktop_streams_registry.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/file_select_listener.h"
@@ -90,9 +92,11 @@
#include "services/service_manager/public/cpp/interface_provider.h"
#include "shell/browser/api/electron_api_browser_window.h"
#include "shell/browser/api/electron_api_debugger.h"
#include "shell/browser/api/electron_api_session.h"
#include "shell/browser/api/electron_api_web_frame_main.h"
#include "shell/browser/api/frame_subscriber.h"
#include "shell/browser/api/message_port.h"
#include "shell/browser/api/save_page_handler.h"
#include "shell/browser/browser.h"
#include "shell/browser/child_web_contents_tracker.h"
#include "shell/browser/electron_autofill_driver_factory.h"
@@ -2885,8 +2889,8 @@ v8::Local<v8::Promise> WebContents::SavePage(
return handle;
}
auto* handler = new SavePageHandler(web_contents(), std::move(promise));
handler->Handle(full_file_path, save_type);
auto* handler = new SavePageHandler{std::move(promise)};
handler->Handle(full_file_path, save_type, web_contents());
return handle;
}

View File

@@ -22,8 +22,6 @@
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h" // nogncheck
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "content/common/frame.mojom-forward.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/frame_tree_node_id.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/javascript_dialog_manager.h"
@@ -33,9 +31,6 @@
#include "content/public/common/stop_find_action.h"
#include "electron/buildflags/buildflags.h"
#include "printing/buildflags/buildflags.h"
#include "shell/browser/api/electron_api_debugger.h"
#include "shell/browser/api/electron_api_session.h"
#include "shell/browser/api/save_page_handler.h"
#include "shell/browser/background_throttling_source.h"
#include "shell/browser/event_emitter_mixin.h"
#include "shell/browser/extended_web_contents_observer.h"
@@ -43,13 +38,10 @@
#include "shell/browser/preload_script.h"
#include "shell/browser/ui/inspectable_web_contents_delegate.h"
#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
#include "shell/common/api/api.mojom.h"
#include "shell/common/gin_helper/cleaned_up_at_exit.h"
#include "shell/common/gin_helper/constructible.h"
#include "shell/common/gin_helper/handle.h"
#include "shell/common/gin_helper/pinnable.h"
#include "shell/common/gin_helper/wrappable.h"
#include "shell/common/web_contents_utility.mojom.h"
#include "ui/base/models/image_model.h"
#include "v8/include/cppgc/persistent.h"
@@ -66,8 +58,14 @@ struct DeviceEmulationParams;
// enum class PermissionType;
} // namespace blink
namespace base {
class FilePath;
class Value;
} // namespace base
namespace content {
enum class KeyboardEventProcessingResult;
class DevToolsAgentHost;
class WebContents;
} // namespace content
@@ -79,6 +77,8 @@ namespace gin_helper {
class Dictionary;
class ErrorThrower;
template <typename T>
class Handle;
template <typename T>
class Promise;
} // namespace gin_helper
@@ -103,7 +103,6 @@ class ElectronBrowserContext;
class InspectableWebContents;
class WebContentsZoomController;
class WebViewGuestDelegate;
class WebDialogHelper;
class NativeWindow;
class OffScreenRenderWidgetHostView;
class OffScreenWebContentsView;
@@ -111,7 +110,9 @@ class OffScreenWebContentsView;
namespace api {
class BaseWindow;
class Debugger;
class FrameSubscriber;
class Session;
// Wrapper around the content::WebContents.
class WebContents final : public ExclusiveAccessContext,

View File

@@ -36,6 +36,8 @@
#include "shell/common/node_includes.h"
#include "shell/common/v8_util.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/blink/public/mojom/frame/media_player_action.mojom.h"
#include "ui/gfx/geometry/point.h"
namespace {
@@ -260,6 +262,28 @@ v8::Local<v8::Promise> WebFrameMain::ExecuteJavaScript(
return handle;
}
void WebFrameMain::CopyVideoFrameAt(int x, int y) {
if (!CheckRenderFrame())
return;
auto location = gfx::Point(x, y);
auto action = blink::mojom::MediaPlayerAction(
blink::mojom::MediaPlayerActionType::kCopyVideoFrame,
/*enable=*/true);
return render_frame_host()->ExecuteMediaPlayerActionAtLocation(location,
action);
}
void WebFrameMain::SaveVideoFrameAs(int x, int y) {
if (!CheckRenderFrame())
return;
auto location = gfx::Point(x, y);
auto action = blink::mojom::MediaPlayerAction(
blink::mojom::MediaPlayerActionType::kSaveVideoFrameAs,
/*enable=*/true);
return render_frame_host()->ExecuteMediaPlayerActionAtLocation(location,
action);
}
bool WebFrameMain::Reload() {
if (!CheckRenderFrame())
return false;
@@ -596,6 +620,8 @@ void WebFrameMain::FillObjectTemplate(v8::Isolate* isolate,
.SetMethod("executeJavaScript", &WebFrameMain::ExecuteJavaScript)
.SetMethod("collectJavaScriptCallStack",
&WebFrameMain::CollectDocumentJSCallStack)
.SetMethod("copyVideoFrameAt", &WebFrameMain::CopyVideoFrameAt)
.SetMethod("saveVideoFrameAs", &WebFrameMain::SaveVideoFrameAs)
.SetMethod("reload", &WebFrameMain::Reload)
.SetMethod("isDestroyed", &WebFrameMain::IsDestroyed)
.SetMethod("_send", &WebFrameMain::Send)

View File

@@ -118,6 +118,8 @@ class WebFrameMain final : public gin_helper::DeprecatedWrappable<WebFrameMain>,
v8::Local<v8::Promise> ExecuteJavaScript(gin::Arguments* args,
const std::u16string& code);
void CopyVideoFrameAt(int x, int y);
void SaveVideoFrameAs(int x, int y);
bool Reload();
bool IsDestroyed() const;
void Send(v8::Isolate* isolate,

View File

@@ -12,9 +12,8 @@
namespace electron::api {
SavePageHandler::SavePageHandler(content::WebContents* web_contents,
gin_helper::Promise<void> promise)
: web_contents_(web_contents), promise_(std::move(promise)) {}
SavePageHandler::SavePageHandler(gin_helper::Promise<void> promise)
: promise_{std::move(promise)} {}
SavePageHandler::~SavePageHandler() = default;
@@ -26,9 +25,10 @@ void SavePageHandler::OnDownloadCreated(content::DownloadManager* manager,
}
bool SavePageHandler::Handle(const base::FilePath& full_path,
const content::SavePageType& save_type) {
const content::SavePageType& save_type,
content::WebContents* web_contents) {
auto* download_manager =
web_contents_->GetBrowserContext()->GetDownloadManager();
web_contents->GetBrowserContext()->GetDownloadManager();
download_manager->AddObserver(this);
// Chromium will create a 'foo_files' directory under the directory of saving
// page 'foo.html' for holding other resource files of 'foo.html'.
@@ -36,7 +36,7 @@ bool SavePageHandler::Handle(const base::FilePath& full_path,
full_path.RemoveExtension().BaseName().value() +
FILE_PATH_LITERAL("_files"));
bool result =
web_contents_->SavePage(full_path, saved_main_directory_path, save_type);
web_contents->SavePage(full_path, saved_main_directory_path, save_type);
download_manager->RemoveObserver(this);
// If initialization fails which means fail to create |DownloadItem|, we need
// to delete the |SavePageHandler| instance to avoid memory-leak.

View File

@@ -5,7 +5,6 @@
#ifndef ELECTRON_SHELL_BROWSER_API_SAVE_PAGE_HANDLER_H_
#define ELECTRON_SHELL_BROWSER_API_SAVE_PAGE_HANDLER_H_
#include "base/memory/raw_ptr.h"
#include "components/download/public/common/download_item.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/save_page_type.h"
@@ -26,12 +25,12 @@ namespace electron::api {
class SavePageHandler : private content::DownloadManager::Observer,
private download::DownloadItem::Observer {
public:
SavePageHandler(content::WebContents* web_contents,
gin_helper::Promise<void> promise);
explicit SavePageHandler(gin_helper::Promise<void> promise);
~SavePageHandler() override;
bool Handle(const base::FilePath& full_path,
const content::SavePageType& save_type);
const content::SavePageType& save_type,
content::WebContents* web_contents);
private:
void Destroy(download::DownloadItem* item);
@@ -43,7 +42,6 @@ class SavePageHandler : private content::DownloadManager::Observer,
// download::DownloadItem::Observer:
void OnDownloadUpdated(download::DownloadItem* item) override;
raw_ptr<content::WebContents> web_contents_; // weak
gin_helper::Promise<void> promise_;
};

View File

@@ -22,6 +22,7 @@
#include "shell/browser/window_list.h"
#include "shell/common/application_info.h"
#include "shell/common/gin_converters/login_item_settings_converter.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/thread_restrictions.h"
namespace electron {

View File

@@ -15,7 +15,6 @@
#include "base/task/cancelable_task_tracker.h"
#include "base/values.h"
#include "shell/browser/window_list_observer.h"
#include "shell/common/gin_helper/promise.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
@@ -35,7 +34,17 @@ class Arguments;
namespace gin_helper {
class Arguments;
}
template <typename T>
class Promise;
} // namespace gin_helper
namespace v8 {
template <typename T>
class Local;
class Isolate;
class Promise;
class Value;
} // namespace v8
namespace electron {

View File

@@ -42,6 +42,7 @@
#include "shell/common/gin_converters/image_converter.h"
#include "shell/common/gin_converters/login_item_settings_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/skia_util.h"
#include "shell/common/thread_restrictions.h"
#include "skia/ext/font_utils.h"

View File

@@ -73,9 +73,8 @@ void CertificateManagerModel::Create(CreationCallback callback) {
}
CertificateManagerModel::CertificateManagerModel(
net::NSSCertDatabase* nss_cert_database,
bool is_user_db_available)
: cert_db_(nss_cert_database), is_user_db_available_(is_user_db_available) {
net::NSSCertDatabase* nss_cert_database)
: cert_db_(nss_cert_database) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
@@ -91,24 +90,6 @@ int CertificateManagerModel::ImportFromPKCS12(
imported_certs);
}
int CertificateManagerModel::ImportUserCert(const std::string& data) {
return cert_db_->ImportUserCert(data);
}
bool CertificateManagerModel::ImportCACerts(
const net::ScopedCERTCertificateList& certificates,
net::NSSCertDatabase::TrustBits trust_bits,
net::NSSCertDatabase::ImportCertFailureList* not_imported) {
return cert_db_->ImportCACerts(certificates, trust_bits, not_imported);
}
bool CertificateManagerModel::ImportServerCert(
const net::ScopedCERTCertificateList& certificates,
net::NSSCertDatabase::TrustBits trust_bits,
net::NSSCertDatabase::ImportCertFailureList* not_imported) {
return cert_db_->ImportServerCert(certificates, trust_bits, not_imported);
}
bool CertificateManagerModel::SetCertTrust(
CERTCertificate* cert,
net::CertType type,
@@ -116,19 +97,13 @@ bool CertificateManagerModel::SetCertTrust(
return cert_db_->SetCertTrust(cert, type, trust_bits);
}
bool CertificateManagerModel::Delete(CERTCertificate* cert) {
return cert_db_->DeleteCertAndKey(cert);
}
// static
void CertificateManagerModel::DidGetCertDBOnUIThread(
net::NSSCertDatabase* cert_db,
bool is_user_db_available,
CreationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto model = base::WrapUnique(
new CertificateManagerModel(cert_db, is_user_db_available));
auto model = base::WrapUnique(new CertificateManagerModel(cert_db));
std::move(callback).Run(std::move(model));
}
@@ -138,11 +113,10 @@ void CertificateManagerModel::DidGetCertDBOnIOThread(
net::NSSCertDatabase* cert_db) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
bool is_user_db_available = !!cert_db->GetPublicSlot();
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&CertificateManagerModel::DidGetCertDBOnUIThread, cert_db,
is_user_db_available, std::move(callback)));
std::move(callback)));
}
// static

View File

@@ -29,8 +29,6 @@ class CertificateManagerModel {
~CertificateManagerModel();
bool is_user_db_available() const { return is_user_db_available_; }
// Accessor for read-only access to the underlying NSSCertDatabase.
const net::NSSCertDatabase* cert_db() const { return cert_db_; }
@@ -44,37 +42,6 @@ class CertificateManagerModel {
bool is_extractable,
net::ScopedCERTCertificateList* imported_certs);
// Import user certificate from DER encoded |data|.
// Returns a net error code on failure.
int ImportUserCert(const std::string& data);
// Import CA certificates.
// Tries to import all the certificates given. The root will be trusted
// according to |trust_bits|. Any certificates that could not be imported
// will be listed in |not_imported|.
// |trust_bits| should be a bit field of TRUST* values from NSSCertDatabase.
// Returns false if there is an internal error, otherwise true is returned and
// |not_imported| should be checked for any certificates that were not
// imported.
bool ImportCACerts(const net::ScopedCERTCertificateList& certificates,
net::NSSCertDatabase::TrustBits trust_bits,
net::NSSCertDatabase::ImportCertFailureList* not_imported);
// Import server certificate. The first cert should be the server cert. Any
// additional certs should be intermediate/CA certs and will be imported but
// not given any trust.
// Any certificates that could not be imported will be listed in
// |not_imported|.
// |trust_bits| can be set to explicitly trust or distrust the certificate, or
// use TRUST_DEFAULT to inherit trust as normal.
// Returns false if there is an internal error, otherwise true is returned and
// |not_imported| should be checked for any certificates that were not
// imported.
bool ImportServerCert(
const net::ScopedCERTCertificateList& certificates,
net::NSSCertDatabase::TrustBits trust_bits,
net::NSSCertDatabase::ImportCertFailureList* not_imported);
// Set trust values for certificate.
// |trust_bits| should be a bit field of TRUST* values from NSSCertDatabase.
// Returns true on success or false on failure.
@@ -82,27 +49,18 @@ class CertificateManagerModel {
net::CertType type,
net::NSSCertDatabase::TrustBits trust_bits);
// Delete the cert. Returns true on success. |cert| is still valid when this
// function returns.
bool Delete(CERTCertificate* cert);
private:
CertificateManagerModel(net::NSSCertDatabase* nss_cert_database,
bool is_user_db_available);
explicit CertificateManagerModel(net::NSSCertDatabase* nss_cert_database);
// Methods used during initialization, see the comment at the top of the .cc
// file for details.
static void DidGetCertDBOnUIThread(net::NSSCertDatabase* cert_db,
bool is_user_db_available,
CreationCallback callback);
static void DidGetCertDBOnIOThread(CreationCallback callback,
net::NSSCertDatabase* cert_db);
static void GetCertDBOnIOThread(CreationCallback callback);
raw_ptr<net::NSSCertDatabase> cert_db_;
// Whether the certificate database has a public slot associated with the
// profile. If not set, importing certificates is not allowed with this model.
bool is_user_db_available_;
};
#endif // ELECTRON_SHELL_BROWSER_CERTIFICATE_MANAGER_MODEL_H_

View File

@@ -91,16 +91,6 @@ AutofillDriver* AutofillDriverFactory::DriverForFrame(
return driver.get();
}
void AutofillDriverFactory::AddDriverForFrame(
content::RenderFrameHost* render_frame_host,
CreationCallback factory_method) {
auto insertion_result = driver_map_.try_emplace(render_frame_host, nullptr);
// This can be called twice for the key representing the main frame.
if (insertion_result.second) {
insertion_result.first->second = std::move(factory_method).Run();
}
}
void AutofillDriverFactory::DeleteDriverForFrame(
content::RenderFrameHost* render_frame_host) {
driver_map_.erase(render_frame_host);

View File

@@ -7,7 +7,6 @@
#include <memory>
#include "base/functional/callback_forward.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "shell/common/api/api.mojom.h"
@@ -21,9 +20,6 @@ class AutofillDriverFactory
: private content::WebContentsObserver,
public content::WebContentsUserData<AutofillDriverFactory> {
public:
typedef base::OnceCallback<std::unique_ptr<AutofillDriver>()>
CreationCallback;
~AutofillDriverFactory() override;
static void BindAutofillDriver(
@@ -32,8 +28,6 @@ class AutofillDriverFactory
content::RenderFrameHost* render_frame_host);
AutofillDriver* DriverForFrame(content::RenderFrameHost* render_frame_host);
void AddDriverForFrame(content::RenderFrameHost* render_frame_host,
CreationCallback factory_method);
void DeleteDriverForFrame(content::RenderFrameHost* render_frame_host);
void CloseAllPopups();

View File

@@ -63,6 +63,7 @@
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_private_key.h"
#include "pdf/pdf_features.h"
#include "printing/buildflags/buildflags.h"
#include "services/device/public/cpp/geolocation/geolocation_system_permission_manager.h"
#include "services/device/public/cpp/geolocation/location_provider.h"
@@ -1591,6 +1592,46 @@ bool ElectronBrowserClient::ShouldEnableStrictSiteIsolation() {
return true;
}
bool ElectronBrowserClient::ShouldEnableSubframeZoom() {
#if BUILDFLAG(ENABLE_PDF_VIEWER)
return chrome_pdf::features::IsOopifPdfEnabled();
#else
return false;
#endif
}
#if BUILDFLAG(ENABLE_PDF_VIEWER)
std::optional<network::CrossOriginEmbedderPolicy>
ElectronBrowserClient::MaybeOverrideLocalURLCrossOriginEmbedderPolicy(
content::NavigationHandle* navigation_handle) {
if (!chrome_pdf::features::IsOopifPdfEnabled() ||
!navigation_handle->IsPdf()) {
return std::nullopt;
}
content::RenderFrameHost* pdf_extension = navigation_handle->GetParentFrame();
if (!pdf_extension) {
return std::nullopt;
}
content::RenderFrameHost* pdf_embedder = pdf_extension->GetParent();
CHECK(pdf_embedder);
return pdf_embedder->GetCrossOriginEmbedderPolicy();
}
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
bool ElectronBrowserClient::DoesSiteRequireDedicatedProcess(
content::BrowserContext* browser_context,
const GURL& effective_site_url) {
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
return GetEnabledExtensionFromEffectiveURL(browser_context,
effective_site_url) != nullptr;
#else
return content::ContentBrowserClient::DoesSiteRequireDedicatedProcess(
browser_context, effective_site_url);
#endif
}
void ElectronBrowserClient::BindHostReceiverForRenderer(
content::RenderProcessHost* render_process_host,
mojo::GenericPendingReceiver receiver) {

View File

@@ -30,6 +30,7 @@ class FilePath;
namespace content {
class ClientCertificateDelegate;
class NavigationHandle;
class PlatformNotificationService;
class NavigationThrottleRegistry;
class QuotaPermissionContext;
@@ -82,6 +83,14 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
// content::ContentBrowserClient:
std::string GetApplicationLocale() override;
bool ShouldEnableStrictSiteIsolation() override;
bool ShouldEnableSubframeZoom() override;
#if BUILDFLAG(ENABLE_PDF_VIEWER)
std::optional<network::CrossOriginEmbedderPolicy>
MaybeOverrideLocalURLCrossOriginEmbedderPolicy(
content::NavigationHandle* navigation_handle) override;
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
bool DoesSiteRequireDedicatedProcess(content::BrowserContext* browser_context,
const GURL& effective_site_url) override;
void BindHostReceiverForRenderer(
content::RenderProcessHost* render_process_host,
mojo::GenericPendingReceiver receiver) override;

View File

@@ -71,7 +71,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
std::move(transferrable_loader), original_url);
#if BUILDFLAG(ENABLE_PDF_VIEWER)
if (base::FeatureList::IsEnabled(chrome_pdf::features::kPdfOopif) &&
if (chrome_pdf::features::IsOopifPdfEnabled() &&
extension_id == extension_misc::kPdfExtensionId) {
pdf::PdfViewerStreamManager::Create(web_contents);
pdf::PdfViewerStreamManager::FromWebContents(web_contents)

View File

@@ -26,7 +26,6 @@ class BrowserContext;
namespace extensions {
class ElectronExtensionLoader;
class ValueStoreFactory;
// A simplified version of ExtensionSystem for app_shell. Allows
// app_shell to skip initialization of services it doesn't need.

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