Compare commits

...

76 Commits

Author SHA1 Message Date
trop[bot]
c09e2aa6b8 fix: outdated execution path for COM activation (#50518)
* fix: outdated execution path

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

* fix: use stub exe when detected

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jan Hannemann <jan.hannemann@outlook.com>
2026-03-26 20:19:14 +00:00
trop[bot]
44f02f61ff fix: hex-encode Windows notification icon temp filenames (#50484)
* fix: hex-encode Windows notification icon temp filenames

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

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

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

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

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

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

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

---------

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

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

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-26 11:47:34 -04:00
trop[bot]
36c88a46db fix: [a11y] fire AXMenuOpened event when ARIA menu is added to DOM (#50505)
* fix: fire AXMenuOpened event when a visible ARIA menu instance is added to the DOM

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

* fix: remove redundent FireMenuPopupEndForDeletedMenus

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

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

* chore: add feature flag (kDynamicMenuPopupEvents)

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

* chore: update patches

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

* chore: update patches

---------

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

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

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

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

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

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

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

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
2026-03-25 23:25:39 -07:00
Keeley Hammond
e1c17fd1e8 chore: cherry-pick fbfb27470bf6 from chromium (#50438)
* chore: cherry-pick fbfb27470bf6 from chromium

* chore: update patches

* chore: cherry-pick bf6dd974238b from angle (#50437)

* fix: remove duplicate MaxGeometryUniformBlocks from angle cherry-pick patch

The angle cherry-pick added MaxGeometryUniformBlocks in new locations,
but it already existed in the EXT_geometry_shader section on 40-x-y,
causing a duplicate struct member build error in ShaderLang.h.

Re-exported the patch from the angle repo to correctly generate context
lines instead of additions for fields that already exist in the base.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: guard validatePerStageMaxUniformBlocks with ANGLE_SH_VERSION check

The angle cherry-pick patch that adds this field to ShCompileOptions
may not be applied in CI due to the angle repo not being found during
patch application. Guard the usage with a version check so compilation
succeeds regardless of angle patch application order.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Revert "fix: guard validatePerStageMaxUniformBlocks with ANGLE_SH_VERSION check"

This reverts commit 9ebf565988.

* fix: add angle to patches config and re-export angle patches for M144

The angle entry was missing from patches/config.json on 40-x-y, which
meant angle patches were never applied during gclient sync or CI builds.
This caused the chromium cherry-pick to fail with "no member named
'validatePerStageMaxUniformBlocks' in 'ShCompileOptions'".

Also re-exports the angle patches against the M144 base, resolving
conflicts from the M142-targeted patches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: Update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 10:27:49 -07:00
Keeley Hammond
2871c1d392 fix: read nodeIntegrationInWorker from per-frame WebPreferences (#50122) (#50467)
Previously the renderer checked a process-wide command-line switch to
decide whether to create a Node.js environment for dedicated workers.
When a renderer process hosted multiple WebContents with different
nodeIntegrationInWorker values (e.g. via window.open with overridden
webPreferences in setWindowOpenHandler), all workers in the process
used whichever value the first WebContents set on the command line.

Instead, plumb the flag through blink's WorkerSettings at worker
creation time, copying it from the initiating frame's WebPreferences.
The check on the worker thread then reads the per-worker value. Nested
workers inherit the flag from their parent worker via
WorkerSettings::Copy.

The --node-integration-in-worker command-line switch is removed as it
is no longer consumed.

Co-authored-by: Samuel Attard <sam@electronjs.org>
2026-03-24 22:48:27 +00:00
Keeley Hammond
0d3f57f3de chore: cherry-pick 074d472db745 from chromium (#50449)
* chore: cherry-pick 074d472db745 from chromium

* chore: update patches

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

---------

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
Co-authored-by: Claude <svc-devxp-claude@slack-corp.com>
2026-03-24 22:45:37 +00:00
Keeley Hammond
6247116f8d chore: cherry-pick 3 changes from chromium (#50460)
* chore: cherry-pick 45c5a70d984d from chromium

Describe a vector of segments as "segments", not "tokens"

Bug: 487117772
Change-Id: I2dc132c4e618e398e1f8bdabc03a8d2ab6c118e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7606599
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1590040}

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

* chore: cherry-pick 05e4b544803c from chromium

Stringify CSSUnparsedValues via toString, as normal

Bug: 484751092
Change-Id: I5db45ad85f780c67a2ea3ba8482c390ebab10068
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7600415
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1590041}

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

* chore: cherry-pick 5efc7a0127a6 from chromium

Validate CSSUnparsedValues upon assignment

Fixed: 484751092
Change-Id: Id7f888a6df8c02ade24910900f5d01909cb2dfad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7595347
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1590110}

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

* chore: update patches

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

---------

Co-authored-by: Claude <svc-devxp-claude@slack-corp.com>
2026-03-24 13:33:29 -07:00
Keeley Hammond
5a1bda2277 chore: cherry-pick 50b057660b4d from chromium (#50441)
* chore: cherry-pick 50b057660b4d from chromium

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2026-03-23 21:41:21 -04:00
trop[bot]
cca4a7388d fix: don't re-parse URL unnecessarily when handling dialogs (#50401)
* fix: fallback to opaque URL when needed inside dialog callback

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

* refactor: remove additional URL parsing entirely when showing dialogs

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

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

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

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

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

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

* style: make linter happy

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

* style: make linter actually happy

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

* fix: address failing `safeDialogs` tests

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

---------

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

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

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

Fixes #49455

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

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

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: João Silva <joaomrsilva@tecnico.ulisboa.pt>
2026-03-19 18:48:40 -07:00
trop[bot]
a4955396e1 ci: output build cache hit rate as GHA annotation (#50368)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2026-03-19 16:46:40 +01:00
trop[bot]
bd193de24b fix: correctly track BaseWindow::IsActive() on MacOS (#50337)
fix: correctly set IsActive() in BaseWindow on MacOS

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Kyle Cutler <kycutler@microsoft.com>
2026-03-19 10:00:28 -04:00
trop[bot]
ef66db337e chore: Respect HTTP(S) proxy env variable for Yarn (#50352)
Respect HTTP(S) proxy env variable for Yarn

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Filip Mösner <filip.mosner@seznam.cz>
2026-03-18 21:11:34 -07:00
trop[bot]
00827a2da4 fix: always call the original impl in swizzled mousedown impls (#50355)
fix: always call the original implementation in swizzled mousedown implementations

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Noah Gregory <noahmgregory@gmail.com>
2026-03-18 20:49:10 -07:00
trop[bot]
5f020c4685 fix: ensure WebContents::WasShown runs when window is shown (#50344)
Avoids a freeze when failing to enter fullscreen on macOS.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Beutner <beutner.john@gmail.com>
2026-03-18 19:13:08 -04:00
trop[bot]
e094b3939e fix: user resizable transparent windows on win32 (#50301)
test: revert win32 frameless and transparent resizable expectations

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Justin Mayfield <tooker@gmail.com>
2026-03-17 09:46:36 +01:00
trop[bot]
339d44c723 fix: add ASAR support to additional copy methods (#50287)
* fix: add ASAR support for additional copy methods

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

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

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Noah Gregory <noahmgregory@gmail.com>
2026-03-16 16:23:38 -04:00
trop[bot]
ee2d3db030 test: fix esm issue in node-spec-runner (#50294)
Chromium added a top-level package.json in CL:7485999 that sets
the type to module and breaks commonjs tests run via
node-spec-runner.js. This commit temporarily changes the type to
commonjs while running the tests, then changes it back to module when done.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-16 21:13:30 +01:00
trop[bot]
139e238d07 build: remove redundant bits of ncrypto node patch (#50280)
build: remove redundant ncrypto node patch

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

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

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-16 12:57:53 +01:00
trop[bot]
3dd04c2725 ci: update actions/cache to 5.0.3 (#50236)
chore: update actions/cache to 5.0.3

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

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-03-13 10:04:57 +01:00
Keeley Hammond
d32b8a64d0 chore: cherry-pick 7911bee5d90e from skia (#50229)
* chore: cherry-pick 7911bee5d90e from skia

* chore: update patch
2026-03-13 02:48:07 +00:00
Keeley Hammond
425fe98c14 chore: cherry-pick d5b0cb2acffe from v8 (#50231)
* chore: cherry-pick d5b0cb2acffe from v8

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2026-03-13 01:48:31 +00:00
Kanishk Ranjan
6b4b7df937 chore: backport running mac app icons from chromium (crrev.com/c/7239386) (#50188)
chore: backport running mac app icons from chromium
2026-03-12 15:15:46 +01:00
John Kleinschmidt
cc81658f40 ci: add timeout to test step (#50211)
ci: add timeout to test step (#50186)

Additionally, take a screenshot on timeout so that we can debug why there is a hang
2026-03-12 14:40:24 +01:00
trop[bot]
9be5389e77 fix: preserve staged update dir when pruning orphaned updates on macOS (#50216)
* fix: preserve staged update dir when pruning orphaned update dirs on macOS

The previous squirrel.mac patch cleaned up all staged update directories
before starting a new download. This kept disk usage bounded but broke
quitAndInstall() if called while a subsequent checkForUpdates() was in
flight — the already-staged bundle would be deleted out from under it.

This reworks the patch to read ShipItState.plist and preserve the
directory it references, deleting only truly orphaned update.XXXXXXX
directories. Disk footprint stays bounded (at most 2 dirs: staged +
in-progress) and quitAndInstall() remains safe mid-check.

Also adds test coverage for the quitAndInstall/checkForUpdates race and
a triple-stack scenario where 3 updates arrive without a restart.

Refs https://github.com/electron/electron/issues/50200

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

* chore: update patches

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
2026-03-12 01:57:22 +00:00
trop[bot]
8264495aff fix: prevent traffic light buttons flashing on deminiaturize (#50207)
* fix: prevent traffic light buttons flashing on deminiaturize

When a window with a custom `trafficLightPosition` is minimized and
restored, macOS re-layouts the title bar container during the
deminiaturize animation, causing the traffic light buttons to briefly
appear at their default position before being repositioned.

Fix this by hiding the buttons container in `windowWillMiniaturize` and
restoring them (with a redraw to the correct position) in
`windowDidDeminiaturize`.

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

* chore: address feedback from review

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-11 15:36:30 -04:00
trop[bot]
ed9ec1a535 fix: don't call TaskDialogIndirect with disabled parent windows (#50189)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Noah Gregory <noahmgregory@gmail.com>
2026-03-10 18:06:15 -07:00
Keeley Hammond
b8362fe96f chore: cherry-pick 12f932985275 from chromium (#50173)
* chore: cherry-pick 12f932985275 from chromium

* chore: update patches
2026-03-10 10:11:13 +01:00
Samuel Attard
4480c3545d fix: correct parsing of second-instance additionalData (#50162)
- POSIX: validate StringToSizeT result and token count when splitting
  the socket message into argv and additionalData; previously a
  malformed message could produce incorrect slicing.
- Windows: base64-encode additionalData before embedding in the
  null-delimited wchar_t buffer. The prior reinterpret_cast approach
  dropped everything after the first aligned 0x0000 in the serialized
  payload, so complex objects could arrive truncated.

Manually backported from #50119
2026-03-10 09:42:27 +01:00
trop[bot]
de5d94bc49 fix: validate protocol scheme names in setAsDefaultProtocolClient (#50158)
fix: validate protocol scheme names in setAsDefaultProtocolClient

On Windows, `app.setAsDefaultProtocolClient(protocol)` directly
concatenates the protocol string into the registry key path with no
validation. A protocol name containing `\` could write to an arbitrary
subkey under `HKCU\Software\Classes\`, potentially hijacking existing
protocol handlers.

To fix this, add `Browser::IsValidProtocolScheme()` which validates that a protocol
name conforms to the RFC 3986 scheme grammar:

  scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )

This rejects backslashes, forward slashes, whitespace, and any other
characters not permitted in URI schemes.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-10 00:04:27 -05:00
trop[bot]
4fe62718b9 fix: use requesting frame origin in permission helper and device choosers (#50149)
* fix: use requesting frame origin instead of top-level URL for permissions

`WebContentsPermissionHelper::RequestPermission` passes
`web_contents_->GetLastCommittedURL()` as the origin to the permission
manager instead of the actual requesting frame's origin. This enables
origin confusion when granting permissions to embedded third-party iframes,
since app permission handlers see the top-level origin instead of the
iframe's. The same pattern exists in the HID, USB, and Serial device
choosers, where grants are keyed to the primary main frame's origin rather
than the requesting frame's.

Fix this by using `requesting_frame->GetLastCommittedOrigin()` in all
affected code paths, renaming `details.requestingUrl` to
`details.requestingOrigin`, and populating it with the serialized
origin only.

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

* chore: keep requestingUrl name in permission handler details

The previous commit changed the details.requestingUrl field to
details.requestingOrigin in permission request/check handlers. That
field was already populated from the requesting frame's RFH, so the
rename was unnecessary and would break apps that read the existing
property. Revert to requestingUrl to preserve the existing API shape.

The functional changes to use the requesting frame in
WebContentsPermissionHelper and the HID/USB/Serial choosers remain.

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-03-09 23:03:45 -05:00
Keeley Hammond
1c9e1cd141 chore: cherry-pick a08731cf6d70 from angle (#50168) 2026-03-09 19:15:10 -07:00
trop[bot]
04e39e24e6 refactor: use WHATWG URL instead of url.parse (#50142)
refactor: use WHATWG URL instead of url.parse

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-09 17:36:12 -04:00
trop[bot]
e0c8b9b168 fix: InspectorFrontendHost override in embedded windows (#50138)
fix: InspectorFrontendHost override in embedded windows

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-09 11:26:55 -04:00
trop[bot]
77f3f5f2b2 fix: screen.getCursorScreenPoint() crash on Wayland (#50104)
* docs: document that getCursorScreenPoint() needs a Window on Wayland

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

* feat: add IsWayland() helper

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

* fix: Wayland crash in GetCursorScreenPoint()

fix: support Screen::GetCursorScreenPoint() on X11

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2026-03-09 12:33:49 +01:00
trop[bot]
a349e616d4 fix: strictly validate sender for internal IPC reply channels (#50125)
The sender-mismatch check in invokeInWebContents and invokeInWebFrameMain
used a negative condition (`type === 'frame' && sender !== expected`),
which only rejected mismatched frame senders and accepted anything else.

Invert to a positive check so only the exact expected frame can resolve
the reply — matches the guard style used elsewhere in lib/browser/.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-03-09 11:36:47 +01:00
trop[bot]
8c1b38d443 build: pin diff.renames for deterministic patch export (#50127)
git format-patch honors diff.renames, which defaults to 'true' (rename
detection only). If a user has diff.renames=copies configured at the
system or global level, exported patches may encode new files as copies
of similar existing files, causing spurious diffs against patches
exported on other machines. Pin diff.renames=true to match git's
default.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-03-09 11:36:45 +01:00
trop[bot]
06278ba3b3 fix: validate response header names and values before AddHeader (#50131)
Matches the existing validation applied to request headers in
electron_api_url_loader.cc.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-03-09 11:36:34 +01:00
trop[bot]
15b95fcd52 fix: Revert "updated Alt detection to explicitly exclude AltGraph/AltGr (#49778)" (#50110)
Revert "fix: updated Alt detection to explicitly exclude AltGraph/AltGr (#49778)"

This reverts commit 90c9de70ac.

Ref: https://github.com/electron/electron/issues/50050

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: clavin <clavin@electronjs.org>
2026-03-06 21:05:45 -08:00
trop[bot]
d574f99c9e docs: cleanup desktop-capturer doc after chromium audio capture additions (#50113)
* docs: cleanup desktop-capturer doc after chromium audio capture additions

Co-authored-by: Michaela Laurencin <mlaurencin@electronjs.org>

* Apply suggestions from code review

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

Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com>

* disable linter for list in note

Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Michaela Laurencin <mlaurencin@electronjs.org>
Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com>
2026-03-06 16:06:03 -05:00
trop[bot]
cbc6959269 docs: remove release schedule in favor of https://releases.electronjs.org/schedule (#50107)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-03-06 15:47:54 -05:00
trop[bot]
f4c7a3ff66 fix: prevent use-after-free in PowerMonitor via dangling OS callbacks (#50088)
PowerMonitor registered OS-level callbacks (HWND UserData and
WTS/suspend notifications on Windows, shutdown handler and lock-screen
observer on macOS) but never cleaned them up in its destructor. The JS
layer also only held the native object in a closure-local variable,
allowing GC to reclaim it while those registrations still referenced
freed memory.

Retain the native PowerMonitor at module level in power-monitor.ts so
it cannot be garbage-collected. Add DestroyPlatformSpecificMonitors()
to properly tear down OS registrations on destruction: on Windows,
unregister WTS and suspend notifications, clear GWLP_USERDATA, and
destroy the HWND; on macOS, remove the emitter from the global
MacLockMonitor and reset the Browser shutdown handler.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-05 17:21:22 -05:00
trop[bot]
66ce2439cc fix: avoid redundant page-favicon-updated events on setBounds (#50084)
* fix: avoid duplicate calls in electron_api_web_contents

Co-authored-by: ANANYA542 <ananyashrma6512@gmail.com>

* Style: fix lint errors

Co-authored-by: ANANYA542 <ananyashrma6512@gmail.com>

* fix: prevent duplicate page-favicon-updated events and add regression test

Co-authored-by: Ananya542 <ananyashrma6512@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: ANANYA542 <ananyashrma6512@gmail.com>
2026-03-05 12:47:58 -05:00
trop[bot]
de61f6c5e8 feat: show toast dismissal reason on Windows (#50030)
* feat: show toast dismissal reason on Windows

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

* Update docs/api/notification.md

Co-authored-by: David Sanders <dsanders11@ucsbalum.com>

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-05 11:29:21 +01:00
electron-roller[bot]
90f85f2bf4 chore: bump chromium to 144.0.7559.236 (40-x-y) (#50060)
* chore: bump chromium in DEPS to 144.0.7559.236

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-03-05 10:30:45 +01:00
trop[bot]
60951cdca9 fix: use proper quoting for exe paths and args on Windows (#50075)
Previously, GetProtocolLaunchPath and FormatCommandLineString in
browser_win.cc used naive quoting which could break when paths or
arguments contained backslashes, spaces, or embedded quotes.

Fix by extracting the CommandLineToArgvW-compatible quoting logic from
relauncher_win.cc into a shared utility and use it in both browser_win.cc
and relauncher_win.cc to properly quote the exe path and each argument
individually.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-05 10:27:40 +01:00
trop[bot]
a3022df30f build: fix code-signing for MacOS x64 tests (#50072)
* fix: code-sign binaries for notification tests

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

* test: remove redundent feedURL test

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

* test: move squirrel feed tests to api-autoupdater

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

* fix: fix SQRLShipItRequest.JSONKeyPathsByPropertyKey mappings

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

* Revert "fix: fix SQRLShipItRequest.JSONKeyPathsByPropertyKey mappings"

This reverts commit 5ad9892a67.

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

* test: unsign tests requiring no signed app

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

* fixup! fix: fix SQRLShipItRequest.JSONKeyPathsByPropertyKey mappings

chore: fix patch shear

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2026-03-05 10:27:29 +01:00
trop[bot]
996fbfd6bc chore: remove applescript from trash (#50065)
Previously, when trashItemAtURL: failed (e.g. on network shares or
under app translocation), the code fell back to constructing an
AppleScript that interpolated the bundle path directly into a string
literal via %@ with no escaping. This was fragile and unnecessary —
trashItemAtURL: has been the standard API since 10.8 and covers the
relevant cases. The fix simply removes the AppleScript fallback
entirely, so Trash() now returns the result of trashItemAtURL: directly.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-04 16:14:15 +01:00
trop[bot]
79d1e32281 fix: uaf in non-client hittest during view teardown (#50053)
* fix: uaf in non-client hittest during view teardown

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

* chore: update crash spec

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2026-03-03 14:16:05 -05:00
trop[bot]
1598b9116d fix: validate USB device selection against filtered device list (#50048)
Previously, UsbChooserController::OnDeviceChosen looked up the chosen
device_id via chooser_context_->GetDeviceInfo(), which searches all
known USB devices on the system rather than the filtered list shown to
the select-usb-device handler. This meant a device excluded by the
renderer's filters or exclusion_filters could still be granted
permission if the handler returned its GUID.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-03 09:08:03 -05:00
trop[bot]
68cd11118d fix: use setter's creation context when proxying setter in OverrideGlobalPropertyFromIsolatedWorld (#50018)
The setter branch was deriving source_context from getter-> instead of
setter->. Currently latent since the only call site passes both from
the same preload context, but this would crash or mis-resolve if a
future call site passed a setter without a getter or from a different
context.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-03-03 10:45:57 +01:00
trop[bot]
1e2d5902a5 fix: bind offscreen paint callback to child WebContents (#50024)
fix: bind offscreen paint callback to child WebContents

Previously, MaybeOverrideCreateParamsForNewWindow bound the
OffScreenWebContentsView's paint callback to the parent WebContents
using base::Unretained(this). This was both unsafe (dangling pointer
risk if the parent is destroyed before the child) and semantically
incorrect — paint events belong to the child window, not the opener.

Replace the callback in MaybeOverrideCreateParamsForNewWindow with
base::DoNothing(), then rebind it to the child WebContents in
AddNewContents via a new SetCallback method on OffScreenWebContentsView.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-03 10:43:08 +01:00
trop[bot]
1bbe8c9610 fix: use destination context when wrapping VideoFrame in contextBridge (#50022)
Enter the destination context scope before creating the VideoFrame V8
wrapper, matching the sibling Element and Blob branches. Without this,
ScriptState::ForCurrentRealm resolved to the calling context instead of
the target context, producing an incorrect wrapper.

Also switch to ScriptState::From with an explicit context argument to
make the intent clearer.

Adds spec coverage for VideoFrame crossing the bridge in both
directions and adds VideoFrame to the existing prototype checks.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-03-02 23:30:16 -08:00
trop[bot]
4bfc2a83fc chore: remove unused commandLineSwitches flag (#50012)
chore: remove unused commandLineSwitches flag

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-02 20:48:33 -08:00
trop[bot]
d80adf4aed fix: prevent use-after-free in permission request callbacks (#50036)
EnterFullscreenModeForTab, RequestPointerLock, and RequestKeyboardLock
bind callbacks with base::Unretained(this); fullscreen also captures a
raw RenderFrameHost*. These callbacks may be invoked by the app's JS
permission handler after the WebContents or RenderFrameHost is destroyed.

Use GetWeakPtr() in all three call sites, and capture a
GlobalRenderFrameHostToken instead of the raw RenderFrameHost* for
fullscreen so the pointer is resolved and null-checked only when the
callback fires. Cancel in-flight permission requests from ~WebContents()
via a new ElectronPermissionManager::CancelPendingRequests()` so stale
callbacks are never handed back to JS.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-02 20:44:50 -08:00
trop[bot]
b39f8e3e1c fix: deliver Universal Links when NSUserActivity.userInfo is nil (#50005)
* fix(mac): deliver Universal Links when NSUserActivity.userInfo is nil

Co-authored-by: Dante Issaias <dante@issaias.com>

* chore: format

Co-authored-by: Dante Issaias <dante@issaias.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Dante Issaias <dante@issaias.com>
2026-03-02 19:12:08 -05:00
trop[bot]
9c3f76f617 build: authenticate curl requests to googlesource in lint workflow (#50027)
fix: authenticate curl requests to googlesource in lint workflow

The "Download GN Binary" and "Download clang-format Binary" steps
fetch files from chromium.googlesource.com without passing
authentication cookies. When googlesource rate-limits or returns a
transient error (502), the HTML error page is piped into `base64 -d`,
causing `base64: invalid input`.

The `set-chromium-cookie` action already configures `~/.gitcookies`
in a prior step. Pass `-b ~/.gitcookies` to both `curl` calls so
they authenticate, matching what the cookie verification step itself
does.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-02 18:38:48 -05:00
trop[bot]
3925b82a07 fix: potential UAF in OnDownloadPathGenerated (#50011)
fix: potential UAF in OnDownloadPathGenerated

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-02 18:35:40 -05:00
trop[bot]
e2b4cbfff3 fix: delete temporary blink* globals after restoring Blink implementations (#49999)
ElectronRendererClient::DidCreateScriptContext (and the worker
equivalent) save Blink's fetch/Response/FormData/Request/Headers/
EventSource as temporary globalThis.blink* properties before Node
initialization may overwrite them. node/init.ts and worker/init.ts
restore the originals but previously never deleted the temporary
blink* globals.

They persisted as non-standard global pollution visible to page
content when contextIsolation is disabled -- a minor fingerprinting
signal and a bypass for any preload that wraps window.fetch (page
could call blinkfetch() instead).

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
2026-03-02 13:13:11 -05:00
trop[bot]
29216464dc fix: update label/sublabel/icon in MenuItems on open (#49973)
fix: update label/sublabel/icon in macOS item on open

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-02 11:37:53 -05:00
trop[bot]
649c19c877 fix: menu bar hiding on two setFullscreen(false) (#49994)
* test: add failing test for `setFullscreen(false)`

`setFullscreen(false)` should do nothing
when not already in fullscreen, but it hides the menu bar
on Linux.

Co-authored-by: WofWca <wofwca@protonmail.com>

* fix: menu bar hiding on two setFullScreen(false)

This fixes the following bug on Linux (and maybe macOS):
1. Create a window with a menu bar.
2. Call `win.setFullScreen(false)`.

The menu bar will hide.

See the original bug in our project:
https://github.com/deltachat/deltachat-desktop/issues/4752.

Co-authored-by: WofWca <wofwca@protonmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: WofWca <wofwca@protonmail.com>
2026-03-02 16:01:22 +01:00
electron-roller[bot]
603bcb73e3 chore: bump node to v24.14.0 (40-x-y) (#49939)
* chore: bump node in DEPS to v24.14.0

* lib: prefer call() over apply() if argument list is not array

https://github.com/nodejs/node/pull/60796

* build,deps: replace cjs-module-lexer with merve

https://github.com/nodejs/node/pull/61456

* test: use fixture directories for sea tests

https://github.com/nodejs/node/pull/61167

* src: throw RangeError on failed ArrayBuffer BackingStore allocation

* chore: fixup patch indices

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-02 14:37:11 +01:00
trop[bot]
488af02eaa fix: menu close event missing after opening a submenu (#49963)
* fix: menu close event missing after opening a submenu

Co-authored-by: Jarek Radosz <jarek@cvx.dev>

* add a unit-like test

Co-authored-by: Jarek Radosz <jarek@cvx.dev>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jarek Radosz <jarek@cvx.dev>
2026-02-26 17:29:31 -05:00
reito
589e08af80 fix: offscreen rendering with correct screen info. (#49681)
* fix: osr use correct screen info.

* fix: 40-x-y types

* Update breaking changes documentation

Removed details about planned breaking API changes for versions 41.0 and 42.0.
2026-02-26 11:08:34 -05:00
trop[bot]
06521fad4c build: exit upload with error code if github upload fails (#49945)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-02-25 23:58:28 -08:00
trop[bot]
771cbce43b fix: potential std::stoi crash in Windows Toasts (#49952)
fix: potential std::stoi crash in Windows Toasts

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-02-25 23:58:04 -08:00
Samuel Attard
b5cfc5dd91 fix: implement keychain item deletion in MAS safeStorage migration (#49817)
The MAS safeStorage patch had three bugs:

1. `AddRandomPasswordToKeychain` was called with the unsuffixed account
   name, so first launches always created the wrong keychain entry,
   triggering an unnecessary migration on the next launch.

2. `FindGenericPassword` never populated the `SecKeychainItemRef` output
   parameter — the SecItem API doesn't return that deprecated type.

3. `ItemDelete` was a no-op stub that always returned `noErr` without
   actually deleting anything.

Fixes all three by using `suffixed_account_name` for new entries,
removing the unused `SecKeychainItemRef` parameter, and implementing
`ItemDelete` with `SecItemDelete` using the modern SecItem API.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:19:56 -08:00
trop[bot]
d6e6fcea86 feat: add support for --experimental-transform-types (#49883)
* feat: add support for `--experimental-transform-types`

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

* chore: add tests

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

* docs: add `--experimental-transform-types` to docs

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Niklas Wenzel <dev@nikwen.de>
2026-02-25 12:54:38 -05:00
trop[bot]
cc64a5e8d9 fix: crash after win.showAllTabs() new tab (#49934)
fix: crash after win.showAllTabs new tab

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-02-25 08:39:27 -05:00
electron-roller[bot]
2d12f059b4 chore: bump chromium to 144.0.7559.225 (40-x-y) (#49928)
chore: bump chromium in DEPS to 144.0.7559.225

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2026-02-24 11:20:35 -05:00
trop[bot]
f715930d49 docs: fix some string enum typings (#49931)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2026-02-24 11:19:28 -05:00
192 changed files with 5482 additions and 918 deletions

View File

@@ -43,7 +43,7 @@ runs:
curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}&getAccountName=true" > sas-token
- name: Save SAS Key
if: ${{ inputs.generate-sas-token == 'true' }}
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: sas-token
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}

View File

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

View File

@@ -46,7 +46,7 @@ jobs:
shell: bash
run: |
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
gn_version="$(curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
gn_version="$(curl -sL -b ~/.gitcookies "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
cipd ensure -ensure-file - -root . <<-CIPD
\$ServiceURL https://chrome-infra-packages.appspot.com/
@@ -62,7 +62,7 @@ jobs:
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
mkdir -p src/buildtools
curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > src/buildtools/DEPS
curl -sL -b ~/.gitcookies "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > src/buildtools/DEPS
gclient sync --spec="solutions=[{'name':'src/buildtools','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':True},'managed':False}]"
- name: Add problem matchers

View File

@@ -191,15 +191,25 @@ jobs:
run: |
cd src/out/Default
unzip -:o dist.zip
#- name: Import & Trust Self-Signed Codesigning Cert on MacOS
# if: ${{ inputs.target-platform == 'macos' && inputs.target-arch == 'x64' }}
# run: |
# sudo security authorizationdb write com.apple.trust-settings.admin allow
# cd src/electron
# ./script/codesign/generate-identity.sh
- name: Import & Trust Self-Signed Codesigning Cert on MacOS
if: ${{ inputs.target-platform == 'macos' }}
run: |
cd src/electron
./script/codesign/generate-identity.sh
# Only sign on x64 — arm64 builds are already ad-hoc signed, and re-signing
# with an untrusted cert breaks macOS system integrations (e.g. dock bounce).
# Autoupdater tests sign their own fixture copies via signApp().
- name: Sign Electron.app for macOS tests
if: ${{ inputs.target-platform == 'macos' && inputs.target-arch == 'x64' }}
run: |
identity=$(src/electron/script/codesign/get-trusted-identity.sh)
if [ -n "$identity" ]; then
codesign -s "$identity" --deep --force src/out/Default/Electron.app
fi
- name: Run Electron Tests
shell: bash
timeout-minutes: 40
env:
MOCHA_REPORTER: mocha-multi-reporters
MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap
@@ -250,6 +260,19 @@ jobs:
fi
fi
- name: Take screenshot on timeout or cancellation
if: ${{ inputs.target-platform != 'linux' && (cancelled() || failure()) }}
shell: bash
run: |
screenshot_dir="src/electron/spec/artifacts"
mkdir -p "$screenshot_dir"
screenshot_file="$screenshot_dir/screenshot-timeout-$(date +%Y%m%d%H%M%S).png"
if [ "${{ inputs.target-platform }}" = "macos" ]; then
screencapture -x "$screenshot_file" || true
elif [ "${{ inputs.target-platform }}" = "win" ]; then
powershell -command "Add-Type -AssemblyName System.Windows.Forms; \$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds; \$bitmap = New-Object System.Drawing.Bitmap(\$screen.Width, \$screen.Height); \$graphics = [System.Drawing.Graphics]::FromImage(\$bitmap); \$graphics.CopyFromScreen(\$screen.Location, [System.Drawing.Point]::Empty, \$screen.Size); \$bitmap.Save('$screenshot_file')" || true
fi
- name: Upload Test results to Datadog
env:
DD_ENV: ci
@@ -265,7 +288,7 @@ jobs:
fi
if: always() && !cancelled()
- name: Upload Test Artifacts
if: always() && !cancelled()
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
with:
name: test_artifacts_${{ env.ARTIFACT_KEY }}_${{ matrix.shard }}

View File

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

4
DEPS
View File

@@ -2,9 +2,9 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'144.0.7559.220',
'144.0.7559.236',
'node_version':
'v24.13.1',
'v24.14.0',
'nan_version':
'675cefebca42410733da8a454c8d9391fcebfbc2',
'squirrel.mac_version':

View File

@@ -255,7 +255,7 @@ async function startRepl () {
if (option.file && !option.webdriver) {
const file = option.file;
// eslint-disable-next-line n/no-deprecated-api
const protocol = url.parse(file).protocol;
const protocol = URL.canParse(file) ? new URL(file).protocol : null;
const extension = path.extname(file);
if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') {
await loadApplicationByURL(file);

View File

@@ -1331,7 +1331,7 @@ Returns `boolean` - Whether the current desktop environment is Unity launcher.
### `app.getLoginItemSettings([options])` _macOS_ _Windows_
* `options` Object (optional)
* `type` string (optional) _macOS_ - Can be one of `mainAppService`, `agentService`, `daemonService`, or `loginItemService`. Defaults to `mainAppService`. Only available on macOS 13 and up. See [app.setLoginItemSettings](app.md#appsetloginitemsettingssettings-macos-windows) for more information about each type.
* `type` string (optional) _macOS_ - Can be `mainAppService`, `agentService`, `daemonService`, or `loginItemService`. Defaults to `mainAppService`. Only available on macOS 13 and up. See [app.setLoginItemSettings](app.md#appsetloginitemsettingssettings-macos-windows) for more information about each type.
* `serviceName` string (optional) _macOS_ - The name of the service. Required if `type` is non-default. Only available on macOS 13 and up.
* `path` string (optional) _Windows_ - The executable path to compare against. Defaults to `process.execPath`.
* `args` string[] (optional) _Windows_ - The command-line arguments to compare against. Defaults to an empty array.
@@ -1346,13 +1346,13 @@ Returns `Object`:
* `wasOpenedAtLogin` boolean _macOS_ - `true` if the app was opened at login automatically.
* `wasOpenedAsHidden` boolean _macOS_ _Deprecated_ - `true` if the app was opened as a hidden login item. This indicates that the app should not open any windows at startup. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
* `restoreState` boolean _macOS_ _Deprecated_ - `true` if the app was opened as a login item that should restore the state from the previous session. This indicates that the app should restore the windows that were open the last time the app was closed. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
* `status` string _macOS_ - can be one of `not-registered`, `enabled`, `requires-approval`, or `not-found`.
* `status` string _macOS_ - can be `not-registered`, `enabled`, `requires-approval`, or `not-found`.
* `executableWillLaunchAtLogin` boolean _Windows_ - `true` if app is set to open at login and its run key is not deactivated. This differs from `openAtLogin` as it ignores the `args` option, this property will be true if the given executable would be launched at login with **any** arguments.
* `launchItems` Object[] _Windows_
* `name` string _Windows_ - name value of a registry entry.
* `path` string _Windows_ - The executable to an app that corresponds to a registry entry.
* `args` string[] _Windows_ - the command-line arguments to pass to the executable.
* `scope` string _Windows_ - one of `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
* `scope` string _Windows_ - can be `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
* `enabled` boolean _Windows_ - `true` if the app registry key is startup approved and therefore shows as `enabled` in Task Manager and Windows settings.
### `app.setLoginItemSettings(settings)` _macOS_ _Windows_

View File

@@ -350,6 +350,11 @@ Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https:
Disable exposition of [Navigator API][] on the global scope from Node.js.
### `--experimental-transform-types`
Enables the [transformation](https://nodejs.org/api/typescript.html#type-stripping)
of TypeScript-only syntax into JavaScript code.
## Chromium Flags
There isn't a documented list of all Chromium switches, but there are a few ways to find them.

View File

@@ -94,7 +94,7 @@ The `desktopCapturer` module has the following methods:
Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured.
> [!NOTE]
<!-- markdownlint-disable-next-line MD032 -->
> * Capturing audio requires `NSAudioCaptureUsageDescription` Info.plist key on macOS 14.2 Sonoma and higher - [read more](#macos-versions-142-or-higher).
> * Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher, which can detected by [`systemPreferences.getMediaAccessStatus`][].
@@ -109,30 +109,41 @@ Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`Desktop
PipeWire supports a single capture for both screens and windows. If you request the window and screen type, the selected source will be returned as a window capture.
---
### macOS versions 14.2 or higher
### MacOS versions 14.2 or higher
`NSAudioCaptureUsageDescription` Info.plist key must be added in-order for audio to be captured by `desktopCapturer`. If instead you are running electron from another program like a terminal or IDE then that parent program must contain the Info.plist key.
`NSAudioCaptureUsageDescription` Info.plist key must be added in order for audio to be captured by
`desktopCapturer`. If instead you are running Electron from another program like a terminal or IDE
then that parent program must contain the Info.plist key.
This is in order to facillitate use of Apple's new [CoreAudio Tap API](https://developer.apple.com/documentation/CoreAudio/capturing-system-audio-with-core-audio-taps#Configure-the-sample-code-project) by Chromium.
> [!WARNING]
> Failure of `desktopCapturer` to start an audio stream due to `NSAudioCaptureUsageDescription` permission not present will still create a dead audio stream however no warnings or errors are displayed.
> Failure of `desktopCapturer` to start an audio stream due to `NSAudioCaptureUsageDescription`
> permission not present will still create a dead audio stream however no warnings or errors are
> displayed.
As of electron `v39.0.0-beta.4` Chromium [made Apple's new `CoreAudio Tap API` the default](https://source.chromium.org/chromium/chromium/src/+/ad17e8f8b93d5f34891b06085d373a668918255e) for desktop audio capture. There is no fallback to the older `Screen & System Audio Recording` permissions system even if [CoreAudio Tap API](https://developer.apple.com/documentation/CoreAudio/capturing-system-audio-with-core-audio-taps) stream creation fails.
As of Electron `v39.0.0-beta.4`, Chromium [made Apple's new `CoreAudio Tap API` the default](https://source.chromium.org/chromium/chromium/src/+/ad17e8f8b93d5f34891b06085d373a668918255e)
for desktop audio capture. There is no fallback to the older `Screen & System Audio Recording`
permissions system even if [CoreAudio Tap API](https://developer.apple.com/documentation/CoreAudio/capturing-system-audio-with-core-audio-taps) stream creation fails.
If you need to continue using `Screen & System Audio Recording` permissions for `desktopCapturer` on macOS versions 14.2 and later, you can apply a chromium feature flag to force use of that older permissions system:
If you need to continue using `Screen & System Audio Recording` permissions for `desktopCapturer`
on macOS versions 14.2 and later, you can apply a Chromium feature flag to force use of that older
permissions system:
```js
// main.js (right beneath your require/import statments)
app.commandLine.appendSwitch('disable-features', 'MacCatapLoopbackAudioForScreenShare')
```
---
### macOS versions 12.7.6 or lower
### MacOS versions 12.7.6 or lower
`navigator.mediaDevices.getUserMedia` does not work on macOS versions 12.7.6 and prior for audio
capture due to a fundamental limitation whereby apps that want to access the system's audio require
a [signed kernel extension](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/KernelExtensions/KernelExtensions.html).
Chromium, and by extension Electron, does not provide this. Only in macOS 13 and onwards does Apple
provide APIs to capture desktop audio without the need for a signed kernel extension.
`navigator.mediaDevices.getUserMedia` does not work on macOS versions 12.7.6 and prior for audio capture due to a fundamental limitation whereby apps that want to access the system's audio require a [signed kernel extension](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/KernelExtensions/KernelExtensions.html). Chromium, and by extension Electron, does not provide this. Only in macOS 13 and onwards does Apple provide APIs to capture desktop audio without the need for a signed kernel extension.
It is possible to circumvent this limitation by capturing system audio with another macOS app like [BlackHole](https://existential.audio/blackhole/) or [Soundflower](https://rogueamoeba.com/freebies/soundflower/) and passing it through a virtual audio input device. This virtual device can then be queried with `navigator.mediaDevices.getUserMedia`.
It is possible to circumvent this limitation by capturing system audio with another macOS app like
[BlackHole](https://existential.audio/blackhole/) or [Soundflower](https://rogueamoeba.com/freebies/soundflower/)
and passing it through a virtual audio input device. This virtual device can then be queried
with `navigator.mediaDevices.getUserMedia`.

View File

@@ -73,13 +73,16 @@ The following properties are available on instances of `MenuItem`:
#### `menuItem.id`
A `string` indicating the item's unique id. This property can be
dynamically changed.
A `string` indicating the item's unique id.
This property can be dynamically changed.
#### `menuItem.label`
A `string` indicating the item's visible label.
This property can be dynamically changed.
#### `menuItem.click`
A `Function` that is fired when the MenuItem receives a click event.
@@ -118,31 +121,37 @@ An `Accelerator | null` indicating the item's [user-assigned accelerator](https:
#### `menuItem.icon`
A `NativeImage | string` (optional) indicating the
item's icon, if set.
A `NativeImage | string` (optional) indicating the item's icon, if set.
This property can be dynamically changed.
#### `menuItem.sublabel`
A `string` indicating the item's sublabel.
This property can be dynamically changed.
#### `menuItem.toolTip` _macOS_
A `string` indicating the item's hover text.
#### `menuItem.enabled`
A `boolean` indicating whether the item is enabled. This property can be
dynamically changed.
A `boolean` indicating whether the item is enabled.
This property can be dynamically changed.
#### `menuItem.visible`
A `boolean` indicating whether the item is visible. This property can be
dynamically changed.
A `boolean` indicating whether the item is visible.
This property can be dynamically changed.
#### `menuItem.checked`
A `boolean` indicating whether the item is checked. This property can be
dynamically changed.
A `boolean` indicating whether the item is checked.
This property can be dynamically changed.
A `checkbox` menu item will toggle the `checked` property on and off when
selected.

View File

@@ -111,7 +111,8 @@ app.whenReady().then(() => {
Returns:
* `event` Event
* `details` Event\<\>
* `reason` _Windows_ string (optional) - The reason the notification was closed. This can be 'userCanceled', 'applicationHidden', or 'timedOut'.
Emitted when the notification is closed by manual intervention from the user.

View File

@@ -110,6 +110,8 @@ Returns [`Point`](structures/point.md)
The current absolute position of the mouse pointer.
Not supported on Wayland (Linux).
> [!NOTE]
> The return value is a DIP point, not a screen physical point.

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.
* `deviceScaleFactor` number (optional) _Experimental_ - The device scale factor of the offscreen rendering output. If not set, will use primary display's scale factor as default.
* `contextIsolation` boolean (optional) - Whether to run Electron APIs and
the specified `preload` script in a separate JavaScript context. Defaults
to `true`. The context that the `preload` script runs in will only have

View File

@@ -7,47 +7,7 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
## Timeline
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
| 40.0.0 | 2025-Oct-30 | 2025-Dec-03 | 2026-Jan-13 | 2026-Jun-30 | M144 | TBD | ✅ |
| 39.0.0 | 2025-Sep-04 | 2025-Oct-01 | 2025-Oct-28 | 2026-May-05 | M142 | v22.20 | ✅ |
| 38.0.0 | 2025-Jun-26 | 2025-Aug-06 | 2025-Sep-02 | 2026-Mar-10 | M140 | v22.18 | ✅ |
| 37.0.0 | 2025-May-01 | 2025-May-28 | 2025-Jun-24 | 2026-Jan-13 | M138 | v22.16 | ✅ |
| 36.0.0 | 2025-Mar-06 | 2025-Apr-02 | 2025-Apr-29 | 2025-Oct-28 | M136 | v22.14 | 🚫 |
| 35.0.0 | 2025-Jan-16 | 2025-Feb-05 | 2025-Mar-04 | 2025-Sep-02 | M134 | v22.14 | 🚫 |
| 34.0.0 | 2024-Oct-17 | 2024-Nov-13 | 2025-Jan-14 | 2025-Jun-24 | M132 | v20.18 | 🚫 |
| 33.0.0 | 2024-Aug-22 | 2024-Sep-18 | 2024-Oct-15 | 2025-Apr-29 | M130 | v20.18 | 🚫 |
| 32.0.0 | 2024-Jun-14 | 2024-Jul-24 | 2024-Aug-20 | 2025-Mar-04 | M128 | v20.16 | 🚫 |
| 31.0.0 | 2024-Apr-18 | 2024-May-15 | 2024-Jun-11 | 2025-Jan-14 | M126 | v20.14 | 🚫 |
| 30.0.0 | 2024-Feb-22 | 2024-Mar-20 | 2024-Apr-16 | 2024-Oct-15 | M124 | v20.11 | 🚫 |
| 29.0.0 | 2023-Dec-07 | 2024-Jan-24 | 2024-Feb-20 | 2024-Aug-20 | M122 | v20.9 | 🚫 |
| 28.0.0 | 2023-Oct-11 | 2023-Nov-06 | 2023-Dec-05 | 2024-Jun-11 | M120 | v18.18 | 🚫 |
| 27.0.0 | 2023-Aug-17 | 2023-Sep-13 | 2023-Oct-10 | 2024-Apr-16 | M118 | v18.17 | 🚫 |
| 26.0.0 | 2023-Jun-01 | 2023-Jun-27 | 2023-Aug-15 | 2024-Feb-20 | M116 | v18.16 | 🚫 |
| 25.0.0 | 2023-Apr-10 | 2023-May-02 | 2023-May-30 | 2023-Dec-05 | M114 | v18.15 | 🚫 |
| 24.0.0 | 2023-Feb-09 | 2023-Mar-07 | 2023-Apr-04 | 2023-Oct-10 | M112 | v18.14 | 🚫 |
| 23.0.0 | 2022-Dec-01 | 2023-Jan-10 | 2023-Feb-07 | 2023-Aug-15 | M110 | v18.12 | 🚫 |
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | 2023-Oct-10 | M108 | v16.17 | 🚫 |
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | 2023-Apr-04 | M106 | v16.16 | 🚫 |
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | 2023-Feb-07 | M104 | v16.15 | 🚫 |
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | 2022-Nov-29 | M102 | v16.14 | 🚫 |
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | 2022-Sep-27 | M100 | v16.13 | 🚫 |
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | 2022-Aug-02 | M98 | v16.13 | 🚫 |
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | 2022-May-24 | M96 | v16.9 | 🚫 |
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | 2022-May-24 | M94 | v16.5 | 🚫 |
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | 2022-Mar-29 | M93 | v14.17 | 🚫 |
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | 2022-Feb-01 | M91 | v14.16 | 🚫 |
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | 2021-Nov-16 | M89 | v14.16 | 🚫 |
| 11.0.0 | -- | 2020-Aug-27 | 2020-Nov-17 | 2021-Aug-31 | M87 | v12.18 | 🚫 |
| 10.0.0 | -- | 2020-May-21 | 2020-Aug-25 | 2021-May-25 | M85 | v12.16 | 🚫 |
| 9.0.0 | -- | 2020-Feb-06 | 2020-May-19 | 2021-Mar-02 | M83 | v12.14 | 🚫 |
| 8.0.0 | -- | 2019-Oct-24 | 2020-Feb-04 | 2020-Nov-17 | M80 | v12.13 | 🚫 |
| 7.0.0 | -- | 2019-Aug-01 | 2019-Oct-22 | 2020-Aug-25 | M78 | v12.8 | 🚫 |
| 6.0.0 | -- | 2019-Apr-25 | 2019-Jul-30 | 2020-May-19 | M76 | v12.14.0 | 🚫 |
| 5.0.0 | -- | 2019-Jan-22 | 2019-Apr-23 | 2020-Feb-04 | M73 | v12.0 | 🚫 |
| 4.0.0 | -- | 2018-Oct-11 | 2018-Dec-20 | 2019-Oct-22 | M69 | v10.11 | 🚫 |
| 3.0.0 | -- | 2018-Jun-21 | 2018-Sep-18 | 2019-Jul-30 | M66 | v10.2 | 🚫 |
| 2.0.0 | -- | 2018-Feb-21 | 2018-May-01 | 2019-Apr-23 | M61 | v8.9 | 🚫 |
[Electron's Release Schedule](https://releases.electronjs.org/schedule) lists a schedule of Electron major releases showing key milestones including alpha, beta, and stable release dates, as well as end-of-life dates and dependency versions.
:::info Official support dates may change

View File

@@ -115,6 +115,8 @@ filenames = {
"shell/browser/win/scoped_hstring.h",
"shell/common/api/electron_api_native_image_win.cc",
"shell/common/application_info_win.cc",
"shell/common/command_line_util_win.cc",
"shell/common/command_line_util_win.h",
"shell/common/language_util_win.cc",
"shell/common/node_bindings_win.cc",
"shell/common/node_bindings_win.h",

View File

@@ -26,9 +26,9 @@ const MenuItem = function (this: any, options: any) {
this.overrideReadOnlyProperty('type', roles.getDefaultType(this.role));
this.overrideReadOnlyProperty('role');
this.overrideReadOnlyProperty('accelerator', roles.getDefaultAccelerator(this.role));
this.overrideReadOnlyProperty('icon');
this.overrideReadOnlyProperty('submenu');
this.overrideProperty('icon');
this.overrideProperty('label', roles.getDefaultLabel(this.role));
this.overrideProperty('sublabel', '');
this.overrideProperty('toolTip', '');

View File

@@ -53,6 +53,18 @@ Menu.prototype._isCommandIdVisible = function (id) {
return this.commandsMap[id]?.visible ?? false;
};
Menu.prototype._getLabelForCommandId = function (id) {
return this.commandsMap[id]?.label ?? '';
};
Menu.prototype._getSecondaryLabelForCommandId = function (id) {
return this.commandsMap[id]?.sublabel ?? '';
};
Menu.prototype._getIconForCommandId = function (id) {
return this.commandsMap[id]?.icon ?? null;
};
Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator) {
const command = this.commandsMap[id];
if (!command) return;
@@ -158,7 +170,6 @@ Menu.prototype.insert = function (pos, item) {
insertItemByType.call(this, item, pos);
// set item properties
if (item.sublabel) this.setSublabel(pos, item.sublabel);
if (item.toolTip) this.setToolTip(pos, item.toolTip);
if (item.icon) this.setIcon(pos, item.icon);
if (item.role) this.setRole(pos, item.role);

View File

@@ -8,13 +8,19 @@ const {
isOnBatteryPower
} = process._linkedBinding('electron_browser_power_monitor');
// Hold the native PowerMonitor at module level so it is never garbage-collected
// while this module is alive. The C++ side registers OS-level callbacks (HWND
// user-data on Windows, shutdown handler on macOS, notification observers) that
// prevent safe collection of the C++ wrapper while those registrations exist.
let pm: any;
class PowerMonitor extends EventEmitter implements Electron.PowerMonitor {
constructor () {
super();
// Don't start the event source until both a) the app is ready and b)
// there's a listener registered for a powerMonitor event.
this.once('newListener', () => {
const pm = createPowerMonitor();
pm = createPowerMonitor();
pm.emit = this.emit.bind(this);
if (process.platform === 'linux') {

View File

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

View File

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

View File

@@ -19,8 +19,8 @@ export function invokeInWebContents<T> (sender: Electron.WebContents, command: s
const requestId = ++nextId;
const channel = `${command}_RESPONSE_${requestId}`;
ipcMainInternal.on(channel, function handler (event, error: Error, result: any) {
if (event.type === 'frame' && event.sender !== sender) {
console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`);
if (event.type !== 'frame' || event.sender !== sender) {
console.error(`Reply to ${command} sent by unexpected sender`);
return;
}
@@ -43,8 +43,8 @@ export function invokeInWebFrameMain<T> (sender: Electron.WebFrameMain, command:
const channel = `${command}_RESPONSE_${requestId}`;
const frameTreeNodeId = sender.frameTreeNodeId;
ipcMainInternal.on(channel, function handler (event, error: Error, result: any) {
if (event.type === 'frame' && event.frameTreeNodeId !== frameTreeNodeId) {
console.error(`Reply to ${command} sent by unexpected WebFrameMain (${event.frameTreeNodeId})`);
if (event.type !== 'frame' || event.frameTreeNodeId !== frameTreeNodeId) {
console.error(`Reply to ${command} sent by unexpected sender`);
return;
}

View File

@@ -227,10 +227,9 @@ function validateHeader (name: any, value: any): void {
}
function parseOptions (optionsIn: ClientRequestConstructorOptions | string): NodeJS.CreateURLLoaderOptions & ExtraURLLoaderOptions {
// eslint-disable-next-line n/no-deprecated-api
const options: any = typeof optionsIn === 'string' ? url.parse(optionsIn) : { ...optionsIn };
const options: any = typeof optionsIn === 'string' ? new URL(optionsIn) : { ...optionsIn };
let urlStr: string = options.url;
let urlStr: string = options.url || options.href;
if (!urlStr) {
const urlObj: url.UrlObject = {};
@@ -260,8 +259,8 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
// an invalid request.
throw new TypeError('Request path contains unescaped characters');
}
// eslint-disable-next-line n/no-deprecated-api
const pathObj = url.parse(options.path || '/');
const pathObj = new URL(options.path || '/', 'http://localhost');
urlObj.pathname = pathObj.pathname;
urlObj.search = pathObj.search;
urlObj.hash = pathObj.hash;

View File

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

View File

@@ -9,6 +9,7 @@ if ((globalThis as any).blinkfetch) {
const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers', 'EventSource'];
for (const key of keys) {
(globalThis as any)[key] = (globalThis as any)[`blink${key}`];
delete (globalThis as any)[`blink${key}`];
}
}

View File

@@ -23,11 +23,14 @@ export default contextBridge;
export const internalContextBridge = {
contextIsolationEnabled: process.contextIsolated,
tryOverrideGlobalValueFromIsolatedWorld: (keys: string[], value: any) => {
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, true, true);
},
overrideGlobalValueFromIsolatedWorld: (keys: string[], value: any) => {
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, false);
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, false, false);
},
overrideGlobalValueWithDynamicPropsFromIsolatedWorld: (keys: string[], value: any) => {
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, true);
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, true, false);
},
overrideGlobalPropertyFromIsolatedWorld: (keys: string[], getter: Function, setter?: Function) => {
return binding._overrideGlobalPropertyFromIsolatedWorld(keys, getter, setter || null);

View File

@@ -11,14 +11,12 @@ const { contextIsolationEnabled } = internalContextBridge;
* 1) Use menu API to show context menu.
*/
window.onload = function () {
if (window.InspectorFrontendHost) {
if (contextIsolationEnabled) {
internalContextBridge.overrideGlobalValueFromIsolatedWorld([
'InspectorFrontendHost', 'showContextMenuAtPoint'
], createMenu);
} else {
window.InspectorFrontendHost.showContextMenuAtPoint = createMenu;
}
if (contextIsolationEnabled) {
internalContextBridge.tryOverrideGlobalValueFromIsolatedWorld([
'InspectorFrontendHost', 'showContextMenuAtPoint'
], createMenu);
} else {
window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu;
}
};

View File

@@ -22,6 +22,7 @@ if ((globalThis as any).blinkfetch) {
const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers', 'EventSource'];
for (const key of keys) {
(globalThis as any)[key] = (globalThis as any)[`blink${key}`];
delete (globalThis as any)[`blink${key}`];
}
}

View File

@@ -0,0 +1 @@
optionally_validate_gl_max_uniform_blocks_at_compile_time.patch

View File

@@ -0,0 +1,376 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Geoff Lang <geofflang@chromium.org>
Date: Wed, 11 Feb 2026 15:51:46 -0500
Subject: Optionally validate GL_MAX_*_UNIFORM_BLOCKS at compile time.
These were validated at link time but some drivers have compiler crashes
when compiling shaders with too many uniform blocks.
Bug: chromium:475877320
Change-Id: I4413ce06307b4fe9e27105d85f66f610c235a301
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7568089
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index 90b1239689c98a8c94f8c6f57d572268a4450923..81445ade5f2285d52a6b83bfc574c0fa56d4a0da 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
-#define ANGLE_SH_VERSION 386
+#define ANGLE_SH_VERSION 387
enum ShShaderSpec
{
@@ -383,6 +383,10 @@ struct ShCompileOptions
uint64_t forceShaderPrecisionHighpToMediump : 1;
+ // Validate that the count of uniform blocks is within the GL_MAX_*_UNIFORM_BLOCKS limits. These
+ // limits must be supplied in the BuiltinResources.
+ uint64_t validatePerStageMaxUniformBlocks : 1;
+
// Ask compiler to generate Vulkan transform feedback emulation support code.
uint64_t addVulkanXfbEmulationSupportCode : 1;
@@ -584,6 +588,12 @@ struct ShBuiltInResources
int MinProgramTexelOffset;
int MaxProgramTexelOffset;
+ // GL_MAX_FRAGMENT_UNIFORM_BLOCKS
+ int MaxFragmentUniformBlocks;
+
+ // GL_MAX_VERTEX_UNIFORM_BLOCKS
+ int MaxVertexUniformBlocks;
+
// Extension constants.
// Value of GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT for OpenGL ES output context.
@@ -701,6 +711,9 @@ struct ShBuiltInResources
// maximum point size (higher limit from ALIASED_POINT_SIZE_RANGE)
float MaxPointSize;
+ // GL_MAX_COMPUTE_UNIFORM_BLOCKS
+ int MaxComputeUniformBlocks;
+
// EXT_geometry_shader constants
int MaxGeometryUniformComponents;
int MaxGeometryUniformBlocks;
@@ -724,6 +737,7 @@ struct ShBuiltInResources
int MaxTessControlImageUniforms;
int MaxTessControlAtomicCounters;
int MaxTessControlAtomicCounterBuffers;
+ int MaxTessControlUniformBlocks;
int MaxTessPatchComponents;
int MaxPatchVertices;
@@ -736,6 +750,7 @@ struct ShBuiltInResources
int MaxTessEvaluationImageUniforms;
int MaxTessEvaluationAtomicCounters;
int MaxTessEvaluationAtomicCounterBuffers;
+ int MaxTessEvaluationUniformBlocks;
// Subpixel bits used in rasterization.
int SubPixelBits;
diff --git a/include/platform/autogen/FeaturesGL_autogen.h b/include/platform/autogen/FeaturesGL_autogen.h
index a732a77d66ec2fad9677d500d8f0ae3d0d92c454..f0b391dfbc4d238df458b334951371429fd496a4 100644
--- a/include/platform/autogen/FeaturesGL_autogen.h
+++ b/include/platform/autogen/FeaturesGL_autogen.h
@@ -638,6 +638,12 @@ struct FeaturesGL : FeatureSetBase
&members,
};
+ FeatureInfo validateMaxPerStageUniformBlocksAtCompileTime = {
+ "validateMaxPerStageUniformBlocksAtCompileTime",
+ FeatureCategory::OpenGLWorkarounds,
+ &members,
+ };
+
};
inline FeaturesGL::FeaturesGL() = default;
diff --git a/include/platform/gl_features.json b/include/platform/gl_features.json
index e993768974e63d19bf4b71702fe9725aa97ea2fb..03454778c2acfd10c2a3762ef6ad1e9eb0fc7bb2 100644
--- a/include/platform/gl_features.json
+++ b/include/platform/gl_features.json
@@ -828,6 +828,14 @@
"Some ES2 Mali drivers are unable to query enough information from a linked program to use passthrough shaders."
],
"issue": "https://crbug.com/451796659"
+ },
+ {
+ "name": "validate_max_per_stage_uniform_blocks_at_compile_time",
+ "category": "Workarounds",
+ "description": [
+ "Validate GL_MAX_*_UNIFORM_BLOCKS at compile time instead of link time to work around compiler bugs."
+ ],
+ "issue": "http://crbug.com/475877320"
}
]
}
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 7e36dc2cd24a5d0a3a1a009a1171684e4dfe62e5..aae0f1008d573a55e822c10cf02149b8ccef6055 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -1563,6 +1563,8 @@ void TCompiler::setResourceString()
<< ":MaxFragmentInputVectors:" << mResources.MaxFragmentInputVectors
<< ":MinProgramTexelOffset:" << mResources.MinProgramTexelOffset
<< ":MaxProgramTexelOffset:" << mResources.MaxProgramTexelOffset
+ << ":MaxFragmentUniformBlocks:" << mResources.MaxFragmentUniformBlocks
+ << ":MaxVertexUniformBlocks:" << mResources.MaxVertexUniformBlocks
<< ":MaxDualSourceDrawBuffers:" << mResources.MaxDualSourceDrawBuffers
<< ":MaxViewsOVR:" << mResources.MaxViewsOVR
<< ":NV_draw_buffers:" << mResources.NV_draw_buffers
@@ -1612,6 +1614,7 @@ void TCompiler::setResourceString()
<< ":MaxFragmentAtomicCounterBuffers:" << mResources.MaxFragmentAtomicCounterBuffers
<< ":MaxCombinedAtomicCounterBuffers:" << mResources.MaxCombinedAtomicCounterBuffers
<< ":MaxAtomicCounterBufferSize:" << mResources.MaxAtomicCounterBufferSize
+ << ":MaxComputeUnformBlocks:" << mResources.MaxComputeUniformBlocks
<< ":MaxGeometryUniformComponents:" << mResources.MaxGeometryUniformComponents
<< ":MaxGeometryUniformBlocks:" << mResources.MaxGeometryUniformBlocks
<< ":MaxGeometryInputComponents:" << mResources.MaxGeometryInputComponents
@@ -1635,6 +1638,7 @@ void TCompiler::setResourceString()
<< ":MaxTessControlImageUniforms:" << mResources.MaxTessControlImageUniforms
<< ":MaxTessControlAtomicCounters:" << mResources.MaxTessControlAtomicCounters
<< ":MaxTessControlAtomicCounterBuffers:" << mResources.MaxTessControlAtomicCounterBuffers
+ << ":MaxTessControlUniformBlocks:" << mResources.MaxTessControlUniformBlocks
<< ":MaxTessPatchComponents:" << mResources.MaxTessPatchComponents
<< ":MaxPatchVertices:" << mResources.MaxPatchVertices
<< ":MaxTessGenLevel:" << mResources.MaxTessGenLevel
@@ -1644,7 +1648,9 @@ void TCompiler::setResourceString()
<< ":MaxTessEvaluationUniformComponents:" << mResources.MaxTessEvaluationUniformComponents
<< ":MaxTessEvaluationImageUniforms:" << mResources.MaxTessEvaluationImageUniforms
<< ":MaxTessEvaluationAtomicCounters:" << mResources.MaxTessEvaluationAtomicCounters
- << ":MaxTessEvaluationAtomicCounterBuffers:" << mResources.MaxTessEvaluationAtomicCounterBuffers;
+ << ":MaxTessEvaluationAtomicCounterBuffers:" << mResources.MaxTessEvaluationAtomicCounterBuffers
+ << ":MaxTessControlUniformBlocks:" << mResources.MaxTessControlUniformBlocks
+ ;
// clang-format on
mBuiltInResourcesString = strstream.str();
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index a8a5e562b2006402e1473c8c6710d75d7c83c42f..e04d27fe695caba55bb29f94c7425d4ecc3d2344 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -367,6 +367,37 @@ bool IsESSL100ConstantExpression(TIntermNode *node)
{
return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
}
+
+unsigned int GetMaxUniformBlocksForShaderType(sh::GLenum shaderType,
+ const ShCompileOptions &options,
+ const ShBuiltInResources &resources)
+{
+ // If the validatePerStageMaxUniformBlocks workaround is disabled. Set a limit that will not be
+ // hit.
+ if (!options.validatePerStageMaxUniformBlocks)
+ {
+ return std::numeric_limits<unsigned int>::max();
+ }
+
+ switch (shaderType)
+ {
+ case GL_FRAGMENT_SHADER:
+ return resources.MaxFragmentUniformBlocks;
+ case GL_VERTEX_SHADER:
+ return resources.MaxVertexUniformBlocks;
+ case GL_COMPUTE_SHADER:
+ return resources.MaxComputeUniformBlocks;
+ case GL_GEOMETRY_SHADER:
+ return resources.MaxGeometryUniformBlocks;
+ case GL_TESS_CONTROL_SHADER:
+ return resources.MaxTessControlUniformBlocks;
+ case GL_TESS_EVALUATION_SHADER:
+ return resources.MaxTessEvaluationUniformBlocks;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+}
} // namespace
// This tracks each binding point's current default offset for inheritance of subsequent
@@ -459,6 +490,8 @@ TParseContext::TParseContext(TSymbolTable &symt,
mMaxPixelLocalStoragePlanes(resources.MaxPixelLocalStoragePlanes),
mMaxFunctionParameters(resources.MaxFunctionParameters),
mMaxCallStackDepth(resources.MaxCallStackDepth),
+ mMaxUniformBlocks(GetMaxUniformBlocksForShaderType(mShaderType, options, resources)),
+ mNumUniformBlocks(0),
mDeclaringFunction(false),
mDeclaringMain(false),
mMainFunction(nullptr),
@@ -6082,6 +6115,22 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
error(arraySizesLine, "geometry shader input blocks must be an array", "");
}
+ // Validate max uniform block limits
+ if (typeQualifier.qualifier == EvqUniform)
+ {
+ unsigned int blockCount =
+ arraySizes == nullptr || arraySizes->empty() ? 1 : (*arraySizes)[0];
+ if (mNumUniformBlocks + blockCount > mMaxUniformBlocks)
+ {
+ error(arraySizesLine,
+ "uniform block count greater than per stage maximum uniform blocks", "");
+ }
+ else
+ {
+ mNumUniformBlocks += blockCount;
+ }
+ }
+
checkIndexIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.index);
if (mShaderVersion < 310)
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index f3d0417dd91cda2226203b9b39c2dc8acc1ac880..baf074bd86fe587a72cd3a431fbf0231d6ebd68c 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -910,6 +910,12 @@ class TParseContext : angle::NonCopyable
// and there are no known users.
TUnorderedMap<TQualifier, bool> mBuiltInQualified;
+ // Maximum number of uniform blocks allowed to be declared in this shader. Taken from the
+ // built-in resources and resolved to this shader type.
+ unsigned int mMaxUniformBlocks;
+ // Current count of declared uniform blocks.
+ unsigned int mNumUniformBlocks;
+
// keeps track whether we are declaring / defining a function
bool mDeclaringFunction;
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index 6044849e6a6392fed77909b45fea475fa4978325..e292cf0403a77f2f70ca9176785fcdad9aac7302 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -263,6 +263,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxFragmentInputVectors = 15;
resources->MinProgramTexelOffset = -8;
resources->MaxProgramTexelOffset = 7;
+ resources->MaxFragmentUniformBlocks = 12;
+ resources->MaxVertexUniformBlocks = 12;
// Extensions constants.
resources->MaxDualSourceDrawBuffers = 0;
@@ -323,6 +325,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxUniformBufferBindings = 32;
resources->MaxShaderStorageBufferBindings = 4;
+ resources->MaxComputeUniformBlocks = 12;
+
resources->MaxGeometryUniformComponents = 1024;
resources->MaxGeometryUniformBlocks = 12;
resources->MaxGeometryInputComponents = 64;
@@ -344,6 +348,7 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxTessControlImageUniforms = 0;
resources->MaxTessControlAtomicCounters = 0;
resources->MaxTessControlAtomicCounterBuffers = 0;
+ resources->MaxTessControlUniformBlocks = 12;
resources->MaxTessPatchComponents = 120;
resources->MaxPatchVertices = 32;
@@ -356,6 +361,7 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxTessEvaluationImageUniforms = 0;
resources->MaxTessEvaluationAtomicCounters = 0;
resources->MaxTessEvaluationAtomicCounterBuffers = 0;
+ resources->MaxTessEvaluationUniformBlocks = 12;
resources->SubPixelBits = 8;
diff --git a/src/libANGLE/Compiler.cpp b/src/libANGLE/Compiler.cpp
index 00684c8ed08609a3a4d6ef6f36107756207ed72b..1893b6bddb33fde567162e2e9dbb12785dad538d 100644
--- a/src/libANGLE/Compiler.cpp
+++ b/src/libANGLE/Compiler.cpp
@@ -169,6 +169,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state, egl::Disp
mResources.MaxFragmentInputVectors = caps.maxFragmentInputComponents / 4;
mResources.MinProgramTexelOffset = caps.minProgramTexelOffset;
mResources.MaxProgramTexelOffset = caps.maxProgramTexelOffset;
+ mResources.MaxFragmentUniformBlocks = caps.maxShaderUniformBlocks[gl::ShaderType::Fragment];
+ mResources.MaxVertexUniformBlocks = caps.maxShaderUniformBlocks[gl::ShaderType::Vertex];
// EXT_blend_func_extended
mResources.EXT_blend_func_extended = extensions.blendFuncExtendedEXT;
@@ -211,6 +213,7 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state, egl::Disp
mResources.MaxCombinedImageUniforms = caps.maxCombinedImageUniforms;
mResources.MaxCombinedShaderOutputResources = caps.maxCombinedShaderOutputResources;
mResources.MaxUniformLocations = caps.maxUniformLocations;
+ mResources.MaxComputeUniformBlocks = caps.maxShaderUniformBlocks[gl::ShaderType::Compute];
for (size_t index = 0u; index < 3u; ++index)
{
@@ -280,6 +283,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state, egl::Disp
mResources.MaxTessControlAtomicCounters = caps.maxShaderAtomicCounters[ShaderType::TessControl];
mResources.MaxTessControlAtomicCounterBuffers =
caps.maxShaderAtomicCounterBuffers[ShaderType::TessControl];
+ mResources.MaxTessControlUniformBlocks =
+ caps.maxShaderUniformBlocks[gl::ShaderType::TessControl];
mResources.MaxTessPatchComponents = caps.maxTessPatchComponents;
mResources.MaxPatchVertices = caps.maxPatchVertices;
@@ -297,6 +302,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state, egl::Disp
caps.maxShaderAtomicCounters[ShaderType::TessEvaluation];
mResources.MaxTessEvaluationAtomicCounterBuffers =
caps.maxShaderAtomicCounterBuffers[ShaderType::TessEvaluation];
+ mResources.MaxTessEvaluationUniformBlocks =
+ caps.maxShaderUniformBlocks[gl::ShaderType::TessEvaluation];
// Subpixel bits.
mResources.SubPixelBits = static_cast<int>(caps.subPixelBits);
diff --git a/src/libANGLE/renderer/gl/ShaderGL.cpp b/src/libANGLE/renderer/gl/ShaderGL.cpp
index a10d5545f409faa259a464437401980e40b80e33..d8db2a1f92494fd0dbf0cb6011ab9cea989c5b45 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.cpp
+++ b/src/libANGLE/renderer/gl/ShaderGL.cpp
@@ -272,6 +272,11 @@ std::shared_ptr<ShaderTranslateTask> ShaderGL::compile(const gl::Context *contex
options->pls = contextGL->getNativePixelLocalStorageOptions();
}
+ if (features.validateMaxPerStageUniformBlocksAtCompileTime.enabled)
+ {
+ options->validatePerStageMaxUniformBlocks = true;
+ }
+
return std::shared_ptr<ShaderTranslateTask>(
new ShaderTranslateTaskGL(functions, mShaderID, contextGL->hasNativeParallelCompile()));
}
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index d9694c09146dcb34948646df5ca95a4d8b993d9e..e82a10262dc128e55d2357bf9efc67903c30206b 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -2718,6 +2718,10 @@ void InitializeFeatures(const FunctionsGL *functions, angle::FeaturesGL *feature
// Mali 400 series drivers fail linking shaders when passthrough shaders are enabled. Likely due
// to not querying correct information from varyings and uniforms.
ANGLE_FEATURE_CONDITION(features, disablePassthroughShaders, IsAdreno4xx(functions));
+
+ // IMG GL drivers crash while compiling shaders with more than the limit of uniform blocks.
+ ANGLE_FEATURE_CONDITION(features, validateMaxPerStageUniformBlocksAtCompileTime,
+ IsPowerVR(vendor));
}
void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features)
diff --git a/util/autogen/angle_features_autogen.cpp b/util/autogen/angle_features_autogen.cpp
index f38ad24bdce73c1ec5facd1130bdf09f896298ee..c07c7e5fa2d28c50101c77bd856f18158777e276 100644
--- a/util/autogen/angle_features_autogen.cpp
+++ b/util/autogen/angle_features_autogen.cpp
@@ -484,6 +484,7 @@ constexpr PackedEnumMap<Feature, const char *> kFeatureNames = {{
{Feature::UseVkEventForBufferBarrier, "useVkEventForBufferBarrier"},
{Feature::UseVkEventForImageBarrier, "useVkEventForImageBarrier"},
{Feature::UseVmaForImageSuballocation, "useVmaForImageSuballocation"},
+ {Feature::ValidateMaxPerStageUniformBlocksAtCompileTime, "validateMaxPerStageUniformBlocksAtCompileTime"},
{Feature::VaryingsRequireMatchingPrecisionInSpirv, "varyingsRequireMatchingPrecisionInSpirv"},
{Feature::VerifyPipelineCacheInBlobCache, "verifyPipelineCacheInBlobCache"},
{Feature::VertexIDDoesNotIncludeBaseVertex, "vertexIDDoesNotIncludeBaseVertex"},
diff --git a/util/autogen/angle_features_autogen.h b/util/autogen/angle_features_autogen.h
index 0eef4d53d814db20bb943fcda9e5b1837c371951..e33fd47ac493a3946d87c03a2d6f29b08e2b63aa 100644
--- a/util/autogen/angle_features_autogen.h
+++ b/util/autogen/angle_features_autogen.h
@@ -484,6 +484,7 @@ enum class Feature
UseVkEventForBufferBarrier,
UseVkEventForImageBarrier,
UseVmaForImageSuballocation,
+ ValidateMaxPerStageUniformBlocksAtCompileTime,
VaryingsRequireMatchingPrecisionInSpirv,
VerifyPipelineCacheInBlobCache,
VertexIDDoesNotIncludeBaseVertex,

View File

@@ -148,3 +148,14 @@ graphite_handle_out_of_order_recording_errors.patch
move_wayland_pointer_lock_overrides_to_common_code.patch
loaf_add_feature_to_enable_sourceurl_for_all_protocols.patch
fix_update_dbus_signal_signature_for_xdg_globalshortcuts_portal.patch
patch_osr_control_screen_info.patch
cherry-pick-12f932985275.patch
fix_mac_high_res_icons.patch
cherry-pick-074d472db745.patch
cherry-pick-50b057660b4d.patch
cherry-pick-45c5a70d984d.patch
cherry-pick-05e4b544803c.patch
cherry-pick-5efc7a0127a6.patch
feat_plumb_node_integration_in_worker_through_workersettings.patch
cherry-pick-fbfb27470bf6.patch
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch

View File

@@ -6,10 +6,10 @@ Subject: build: allow electron to use exec_script
This is similar to the //build usecase so we're OK adding ourselves here
diff --git a/.gn b/.gn
index ae58a0b0a64ae1fdb3f9cd8587041d71a121c6b9..0ed56526002b12deb6d29f3dd23a0d74d8e7473c 100644
index ae58a0b0a64ae1fdb3f9cd8587041d71a121c6b9..f9d4e9b015ad266452dfa2a442b432ef31d09a5b 100644
--- a/.gn
+++ b/.gn
@@ -167,4 +167,27 @@ exec_script_allowlist =
@@ -167,4 +167,28 @@ exec_script_allowlist =
"//tools/grit/grit_rule.gni",
"//tools/gritsettings/BUILD.gn",
@@ -25,6 +25,7 @@ index ae58a0b0a64ae1fdb3f9cd8587041d71a121c6b9..0ed56526002b12deb6d29f3dd23a0d74
+ "//third_party/electron_node/deps/googletest/unofficial.gni",
+ "//third_party/electron_node/deps/histogram/unofficial.gni",
+ "//third_party/electron_node/deps/llhttp/unofficial.gni",
+ "//third_party/electron_node/deps/merve/unofficial.gni",
+ "//third_party/electron_node/deps/nbytes/unofficial.gni",
+ "//third_party/electron_node/deps/ncrypto/unofficial.gni",
+ "//third_party/electron_node/deps/nghttp2/unofficial.gni",

View File

@@ -9,10 +9,10 @@ potentially prevent a window from being created.
TODO(loc): this patch is currently broken.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index edaf9a7b2efc5ed7f4e946d720de54d5001f44d4..7d27b076c1947d2cd08364f87286ed6d9f460cdc 100644
index ce64151028cc30c81292326ee73126cb8415aec5..3feca12a6185afef139a0cb4a8148b5a3ca9e32f 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -9867,6 +9867,7 @@ void RenderFrameHostImpl::CreateNewWindow(
@@ -9868,6 +9868,7 @@ void RenderFrameHostImpl::CreateNewWindow(
last_committed_origin_, params->window_container_type,
params->target_url, params->referrer.To<Referrer>(),
params->frame_name, params->disposition, *params->features,

View File

@@ -0,0 +1,204 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Anders Hartvoll Ruud <andruud@chromium.org>
Date: Wed, 25 Feb 2026 03:24:31 -0800
Subject: Stringify CSSUnparsedValues via toString, as normal
CSSUnparsedValue exposes a special stringification function
ToUnparsedString() in addition to the regular toString().
The documentation says it returns "tokens without substituting
variables", but it's not clear what this means; we don't substitute
any variables in CSSStyleValue::toString() either.
This CL makes ToUnparsedString() private (and renames it).
Clients needing to serialize a CSSUnparsedValue can do so via
the normal toString() function. (If ToUnparsedString() existed
for performance reasons, that should have been documented.)
Also, the /**/-"fixup" pass over the value has been folded into
ToStringInternal(). This is to make it easy to find the canonical string
representation of this value within CSSUnparsedValue (without going
through a CSSValue).
The main point of this CL is to prepare for validating
the "argument grammar" of the value during the StyleValue-to-CSSValue
conversion in StylePropertyMap (which requires item (2) above).
We now jump through additional hoops to ultimately get a string
from the outside of CSSUnparsedValue, but there should otherwise
be no behavior change.
Bug: 484751092
Change-Id: I5db45ad85f780c67a2ea3ba8482c390ebab10068
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7600415
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1590041}
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
index dcc2eccbc84e6cd5710ab51cee2dab49661467c1..86d42c87a6bd10838a3e059c9227868e5bfc0798 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
@@ -19,12 +19,12 @@
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
-#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_color.h"
#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
namespace blink {
@@ -152,8 +152,7 @@ TEST_F(CrossThreadStyleValueTest, CrossThreadUnparsedValueToCSSStyleValue) {
CSSStyleValue* style_value = value->ToCSSStyleValue();
EXPECT_EQ(style_value->GetType(),
CSSStyleValue::StyleValueType::kUnparsedType);
- EXPECT_EQ(static_cast<CSSUnparsedValue*>(style_value)->ToUnparsedString(),
- "Unparsed");
+ EXPECT_EQ(style_value->toString(), "Unparsed");
}
TEST_F(CrossThreadStyleValueTest, PassKeywordValueCrossThread) {
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
index 567d4fad7436c24d4c42bc36ebfd7ee3641e3b90..12d70ed096cb1c509a2acf14b7f421273d833d0e 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
@@ -137,16 +137,26 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
}
const CSSValue* CSSUnparsedValue::ToCSSValue() const {
- String unparsed_string = ToUnparsedString();
- CSSParserTokenStream stream(unparsed_string);
+ String unparsed_string = ToStringInternal();
- if (stream.AtEnd()) {
+ if (unparsed_string.IsNull()) {
return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
MakeGarbageCollected<CSSVariableData>());
}
- // The string we just parsed has /**/ inserted between every token
- // to make sure we get back the correct sequence of tokens.
+ // TODO(crbug.com/985028): We should probably propagate the CSSParserContext
+ // to here.
+ return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
+ CSSVariableData::Create(unparsed_string, false /* is_animation_tainted */,
+ false /* is_attr_tainted */,
+ false /* needs_variable_resolution */));
+}
+
+String CSSUnparsedValue::ToStringInternal() const {
+ String serialized = SerializeSegments();
+
+ // The serialization above defensively inserted /**/ between segments
+ // to make sure that e.g. ['foo', 'bar'] does not collapse into 'foobar'.
// The spec mentions nothing of the sort:
// https://drafts.css-houdini.org/css-typed-om-1/#unparsedvalue-serialization
//
@@ -160,6 +170,10 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
// the original contents of any comments will be lost, but Typed OM does
// not have anywhere to store that kind of data, so it is expected.
StringBuilder builder;
+ CSSParserTokenStream stream(serialized);
+ if (stream.AtEnd()) {
+ return g_null_atom;
+ }
CSSParserToken token = stream.ConsumeRaw();
token.Serialize(builder);
while (!stream.Peek().IsEOF()) {
@@ -169,17 +183,10 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
token = stream.ConsumeRaw();
token.Serialize(builder);
}
- String original_text = builder.ReleaseString();
-
- // TODO(crbug.com/985028): We should probably propagate the CSSParserContext
- // to here.
- return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
- CSSVariableData::Create(original_text, false /* is_animation_tainted */,
- false /* is_attr_tainted */,
- false /* needs_variable_resolution */));
+ return builder.ReleaseString();
}
-String CSSUnparsedValue::ToUnparsedString() const {
+String CSSUnparsedValue::SerializeSegments() const {
StringBuilder builder;
HeapHashSet<Member<const CSSUnparsedValue>> values_on_stack;
if (AppendUnparsedString(builder, values_on_stack)) {
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
index 5d1961b170f14ae21ca8f69b3c3cd8af28f4478a..ec7e3ed708f406d7a61fdb370b2eed8a8297cffb 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
@@ -67,15 +67,9 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
CSSStyleValue::Trace(visitor);
}
- // Unlike CSSStyleValue::toString(), this returns tokens without
- // substituting variables. There are extra /**/ inserted between
- // every token to ensure there are no ambiguities, which is fine
- // because this value is never presented directly to the user
- // (ToCSSValue() will parse to a token range and then re-serialize
- // using extra /**/ only where needed).
- String ToUnparsedString() const;
-
private:
+ String ToStringInternal() const;
+ String SerializeSegments() const;
// Return 'false' if there is a cycle in the serialization.
bool AppendUnparsedString(
StringBuilder&,
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
index f81fa39423a9235bc58e1600ca7a250affd3d9bb..2ee4dd7e591095b8460ca559b29b78e37ab71729 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h"
#include <memory>
+
#include "base/synchronization/waitable_event.h"
#include "base/task/single_thread_task_runner.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -13,7 +14,6 @@
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_paint_worklet_input.h"
#include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
-#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_color.h"
#include "third_party/blink/renderer/core/css/properties/longhands/custom_property.h"
#include "third_party/blink/renderer/core/dom/element.h"
@@ -23,6 +23,7 @@
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
namespace blink {
@@ -66,8 +67,7 @@ class PaintWorkletStylePropertyMapTest : public PageTestBase {
CSSStyleValue* style_value = data.at("--x")->ToCSSStyleValue();
EXPECT_EQ(style_value->GetType(),
CSSStyleValue::StyleValueType::kUnparsedType);
- EXPECT_EQ(static_cast<CSSUnparsedValue*>(style_value)->ToUnparsedString(),
- "50");
+ EXPECT_EQ(style_value->toString(), "50");
waitable_event->Signal();
}
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 79b292f72efe32e6b56971ea577481710b0c750c..8b0c9f73656d664b04b640016391965009b667d6 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -5059,7 +5059,7 @@ ComputedStyleUtils::CrossThreadStyleValueFromCSSStyleValue(
To<CSSUnsupportedColor>(style_value)->Value());
case CSSStyleValue::StyleValueType::kUnparsedType:
return std::make_unique<CrossThreadUnparsedValue>(
- To<CSSUnparsedValue>(style_value)->ToUnparsedString());
+ To<CSSUnparsedValue>(style_value)->toString());
default:
return std::make_unique<CrossThreadUnsupportedValue>(
style_value->toString());

View File

@@ -0,0 +1,296 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mikel Astiz <mastiz@chromium.org>
Date: Tue, 10 Mar 2026 13:22:17 -0700
Subject: [M146][base] Fix UAF in base::OnceCallbackList on re-entrant Notify()
Before this patch, `base::OnceCallbackList` was susceptible to a
heap-use-after-free when `Notify()` was called re-entrantly.
The UAF occurred because `OnceCallbackList::RunCallback()` immediately
spliced executed nodes out of `callbacks_` and into `null_callbacks_`.
If a nested `Notify()` executed a node that an outer `Notify()` loop was
already holding an iterator to, and that node's subscription was
subsequently destroyed during the re-entrant cycle, the node would be
physically erased from `null_callbacks_`. When control returned to the
outer loop, it would attempt to evaluate the now-dangling iterator.
This CL fixes the bug by deferring list mutations until the outermost
iteration completes:
1. `RunCallback()` no longer splices nodes during iteration.
2. Cancellation logic is pushed down to the subclasses via a new
`CancelCallback()` hook, which is an extension to the pre-existing
`CancelNullCallback()` with increased responsibilities and clearer
semantics.
3. If a subscription is destroyed while `is_iterating` is true,
`OnceCallbackList` resets the node and stashes its iterator in
`pending_erasures_`.
4. A new `CleanUpNullCallbacksPostIteration()` phase runs at the end
of the outermost `Notify()`, which safely splices executed nodes
into `null_callbacks_` and physically erases the pending dead nodes.
As a side effect, the type-trait hack in `Notify()` based on
`is_instantiation<CallbackType, OnceCallback>` can be removed, because
this information is exposed directly by
`OnceCallbackList::CleanUpNullCallbacksPostIteration()`.
The newly-added unit-test
CallbackListTest.OnceCallbackListCancelDuringReentrantNotify reproduces
the scenario and crashed before this patch.
(cherry picked from commit 36acd49636845be2419269acbe9a5137da3d5d96)
Change-Id: I6b1e2bcb97be1bc8d6a15e5ca7511992e00e1772
Fixed: 489381399
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7627506
Commit-Queue: Mikel Astiz <mastiz@chromium.org>
Reviewed-by: Gabriel Charette <gab@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1594520}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7653916
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/7680@{#2287}
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
diff --git a/base/callback_list.h b/base/callback_list.h
index 82cb11dc0ee02906b009cc383c41a056861199d0..d5f99cf685486f1ea74718b4e6b228a5d83f0c29 100644
--- a/base/callback_list.h
+++ b/base/callback_list.h
@@ -9,6 +9,7 @@
#include <list>
#include <memory>
#include <utility>
+#include <vector>
#include "base/auto_reset.h"
#include "base/base_export.h"
@@ -16,7 +17,6 @@
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
-#include "base/types/is_instantiation.h"
// OVERVIEW:
//
@@ -240,17 +240,14 @@ class CallbackListBase {
// Any null callbacks remaining in the list were canceled due to
// Subscription destruction during iteration, and can safely be erased now.
- const size_t erased_callbacks =
- std::erase_if(callbacks_, [](const auto& cb) { return cb.is_null(); });
-
- // Run |removal_callback_| if any callbacks were canceled. Note that we
- // cannot simply compare list sizes before and after iterating, since
- // notification may result in Add()ing new callbacks as well as canceling
- // them. Also note that if this is a OnceCallbackList, the OnceCallbacks
- // that were executed above have all been removed regardless of whether
- // they're counted in |erased_callbacks_|.
- if (removal_callback_ &&
- (erased_callbacks || is_instantiation<CallbackType, OnceCallback>)) {
+ const bool any_callbacks_erased = static_cast<CallbackListImpl*>(this)
+ ->CleanUpNullCallbacksPostIteration();
+
+ // Run |removal_callback_| if any callbacks were canceled or executed. Note
+ // that simply comparing list sizes before and after iterating cannot be
+ // done, since notification may result in Add()ing new callbacks as well as
+ // canceling them.
+ if (removal_callback_ && any_callbacks_erased) {
removal_callback_.Run(); // May delete |this|!
}
}
@@ -264,21 +261,9 @@ class CallbackListBase {
private:
// Cancels the callback pointed to by |it|, which is guaranteed to be valid.
void CancelCallback(const typename Callbacks::iterator& it) {
- if (static_cast<CallbackListImpl*>(this)->CancelNullCallback(it)) {
- return;
- }
-
- if (iterating_) {
- // Calling erase() here is unsafe, since the loop in Notify() may be
- // referencing this same iterator, e.g. if adjacent callbacks'
- // Subscriptions are both destroyed when the first one is Run(). Just
- // reset the callback and let Notify() clean it up at the end.
- it->Reset();
- } else {
- callbacks_.erase(it);
- if (removal_callback_) {
- removal_callback_.Run(); // May delete |this|!
- }
+ if (static_cast<CallbackListImpl*>(this)->CancelCallback(it, iterating_) &&
+ removal_callback_) {
+ removal_callback_.Run(); // May delete |this|!
}
}
@@ -304,23 +289,71 @@ class OnceCallbackList
// Runs the current callback, which may cancel it or any other callbacks.
template <typename... RunArgs>
void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) {
- // OnceCallbacks still have Subscriptions with outstanding iterators;
- // splice() removes them from |callbacks_| without invalidating those.
- null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it);
+ // Do not splice here. Splicing during iteration breaks re-entrant Notify()
+ // by invalidating the outer loop's iterator. Splicing is deferred to
+ // CleanUpNullCallbacksPostIteration(), which is called when the outermost
+ // Notify() finishes.
// NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see
// comments in Notify().
std::move(*it).Run(args...);
}
- // If |it| refers to an already-canceled callback, does any necessary cleanup
- // and returns true. Otherwise returns false.
- bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) {
+ // Called during subscription destruction to cancel the callback. Returns true
+ // if the callback was removed from the active list and the generic removal
+ // callback should be executed. Returns false if the callback was already
+ // executed, or if the erasure is deferred due to active iteration.
+ bool CancelCallback(const typename Traits::Callbacks::iterator& it,
+ bool is_iterating) {
+ if (is_iterating) {
+ // During iteration, nodes cannot be safely erased from |callbacks_|
+ // without invalidating iterators. They also cannot be spliced into
+ // |null_callbacks_| right now. Thus, the node is reset and tracked for
+ // erasure in CleanUpNullCallbacksPostIteration().
+ it->Reset();
+ pending_erasures_.push_back(it);
+ return false;
+ }
+
if (it->is_null()) {
+ // The callback already ran, so it's safely sitting in |null_callbacks_|.
null_callbacks_.erase(it);
- return true;
+ return false;
}
- return false;
+
+ // The callback hasn't run yet, so it's still in |callbacks_|.
+ this->callbacks_.erase(it);
+ return true;
+ }
+
+ // Performs post-iteration cleanup. Successfully executed callbacks (which
+ // become null) are spliced into |null_callbacks_| to keep their
+ // Subscriptions' iterators valid. Callbacks explicitly canceled during
+ // iteration (tracked in |pending_erasures_|) are erased. Returns true if any
+ // callbacks were erased or spliced out.
+ bool CleanUpNullCallbacksPostIteration() {
+ bool any_spliced = false;
+ for (auto it = this->callbacks_.begin(); it != this->callbacks_.end();) {
+ if (it->is_null()) {
+ any_spliced = true;
+ auto next = std::next(it);
+ null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it);
+ it = next;
+ } else {
+ ++it;
+ }
+ }
+
+ bool any_erased = !pending_erasures_.empty();
+ for (auto pending_it : pending_erasures_) {
+ // Note: `pending_it` was originally an iterator into `callbacks_`, but
+ // the node it points to has just been spliced into `null_callbacks_`. The
+ // iterator itself remains valid and can now be used for erasure from
+ // `null_callbacks_`.
+ null_callbacks_.erase(pending_it);
+ }
+ pending_erasures_.clear();
+ return any_spliced || any_erased;
}
// Holds null callbacks whose Subscriptions are still alive, so the
@@ -328,6 +361,11 @@ class OnceCallbackList
// OnceCallbacks, since RepeatingCallbacks are not canceled except by
// Subscription destruction.
typename Traits::Callbacks null_callbacks_;
+
+ // Holds iterators for callbacks canceled during iteration.
+ // Erasure is deferred to CleanUpNullCallbacksPostIteration() when iteration
+ // completes to prevent invalidating iterators that an outer loop might hold.
+ std::vector<typename Traits::Callbacks::iterator> pending_erasures_;
};
template <typename Signature>
@@ -344,14 +382,29 @@ class RepeatingCallbackList
it->Run(args...);
}
- // If |it| refers to an already-canceled callback, does any necessary cleanup
- // and returns true. Otherwise returns false.
- bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) {
- // Because at most one Subscription can point to a given callback, and
- // RepeatingCallbacks are only reset by CancelCallback(), no one should be
- // able to request cancellation of a canceled RepeatingCallback.
- DCHECK(!it->is_null());
- return false;
+ // Called during subscription destruction to cancel the callback. Returns true
+ // if the callback was removed from the active list and the generic removal
+ // callback should be executed. Returns false if the callback was already
+ // executed, or if the erasure is deferred due to active iteration.
+ bool CancelCallback(const typename Traits::Callbacks::iterator& it,
+ bool is_iterating) {
+ if (is_iterating) {
+ // During iteration, nodes cannot be safely erased from |callbacks_|
+ // without invalidating iterators. The node is reset and will be swept up
+ // by CleanUpNullCallbacksPostIteration().
+ it->Reset();
+ return false;
+ }
+
+ this->callbacks_.erase(it);
+ return true;
+ }
+
+ // Performs post-iteration cleanup by erasing all canceled callbacks. Returns
+ // true if any callbacks were erased.
+ bool CleanUpNullCallbacksPostIteration() {
+ return std::erase_if(this->callbacks_,
+ [](const auto& cb) { return cb.is_null(); }) > 0;
}
};
diff --git a/base/callback_list_unittest.cc b/base/callback_list_unittest.cc
index 7474278525e5efecc0de903809a54d366896d524..a855443fbae862befbc3a2a484ea335632136e94 100644
--- a/base/callback_list_unittest.cc
+++ b/base/callback_list_unittest.cc
@@ -10,6 +10,7 @@
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
+#include "base/test/bind.h"
#include "base/test/test_future.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -577,6 +578,30 @@ TEST(CallbackListTest, ReentrantNotify) {
EXPECT_EQ(1, d.total());
}
+// Regression test for crbug.com/489381399: Verifies Notify() can be called
+// reentrantly for OnceCallbackList even if a callback is canceled during the
+// reentrant notification.
+TEST(CallbackListTest, OnceCallbackListCancelDuringReentrantNotify) {
+ OnceClosureList cb_reg;
+ CallbackListSubscription sub_a, sub_b;
+
+ auto cb_a = base::BindLambdaForTesting([&]() {
+ // Re-entrant notification.
+ cb_reg.Notify();
+ // After re-entrant notification returns, sub_b has been run. Destroying it
+ // now should be a no-op.
+ sub_b = {};
+ });
+
+ auto cb_b = base::DoNothing();
+
+ sub_a = cb_reg.Add(std::move(cb_a));
+ sub_b = cb_reg.Add(std::move(cb_b));
+
+ // This should not crash.
+ cb_reg.Notify();
+}
+
TEST(CallbackListTest, ClearPreventsInvocation) {
Listener listener;
RepeatingClosureList cb_reg;

View File

@@ -0,0 +1,762 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Geoff Lang <geofflang@chromium.org>
Date: Wed, 11 Feb 2026 08:05:52 -0800
Subject: Ensure the previous complete fbo is not deleted on IMG.
Change-Id: I7d84833312749fc58ecb511b276ff6bd783af1ba
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7533383
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1583241}
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index 9a913cdefd0681d9972a83d8f940a3c48af0f46d..4a3a68b4415b6cc05eab01f98b1a0afffe73afc9 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -120,9 +120,17 @@ ContextGroup::ContextGroup(
use_passthrough_cmd_decoder_ = gpu_preferences_.use_passthrough_cmd_decoder;
}
-
gpu::ContextResult ContextGroup::Initialize(DecoderContext* decoder,
ContextType context_type) {
+ return InitializeWithCompleteFramebufferForWorkarounds(decoder, context_type,
+ 0);
+}
+
+gpu::ContextResult
+ContextGroup::InitializeWithCompleteFramebufferForWorkarounds(
+ DecoderContext* decoder,
+ ContextType context_type,
+ uint32_t complete_fbo_for_workarounds) {
switch (context_type) {
case CONTEXT_TYPE_WEBGL1:
if (kGpuFeatureStatusBlocklisted ==
@@ -156,8 +164,9 @@ gpu::ContextResult ContextGroup::Initialize(DecoderContext* decoder,
DisallowedFeatures adjusted_disallowed_features =
GetDisallowedFeatures(context_type);
- feature_info_->Initialize(context_type, use_passthrough_cmd_decoder_,
- adjusted_disallowed_features);
+ feature_info_->InitializeWithCompleteFramebufferForWorkarounds(
+ context_type, use_passthrough_cmd_decoder_, adjusted_disallowed_features,
+ complete_fbo_for_workarounds);
// Fail early if ES3 is requested and driver does not support it.
if ((context_type == CONTEXT_TYPE_WEBGL2 ||
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index 78ea1ccd987cfc6f8ec0e378f906fb19fdccbc19..051d8126c128c47af71d0862cc0ec9476414825e 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -72,7 +72,10 @@ class GPU_GLES2_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
// call to destroy if it succeeds.
gpu::ContextResult Initialize(DecoderContext* decoder,
ContextType context_type);
-
+ gpu::ContextResult InitializeWithCompleteFramebufferForWorkarounds(
+ DecoderContext* decoder,
+ ContextType context_type,
+ uint32_t complete_fbo_for_workarounds);
// Destroys all the resources when called for the last context in the group.
// It should only be called by DecoderContext.
void Destroy(DecoderContext* decoder, bool have_context);
diff --git a/gpu/command_buffer/service/decoder_context.h b/gpu/command_buffer/service/decoder_context.h
index f00ad2459ff2b4993af37ac6fe0a5aa661fee692..96edc25e2d1ce364470b75ec1a3658ed0e217e54 100644
--- a/gpu/command_buffer/service/decoder_context.h
+++ b/gpu/command_buffer/service/decoder_context.h
@@ -139,6 +139,12 @@ class GPU_GLES2_EXPORT DecoderContext : public AsyncAPIInterface,
virtual gles2::ContextGroup* GetContextGroup() = 0;
virtual gles2::ErrorState* GetErrorState() = 0;
+ //
+ // Methods required by GLES2 Decoder helpers
+ //
+ // Bind the framebuffer `service_id` and perform any workarounds needed.
+ virtual void BindFramebuffer(unsigned target, uint32_t service_id) const = 0;
+
//
// Methods required by Texture.
//
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index 14bf6c1a220f7b37fa81ddbc7ac1f2a16fb08e4c..6f1cb7f75f8bc4b80d1bd95be721285bd0bab1a8 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -64,7 +64,8 @@ class ScopedPixelUnpackBufferOverride {
bool IsWebGLDrawBuffersSupported(bool webglCompatibilityContext,
GLenum depth_texture_internal_format,
- GLenum depth_stencil_texture_internal_format) {
+ GLenum depth_stencil_texture_internal_format,
+ GLuint complete_fbo_for_workarounds) {
// This is called after we make sure GL_EXT_draw_buffers is supported.
GLint max_draw_buffers = 0;
GLint max_color_attachments = 0;
@@ -81,6 +82,9 @@ bool IsWebGLDrawBuffersSupported(bool webglCompatibilityContext,
GLuint fbo;
glGenFramebuffersEXT(1, &fbo);
+ if (complete_fbo_for_workarounds) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER, complete_fbo_for_workarounds);
+ }
glBindFramebufferEXT(GL_FRAMEBUFFER, fbo);
GLuint depth_stencil_texture = 0;
@@ -157,6 +161,9 @@ bool IsWebGLDrawBuffersSupported(bool webglCompatibilityContext,
}
}
+ if (complete_fbo_for_workarounds) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER, complete_fbo_for_workarounds);
+ }
glBindFramebufferEXT(GL_FRAMEBUFFER, static_cast<GLuint>(fb_binding));
glDeleteFramebuffersEXT(1, &fbo);
@@ -238,6 +245,15 @@ void FeatureInfo::InitializeBasicState(const base::CommandLine* command_line) {
void FeatureInfo::Initialize(ContextType context_type,
bool is_passthrough_cmd_decoder,
const DisallowedFeatures& disallowed_features) {
+ InitializeWithCompleteFramebufferForWorkarounds(
+ context_type, is_passthrough_cmd_decoder, disallowed_features, 0);
+}
+
+void FeatureInfo::InitializeWithCompleteFramebufferForWorkarounds(
+ ContextType context_type,
+ bool is_passthrough_cmd_decoder,
+ const DisallowedFeatures& disallowed_features,
+ unsigned complete_fbo_for_workarounds) {
if (initialized_) {
DCHECK_EQ(context_type, context_type_);
DCHECK_EQ(is_passthrough_cmd_decoder, is_passthrough_cmd_decoder_);
@@ -248,14 +264,14 @@ void FeatureInfo::Initialize(ContextType context_type,
disallowed_features_ = disallowed_features;
context_type_ = context_type;
is_passthrough_cmd_decoder_ = is_passthrough_cmd_decoder;
- InitializeFeatures();
+ InitializeFeatures(complete_fbo_for_workarounds);
initialized_ = true;
}
void FeatureInfo::ForceReinitialize() {
CHECK(initialized_);
CHECK(is_passthrough_cmd_decoder_);
- InitializeFeatures();
+ InitializeFeatures(0);
}
void FeatureInfo::InitializeForTesting(
@@ -277,7 +293,7 @@ void FeatureInfo::InitializeForTesting(ContextType context_type) {
DisallowedFeatures());
}
-bool IsGL_REDSupportedOnFBOs() {
+bool IsGL_REDSupportedOnFBOs(uint32_t complete_fbo_for_workarounds) {
#if BUILDFLAG(IS_MAC)
// The glTexImage2D call below can hang on Mac so skip this since it's only
// really needed to workaround a Mesa issue. See https://crbug.com/1158744.
@@ -311,6 +327,9 @@ bool IsGL_REDSupportedOnFBOs() {
GL_UNSIGNED_BYTE, nullptr);
GLuint textureFBOID = 0;
glGenFramebuffersEXT(1, &textureFBOID);
+ if (complete_fbo_for_workarounds) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER, complete_fbo_for_workarounds);
+ }
glBindFramebufferEXT(GL_FRAMEBUFFER, textureFBOID);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
textureId, 0);
@@ -319,6 +338,9 @@ bool IsGL_REDSupportedOnFBOs() {
glDeleteFramebuffersEXT(1, &textureFBOID);
glDeleteTextures(1, &textureId);
+ if (complete_fbo_for_workarounds) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER, complete_fbo_for_workarounds);
+ }
glBindFramebufferEXT(GL_FRAMEBUFFER, static_cast<GLuint>(fb_binding));
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(tex_binding));
@@ -476,7 +498,7 @@ void FeatureInfo::EnableWEBGLMultiDrawIfPossible(
}
}
-void FeatureInfo::InitializeFeatures() {
+void FeatureInfo::InitializeFeatures(uint32_t complete_fbo_for_workarounds) {
// Figure out what extensions to turn on.
std::string extensions_string(gl::GetGLExtensionsFromCurrentContext());
gfx::ExtensionSet extensions(gfx::MakeExtensionSet(extensions_string));
@@ -1264,9 +1286,9 @@ void FeatureInfo::InitializeFeatures() {
can_emulate_es2_draw_buffers_on_es3_nv) &&
(context_type_ == CONTEXT_TYPE_OPENGLES2 ||
(context_type_ == CONTEXT_TYPE_WEBGL1 &&
- IsWebGLDrawBuffersSupported(is_webgl_compatibility_context,
- depth_texture_format,
- depth_stencil_texture_format)));
+ IsWebGLDrawBuffersSupported(
+ is_webgl_compatibility_context, depth_texture_format,
+ depth_stencil_texture_format, complete_fbo_for_workarounds)));
if (have_es2_draw_buffers) {
AddExtensionString("GL_EXT_draw_buffers");
feature_flags_.ext_draw_buffers = true;
@@ -1387,7 +1409,7 @@ void FeatureInfo::InitializeFeatures() {
if ((gl_version_info_->is_es3 ||
gfx::HasExtension(extensions, "GL_EXT_texture_rg")) &&
- IsGL_REDSupportedOnFBOs()) {
+ IsGL_REDSupportedOnFBOs(complete_fbo_for_workarounds)) {
feature_flags_.ext_texture_rg = true;
AddExtensionString("GL_EXT_texture_rg");
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index 2db3588ca3ed729799b113350ea8a7c449712587..83c683e900a3267061ced97ba971bf9dc0b88f4f 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -163,6 +163,14 @@ class GPU_GLES2_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> {
bool is_passthrough_cmd_decoder,
const DisallowedFeatures& disallowed_features);
+ // Same as initialize but with a provided `complete_fbo_for_workarounds` to
+ // use with the ensure_previous_framebuffer_not_deleted driver bug workaround.
+ void InitializeWithCompleteFramebufferForWorkarounds(
+ ContextType context_type,
+ bool is_passthrough_cmd_decoder,
+ const DisallowedFeatures& disallowed_features,
+ uint32_t complete_fbo_for_workarounds);
+
// Same as above, but allows reinitialization.
void ForceReinitialize();
@@ -250,7 +258,7 @@ class GPU_GLES2_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> {
void AddExtensionString(std::string_view s);
void InitializeBasicState(const base::CommandLine* command_line);
- void InitializeFeatures();
+ void InitializeFeatures(uint32_t complete_fbo_for_workarounds);
void InitializeFloatAndHalfFloatFeatures(const gfx::ExtensionSet& extensions);
void EnableANGLEInstancedArrayIfPossible(const gfx::ExtensionSet& extensions);
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
index b500acb4fe7cbff0e84a4e52e66459d5b85fdf75..a08bb3ac4ab625f220865803352e206713763d77 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
+++ b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
@@ -185,7 +185,7 @@ void CopyTexImageResourceManager::DoCopyTexSubImageToLUMACompatibilityTexture(
// framebuffer is copying from a texture and sample directly from that texture
// instead of doing an extra copy
- glBindFramebufferEXT(GL_FRAMEBUFFER, source_framebuffer);
+ decoder->BindFramebuffer(GL_FRAMEBUFFER, source_framebuffer);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, scratch_textures_[0]);
glCopyTexImage2D(GL_TEXTURE_2D, 0, source_framebuffer_internal_format, x, y,
@@ -217,7 +217,7 @@ void CopyTexImageResourceManager::DoCopyTexSubImageToLUMACompatibilityTexture(
glTexImage2D(GL_TEXTURE_2D, 0, compatability_format, width, height, 0,
compatability_format, luma_type, nullptr);
- glBindFramebufferEXT(GL_FRAMEBUFFER, scratch_fbo_);
+ decoder->BindFramebuffer(GL_FRAMEBUFFER, scratch_fbo_);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
scratch_textures_[1], 0);
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
index 469642028b839d490199379254a5a44c2fcd7f02..98f476de9e4d423b6ec86f2a69638e43bd0fb423 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
@@ -491,7 +491,8 @@ void DeleteShader(GLuint shader) {
glDeleteShader(shader);
}
-bool BindFramebufferTexture2D(GLenum target,
+bool BindFramebufferTexture2D(DecoderContext* decoder,
+ GLenum target,
GLuint texture_id,
GLint level,
GLuint framebuffer) {
@@ -511,7 +512,7 @@ bool BindFramebufferTexture2D(GLenum target,
glTexParameterf(binding_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(binding_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(binding_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer);
+ decoder->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target,
texture_id, level);
@@ -545,7 +546,7 @@ void DoCopyTexImage2D(
DCHECK(dest_binding_target == GL_TEXTURE_2D ||
dest_binding_target == GL_TEXTURE_CUBE_MAP);
DCHECK(source_level == 0 || decoder->GetFeatureInfo()->IsES3Capable());
- if (BindFramebufferTexture2D(source_target, source_id, source_level,
+ if (BindFramebufferTexture2D(decoder, source_target, source_id, source_level,
framebuffer)) {
glBindTexture(dest_binding_target, dest_id);
glTexParameterf(dest_binding_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -603,7 +604,7 @@ void DoCopyTexSubImage2D(
DCHECK(dest_binding_target == GL_TEXTURE_2D ||
dest_binding_target == GL_TEXTURE_CUBE_MAP);
DCHECK(source_level == 0 || decoder->GetFeatureInfo()->IsES3Capable());
- if (BindFramebufferTexture2D(source_target, source_id, source_level,
+ if (BindFramebufferTexture2D(decoder, source_target, source_id, source_level,
framebuffer)) {
glBindTexture(dest_binding_target, dest_id);
glTexParameterf(dest_binding_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -767,7 +768,7 @@ void DoReadbackAndTexImage(TexImageCommandType command_type,
DCHECK(dest_binding_target == GL_TEXTURE_2D ||
dest_binding_target == GL_TEXTURE_CUBE_MAP);
DCHECK(source_level == 0 || decoder->GetFeatureInfo()->IsES3Capable());
- if (BindFramebufferTexture2D(source_target, source_id, source_level,
+ if (BindFramebufferTexture2D(decoder, source_target, source_id, source_level,
framebuffer)) {
glBindTexture(dest_binding_target, dest_id);
glTexParameterf(dest_binding_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -1341,7 +1342,7 @@ void CopyTextureResourceManagerImpl::DoCopyTextureInternal(
(y + height / 2.f) * m_y / source_height);
DCHECK(dest_level == 0 || decoder->GetFeatureInfo()->IsES3Capable());
- if (BindFramebufferTexture2D(dest_target, dest_id, dest_level,
+ if (BindFramebufferTexture2D(decoder, dest_target, dest_id, dest_level,
framebuffer_)) {
#ifndef NDEBUG
// glValidateProgram of MACOSX validates FBO unlike other platforms, so
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 0bb2581fa602217ab77aa429ed51ce0ce5a06f00..895266f5988e2446eadf3d3e1d5c4919416cba76 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -675,6 +675,9 @@ class GLES2DecoderImpl : public GLES2Decoder,
// Implements GpuSwitchingObserver.
void OnGpuSwitched() override;
+ // Bind the framebuffer `fbo` and perform any workarounds needed.
+ void BindFramebuffer(unsigned target, uint32_t service_id) const override;
+
// Restores the current state to the user's settings.
void RestoreCurrentFramebufferBindings();
@@ -2418,6 +2421,10 @@ class GLES2DecoderImpl : public GLES2Decoder,
// Backbuffer attachments that are currently undefined.
uint32_t backbuffer_needs_clear_bits_;
+ // An always-complete FBO to use for workarounds
+ GLuint complete_fbo_ = 0;
+ GLuint complete_fbo_color_texture_ = 0;
+
// The current decoder error communicates the decoder error through command
// processing functions that do not return the error value. Should be set only
// if not returning an error.
@@ -2594,7 +2601,7 @@ ScopedFramebufferBinder::ScopedFramebufferBinder(GLES2DecoderImpl* decoder,
: decoder_(decoder) {
ScopedGLErrorSuppressor suppressor("ScopedFramebufferBinder::ctor",
decoder_->error_state_.get());
- decoder->api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, id);
+ decoder->BindFramebuffer(GL_FRAMEBUFFER, id);
decoder->OnFboChanged();
}
@@ -2980,7 +2987,27 @@ gpu::ContextResult GLES2DecoderImpl::Initialize(
return gpu::ContextResult::kFatalFailure;
}
- auto result = group_->Initialize(this, context_type);
+ if (workarounds().ensure_previous_framebuffer_not_deleted) {
+ // Use a 1x1 RGBA8 framebuffer as the "always complete" framebuffer to bind
+ // before binding other framebuffers
+ api()->glGenTexturesFn(1, &complete_fbo_color_texture_);
+ api()->glBindTextureFn(GL_TEXTURE_2D, complete_fbo_color_texture_);
+ api()->glTexImage2DFn(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, nullptr);
+
+ api()->glGenFramebuffersEXTFn(1, &complete_fbo_);
+ api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, complete_fbo_);
+ api()->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ complete_fbo_color_texture_, 0);
+ CHECK_EQ(api()->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
+ static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE));
+ }
+ CHECK_GL_ERROR();
+
+ auto result = group_->InitializeWithCompleteFramebufferForWorkarounds(
+ this, context_type, complete_fbo_);
+
if (result != gpu::ContextResult::kSuccess) {
// Must not destroy ContextGroup if it is not initialized.
group_ = nullptr;
@@ -3116,7 +3143,7 @@ gpu::ContextResult GLES2DecoderImpl::Initialize(
state_.viewport_width = initial_size.width();
state_.viewport_height = initial_size.height();
} else {
- api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, GetBackbufferServiceId());
+ BindFramebuffer(GL_FRAMEBUFFER, GetBackbufferServiceId());
// These are NOT if the back buffer has these proprorties. They are
// if we want the command buffer to enforce them regardless of what
// the real backbuffer is assuming the real back buffer gives us more than
@@ -3809,7 +3836,7 @@ void GLES2DecoderImpl::DeleteFramebuffersHelper(
if (workarounds().unbind_attachments_on_bound_render_fbo_delete)
framebuffer->DoUnbindGLAttachmentsForWorkaround(target);
- api()->glBindFramebufferEXTFn(target, GetBackbufferServiceId());
+ BindFramebuffer(target, GetBackbufferServiceId());
state_.UpdateWindowRectanglesForBoundDrawFramebufferClientID(0);
framebuffer_state_.bound_draw_framebuffer = nullptr;
framebuffer_state_.clear_state_dirty = true;
@@ -3817,7 +3844,7 @@ void GLES2DecoderImpl::DeleteFramebuffersHelper(
if (framebuffer == framebuffer_state_.bound_read_framebuffer.get()) {
framebuffer_state_.bound_read_framebuffer = nullptr;
GLenum target = GetReadFramebufferTarget();
- api()->glBindFramebufferEXTFn(target, GetBackbufferServiceId());
+ BindFramebuffer(target, GetBackbufferServiceId());
}
OnFboChanged();
RemoveFramebuffer(client_id);
@@ -3965,33 +3992,32 @@ void GLES2DecoderImpl::ProcessFinishedAsyncTransfers() {
ProcessPendingReadPixels(false);
}
-static void RebindCurrentFramebuffer(gl::GLApi* api,
- GLenum target,
- Framebuffer* framebuffer,
- GLuint back_buffer_service_id) {
- GLuint framebuffer_id = framebuffer ? framebuffer->service_id() : 0;
+void GLES2DecoderImpl::RestoreCurrentFramebufferBindings() {
+ framebuffer_state_.clear_state_dirty = true;
- if (framebuffer_id == 0) {
- framebuffer_id = back_buffer_service_id;
- }
+ auto rebind_current_framebuffer = [this](GLenum target,
+ Framebuffer* framebuffer,
+ GLuint back_buffer_service_id) {
+ GLuint framebuffer_id = framebuffer ? framebuffer->service_id() : 0;
- api->glBindFramebufferEXTFn(target, framebuffer_id);
-}
+ if (framebuffer_id == 0) {
+ framebuffer_id = back_buffer_service_id;
+ }
-void GLES2DecoderImpl::RestoreCurrentFramebufferBindings() {
- framebuffer_state_.clear_state_dirty = true;
+ BindFramebuffer(target, framebuffer_id);
+ };
if (!SupportsSeparateFramebufferBinds()) {
- RebindCurrentFramebuffer(api(), GL_FRAMEBUFFER,
- framebuffer_state_.bound_draw_framebuffer.get(),
- GetBackbufferServiceId());
+ rebind_current_framebuffer(GL_FRAMEBUFFER,
+ framebuffer_state_.bound_draw_framebuffer.get(),
+ GetBackbufferServiceId());
} else {
- RebindCurrentFramebuffer(api(), GL_READ_FRAMEBUFFER,
- framebuffer_state_.bound_read_framebuffer.get(),
- GetBackbufferServiceId());
- RebindCurrentFramebuffer(api(), GL_DRAW_FRAMEBUFFER,
- framebuffer_state_.bound_draw_framebuffer.get(),
- GetBackbufferServiceId());
+ rebind_current_framebuffer(GL_READ_FRAMEBUFFER,
+ framebuffer_state_.bound_read_framebuffer.get(),
+ GetBackbufferServiceId());
+ rebind_current_framebuffer(GL_DRAW_FRAMEBUFFER,
+ framebuffer_state_.bound_draw_framebuffer.get(),
+ GetBackbufferServiceId());
}
OnFboChanged();
}
@@ -4380,6 +4406,16 @@ void GLES2DecoderImpl::OnGpuSwitched() {
client()->OnGpuSwitched();
}
+void GLES2DecoderImpl::BindFramebuffer(unsigned target,
+ uint32_t service_id) const {
+ if (workarounds().ensure_previous_framebuffer_not_deleted) {
+ DCHECK(complete_fbo_);
+ api()->glBindFramebufferEXTFn(target, complete_fbo_);
+ }
+
+ api()->glBindFramebufferEXTFn(target, service_id);
+}
+
void GLES2DecoderImpl::Destroy(bool have_context) {
if (!initialized())
return;
@@ -4429,6 +4465,13 @@ void GLES2DecoderImpl::Destroy(bool have_context) {
offscreen_target_frame_buffer_->Destroy();
if (offscreen_target_color_texture_.get())
offscreen_target_color_texture_->Destroy();
+
+ if (complete_fbo_color_texture_) {
+ api()->glDeleteTexturesFn(1, &complete_fbo_color_texture_);
+ }
+ if (complete_fbo_) {
+ api()->glDeleteFramebuffersEXTFn(1, &complete_fbo_);
+ }
} else {
if (offscreen_target_frame_buffer_.get())
offscreen_target_frame_buffer_->Invalidate();
@@ -5058,13 +5101,13 @@ void GLES2DecoderImpl::RestoreFramebufferBindings() const {
? framebuffer_state_.bound_draw_framebuffer->service_id()
: GetBackbufferServiceId();
if (!SupportsSeparateFramebufferBinds()) {
- api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, service_id);
+ BindFramebuffer(GL_FRAMEBUFFER, service_id);
} else {
- api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, service_id);
+ BindFramebuffer(GL_DRAW_FRAMEBUFFER, service_id);
service_id = framebuffer_state_.bound_read_framebuffer.get()
? framebuffer_state_.bound_read_framebuffer->service_id()
: GetBackbufferServiceId();
- api()->glBindFramebufferEXTFn(GL_READ_FRAMEBUFFER, service_id);
+ BindFramebuffer(GL_READ_FRAMEBUFFER, service_id);
}
OnFboChanged();
}
@@ -5205,7 +5248,7 @@ void GLES2DecoderImpl::DoBindFramebuffer(GLenum target, GLuint client_id) {
service_id = GetBackbufferServiceId();
}
- api()->glBindFramebufferEXTFn(target, service_id);
+ BindFramebuffer(target, service_id);
OnFboChanged();
}
@@ -6976,8 +7019,7 @@ void GLES2DecoderImpl::ClearUnclearedAttachments(
if (target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) {
// TODO(zmo): There is no guarantee that an FBO that is complete on the
// READ attachment will be complete as a DRAW attachment.
- api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER,
- framebuffer->service_id());
+ BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->service_id());
}
state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false);
@@ -7024,8 +7066,7 @@ void GLES2DecoderImpl::ClearUnclearedAttachments(
target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) {
// TODO(zmo): There is no guarantee that an FBO that is complete on the
// READ attachment will be complete as a DRAW attachment.
- api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER,
- framebuffer->service_id());
+ BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->service_id());
}
state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false);
ClearDeviceWindowRectangles();
@@ -7043,7 +7084,7 @@ void GLES2DecoderImpl::ClearUnclearedAttachments(
if (target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) {
GLuint service_id = draw_framebuffer ? draw_framebuffer->service_id() :
GetBackbufferServiceId();
- api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, service_id);
+ BindFramebuffer(GL_DRAW_FRAMEBUFFER, service_id);
}
}
@@ -7900,7 +7941,8 @@ void GLES2DecoderImpl::RenderbufferStorageMultisampleHelperAMD(
bool GLES2DecoderImpl::RegenerateRenderbufferIfNeeded(
Renderbuffer* renderbuffer) {
- if (!renderbuffer->RegenerateAndBindBackingObjectIfNeeded(workarounds())) {
+ if (!renderbuffer->RegenerateAndBindBackingObjectIfNeeded(this,
+ workarounds())) {
return false;
}
@@ -12059,7 +12101,7 @@ bool GLES2DecoderImpl::ClearLevelUsingGL(Texture* texture,
GLenum fb_target = GetDrawFramebufferTarget();
GLuint fb = 0;
api()->glGenFramebuffersEXTFn(1, &fb);
- api()->glBindFramebufferEXTFn(fb_target, fb);
+ BindFramebuffer(fb_target, fb);
bool have_color = (channels & GLES2Util::kRGBA) != 0;
if (have_color) {
@@ -12102,7 +12144,7 @@ bool GLES2DecoderImpl::ClearLevelUsingGL(Texture* texture,
Framebuffer* framebuffer = GetFramebufferInfoForTarget(fb_target);
GLuint fb_service_id =
framebuffer ? framebuffer->service_id() : GetBackbufferServiceId();
- api()->glBindFramebufferEXTFn(fb_target, fb_service_id);
+ BindFramebuffer(fb_target, fb_service_id);
return result;
}
@@ -14581,8 +14623,9 @@ error::Error GLES2DecoderImpl::HandleGetRequestableExtensionsCHROMIUM(
new FeatureInfo(workarounds(), group_->gpu_feature_info()));
DisallowedFeatures disallowed_features = feature_info_->disallowed_features();
disallowed_features.AllowExtensions();
- info->Initialize(feature_info_->context_type(),
- false /* is_passthrough_cmd_decoder */, disallowed_features);
+ info->InitializeWithCompleteFramebufferForWorkarounds(
+ feature_info_->context_type(), false /* is_passthrough_cmd_decoder */,
+ disallowed_features, complete_fbo_);
bucket->SetFromString(gfx::MakeExtensionString(info->extensions()).c_str());
return error::kNoError;
}
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index bd3158e174f0881f99c45a0c8b8d5640e3be2b8f..cc160b5771954b485652f4b0cdc6695bfdea9954 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -147,6 +147,8 @@ class MockGLES2Decoder : public GLES2Decoder {
int height,
int depth));
MOCK_METHOD0(GetErrorState, ErrorState *());
+ MOCK_CONST_METHOD2(BindFramebuffer,
+ void(unsigned target, uint32_t service_id));
MOCK_METHOD0(GetLogger, Logger*());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index e8808f50df6d179879bc44fcacfb4154a4ac0454..de9f2f5cfb247a914606d3ba8b24f0924ac728e0 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1541,6 +1541,11 @@ gpu::gles2::ErrorState* GLES2DecoderPassthroughImpl::GetErrorState() {
return nullptr;
}
+void GLES2DecoderPassthroughImpl::BindFramebuffer(unsigned target,
+ uint32_t service_id) const {
+ NOTREACHED();
+}
+
void GLES2DecoderPassthroughImpl::WaitForReadPixels(
base::OnceClosure callback) {}
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
index 30dc611c8f0a84ec3493e3f865b6640cab157e2a..50938ca4a0086d14493ec7e0d85b99615c0c7fb7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
@@ -315,6 +315,8 @@ class GPU_GLES2_EXPORT GLES2DecoderPassthroughImpl
ErrorState* GetErrorState() override;
+ void BindFramebuffer(unsigned target, uint32_t service_id) const override;
+
void WaitForReadPixels(base::OnceClosure callback) override;
// Returns true if the context was lost either by GL_ARB_robustness, forced
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 5f61aac4fc217a97ccfcdbd6634056e5f305a425..767fee8cace6dec4cfaf1ccdd46f1764a7ae5318 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -560,6 +560,7 @@ class RasterDecoderImpl final : public RasterDecoder,
gles2::ContextGroup* GetContextGroup() override;
gles2::ErrorState* GetErrorState() override;
+ void BindFramebuffer(unsigned target, uint32_t service_id) const override;
bool IsCompressedTextureFormat(unsigned format) override;
bool ClearLevel(gles2::Texture* texture,
@@ -1598,6 +1599,11 @@ gles2::ErrorState* RasterDecoderImpl::GetErrorState() {
return error_state_.get();
}
+void RasterDecoderImpl::BindFramebuffer(unsigned target,
+ uint32_t service_id) const {
+ NOTREACHED();
+}
+
bool RasterDecoderImpl::IsCompressedTextureFormat(unsigned format) {
return feature_info()->validators()->compressed_texture_format.IsValid(
format);
diff --git a/gpu/command_buffer/service/renderbuffer_manager.cc b/gpu/command_buffer/service/renderbuffer_manager.cc
index 8075cb3acf7204a661a6c094edc1c1a783d46dfb..6cdb9186c4e3bfe40dd437db6b92343f60830171 100644
--- a/gpu/command_buffer/service/renderbuffer_manager.cc
+++ b/gpu/command_buffer/service/renderbuffer_manager.cc
@@ -15,6 +15,7 @@
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
+#include "gpu/command_buffer/service/decoder_context.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/framebuffer_manager.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
@@ -141,6 +142,7 @@ Renderbuffer::Renderbuffer(RenderbufferManager* manager,
}
bool Renderbuffer::RegenerateAndBindBackingObjectIfNeeded(
+ const DecoderContext* decoder,
const GpuDriverBugWorkarounds& workarounds) {
bool multisample_workaround =
workarounds.multisample_renderbuffer_resize_emulation;
@@ -167,7 +169,7 @@ bool Renderbuffer::RegenerateAndBindBackingObjectIfNeeded(
// Attach new renderbuffer to all framebuffers
for (auto& point : framebuffer_attachment_points_) {
- glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, point.first->service_id());
+ decoder->BindFramebuffer(GL_DRAW_FRAMEBUFFER, point.first->service_id());
glFramebufferRenderbufferEXT(GL_DRAW_FRAMEBUFFER, point.second,
GL_RENDERBUFFER, service_id_);
}
diff --git a/gpu/command_buffer/service/renderbuffer_manager.h b/gpu/command_buffer/service/renderbuffer_manager.h
index 7d575387ca8f6a7cb1b4bb020c52f7b53bed5d10..5eabeb9dd160122366507b7dfba53fd1c3285115 100644
--- a/gpu/command_buffer/service/renderbuffer_manager.h
+++ b/gpu/command_buffer/service/renderbuffer_manager.h
@@ -22,6 +22,7 @@
namespace gpu {
class GpuDriverBugWorkarounds;
+class DecoderContext;
namespace gles2 {
@@ -79,6 +80,7 @@ class GPU_GLES2_EXPORT Renderbuffer : public base::RefCounted<Renderbuffer> {
// Regenerates the object backing this client_id, creating a new service_id.
// Also reattaches any framebuffers using this renderbuffer.
bool RegenerateAndBindBackingObjectIfNeeded(
+ const DecoderContext* decoder,
const GpuDriverBugWorkarounds& workarounds);
void AddFramebufferAttachmentPoint(Framebuffer* framebuffer,
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 172834911e75edb3ed1c4dceb3ec23755bf3b5f6..fb873fee0e4b1d33edd4210fad8ad74c6d563355 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -305,6 +305,9 @@ class WebGPUDecoderImpl final : public WebGPUDecoder {
std::string_view GetLogPrefix() override { return "WebGPUDecoderImpl"; }
gles2::ContextGroup* GetContextGroup() override { return nullptr; }
gles2::ErrorState* GetErrorState() override { NOTREACHED(); }
+ void BindFramebuffer(unsigned target, uint32_t service_id) const override {
+ NOTREACHED();
+ }
bool IsCompressedTextureFormat(unsigned format) override { NOTREACHED(); }
bool ClearLevel(gles2::Texture* texture,
unsigned target,
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index ebea892a523322b38a22ba6b0442262edcd6166b..2af5b0460beed7b78c00c9f2a70e14e5f7696ac0 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3818,6 +3818,31 @@
"features": [
"disable_d3d12_video_encoder"
]
+ },
+ {
+ "id": 470,
+ "description": "Disable D3D12 video encoder on Windows versions older 11 24H2",
+ "os": {
+ "type": "win",
+ "version": {
+ "op": "<",
+ "value": "10.0.26100.2033"
+ }
+ },
+ "features": [
+ "disable_d3d12_video_encoder"
+ ]
+ },
+ {
+ "id": 471,
+ "description": "IMG drivers can sometimes reference previously bound complete framebuffers.",
+ "os": {
+ "type": "android"
+ },
+ "gl_vendor": "Imagination.*",
+ "features": [
+ "ensure_previous_framebuffer_not_deleted"
+ ]
}
]
}
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index 7f8b6e019f9b1986411b17c4ef1a2e863eb689f0..30ee7799cdd0a344e433e95cd74e4630a7c87aff 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -78,6 +78,7 @@ dont_delete_source_texture_for_egl_image
dont_use_loops_to_initialize_variables
enable_bgra8_overlays_with_yuv_overlay_support
enable_webgl_timer_query_extensions
+ensure_previous_framebuffer_not_deleted
etc1_power_of_two_only
exit_on_context_lost
flush_before_create_fence

View File

@@ -0,0 +1,199 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Anders Hartvoll Ruud <andruud@chromium.org>
Date: Wed, 25 Feb 2026 03:24:19 -0800
Subject: Describe a vector of segments as "segments", not "tokens"
The specification uses the term "tokens" to refer to a sequence
of V8CSSUnparsedSegment objects, and CSSUnparsedValue has adopted
this terminology. While it is usually a good idea for Blink
to mirror the language used in specifications, "tokens" is very
confusing here, since it always means CSSParserTokens in every other
place in the style code.
Bug: 487117772
Change-Id: I2dc132c4e618e398e1f8bdabc03a8d2ab6c118e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7606599
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1590040}
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
index 486e9d10c1e0a682ec239f4df696f4133300eebb..567d4fad7436c24d4c42bc36ebfd7ee3641e3b90 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
@@ -28,12 +28,12 @@ String FindVariableName(CSSParserTokenStream& stream) {
V8CSSUnparsedSegment* VariableReferenceValue(
const StringView& variable_name,
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens) {
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments) {
CSSUnparsedValue* unparsed_value;
- if (tokens.size() == 0) {
+ if (segments.size() == 0) {
unparsed_value = nullptr;
} else {
- unparsed_value = CSSUnparsedValue::Create(tokens);
+ unparsed_value = CSSUnparsedValue::Create(segments);
}
CSSStyleVariableReferenceValue* variable_reference =
@@ -50,13 +50,13 @@ V8CSSUnparsedSegment* VariableReferenceValue(
HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
CSSParserTokenStream& stream) {
int nesting_level = 0;
- HeapVector<Member<V8CSSUnparsedSegment>> tokens;
+ HeapVector<Member<V8CSSUnparsedSegment>> segments;
StringBuilder builder;
while (stream.Peek().GetType() != kEOFToken) {
if (stream.Peek().FunctionId() == CSSValueID::kVar ||
stream.Peek().FunctionId() == CSSValueID::kEnv) {
if (!builder.empty()) {
- tokens.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(
+ segments.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(
builder.ReleaseString()));
}
@@ -71,7 +71,7 @@ HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
if (!ref) {
break;
}
- tokens.push_back(ref);
+ segments.push_back(ref);
} else {
if (stream.Peek().GetBlockType() == CSSParserToken::kBlockStart) {
++nesting_level;
@@ -86,10 +86,10 @@ HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
}
}
if (!builder.empty()) {
- tokens.push_back(
+ segments.push_back(
MakeGarbageCollected<V8CSSUnparsedSegment>(builder.ReleaseString()));
}
- return tokens;
+ return segments;
}
} // namespace
@@ -109,8 +109,8 @@ CSSUnparsedValue* CSSUnparsedValue::FromCSSVariableData(
V8CSSUnparsedSegment* CSSUnparsedValue::AnonymousIndexedGetter(
uint32_t index,
ExceptionState& exception_state) const {
- if (index < tokens_.size()) {
- return tokens_[index].Get();
+ if (index < segments_.size()) {
+ return segments_[index].Get();
}
return nullptr;
}
@@ -119,20 +119,20 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
uint32_t index,
V8CSSUnparsedSegment* segment,
ExceptionState& exception_state) {
- if (index < tokens_.size()) {
- tokens_[index] = segment;
+ if (index < segments_.size()) {
+ segments_[index] = segment;
return IndexedPropertySetterResult::kIntercepted;
}
- if (index == tokens_.size()) {
- tokens_.push_back(segment);
+ if (index == segments_.size()) {
+ segments_.push_back(segment);
return IndexedPropertySetterResult::kIntercepted;
}
exception_state.ThrowRangeError(
ExceptionMessages::IndexOutsideRange<unsigned>(
- "index", index, 0, ExceptionMessages::kInclusiveBound, tokens_.size(),
- ExceptionMessages::kInclusiveBound));
+ "index", index, 0, ExceptionMessages::kInclusiveBound,
+ segments_.size(), ExceptionMessages::kInclusiveBound));
return IndexedPropertySetterResult::kIntercepted;
}
@@ -195,14 +195,14 @@ bool CSSUnparsedValue::AppendUnparsedString(
return false; // Cycle.
}
values_on_stack.insert(this);
- for (unsigned i = 0; i < tokens_.size(); i++) {
+ for (unsigned i = 0; i < segments_.size(); i++) {
if (i) {
builder.Append("/**/");
}
- switch (tokens_[i]->GetContentType()) {
+ switch (segments_[i]->GetContentType()) {
case V8CSSUnparsedSegment::ContentType::kCSSVariableReferenceValue: {
const auto* reference_value =
- tokens_[i]->GetAsCSSVariableReferenceValue();
+ segments_[i]->GetAsCSSVariableReferenceValue();
builder.Append("var(");
builder.Append(reference_value->variable());
if (reference_value->fallback()) {
@@ -216,7 +216,7 @@ bool CSSUnparsedValue::AppendUnparsedString(
break;
}
case V8CSSUnparsedSegment::ContentType::kString:
- builder.Append(tokens_[i]->GetAsString());
+ builder.Append(segments_[i]->GetAsString());
break;
}
}
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
index c9dab7a0b3ffeaeb6b5d2ab50d876d40c38a760e..5d1961b170f14ae21ca8f69b3c3cd8af28f4478a 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
@@ -26,8 +26,8 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
public:
static CSSUnparsedValue* Create(
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens) {
- return MakeGarbageCollected<CSSUnparsedValue>(tokens);
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments) {
+ return MakeGarbageCollected<CSSUnparsedValue>(segments);
}
// Blink-internal constructor
@@ -37,14 +37,14 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
static CSSUnparsedValue* FromCSSValue(const CSSUnparsedDeclarationValue&);
static CSSUnparsedValue* FromCSSVariableData(const CSSVariableData&);
static CSSUnparsedValue* FromString(const String& string) {
- HeapVector<Member<V8CSSUnparsedSegment>> tokens;
- tokens.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(string));
- return Create(tokens);
+ HeapVector<Member<V8CSSUnparsedSegment>> segments;
+ segments.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(string));
+ return Create(segments);
}
explicit CSSUnparsedValue(
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens)
- : tokens_(tokens) {}
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments)
+ : segments_(segments) {}
CSSUnparsedValue(const CSSUnparsedValue&) = delete;
CSSUnparsedValue& operator=(const CSSUnparsedValue&) = delete;
@@ -60,10 +60,10 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
V8CSSUnparsedSegment* segment,
ExceptionState& exception_state);
- wtf_size_t length() const { return tokens_.size(); }
+ wtf_size_t length() const { return segments_.size(); }
void Trace(Visitor* visitor) const override {
- visitor->Trace(tokens_);
+ visitor->Trace(segments_);
CSSStyleValue::Trace(visitor);
}
@@ -81,7 +81,7 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
StringBuilder&,
HeapHashSet<Member<const CSSUnparsedValue>>& values_on_stack) const;
- HeapVector<Member<V8CSSUnparsedSegment>> tokens_;
+ HeapVector<Member<V8CSSUnparsedSegment>> segments_;
FRIEND_TEST_ALL_PREFIXES(CSSUnparsedDeclarationValueTest, MixedList);
};

View File

@@ -0,0 +1,149 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Kai Ninomiya <kainino@chromium.org>
Date: Wed, 11 Mar 2026 14:52:44 -0700
Subject: [M146] Increment WebGL context generation number on context restore
Objects created while the context is lost should not be valid to use
after the context is restored.
- Replace number_of_context_losses_ with a "context generation number"
which increments on both context loss and context restore.
- Technically, it would make sense to increment it only on context
restore, but just in case any logic is relying on the current
behavior, increment it in both places.
- It's uint64_t just in case someone figures out how to increment it 4
billion times.
- Remove unused WebGLRenderingContextBase::number_of_context_losses_,
left over from before it was moved into WebGLContextObjectSupport.
(cherry picked from commit c1433740f3ea902fd6b15d63c4865ad60a3761f9)
Bug: 485935305
Change-Id: I1007217c8e69cfb8de4f117e0b7845ca574579c4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7630664
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1593726}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7658823
Auto-Submit: Kai Ninomiya <kainino@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/7680@{#2370}
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
index 6a3b1416354e7993e7a9ebd25c4ca08593105d9a..83941f8163a5e9425f2df8fd3bb98e1fd37537ad 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
@@ -22,7 +22,10 @@ WebGLContextObjectSupport::WebGLContextObjectSupport(
void WebGLContextObjectSupport::OnContextLost() {
DCHECK(!is_lost_);
- number_of_context_losses_++;
+ // Invalidate all past objects.
+ // (It may not be strictly necessary to do this here, since it's also done in
+ // OnContextRestored, but we did it historically, and there's no harm in it.)
+ context_generation_++;
is_lost_ = true;
gles2_interface_ = nullptr;
extensions_enabled_.reset();
@@ -31,6 +34,8 @@ void WebGLContextObjectSupport::OnContextLost() {
void WebGLContextObjectSupport::OnContextRestored(
gpu::gles2::GLES2Interface* gl) {
DCHECK(is_lost_);
+ // Invalidate all past objects.
+ context_generation_++;
is_lost_ = false;
gles2_interface_ = gl;
}
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
index 907866bb21acf9647d1c0ecd791e642e96b734fc..ba8b79f8bb9db12058614982a625baaff5546af7 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
@@ -33,10 +33,10 @@ class MODULES_EXPORT WebGLContextObjectSupport : public ScriptWrappable {
bool IsWebGL2() const { return is_webgl2_; }
bool IsLost() const { return is_lost_; }
- // How many context losses there were, to check whether a WebGLObject was
- // created since the last context resoration or before that (and hence invalid
- // to use).
- uint32_t NumberOfContextLosses() const { return number_of_context_losses_; }
+ // Which "generation" the context is on (essentially, how many times it has
+ // been restored), to check whether a WebGLObject was created since the last
+ // context restoration, or before that (and hence invalid to use).
+ uint64_t GetContextGeneration() const { return context_generation_; }
bool ExtensionEnabled(WebGLExtensionName name) const {
return extensions_enabled_.test(name);
@@ -65,7 +65,7 @@ class MODULES_EXPORT WebGLContextObjectSupport : public ScriptWrappable {
std::bitset<kWebGLExtensionNameCount> extensions_enabled_ = {};
raw_ptr<gpu::gles2::GLES2Interface> gles2_interface_ = nullptr;
- uint32_t number_of_context_losses_ = 0;
+ uint64_t context_generation_ = 0;
bool is_lost_ = true;
bool is_webgl2_;
};
diff --git a/third_party/blink/renderer/modules/webgl/webgl_object.cc b/third_party/blink/renderer/modules/webgl/webgl_object.cc
index 9d984de0073796f23a5038bfc0a51ec676179765..07e0a9a4aa3406a1298a677a3159edadc5f2cbb5 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_object.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_object.cc
@@ -33,9 +33,9 @@ namespace blink {
WebGLObject::WebGLObject(WebGLContextObjectSupport* context)
: context_(context),
- cached_number_of_context_losses_(std::numeric_limits<uint32_t>::max()) {
+ context_generation_at_creation_(std::numeric_limits<uint64_t>::max()) {
if (context_) {
- cached_number_of_context_losses_ = context->NumberOfContextLosses();
+ context_generation_at_creation_ = context->GetContextGeneration();
}
}
@@ -46,7 +46,7 @@ bool WebGLObject::Validate(const WebGLContextObjectSupport* context) const {
// the objects they ever created, so there's no way to invalidate them
// eagerly during context loss. The invalidation is discovered lazily.
return (context == context_ && context_ != nullptr &&
- cached_number_of_context_losses_ == context->NumberOfContextLosses());
+ context_generation_at_creation_ == context->GetContextGeneration());
}
void WebGLObject::SetObject(GLuint object) {
@@ -71,7 +71,7 @@ void WebGLObject::DeleteObject(gpu::gles2::GLES2Interface* gl) {
return;
}
- if (context_->NumberOfContextLosses() != cached_number_of_context_losses_) {
+ if (context_->GetContextGeneration() != context_generation_at_creation_) {
// This object has been invalidated.
return;
}
diff --git a/third_party/blink/renderer/modules/webgl/webgl_object.h b/third_party/blink/renderer/modules/webgl/webgl_object.h
index bb56df0f99e8e8432e03442feb9302b8dde27d01..97caa90e34288911b1a827e60c2569544d2b8f69 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_object.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_object.h
@@ -123,9 +123,9 @@ class WebGLObject : public ScriptWrappable {
GLuint object_ = 0;
- // This was the number of context losses of the object's associated
- // WebGLContext at the time this object was created.
- uint32_t cached_number_of_context_losses_;
+ // The context generation number of the associated WebGLContext when the
+ // object was created, to prevent reuse in later generations.
+ uint64_t context_generation_at_creation_;
unsigned attachment_count_ = 0;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index 060563a9955a8564d176177fc389c4f98aa64e9f..f24221cb2f47cfde515179ff945c13756487ebfc 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -2073,8 +2073,6 @@ class MODULES_EXPORT WebGLRenderingContextBase
bool has_been_drawn_to_ = false;
- uint32_t number_of_context_losses_ = 0;
-
// Tracks if the context has ever called glBeginPixelLocalStorageANGLE. If it
// has, we need to start using the pixel local storage interrupt mechanism
// when we take over the client's context.

View File

@@ -0,0 +1,219 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Anders Hartvoll Ruud <andruud@chromium.org>
Date: Wed, 25 Feb 2026 06:21:21 -0800
Subject: Validate CSSUnparsedValues upon assignment
CSS Typed OM has a concept of a value "matching a grammar" (or not)
upon assignment to a property [1]. For CSSUnparsedValues, we currently
don't perform any significant validation, and as a consequence
we allow "invalid" CSSUnparsedDeclarationValues to be created
(causing DCHECKs later in the pipeline).
This CL makes sure values can be parsed using CSSVariableParser::
ConsumeUnparsedDeclaration before assignment.
We're still not handling the value in the context of the destination
property, which we probably should. This is also a problem with
current state of things, however, so for now the goal is primarily
to avoid the DCHECKs in Issue 484751092.
Finally, I opened an issue against the specification [2], which
currently doesn't define any of this.
[1] https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
[2] https://github.com/w3c/csswg-drafts/issues/13547
Fixed: 484751092
Change-Id: Id7f888a6df8c02ade24910900f5d01909cb2dfad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7595347
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1590110}
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
index edfa73a57d30ebd4f9a7147702df42b836f7d82b..4442ba0872ca4c739596b546e6d3b600c5a31598 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
@@ -11,6 +11,7 @@
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_numeric_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
+#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h"
#include "third_party/blink/renderer/core/css/cssom/cssom_keywords.h"
#include "third_party/blink/renderer/core/css/properties/css_property.h"
@@ -105,8 +106,8 @@ bool CSSOMTypes::PropertyCanTake(CSSPropertyID id,
: CSSPropertyName(id);
return unsupported_style_value->IsValidFor(name);
}
- if (value.GetType() == CSSStyleValue::kUnparsedType) {
- return true;
+ if (auto* unparsed_value = DynamicTo<CSSUnparsedValue>(value)) {
+ return unparsed_value->IsValidDeclarationValue();
}
switch (id) {
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
index 12d70ed096cb1c509a2acf14b7f421273d833d0e..5f9d6a39effe207e44dd84cececebdb6c666f011 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
@@ -4,11 +4,13 @@
#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
+#include "css_style_value.h"
#include "third_party/blink/renderer/core/css/css_unparsed_declaration_value.h"
#include "third_party/blink/renderer/core/css/css_variable_data.h"
#include "third_party/blink/renderer/core/css/cssom/css_style_variable_reference_value.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
+#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -136,6 +138,10 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
return IndexedPropertySetterResult::kIntercepted;
}
+bool CSSUnparsedValue::IsValidDeclarationValue() const {
+ return IsValidDeclarationValue(ToStringInternal());
+}
+
const CSSValue* CSSUnparsedValue::ToCSSValue() const {
String unparsed_string = ToStringInternal();
@@ -144,12 +150,40 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
MakeGarbageCollected<CSSVariableData>());
}
+ CHECK(IsValidDeclarationValue(unparsed_string));
+ // The call to IsValidDeclarationValue() above also creates a CSSVariableData
+ // to carry out its check. It would be nice to use that here, but WPTs
+ // expect leading whitespace to be preserved, even though it's not possible
+ // to create such declaration values normally.
+ CSSVariableData* variable_data =
+ CSSVariableData::Create(unparsed_string,
+ /*is_animation_tainted=*/false,
+ /*is_attr_tainted=*/false,
+ /*needs_variable_resolution=*/false);
+
// TODO(crbug.com/985028): We should probably propagate the CSSParserContext
// to here.
- return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
- CSSVariableData::Create(unparsed_string, false /* is_animation_tainted */,
- false /* is_attr_tainted */,
- false /* needs_variable_resolution */));
+ return MakeGarbageCollected<CSSUnparsedDeclarationValue>(variable_data);
+}
+
+bool CSSUnparsedValue::IsValidDeclarationValue(const String& string) {
+ CSSParserTokenStream stream(string);
+ bool important_unused;
+ // This checks that the value does not violate the "argument grammar" [1]
+ // of any substitution functions, and that it is a valid <declaration-value>
+ // otherwise.
+ //
+ // [1] https://drafts.csswg.org/css-values-5/#argument-grammar
+ //
+ // TODO(andruud): 'restricted_value' depends on the destination property.
+ return CSSVariableParser::ConsumeUnparsedDeclaration(
+ stream,
+ /*allow_important_annotation=*/false,
+ /*is_animation_tainted=*/false,
+ /*must_contain_variable_reference=*/false,
+ /*restricted_value=*/false,
+ /*comma_ends_declaration=*/false, important_unused,
+ *StrictCSSParserContext(SecureContextMode::kInsecureContext));
}
String CSSUnparsedValue::ToStringInternal() const {
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
index ec7e3ed708f406d7a61fdb370b2eed8a8297cffb..7fd66aed677e31046a1bd206854b2cbeac07c25b 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
@@ -48,6 +48,14 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
CSSUnparsedValue(const CSSUnparsedValue&) = delete;
CSSUnparsedValue& operator=(const CSSUnparsedValue&) = delete;
+ // True if this CSSUnparsedValue can be converted into
+ // a CSSUnparsedDeclarationValue.
+ //
+ // We may want to ban some invalid values earlier, see:
+ // https://github.com/w3c/csswg-drafts/issues/13547
+ bool IsValidDeclarationValue() const;
+
+ // Requires IsValidDeclarationValue()==true.
const CSSValue* ToCSSValue() const override;
StyleValueType GetType() const override { return kUnparsedType; }
@@ -68,6 +76,7 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
}
private:
+ static bool IsValidDeclarationValue(const String&);
String ToStringInternal() const;
String SerializeSegments() const;
// Return 'false' if there is a cycle in the serialization.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html
deleted file mode 100644
index b92bd62deb71f2623b0265bed099d739cd1fce3a..0000000000000000000000000000000000000000
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<title>Crash Test: Missing variable name in CSSUnparsedValue</title>
-<link rel="help" href="https://issues.chromium.org/issues/484811719">
-<div id="div"></div>
-<script>
- for (let i = 0; i < 5000; ++i) {
- const bad = new CSSUnparsedValue(['var(,)']);
- div.attributeStyleMap.set('--x', bad);
- div.attributeStyleMap.get('--x');
- }
-</script>
-<p>PASS if no crash</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html
new file mode 100644
index 0000000000000000000000000000000000000000..ce618bf38fe651297b969ffdc16e212dee6a3688
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<title>Crash when setting invalid CSSUnparsedValue</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/13547">
+<div id=target></div>
+<script>
+ let examples = [
+ 'var()',
+ 'var(,)',
+ 'var(0)',
+ 'env()',
+ 'env(,)',
+ 'env(0)',
+ 'attr()',
+ 'attr(,)',
+ 'attr(0)',
+ 'if()',
+ 'if(,)',
+ 'if(0)',
+ '--f()',
+ '--f(,)',
+ '--f(0)',
+ 'thing!!!',
+ 'var(--x) !important',
+ ];
+ // Some of the above cases may be valid. That's fine; just don't crash.
+
+ for (let e of examples) {
+ try {
+ let value = new CSSUnparsedValue([e]);
+ target.attributeStyleMap.set('width', value);
+ // One of the two above statements should likely throw an exception.
+ // If they don't, then we should at least not crash on get():
+ target.attributeStyleMap.get('width');
+ } catch (e) {
+ // Intentionally empty.
+ }
+ target.offsetTop;
+ }
+</script>

View File

@@ -0,0 +1,65 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Geoff Lang <geofflang@chromium.org>
Date: Wed, 18 Feb 2026 13:54:37 -0800
Subject: Validate uniform block count limits at compile time on IMG.
Normally these limits are validated at link time but the IMG compiler
has issues when these limits are exceeded. Validate at compile time
instead.
Bug: chromium:475877320
Change-Id: Ieeed6914b8cdd2b5e50242d06facae62badddefd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7568129
Auto-Submit: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
Commit-Queue: Kyle Charbonneau <kylechar@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1586673}
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 895266f5988e2446eadf3d3e1d5c4919416cba76..aeafafece78faa6ece837386fe170592589d1b10 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -3678,6 +3678,9 @@ bool GLES2DecoderImpl::InitializeShaderTranslator() {
driver_bug_workarounds.dontUseLoopsToInitializeVariables = true;
if (workarounds().remove_dynamic_indexing_of_swizzled_vector)
driver_bug_workarounds.removeDynamicIndexingOfSwizzledVector = true;
+ if (workarounds().validate_max_per_stage_uniform_blocks_at_compile_time) {
+ driver_bug_workarounds.validatePerStageMaxUniformBlocks = true;
+ }
// Initialize uninitialized locals by default
driver_bug_workarounds.initializeUninitializedLocals = true;
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 2af5b0460beed7b78c00c9f2a70e14e5f7696ac0..3cc48e4a79576f93bfa22b8f8d1b74e5ba5baae7 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3843,6 +3843,17 @@
"features": [
"ensure_previous_framebuffer_not_deleted"
]
+ },
+ {
+ "id": 472,
+ "description": "Validate GL_MAX_*_UNIFORM_BLOCKS at compile time instead of link time to work around compiler bugs.",
+ "os": {
+ "type": "android"
+ },
+ "gl_vendor": "Imagination.*",
+ "features": [
+ "validate_max_per_stage_uniform_blocks_at_compile_time"
+ ]
}
]
}
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index 30ee7799cdd0a344e433e95cd74e4630a7c87aff..6d43d0b1937cd1eeaa044fb9949a52ba2e07d446 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -126,6 +126,7 @@ use_first_valid_ref_for_av1_invalid_ref
use_gpu_driver_workaround_for_testing
use_non_zero_size_for_client_side_stream_buffers
use_virtualized_gl_contexts
+validate_max_per_stage_uniform_blocks_at_compile_time
wake_up_gpu_before_drawing
webgl_or_caps_max_texture_size_limit_4096
webgl_or_caps_max_texture_size_limit_8192

View File

@@ -65,7 +65,7 @@ index 2748dd196fe1f56357348a204e24f0b8a28b97dd..5800dd00b47c657d9e6766f3fc5a3065
#if BUILDFLAG(IS_WIN)
bool EscapeVirtualization(const base::FilePath& user_data_dir);
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0aa3183a0b 100644
index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..144788ceadea85c9d1fae12d1ba4dbc1fc7cd699 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -619,6 +619,7 @@ class ProcessSingleton::LinuxWatcher
@@ -106,22 +106,41 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
const size_t kMinMessageLength = kStartToken.length() + 4;
if (bytes_read_ < kMinMessageLength) {
buf_[bytes_read_] = 0;
@@ -745,10 +751,26 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
@@ -745,10 +751,45 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
tokens.erase(tokens.begin());
tokens.erase(tokens.begin());
+ size_t num_args;
+ base::StringToSizeT(tokens[0], &num_args);
+ std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
+ if (!base::StringToSizeT(tokens[0], &num_args) ||
+ num_args > tokens.size() - 1) {
+ LOG(ERROR) << "Invalid num_args in socket message";
+ CleanupAndDeleteSelf();
+ return;
+ }
+ std::vector<std::string> command_line(tokens.begin() + 1,
+ tokens.begin() + 1 + num_args);
+
+ std::vector<uint8_t> additional_data;
+ if (tokens.size() >= 3 + num_args) {
+ // After consuming [num_args, argv...], two more tokens are needed for
+ // additional data: [size, payload]. Subtract to avoid overflow when
+ // num_args is large.
+ if (tokens.size() - 1 - num_args >= 2) {
+ size_t additional_data_size;
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
+ if (!base::StringToSizeT(tokens[1 + num_args], &additional_data_size)) {
+ LOG(ERROR) << "Invalid additional_data_size in socket message";
+ CleanupAndDeleteSelf();
+ return;
+ }
+ std::string remaining_args = base::JoinString(
+ base::span(tokens).subspan(2 + num_args),
+ std::string(1, kTokenDelimiter));
+ const auto adspan = base::as_byte_span(remaining_args).first(additional_data_size);
+ if (additional_data_size > remaining_args.size()) {
+ LOG(ERROR) << "additional_data_size exceeds payload length";
+ CleanupAndDeleteSelf();
+ return;
+ }
+ const auto adspan =
+ base::as_byte_span(remaining_args).first(additional_data_size);
+ additional_data.assign(adspan.begin(), adspan.end());
+ }
+
@@ -134,7 +153,7 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
fd_watch_controller_.reset();
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
@@ -777,8 +799,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
@@ -777,8 +818,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
//
ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
@@ -145,7 +164,7 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
current_pid_(base::GetCurrentProcId()) {
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
@@ -899,7 +923,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
@@ -899,7 +942,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
sizeof(socket_timeout));
// Found another process, prepare our command line
@@ -155,7 +174,7 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
std::string to_send(kStartToken);
to_send.push_back(kTokenDelimiter);
@@ -909,11 +934,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
@@ -909,11 +953,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
to_send.append(current_dir.value());
const std::vector<std::string>& argv = cmd_line.argv();
@@ -178,10 +197,18 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
if (!WriteToSocket(socket.fd(), to_send)) {
// Try to kill the other process, because it might have been dead.
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f947e6bfc 100644
index ae659d84a5ae2f2e87ce288477506575f8d86839..274887d62ff8d008bb86815a11205fcaa5f2c2ff 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -81,10 +81,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
@@ -9,6 +9,7 @@
#include <shellapi.h>
#include <stddef.h>
+#include "base/base64.h"
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
@@ -81,10 +82,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
bool ParseCommandLine(const COPYDATASTRUCT* cds,
base::CommandLine* parsed_command_line,
@@ -196,7 +223,7 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
static const int min_message_size = 7;
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
cds->cbData % sizeof(wchar_t) != 0) {
@@ -134,6 +136,23 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
@@ -134,6 +137,25 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
const std::wstring cmd_line =
msg.substr(second_null + 1, third_null - second_null);
*parsed_command_line = base::CommandLine::FromString(cmd_line);
@@ -209,18 +236,20 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
+ return true;
+ }
+
+ // Get the actual additional data.
+ const std::wstring additional_data =
+ msg.substr(third_null + 1, fourth_null - third_null);
+ base::span<const uint8_t> additional_data_bytes =
+ base::as_byte_span(additional_data);
+ *parsed_additional_data = std::vector<uint8_t>(
+ additional_data_bytes.begin(), additional_data_bytes.end());
+ // Get the actual additional data. It is base64-encoded so it can
+ // safely traverse the null-delimited wchar_t buffer.
+ const std::wstring encoded_w =
+ msg.substr(third_null + 1, fourth_null - third_null - 1);
+ std::string encoded = base::WideToASCII(encoded_w);
+ std::optional<std::vector<uint8_t>> decoded = base::Base64Decode(encoded);
+ if (decoded) {
+ *parsed_additional_data = std::move(*decoded);
+ }
+
return true;
}
return false;
@@ -155,13 +174,14 @@ bool ProcessLaunchNotification(
@@ -155,13 +177,14 @@ bool ProcessLaunchNotification(
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
base::FilePath current_directory;
@@ -238,7 +267,7 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
return true;
}
@@ -265,9 +285,11 @@ bool ProcessSingleton::EscapeVirtualization(
@@ -265,9 +288,11 @@ bool ProcessSingleton::EscapeVirtualization(
ProcessSingleton::ProcessSingleton(
const std::string& program_name,
const base::FilePath& user_data_dir,
@@ -250,7 +279,7 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
program_name_(program_name),
is_app_sandboxed_(is_app_sandboxed),
is_virtualized_(false),
@@ -294,7 +316,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
@@ -294,7 +319,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
return PROCESS_NONE;
}
@@ -260,10 +289,18 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
return PROCESS_NOTIFIED;
case NotifyChromeResult::kFailed:
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
index 594f3bc08a4385c177fb488123cef79448e94850..5a1dde19a4bc2bf728eba4c738f831c3e5b73942 100644
index 594f3bc08a4385c177fb488123cef79448e94850..28e5a18a19718b2e748ada6882341413a1ab0705 100644
--- a/chrome/browser/win/chrome_process_finder.cc
+++ b/chrome/browser/win/chrome_process_finder.cc
@@ -39,7 +39,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
@@ -11,6 +11,7 @@
#include <string>
#include <string_view>
+#include "base/base64.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
@@ -39,7 +40,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
return base::win::MessageWindow::FindWindow(user_data_dir.value());
}
@@ -274,7 +311,7 @@ index 594f3bc08a4385c177fb488123cef79448e94850..5a1dde19a4bc2bf728eba4c738f831c3
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
DCHECK(remote_window);
@@ -70,12 +72,24 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
@@ -70,12 +73,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
new_command_line.AppendSwitch(switches::kSourceAppId);
}
// Send the command line to the remote chrome window.
@@ -286,14 +323,12 @@ index 594f3bc08a4385c177fb488123cef79448e94850..5a1dde19a4bc2bf728eba4c738f831c3
std::wstring_view{L"\0", 1}, new_command_line.GetCommandLineString(),
std::wstring_view{L"\0", 1}});
+ size_t additional_data_size = additional_data.size_bytes();
+ if (additional_data_size) {
+ size_t padded_size = additional_data_size / sizeof(wchar_t);
+ if (additional_data_size % sizeof(wchar_t) != 0) {
+ padded_size++;
+ }
+ to_send.append(reinterpret_cast<const wchar_t*>(additional_data.data()),
+ padded_size);
+ if (!additional_data.empty()) {
+ // Base64-encode so the payload survives the null-delimited wchar_t
+ // framing; raw serialized bytes can contain 0x0000 sequences which
+ // would otherwise terminate the field early.
+ std::string encoded = base::Base64Encode(additional_data);
+ to_send.append(base::ASCIIToWide(encoded));
+ to_send.append(L"\0", 1); // Null separator.
+ }
+

View File

@@ -12,7 +12,7 @@ We attempt to migrate the safe storage key from the old account, if that migrati
Existing apps that aren't built for the app store should be unimpacted, there is one edge case where a user uses BOTH an AppStore and a darwin build of the same app only one will keep it's access to the safestorage key as during the migration we delete the old account. This is an acceptable edge case as no one should be actively using two versions of the same app.
diff --git a/components/os_crypt/common/keychain_password_mac.mm b/components/os_crypt/common/keychain_password_mac.mm
index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50ab1e8f05d 100644
index f19628cc0cdba39b232f55935e8eee9786b02a77..ab6ce9b1cf85029050a7497cd53813a03a46d408 100644
--- a/components/os_crypt/common/keychain_password_mac.mm
+++ b/components/os_crypt/common/keychain_password_mac.mm
@@ -27,6 +27,12 @@
@@ -28,14 +28,13 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
namespace {
// These two strings ARE indeed user facing. But they are used to access
@@ -96,11 +102,51 @@
@@ -96,18 +102,56 @@
uma_result);
};
+ const std::string account_name_suffix = kAccountNameSuffix;
+ const std::string suffixed_account_name = GetAccountName() + account_name_suffix;
auto password =
- keychain_->FindGenericPassword(GetServiceName(), GetAccountName());
+ keychain_->FindGenericPassword(GetServiceName(), suffixed_account_name);
+
+ if (password.has_value()) {
@@ -53,10 +52,8 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
+
+ // If the suffixed account didn't exist, we should check if the legacy non-suffixed account
+ // exists. If it does we can use that key and migrate it to the new account
+ base::apple::ScopedCFTypeRef<SecKeychainItemRef> item_ref;
+ password =
+ keychain_->FindGenericPassword(GetServiceName(), GetAccountName(),
+ item_ref.InitializeInto());
keychain_->FindGenericPassword(GetServiceName(), GetAccountName());
if (password.has_value()) {
uma_result = FindGenericPasswordResult::kPasswordFound;
@@ -70,7 +67,7 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
+ // If we successfully made the suffixed account we can delete the old
+ // account to ensure new apps don't try to use it and run into IAM
+ // issues
+ error = keychain_->ItemDelete(item_ref.get());
+ error = keychain_->ItemDelete(GetServiceName(), GetAccountName());
+ if (error != noErr) {
+ OSSTATUS_DLOG(ERROR, error) << "Keychain delete for legacy key failed";
+ }
@@ -81,115 +78,79 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
return std::string(base::as_string_view(*password));
}
if (password.error() == errSecItemNotFound) {
uma_result = FindGenericPasswordResult::kPasswordNotFound;
return AddRandomPasswordToKeychain(*keychain_, GetServiceName(),
- GetAccountName());
+ suffixed_account_name);
}
OSSTATUS_LOG(ERROR, password.error()) << "Keychain lookup failed";
diff --git a/crypto/apple/keychain.h b/crypto/apple/keychain.h
index 1d2264a5229206f45d1a9bcb009d47180efa6a8b..1dcf2b1d09831012c7f5768a5c6193d529efc821 100644
index 1d2264a5229206f45d1a9bcb009d47180efa6a8b..4472e5b605e09659bd75cd4797f073775fe4b354 100644
--- a/crypto/apple/keychain.h
+++ b/crypto/apple/keychain.h
@@ -17,6 +17,14 @@
namespace crypto::apple {
+// TODO(smaddock): Migrate to SecItem* as part of
+// https://issues.chromium.org/issues/40233280
+#if BUILDFLAG(IS_IOS)
+using AppleSecKeychainItemRef = void*;
+#else
+using AppleSecKeychainItemRef = SecKeychainItemRef;
+#endif
+
// Wraps the KeychainServices API in a very thin layer, to allow it to be
// mocked out for testing.
@@ -44,13 +52,18 @@ class CRYPTO_EXPORT Keychain {
// std::vector<uint8_t> arm is populated instead.
virtual base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
std::string_view service_name,
- std::string_view account_name) const = 0;
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item = nullptr) const = 0;
virtual OSStatus AddGenericPassword(
std::string_view service_name,
@@ -51,6 +51,11 @@ class CRYPTO_EXPORT Keychain {
std::string_view account_name,
base::span<const uint8_t> password) const = 0;
+#if BUILDFLAG(IS_MAC)
+ virtual OSStatus ItemDelete(AppleSecKeychainItemRef item) const = 0;
+#endif // !BUILDFLAG(IS_MAC)
+ virtual OSStatus ItemDelete(std::string_view service_name,
+ std::string_view account_name) const = 0;
+#endif // BUILDFLAG(IS_MAC)
+
protected:
Keychain();
};
diff --git a/crypto/apple/keychain_secitem.h b/crypto/apple/keychain_secitem.h
index eb74282adaba24ebd667f0ab3fc34dbe4cd8b527..7b91eb27489cece38eca719986255c5ec01c4bac 100644
index eb74282adaba24ebd667f0ab3fc34dbe4cd8b527..0d25e49e2fa1b374d6867b8c602f7685a7f9498d 100644
--- a/crypto/apple/keychain_secitem.h
+++ b/crypto/apple/keychain_secitem.h
@@ -17,12 +17,17 @@ class CRYPTO_EXPORT KeychainSecItem : public Keychain {
base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
std::string_view service_name,
- std::string_view account_name) const override;
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item) const override;
OSStatus AddGenericPassword(
@@ -23,6 +23,11 @@ class CRYPTO_EXPORT KeychainSecItem : public Keychain {
std::string_view service_name,
std::string_view account_name,
base::span<const uint8_t> password) const override;
+
+#if BUILDFLAG(IS_MAC)
+ OSStatus ItemDelete(AppleSecKeychainItemRef item) const override;
+#endif // !BUILDFLAG(IS_MAC)
+ OSStatus ItemDelete(std::string_view service_name,
+ std::string_view account_name) const override;
+#endif // BUILDFLAG(IS_MAC)
};
} // namespace crypto::apple
diff --git a/crypto/apple/keychain_secitem.mm b/crypto/apple/keychain_secitem.mm
index a8d50dd27db52526b0635c2b97f076df1994a6aa..e45f0d1079c8acfae55cf873e66ab3d9a10ad8ee 100644
index a8d50dd27db52526b0635c2b97f076df1994a6aa..464c17909b9a554b269a70ea08771da6ec7ac011 100644
--- a/crypto/apple/keychain_secitem.mm
+++ b/crypto/apple/keychain_secitem.mm
@@ -138,7 +138,8 @@
base::expected<std::vector<uint8_t>, OSStatus>
KeychainSecItem::FindGenericPassword(std::string_view service_name,
- std::string_view account_name) const {
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item) const {
base::apple::ScopedCFTypeRef<CFDictionaryRef> query =
MakeGenericPasswordQuery(service_name, account_name);
@@ -165,4 +166,13 @@
@@ -165,4 +165,18 @@
return base::ToVector(base::apple::CFDataToSpan(password_data));
}
+#if BUILDFLAG(IS_MAC)
+OSStatus KeychainSecItem::ItemDelete(AppleSecKeychainItemRef item) const {
+ // TODO(smaddock): AppleSecKeychainItemRef aliases the deprecated
+ // SecKeychainItemRef. Need to update this to accept a CFDictionary in the
+ // case of SecItemDelete.
+ return noErr;
+OSStatus KeychainSecItem::ItemDelete(std::string_view service_name,
+ std::string_view account_name) const {
+ NSDictionary* query = @{
+ CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassGenericPassword),
+ CFToNSPtrCast(kSecAttrService) : base::SysUTF8ToNSString(service_name),
+ CFToNSPtrCast(kSecAttrAccount) : base::SysUTF8ToNSString(account_name),
+ };
+ base::apple::ScopedCFTypeRef<CFDictionaryRef> cf_query(
+ NSToCFOwnershipCast(query));
+ return SecItemDelete(cf_query.get());
+}
+#endif
+
} // namespace crypto::apple
diff --git a/crypto/apple/mock_keychain.cc b/crypto/apple/mock_keychain.cc
index 080806aaf3fc10548b160850ad36ef3519ea2b6f..21f04059d67ba41118face6ee9327aa05e854362 100644
index 080806aaf3fc10548b160850ad36ef3519ea2b6f..98625524b668b86c857d5a8910bfb53b3ab40575 100644
--- a/crypto/apple/mock_keychain.cc
+++ b/crypto/apple/mock_keychain.cc
@@ -32,7 +32,8 @@ MockKeychain::~MockKeychain() = default;
base::expected<std::vector<uint8_t>, OSStatus>
MockKeychain::FindGenericPassword(std::string_view service_name,
- std::string_view account_name) const {
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item) const {
IncrementKeychainAccessHistogram();
// When simulating |noErr|, return canned |passwordData| and
@@ -56,6 +57,10 @@ OSStatus MockKeychain::AddGenericPassword(
@@ -56,6 +56,11 @@ OSStatus MockKeychain::AddGenericPassword(
return noErr;
}
+OSStatus MockKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
+OSStatus MockKeychain::ItemDelete(std::string_view service_name,
+ std::string_view account_name) const {
+ return noErr;
+}
+
@@ -197,25 +158,17 @@ index 080806aaf3fc10548b160850ad36ef3519ea2b6f..21f04059d67ba41118face6ee9327aa0
IncrementKeychainAccessHistogram();
return kPassword;
diff --git a/crypto/apple/mock_keychain.h b/crypto/apple/mock_keychain.h
index 680efe0312c81449e069c19d9c6ef146da7834db..b49c2ba5f639344ab57e9f14c098effc38729d1f 100644
index 680efe0312c81449e069c19d9c6ef146da7834db..102db6013b505fed32db176a90f5176118f62773 100644
--- a/crypto/apple/mock_keychain.h
+++ b/crypto/apple/mock_keychain.h
@@ -36,13 +36,18 @@ class CRYPTO_EXPORT MockKeychain : public Keychain {
// Keychain implementation.
base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
std::string_view service_name,
- std::string_view account_name) const override;
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item) const override;
OSStatus AddGenericPassword(
std::string_view service_name,
@@ -43,6 +43,11 @@ class CRYPTO_EXPORT MockKeychain : public Keychain {
std::string_view account_name,
base::span<const uint8_t> password) const override;
+#if !BUILDFLAG(IS_IOS)
+ OSStatus ItemDelete(SecKeychainItemRef itemRef) const override;
+#endif // !BUILDFLAG(IS_IOS)
+#if BUILDFLAG(IS_MAC)
+ OSStatus ItemDelete(std::string_view service_name,
+ std::string_view account_name) const override;
+#endif // BUILDFLAG(IS_MAC)
+
// Returns the password that OSCrypt uses to generate its encryption key.
std::string GetEncryptionPassword() const;

View File

@@ -0,0 +1,73 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <sattard@anthropic.com>
Date: Sat, 7 Mar 2026 23:07:30 -0800
Subject: feat: plumb node_integration_in_worker through WorkerSettings
Copy the node_integration_in_worker flag from the initiating frame's
WebPreferences into WorkerSettings at dedicated worker creation time,
so the value is readable per-worker on the worker thread rather than
relying on a process-wide command line switch. The value is also
propagated to nested workers via WorkerSettings::Copy.
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
index a0f78583334fdf4912b897e88d8ce518773dbfb1..300c5a3b806222e46388d2f0d906737cf282e52e 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -37,6 +37,7 @@
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
+#include "third_party/blink/renderer/core/exported/web_view_impl.h"
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
@@ -555,6 +556,12 @@ DedicatedWorker::CreateGlobalScopeCreationParams(
auto* frame = window->GetFrame();
parent_devtools_token = frame->GetDevToolsFrameToken();
settings = std::make_unique<WorkerSettings>(frame->GetSettings());
+ if (auto* web_local_frame = WebLocalFrameImpl::FromFrame(frame)) {
+ if (auto* web_view = web_local_frame->ViewImpl()) {
+ settings->SetNodeIntegrationInWorker(
+ web_view->GetWebPreferences().node_integration_in_worker);
+ }
+ }
agent_group_scheduler_compositor_task_runner =
execution_context->GetScheduler()
->ToFrameScheduler()
diff --git a/third_party/blink/renderer/core/workers/worker_settings.cc b/third_party/blink/renderer/core/workers/worker_settings.cc
index 45680c5f6ea0c7e89ccf43eb88f8a11e3318c02e..3fa3af62f4e7ba8186441c5e3184b1c04fe32d12 100644
--- a/third_party/blink/renderer/core/workers/worker_settings.cc
+++ b/third_party/blink/renderer/core/workers/worker_settings.cc
@@ -40,6 +40,8 @@ std::unique_ptr<WorkerSettings> WorkerSettings::Copy(
old_settings->strictly_block_blockable_mixed_content_;
new_settings->generic_font_family_settings_ =
old_settings->generic_font_family_settings_;
+ new_settings->node_integration_in_worker_ =
+ old_settings->node_integration_in_worker_;
return new_settings;
}
diff --git a/third_party/blink/renderer/core/workers/worker_settings.h b/third_party/blink/renderer/core/workers/worker_settings.h
index 45c60dd2c44b05fdd279f759069383479823c7f2..33a2a0337efb9a46293e11d0d09b3fc182ab9618 100644
--- a/third_party/blink/renderer/core/workers/worker_settings.h
+++ b/third_party/blink/renderer/core/workers/worker_settings.h
@@ -43,6 +43,11 @@ class CORE_EXPORT WorkerSettings {
return generic_font_family_settings_;
}
+ bool NodeIntegrationInWorker() const { return node_integration_in_worker_; }
+ void SetNodeIntegrationInWorker(bool value) {
+ node_integration_in_worker_ = value;
+ }
+
private:
void CopyFlagValuesFromSettings(Settings*);
@@ -54,6 +59,7 @@ class CORE_EXPORT WorkerSettings {
bool strict_mixed_content_checking_ = false;
bool allow_running_of_insecure_content_ = false;
bool strictly_block_blockable_mixed_content_ = false;
+ bool node_integration_in_worker_ = false;
GenericFontFamilySettings generic_font_family_settings_;
};

View File

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

View File

@@ -0,0 +1,80 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Kanishk Ranjan <kanishkranjan17@gmail.com>
Date: Thu, 11 Dec 2025 10:03:47 -0800
Subject: Mac: Fix WebRTC window icon conversion via gfx::Image
The current WebRTC window picker implementation tries to manually convert
NSImages to ImageSkia, but it does this incorrectly. As a result, the
icons can appear corrupted or blank.
This CL resolves the issue by using gfx::Image for the conversion. This
method offers a reliable and standard way to change an NSImage into an
ImageSkia.
Feature-Flag: kUseGfxImageForMacWindowIcons
Bug: 465028835
Change-Id: Ib69bc151e9542d2402c1cd7d282e5f3298581862
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7239386
Reviewed-by: Elad Alon <eladalon@chromium.org>
Commit-Queue: Avi Drissman <avi@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Tove Petersson <tovep@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1557501}
diff --git a/AUTHORS b/AUTHORS
index 7eb8f26120a23539b0780eb3f7e1d6a7ac52b102..fb0796cabdec4419e953306608a5b816ea1f2662 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -824,6 +824,7 @@ Kamil Rytarowski <krytarowski@gmail.com>
Kanaru Sato <i.am.kanaru.sato@gmail.com>
Kangil Han <kangil.han@samsung.com>
Kangyuan Shu <kangyuan.shu@intel.com>
+Kanishk Ranjan <kanishkranjan17@gmail.com>
Karan Thakkar <karanjthakkar@gmail.com>
Karel Král <kralkareliv@gmail.com>
Karl <karlpolicechromium@gmail.com>
diff --git a/chrome/browser/media/webrtc/window_icon_util_mac.mm b/chrome/browser/media/webrtc/window_icon_util_mac.mm
index 8bd216b9da864c9a8b82231ce6613cc120b32de7..c37b753c6aaf3b5036aacc74b310343fc7379188 100644
--- a/chrome/browser/media/webrtc/window_icon_util_mac.mm
+++ b/chrome/browser/media/webrtc/window_icon_util_mac.mm
@@ -8,9 +8,19 @@
#include "base/apple/foundation_util.h"
#include "base/apple/scoped_cftyperef.h"
+#include "base/feature_list.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
+
+// TODO(crbug.com/465028835): Remove these includes and the fallback code once
+// kUseGfxImageForMacWindowIcons is stable and the feature flag is removed
#include "third_party/libyuv/include/libyuv/convert_argb.h"
#include "third_party/skia/include/core/SkBitmap.h"
+BASE_FEATURE(kUseGfxImageForMacWindowIcons,
+ "UseGfxImageForMacWindowIcons",
+ base::FEATURE_ENABLED_BY_DEFAULT);
+
gfx::ImageSkia GetWindowIcon(content::DesktopMediaID id) {
DCHECK(id.type == content::DesktopMediaID::TYPE_WINDOW);
@@ -35,6 +45,20 @@
NSImage* icon_image =
[[NSRunningApplication runningApplicationWithProcessIdentifier:pid] icon];
+ // TODO(crbug.com/465028835): Remove this feature check and the fallback
+ // path once kUseGfxImageForMacWindowIcons is stable and the flag is removed
+ if (base::FeatureList::IsEnabled(kUseGfxImageForMacWindowIcons)) {
+ // The app may have terminated, resulting in a nil icon.
+ if (!icon_image) {
+ return gfx::ImageSkia();
+ }
+
+ return gfx::Image(icon_image).AsImageSkia();
+ }
+
+ // TODO(crbug.com/465028835): Remove the code below this line once
+ // kUseGfxImageForMacWindowIcons is stable and the flag is removed.
+
// Icon's NSImage defaults to the smallest which can be only 32x32.
NSRect proposed_rect = NSMakeRect(0, 0, 128, 128);
CGImageRef cg_icon_image =

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: reito <reito@chromium.org>
Date: Wed, 29 Oct 2025 00:50:03 +0800
Subject: patch: osr control screen info
We need to override GetNewScreenInfosForUpdate to ensure the screen info
is updated correctly, instead of overriding GetScreenInfo which seems not
working.
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index 2fc206be84d3d41379bb26540cbf0fa4b1ba95fb..cd85325d60f0c47bbd1e15624e96e62a7f51cd47 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -685,7 +685,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
// Generates the most current set of ScreenInfos from the current set of
// displays in the system for use in UpdateScreenInfo.
- display::ScreenInfos GetNewScreenInfosForUpdate();
+ virtual display::ScreenInfos GetNewScreenInfosForUpdate();
// Called when display properties that need to be synchronized with the
// renderer process changes. This method is called before notifying

View File

@@ -15,10 +15,10 @@ Note that we also need to manually update embedder's
`api::WebContents::IsFullscreenForTabOrPending` value.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 7d27b076c1947d2cd08364f87286ed6d9f460cdc..55d1609ba46abe9433d22a894fc2b498735ff778 100644
index 3feca12a6185afef139a0cb4a8148b5a3ca9e32f..393808a7959d684fe4e46f86eff687ec15258fa9 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8973,6 +8973,17 @@ void RenderFrameHostImpl::EnterFullscreen(
@@ -8974,6 +8974,17 @@ void RenderFrameHostImpl::EnterFullscreen(
}
}

View File

@@ -13,5 +13,6 @@
{ "patch_dir": "src/electron/patches/webrtc", "repo": "src/third_party/webrtc" },
{ "patch_dir": "src/electron/patches/reclient-configs", "repo": "src/third_party/engflow-reclient-configs" },
{ "patch_dir": "src/electron/patches/sqlite", "repo": "src/third_party/sqlite/src" },
{ "patch_dir": "src/electron/patches/skia", "repo": "src/third_party/skia" }
{ "patch_dir": "src/electron/patches/skia", "repo": "src/third_party/skia" },
{ "patch_dir": "src/electron/patches/angle", "repo": "src/third_party/angle/src" }
]

View File

@@ -1 +1,2 @@
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
fix_prefer_browser_runtime_over_node_in_hostruntime_detection.patch

View File

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

View File

@@ -6,7 +6,7 @@ Subject: Delete deprecated fields on v8::Isolate
https://chromium-review.googlesource.com/c/v8/v8/+/7081397
diff --git a/src/api/environment.cc b/src/api/environment.cc
index cfc9b3157d08d62f43e2e5bb01229fe663f3ca61..cce0e1cdc37aa324aa2c52ba134fc1a9a55b10ba 100644
index 2111ee63a6ace438c1a143c90a807ed9fc2bcc9d..ce6426a1bf2dadb1a642874a05718724ef0f3d7c 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -218,8 +218,6 @@ void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {

View File

@@ -6,10 +6,10 @@ Subject: Remove deprecated `GetIsolate`
https://chromium-review.googlesource.com/c/v8/v8/+/6905244
diff --git a/src/api/environment.cc b/src/api/environment.cc
index d753ad6c6b49b26b86920124f7ac90c1e052638e..cfc9b3157d08d62f43e2e5bb01229fe663f3ca61 100644
index 8974bac7dca43294cc5cc4570f8e2e78f42aefaa..2111ee63a6ace438c1a143c90a807ed9fc2bcc9d 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -668,7 +668,7 @@ std::unique_ptr<MultiIsolatePlatform> MultiIsolatePlatform::Create(
@@ -795,7 +795,7 @@ std::unique_ptr<MultiIsolatePlatform> MultiIsolatePlatform::Create(
MaybeLocal<Object> GetPerContextExports(Local<Context> context,
IsolateData* isolate_data) {
@@ -18,7 +18,7 @@ index d753ad6c6b49b26b86920124f7ac90c1e052638e..cfc9b3157d08d62f43e2e5bb01229fe6
EscapableHandleScope handle_scope(isolate);
Local<Object> global = context->Global();
@@ -714,7 +714,7 @@ void ProtoThrower(const FunctionCallbackInfo<Value>& info) {
@@ -841,7 +841,7 @@ void ProtoThrower(const FunctionCallbackInfo<Value>& info) {
// This runs at runtime, regardless of whether the context
// is created from a snapshot.
Maybe<void> InitializeContextRuntime(Local<Context> context) {
@@ -27,7 +27,7 @@ index d753ad6c6b49b26b86920124f7ac90c1e052638e..cfc9b3157d08d62f43e2e5bb01229fe6
HandleScope handle_scope(isolate);
// When `IsCodeGenerationFromStringsAllowed` is true, V8 takes the fast path
@@ -793,7 +793,7 @@ Maybe<void> InitializeContextRuntime(Local<Context> context) {
@@ -920,7 +920,7 @@ Maybe<void> InitializeContextRuntime(Local<Context> context) {
}
Maybe<void> InitializeBaseContextForSnapshot(Local<Context> context) {
@@ -36,7 +36,7 @@ index d753ad6c6b49b26b86920124f7ac90c1e052638e..cfc9b3157d08d62f43e2e5bb01229fe6
HandleScope handle_scope(isolate);
// Delete `Intl.v8BreakIterator`
@@ -818,7 +818,7 @@ Maybe<void> InitializeBaseContextForSnapshot(Local<Context> context) {
@@ -945,7 +945,7 @@ Maybe<void> InitializeBaseContextForSnapshot(Local<Context> context) {
}
Maybe<void> InitializeMainContextForSnapshot(Local<Context> context) {
@@ -45,7 +45,7 @@ index d753ad6c6b49b26b86920124f7ac90c1e052638e..cfc9b3157d08d62f43e2e5bb01229fe6
HandleScope handle_scope(isolate);
// Initialize the default values.
@@ -836,7 +836,7 @@ Maybe<void> InitializeMainContextForSnapshot(Local<Context> context) {
@@ -963,7 +963,7 @@ Maybe<void> InitializeMainContextForSnapshot(Local<Context> context) {
MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
IsolateData* isolate_data) {
CHECK(isolate_data);
@@ -54,7 +54,7 @@ index d753ad6c6b49b26b86920124f7ac90c1e052638e..cfc9b3157d08d62f43e2e5bb01229fe6
EscapableHandleScope scope(isolate);
Context::Scope context_scope(context);
@@ -860,7 +860,7 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
@@ -987,7 +987,7 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
IsolateData* isolate_data) {
CHECK(isolate_data);
@@ -63,7 +63,7 @@ index d753ad6c6b49b26b86920124f7ac90c1e052638e..cfc9b3157d08d62f43e2e5bb01229fe6
EscapableHandleScope scope(isolate);
Context::Scope context_scope(context);
@@ -886,7 +886,7 @@ MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
@@ -1013,7 +1013,7 @@ MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
Maybe<void> InitializePrimordials(Local<Context> context,
IsolateData* isolate_data) {
// Run per-context JS files.
@@ -120,10 +120,10 @@ index 4c5427596d1c90d3a413cdd9ff4f1151e657073d..70135a6be65e41fcb3564ddf6d1e8083
NewStringType::kNormal,
mem->length)
diff --git a/src/encoding_binding.cc b/src/encoding_binding.cc
index a913e34c73db3fb62aedcf28bee1bf1c4d59de7a..9de38eb9907269e99fdf0907aa35862572a2c643 100644
index 6fe4f0492dc1f3eaf576c8ff7866080a54cb81c1..41e8e052ff81df78ece87163b0499966cc2ed1b9 100644
--- a/src/encoding_binding.cc
+++ b/src/encoding_binding.cc
@@ -75,7 +75,7 @@ void BindingData::Deserialize(Local<Context> context,
@@ -76,7 +76,7 @@ void BindingData::Deserialize(Local<Context> context,
int index,
InternalFieldInfoBase* info) {
DCHECK_IS_SNAPSHOT_SLOT(index);
@@ -353,10 +353,10 @@ index 52483740bb377a2bc2a16af701615d9a4e448eae..84d17a46efe146c1794a43963c41a446
CHECK(!env->temporary_required_module_facade_original.IsEmpty());
return env->temporary_required_module_facade_original.Get(isolate);
diff --git a/src/node.h b/src/node.h
index 154fb15e6c8fe985e92378cc8471aa6a9d579b07..6252c41a14f4bd092cb424858be58d525b235625 100644
index 6e99e8ca1b53cce05615c16c3221ae3a203f50df..72de9923dacd0c25bbf83bc5b22ce243fd3769c2 100644
--- a/src/node.h
+++ b/src/node.h
@@ -1063,7 +1063,7 @@ NODE_DEPRECATED("Use v8::Date::ValueOf() directly",
@@ -1141,7 +1141,7 @@ NODE_DEPRECATED("Use v8::Date::ValueOf() directly",
#define NODE_DEFINE_CONSTANT(target, constant) \
do { \
@@ -365,7 +365,7 @@ index 154fb15e6c8fe985e92378cc8471aa6a9d579b07..6252c41a14f4bd092cb424858be58d52
v8::Local<v8::Context> context = isolate->GetCurrentContext(); \
v8::Local<v8::String> constant_name = v8::String::NewFromUtf8Literal( \
isolate, #constant, v8::NewStringType::kInternalized); \
@@ -1079,7 +1079,7 @@ NODE_DEPRECATED("Use v8::Date::ValueOf() directly",
@@ -1157,7 +1157,7 @@ NODE_DEPRECATED("Use v8::Date::ValueOf() directly",
#define NODE_DEFINE_HIDDEN_CONSTANT(target, constant) \
do { \
@@ -375,10 +375,10 @@ index 154fb15e6c8fe985e92378cc8471aa6a9d579b07..6252c41a14f4bd092cb424858be58d52
v8::Local<v8::String> constant_name = v8::String::NewFromUtf8Literal( \
isolate, #constant, v8::NewStringType::kInternalized); \
diff --git a/src/node_blob.cc b/src/node_blob.cc
index d278a32c9934c15bc721da164efccca7bc7e7111..ab862bf93a411e6ae6da7c9f9706cee279a0ad70 100644
index 4311d71bb0526f9a83a16525243446a590092910..417cd8cbd307b9bfc498ad2df24ed193616ac512 100644
--- a/src/node_blob.cc
+++ b/src/node_blob.cc
@@ -554,7 +554,7 @@ void BlobBindingData::Deserialize(Local<Context> context,
@@ -562,7 +562,7 @@ void BlobBindingData::Deserialize(Local<Context> context,
int index,
InternalFieldInfoBase* info) {
DCHECK_IS_SNAPSHOT_SLOT(index);
@@ -388,10 +388,10 @@ index d278a32c9934c15bc721da164efccca7bc7e7111..ab862bf93a411e6ae6da7c9f9706cee2
BlobBindingData* binding = realm->AddBindingData<BlobBindingData>(holder);
CHECK_NOT_NULL(binding);
diff --git a/src/node_builtins.cc b/src/node_builtins.cc
index 703ac1be06249736073f797058d170576a0db1bf..f0607ec9fd1983386166d0f4782adac99ace943e 100644
index 3377d697615ee168e49e83c4202bc227581f1aaf..1a9a57b73e635ac61016598687167a08b073f84a 100644
--- a/src/node_builtins.cc
+++ b/src/node_builtins.cc
@@ -275,7 +275,7 @@ MaybeLocal<Function> BuiltinLoader::LookupAndCompileInternal(
@@ -260,7 +260,7 @@ MaybeLocal<Function> BuiltinLoader::LookupAndCompileInternal(
const char* id,
LocalVector<String>* parameters,
Realm* optional_realm) {
@@ -400,7 +400,7 @@ index 703ac1be06249736073f797058d170576a0db1bf..f0607ec9fd1983386166d0f4782adac9
EscapableHandleScope scope(isolate);
Local<String> source;
@@ -397,7 +397,7 @@ void BuiltinLoader::SaveCodeCache(const char* id, Local<Function> fun) {
@@ -382,7 +382,7 @@ void BuiltinLoader::SaveCodeCache(const char* id, Local<Function> fun) {
MaybeLocal<Function> BuiltinLoader::LookupAndCompile(Local<Context> context,
const char* id,
Realm* optional_realm) {
@@ -409,7 +409,7 @@ index 703ac1be06249736073f797058d170576a0db1bf..f0607ec9fd1983386166d0f4782adac9
LocalVector<String> parameters(isolate);
// Detects parameters of the scripts based on module ids.
// internal/bootstrap/realm: process, getLinkedBinding,
@@ -451,7 +451,7 @@ MaybeLocal<Function> BuiltinLoader::LookupAndCompile(Local<Context> context,
@@ -436,7 +436,7 @@ MaybeLocal<Function> BuiltinLoader::LookupAndCompile(Local<Context> context,
MaybeLocal<Value> BuiltinLoader::CompileAndCall(Local<Context> context,
const char* id,
Realm* realm) {
@@ -418,7 +418,7 @@ index 703ac1be06249736073f797058d170576a0db1bf..f0607ec9fd1983386166d0f4782adac9
// Detects parameters of the scripts based on module ids.
// internal/bootstrap/realm: process, getLinkedBinding,
// getInternalBinding, primordials
@@ -507,7 +507,7 @@ MaybeLocal<Value> BuiltinLoader::CompileAndCall(Local<Context> context,
@@ -492,7 +492,7 @@ MaybeLocal<Value> BuiltinLoader::CompileAndCall(Local<Context> context,
if (!maybe_fn.ToLocal(&fn)) {
return MaybeLocal<Value>();
}
@@ -427,7 +427,7 @@ index 703ac1be06249736073f797058d170576a0db1bf..f0607ec9fd1983386166d0f4782adac9
return fn->Call(context, undefined, argc, argv);
}
@@ -545,14 +545,14 @@ bool BuiltinLoader::CompileAllBuiltinsAndCopyCodeCache(
@@ -530,14 +530,14 @@ bool BuiltinLoader::CompileAllBuiltinsAndCopyCodeCache(
to_eager_compile_.emplace(id);
}
@@ -546,7 +546,7 @@ index ba6ffc2b6565dea500bc8dd4818c8fcb7648694a..e834325a763f7ea8f53210145b5edd13
InternalFieldInfo* casted_info = static_cast<InternalFieldInfo*>(info);
BindingData* binding =
diff --git a/src/node_messaging.cc b/src/node_messaging.cc
index 57e068ae249d618c2658638f9f3b03e1fedb6524..8c51ae4e0a435971c6d0288af87810877dd31a49 100644
index 9ff675d23914aaa168de2f82b8509027de477cbf..76c934ccbc98737918c18a28e63df7eb759fed7c 100644
--- a/src/node_messaging.cc
+++ b/src/node_messaging.cc
@@ -254,7 +254,7 @@ namespace {
@@ -586,7 +586,7 @@ index 57e068ae249d618c2658638f9f3b03e1fedb6524..8c51ae4e0a435971c6d0288af8781087
data_.Reset();
return ret;
diff --git a/src/node_modules.cc b/src/node_modules.cc
index cecdda74847801fd5821bc0afdf0dfc9f131c44a..04ebecc5d924f6c2fddd9992462d1ff692e1cee5 100644
index ffc19850ac563082b14729e93d69695ef2c868f5..0cd351e7aff9e293e736e0aca22e67b577eb48eb 100644
--- a/src/node_modules.cc
+++ b/src/node_modules.cc
@@ -70,7 +70,7 @@ void BindingData::Deserialize(v8::Local<v8::Context> context,
@@ -660,10 +660,10 @@ index c2e24b4645e7903e08c80aead1c18c7bcff1bd89..e34d24d51d5c090b560d06f727043f20
// Recreate the buffer in the constructor.
InternalFieldInfo* casted_info = static_cast<InternalFieldInfo*>(info);
diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc
index 6bfc54dd81446545ebbb0faedb55a5383b81de49..2e52fb801684feb22800d4809daab006fc7cae9c 100644
index 050d779bdcd2b3129abddc3fefa1e852831df236..3f4749286406e03e77de6567b667c0098fbc2a18 100644
--- a/src/node_sqlite.cc
+++ b/src/node_sqlite.cc
@@ -2061,7 +2061,7 @@ bool StatementSync::BindParams(const FunctionCallbackInfo<Value>& args) {
@@ -2162,7 +2162,7 @@ bool StatementSync::BindParams(const FunctionCallbackInfo<Value>& args) {
if (args[0]->IsObject() && !args[0]->IsArrayBufferView()) {
Local<Object> obj = args[0].As<Object>();
@@ -758,7 +758,7 @@ index 5c7d268d38ff55ce4db07463b1ea0bcb2f4e63ea..bd83654012442195866e57173b6e5d4d
static void Clear(const FunctionCallbackInfo<Value>& info) {
diff --git a/src/node_worker.cc b/src/node_worker.cc
index e7d26b4c8cbb08a175084ceac51395860dc60598..fa4ec53ee556a23c8fd018caa1eee51bc5e004fe 100644
index 1acc61af0c995ddefbc00fe232b2454de77a84a3..3041746fc8a132f68cc1d801bb1700634699828d 100644
--- a/src/node_worker.cc
+++ b/src/node_worker.cc
@@ -1465,8 +1465,6 @@ void GetEnvMessagePort(const FunctionCallbackInfo<Value>& args) {

View File

@@ -11,10 +11,10 @@ really in 20/21. We have to wait until 22 is released to be able to
build with upstream GN files.
diff --git a/configure.py b/configure.py
index f31d460038db2fa2fa4c47d62be3100da959978f..209f23b04663113e4f6b3c3242c0544cfed9a950 100755
index 98a8b147e4cbfd5957c35688f2b37ae0ca52a818..fd13970ae73bbe5db186f81faed792a5597bbcd0 100755
--- a/configure.py
+++ b/configure.py
@@ -1736,7 +1736,7 @@ def configure_v8(o, configs):
@@ -1821,7 +1821,7 @@ def configure_v8(o, configs):
# Until we manage to get rid of all those, v8_enable_sandbox cannot be used.
# Note that enabling pointer compression without enabling sandbox is unsupported by V8,
# so this can be broken at any time.
@@ -23,8 +23,54 @@ index f31d460038db2fa2fa4c47d62be3100da959978f..209f23b04663113e4f6b3c3242c0544c
# We set v8_enable_pointer_compression_shared_cage to 0 always, even when
# pointer compression is enabled so that we don't accidentally enable shared
# cage mode when pointer compression is on.
diff --git a/deps/merve/BUILD.gn b/deps/merve/BUILD.gn
new file mode 100644
index 0000000000000000000000000000000000000000..7bb318f8835dba6f4a6f211d8534bb6923958747
--- /dev/null
+++ b/deps/merve/BUILD.gn
@@ -0,0 +1,14 @@
+##############################################################################
+# #
+# DO NOT EDIT THIS FILE! #
+# #
+##############################################################################
+
+# This file is used by GN for building, which is NOT the build system used for
+# building official binaries.
+# Please modify the gyp files if you are making changes to build system.
+
+import("unofficial.gni")
+
+merve_gn_build("merve") {
+}
diff --git a/deps/merve/unofficial.gni b/deps/merve/unofficial.gni
new file mode 100644
index 0000000000000000000000000000000000000000..dfb508d1d22f84accb146620ed07d89715b367e6
--- /dev/null
+++ b/deps/merve/unofficial.gni
@@ -0,0 +1,20 @@
+# This file is used by GN for building, which is NOT the build system used for
+# building official binaries.
+# Please edit the gyp files if you are making changes to build system.
+
+# The actual configurations are put inside a template in unofficial.gni to
+# prevent accidental edits from contributors.
+template("merve_gn_build") {
+ config("merve_config") {
+ include_dirs = [ "." ]
+ }
+ gypi_values = exec_script("../../tools/gypi_to_gn.py",
+ [ rebase_path("merve.gyp") ],
+ "scope",
+ [ "merve.gyp" ])
+ source_set(target_name) {
+ forward_variables_from(invoker, "*")
+ public_configs = [ ":merve_config" ]
+ sources = gypi_values.merve_sources
+ }
+}
diff --git a/node.gni b/node.gni
index d4438f7fd61598afac2c1e3184721a759d22b10c..e2407027ab05e59b2f0f1c213b98ea469db7a91b 100644
index d4438f7fd61598afac2c1e3184721a759d22b10c..156fee33b3813fe4d94a1c9585f217a99dbfbd5f 100644
--- a/node.gni
+++ b/node.gni
@@ -5,10 +5,10 @@
@@ -40,7 +86,16 @@ index d4438f7fd61598afac2c1e3184721a759d22b10c..e2407027ab05e59b2f0f1c213b98ea46
# The location of OpenSSL - use the one from node's deps by default.
node_openssl_path = "$node_path/deps/openssl"
@@ -50,7 +50,7 @@ declare_args() {
@@ -26,8 +26,6 @@ declare_args() {
# TODO(zcbenz): This is currently copied from configure.py, we should share
# the list between configure.py and GN configurations.
node_builtin_shareable_builtins = [
- "deps/cjs-module-lexer/lexer.js",
- "deps/cjs-module-lexer/dist/lexer.js",
"deps/undici/undici.js",
"deps/amaro/dist/index.js",
]
@@ -50,7 +48,7 @@ declare_args() {
node_openssl_system_ca_path = ""
# Initialize v8 platform during node.js startup.
@@ -49,7 +104,7 @@ index d4438f7fd61598afac2c1e3184721a759d22b10c..e2407027ab05e59b2f0f1c213b98ea46
# Custom build tag.
node_tag = ""
@@ -70,10 +70,16 @@ declare_args() {
@@ -70,10 +68,16 @@ declare_args() {
# TODO(zcbenz): There are few broken things for now:
# 1. cross-os compilation is not supported.
# 2. node_mksnapshot crashes when cross-compiling for x64 from arm64.
@@ -68,10 +123,10 @@ index d4438f7fd61598afac2c1e3184721a759d22b10c..e2407027ab05e59b2f0f1c213b98ea46
assert(!node_enable_inspector || node_use_openssl,
diff --git a/src/node_builtins.cc b/src/node_builtins.cc
index 7b34b14856a5193c723987f69d6040bdb6aa7c34..90fdf52d79954bf2cd86fd1d2d6da8199683d344 100644
index f25ca01d6ef016489371a3a1c9d8500da65e8023..2c816bef8d64f3e0ba2993c4885641620ee64272 100644
--- a/src/node_builtins.cc
+++ b/src/node_builtins.cc
@@ -775,6 +775,7 @@ void BuiltinLoader::RegisterExternalReferences(
@@ -760,6 +760,7 @@ void BuiltinLoader::RegisterExternalReferences(
registry->Register(GetNatives);
RegisterExternalReferencesForInternalizedBuiltinCode(registry);
@@ -251,10 +306,10 @@ index 856878c33681a73d41016729dabe48b0a6a80589..91a11852d206b65485fe90fd037a0bd1
if sys.platform == 'win32':
files = [ x.replace('\\', '/') for x in files ]
diff --git a/unofficial.gni b/unofficial.gni
index c742b62c484e9dd205eff63dcffad78c76828375..20d2483bb16e297ab5b12aab6f56948d6d25cb03 100644
index c742b62c484e9dd205eff63dcffad78c76828375..bff7b0650cfe8578a044e45d0f9e352859909695 100644
--- a/unofficial.gni
+++ b/unofficial.gni
@@ -147,31 +147,41 @@ template("node_gn_build") {
@@ -147,31 +147,42 @@ template("node_gn_build") {
public_configs = [
":node_external_config",
"deps/googletest:googletest_config",
@@ -273,6 +328,7 @@ index c742b62c484e9dd205eff63dcffad78c76828375..20d2483bb16e297ab5b12aab6f56948d
"deps/cares",
"deps/histogram",
"deps/llhttp",
+ "deps/merve",
"deps/nbytes",
"deps/nghttp2",
- "deps/ngtcp2",
@@ -299,7 +355,7 @@ index c742b62c484e9dd205eff63dcffad78c76828375..20d2483bb16e297ab5b12aab6f56948d
"$target_gen_dir/node_javascript.cc",
] + gypi_values.node_sources
@@ -194,7 +204,7 @@ template("node_gn_build") {
@@ -194,7 +205,7 @@ template("node_gn_build") {
}
if (node_use_openssl) {
deps += [ "deps/ncrypto" ]
@@ -308,7 +364,7 @@ index c742b62c484e9dd205eff63dcffad78c76828375..20d2483bb16e297ab5b12aab6f56948d
sources += gypi_values.node_crypto_sources
}
if (node_use_sqlite) {
@@ -223,6 +233,10 @@ template("node_gn_build") {
@@ -223,6 +234,10 @@ template("node_gn_build") {
}
}
@@ -319,7 +375,7 @@ index c742b62c484e9dd205eff63dcffad78c76828375..20d2483bb16e297ab5b12aab6f56948d
executable(target_name) {
forward_variables_from(invoker, "*")
@@ -314,6 +328,7 @@ template("node_gn_build") {
@@ -314,6 +329,7 @@ template("node_gn_build") {
}
executable("node_js2c") {
@@ -327,7 +383,7 @@ index c742b62c484e9dd205eff63dcffad78c76828375..20d2483bb16e297ab5b12aab6f56948d
deps = [
"deps/uv",
"$node_simdutf_path",
@@ -324,26 +339,75 @@ template("node_gn_build") {
@@ -324,26 +340,75 @@ template("node_gn_build") {
"src/embedded_data.cc",
"src/embedded_data.h",
]
@@ -413,7 +469,7 @@ index c742b62c484e9dd205eff63dcffad78c76828375..20d2483bb16e297ab5b12aab6f56948d
outputs = [ "$target_gen_dir/node_javascript.cc" ]
# Get the path to node_js2c executable of the host toolchain.
@@ -357,11 +421,11 @@ template("node_gn_build") {
@@ -357,11 +422,11 @@ template("node_gn_build") {
get_label_info(":node_js2c($host_toolchain)", "name") +
host_executable_suffix

View File

@@ -14,18 +14,10 @@ We don't need to do this for zlib, as the existing gn workflow uses the same
Upstreamed at https://github.com/nodejs/node/pull/55903
diff --git a/unofficial.gni b/unofficial.gni
index 20d2483bb16e297ab5b12aab6f56948d6d25cb03..253226009faf563f6db285d4b2908f308c1f96ea 100644
index bff7b0650cfe8578a044e45d0f9e352859909695..4ab316e45bd84e43a53335df60f847b17fe6c2fa 100644
--- a/unofficial.gni
+++ b/unofficial.gni
@@ -160,7 +160,6 @@ template("node_gn_build") {
":run_node_js2c",
"deps/cares",
"deps/histogram",
- "deps/llhttp",
"deps/nbytes",
"deps/nghttp2",
"deps/postject",
@@ -198,7 +197,17 @@ template("node_gn_build") {
@@ -199,7 +199,17 @@ template("node_gn_build") {
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
configs += [ "//build/config/gcc:symbol_visibility_default" ]
}
@@ -44,7 +36,7 @@ index 20d2483bb16e297ab5b12aab6f56948d6d25cb03..253226009faf563f6db285d4b2908f30
if (v8_enable_i18n_support) {
deps += [ "//third_party/icu" ]
}
@@ -231,6 +240,19 @@ template("node_gn_build") {
@@ -232,6 +242,19 @@ template("node_gn_build") {
sources += node_inspector.node_inspector_sources +
node_inspector.node_inspector_generated_sources
}

View File

@@ -33,7 +33,7 @@ index 8d7204f6cb48f783adc4d1c1eb2de0c83b7fffe2..a154559a56bf383d3c26af523c9bb07b
// Non-alphabetic chars.
diff --git a/lib/internal/http.js b/lib/internal/http.js
index e664663348adc7bb31f7c9ec78481bbeb71401d9..62b659beb766b8256b214447af376f438278b058 100644
index f8b4fd7c4ca5a0907806c7e804de8c951675a36a..209e3bcf8be5a23ac528dcd673bed82cbad709ca 100644
--- a/lib/internal/http.js
+++ b/lib/internal/http.js
@@ -11,8 +11,8 @@ const {
@@ -64,10 +64,10 @@ index e664663348adc7bb31f7c9ec78481bbeb71401d9..62b659beb766b8256b214447af376f43
function ipToInt(ip) {
diff --git a/node.gyp b/node.gyp
index c035d1d7cdac1d18cca0ef5cefbc5ce4c1fd1b86..a48e4e5d1fb7621b12b9abeaaf78214515a23efc 100644
index f5cd416b5fe7a51084bc4af9a4427a8e62599fd8..5eb70ce3820f2b82121bc102c5182ab768cbef36 100644
--- a/node.gyp
+++ b/node.gyp
@@ -176,7 +176,6 @@
@@ -182,7 +182,6 @@
'src/timers.cc',
'src/timer_wrap.cc',
'src/tracing/agent.cc',
@@ -75,7 +75,7 @@ index c035d1d7cdac1d18cca0ef5cefbc5ce4c1fd1b86..a48e4e5d1fb7621b12b9abeaaf782145
'src/tracing/node_trace_writer.cc',
'src/tracing/trace_event.cc',
'src/tracing/traced_value.cc',
@@ -308,7 +307,6 @@
@@ -314,7 +313,6 @@
'src/tcp_wrap.h',
'src/timers.h',
'src/tracing/agent.h',

View File

@@ -7,7 +7,7 @@ Subject: build: ensure native module compilation fails if not using a new
This should not be upstreamed, it is a quality-of-life patch for downstream module builders.
diff --git a/common.gypi b/common.gypi
index cf3ceaf19972ee107deff63b4dba16c12191c615..6159350823fd9f0090e2b98e8797e829fd481750 100644
index b3b5c23e471ece7584d209b3ae4197c46011d50e..bdcea65ad3e0315c85b1818e695d8b63093aed34 100644
--- a/common.gypi
+++ b/common.gypi
@@ -89,6 +89,8 @@
@@ -42,10 +42,10 @@ index cf3ceaf19972ee107deff63b4dba16c12191c615..6159350823fd9f0090e2b98e8797e829
# list in v8/BUILD.gn.
['v8_enable_v8_checks == 1', {
diff --git a/configure.py b/configure.py
index 209f23b04663113e4f6b3c3242c0544cfed9a950..88164b99ae3d37f47e5e9a9ba96d7b450d6ba4ab 100755
index fd13970ae73bbe5db186f81faed792a5597bbcd0..162e3b09c92b49cd39d32a87ff97a54555d3e47b 100755
--- a/configure.py
+++ b/configure.py
@@ -1717,6 +1717,7 @@ def configure_library(lib, output, pkgname=None):
@@ -1802,6 +1802,7 @@ def configure_library(lib, output, pkgname=None):
def configure_v8(o, configs):
set_configuration_variable(configs, 'v8_enable_v8_checks', release=1, debug=0)
@@ -54,7 +54,7 @@ index 209f23b04663113e4f6b3c3242c0544cfed9a950..88164b99ae3d37f47e5e9a9ba96d7b45
o['variables']['v8_enable_javascript_promise_hooks'] = 1
o['variables']['v8_enable_lite_mode'] = 1 if options.v8_lite_mode else 0
diff --git a/src/node.h b/src/node.h
index be22ad370217a13aef2479d478a6373feaf6f208..19c34a430d095c06ccf5a988db91311d420a485a 100644
index ebfd7229b5f0044b628fbe0b03ac211f0c6ed9a6..b92a9d42da8419741c435643b7401efcb21a9e8b 100644
--- a/src/node.h
+++ b/src/node.h
@@ -22,6 +22,12 @@

View File

@@ -10,10 +10,10 @@ JS errors and ensures embedder JS is loaded via LoadEmbedderJavaScriptSource.
That method is generated by our modifications to js2c.cc in the BUILD.gn patch
diff --git a/lib/internal/fs/watchers.js b/lib/internal/fs/watchers.js
index 605dee28cace56f2366fec9d7f18894559044ae4..15dcabb3b1682438eb6d5a681363b7ea8602b9e7 100644
index a493c9579669072d97c7caa9049e846bda36f8b9..334ffaa6f2d955125ca8b427ace1442c94011e18 100644
--- a/lib/internal/fs/watchers.js
+++ b/lib/internal/fs/watchers.js
@@ -299,12 +299,13 @@ function emitCloseNT(self) {
@@ -361,12 +361,13 @@ function emitCloseNT(self) {
}
// Legacy alias on the C++ wrapper object. This is not public API, so we may
@@ -34,7 +34,7 @@ index 605dee28cace56f2366fec9d7f18894559044ae4..15dcabb3b1682438eb6d5a681363b7ea
let kResistStopPropagation;
diff --git a/src/node_builtins.cc b/src/node_builtins.cc
index 90fdf52d79954bf2cd86fd1d2d6da8199683d344..703ac1be06249736073f797058d170576a0db1bf 100644
index 2c816bef8d64f3e0ba2993c4885641620ee64272..3377d697615ee168e49e83c4202bc227581f1aaf 100644
--- a/src/node_builtins.cc
+++ b/src/node_builtins.cc
@@ -39,6 +39,7 @@ using v8::Value;
@@ -42,9 +42,9 @@ index 90fdf52d79954bf2cd86fd1d2d6da8199683d344..703ac1be06249736073f797058d17057
: config_(GetConfig()), code_cache_(std::make_shared<BuiltinCodeCache>()) {
LoadJavaScriptSource();
+ LoadEmbedderJavaScriptSource();
#ifdef NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_LEXER_PATH
AddExternalizedBuiltin(
"internal/deps/cjs-module-lexer/lexer",
#ifdef NODE_SHARED_BUILTIN_UNDICI_UNDICI_PATH
AddExternalizedBuiltin("internal/deps/undici/undici",
STRINGIFY(NODE_SHARED_BUILTIN_UNDICI_UNDICI_PATH));
diff --git a/src/node_builtins.h b/src/node_builtins.h
index bcdd50f635757f41287c87df1db9cd3b55c4b6b9..e908f3c0e314b90ff7b6c599940ea8f4e657c709 100644
--- a/src/node_builtins.h

View File

@@ -11,7 +11,7 @@ node-gyp will use the result of `process.config` that reflects the environment
in which the binary got built.
diff --git a/common.gypi b/common.gypi
index 6159350823fd9f0090e2b98e8797e829fd481750..5f47ec9bb405f6c1574304ac68807929604cd402 100644
index bdcea65ad3e0315c85b1818e695d8b63093aed34..0653735a0b154e326e5df7049a7beb395f0015c8 100644
--- a/common.gypi
+++ b/common.gypi
@@ -128,6 +128,7 @@

View File

@@ -10,7 +10,7 @@ M151, and so we should allow for building until then.
This patch can be removed at the M151 branch point.
diff --git a/common.gypi b/common.gypi
index 5f47ec9bb405f6c1574304ac68807929604cd402..914f28b41d05b1485874885570ae5adaabd8e1b2 100644
index 0653735a0b154e326e5df7049a7beb395f0015c8..006f52ed18d955da0d9a06e881e86e6e724095ac 100644
--- a/common.gypi
+++ b/common.gypi
@@ -677,7 +677,7 @@

View File

@@ -20,7 +20,7 @@ index ab7dc27de3e304f6d912d5834da47e3b4eb25495..b6c0fd4ceee989dac55c7d54e52fef18
}
}
diff --git a/unofficial.gni b/unofficial.gni
index 253226009faf563f6db285d4b2908f308c1f96ea..dd686d2f7c8d2f6e8d6bd13a7bf2b4b140556ba9 100644
index 4ab316e45bd84e43a53335df60f847b17fe6c2fa..def9a302830e493e51cc2b3588816fcbd3a1bb51 100644
--- a/unofficial.gni
+++ b/unofficial.gni
@@ -143,7 +143,10 @@ template("node_gn_build") {
@@ -35,7 +35,7 @@ index 253226009faf563f6db285d4b2908f308c1f96ea..dd686d2f7c8d2f6e8d6bd13a7bf2b4b1
public_configs = [
":node_external_config",
"deps/googletest:googletest_config",
@@ -362,6 +365,7 @@ template("node_gn_build") {
@@ -364,6 +367,7 @@ template("node_gn_build") {
"src/embedded_data.h",
]
include_dirs = [ "src", "tools" ]

View File

@@ -9,10 +9,10 @@ modules to sandboxed renderers.
TODO(codebytere): remove and replace with a public facing API.
diff --git a/src/node_binding.cc b/src/node_binding.cc
index 3b284583d6ccc9b2d0273d678335b9ab0a6fa81c..fbbdc26b8d1428084709ab113f102c42424842b0 100644
index 740706e917b7d28c520abdbd743605bf73274f30..9ab30b3c9bc663d2947fcbfaac6f06d2c8f8a5b1 100644
--- a/src/node_binding.cc
+++ b/src/node_binding.cc
@@ -655,6 +655,10 @@ void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
@@ -656,6 +656,10 @@ void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(exports);
}

View File

@@ -18,10 +18,10 @@ Stage 3.
Upstreamed in https://github.com/nodejs/node/pull/60364
diff --git a/src/node.cc b/src/node.cc
index ae082f2d0498e0e694e427da71078ca19086e275..f9630a5cd9bed1535a7839517313a2d47d403b1f 100644
index b9d35e60f39d1edd910cd0fc1e57157458db93f5..4421ddd05f69e32f38d074a4cc04e4e7eac89e76 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -782,7 +782,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
@@ -778,7 +778,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
if (std::ranges::find(v8_args, "--no-js-source-phase-imports") ==
v8_args.end()) {

View File

@@ -7,7 +7,7 @@ common.gypi is a file that's included in the node header bundle, despite
the fact that we do not build node with gyp.
diff --git a/common.gypi b/common.gypi
index 5adfd888711ae46a3ba157853359145c4409169b..cf3ceaf19972ee107deff63b4dba16c12191c615 100644
index c5a7dc9cacf8b983984e7c7de9e63d26e418cc8d..b3b5c23e471ece7584d209b3ae4197c46011d50e 100644
--- a/common.gypi
+++ b/common.gypi
@@ -91,6 +91,23 @@

View File

@@ -9,10 +9,10 @@ conflict with Blink's in renderer and worker processes.
We should try to upstream some version of this.
diff --git a/doc/api/cli.md b/doc/api/cli.md
index d94226df8d90d791c3c5fdb7cab7357b0e2555b0..7512ec43ea10d9b0e167125040bc8136f7ad568a 100644
index f05686608297e538f0a6f65abb389281bced4291..c8da076f80a559b9ee6d2ffed831b088c15c8e88 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -1814,6 +1814,14 @@ changes:
@@ -1820,6 +1820,14 @@ changes:
Disable using [syntax detection][] to determine module type.
@@ -27,7 +27,7 @@ index d94226df8d90d791c3c5fdb7cab7357b0e2555b0..7512ec43ea10d9b0e167125040bc8136
### `--no-experimental-global-navigator`
<!-- YAML
@@ -3493,6 +3501,7 @@ one is included in the list below.
@@ -3499,6 +3507,7 @@ one is included in the list below.
* `--no-addons`
* `--no-async-context-frame`
* `--no-deprecation`
@@ -96,7 +96,7 @@ index f6f81f50c8bd91a72ca96093dc64c183bd58039b..aa18dab6e4171d8a7f0af4b7db1b8c2c
"experimental WebSocket API",
&EnvironmentOptions::experimental_websocket,
diff --git a/test/parallel/test-process-env-allowed-flags-are-documented.js b/test/parallel/test-process-env-allowed-flags-are-documented.js
index f09bf0940dad2068f0aa5dce783dd422773d4bbb..ccf4ffcacf9bd9965978738656b8fe091fee4d6a 100644
index d62a2583523c6b3c1de394a18e8060199e446bf1..7d6c6ea7949fd6b52bb54c31a2d71257349e64a9 100644
--- a/test/parallel/test-process-env-allowed-flags-are-documented.js
+++ b/test/parallel/test-process-env-allowed-flags-are-documented.js
@@ -122,7 +122,6 @@ const undocumented = difference(process.allowedNodeEnvironmentFlags,

View File

@@ -11,7 +11,7 @@ We can fix this by allowing the C++ implementation of legacyMainResolve to use
a fileExists function that does take Asar into account.
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
index 8dc8bbfe8d73e2d11edc261d6fe9f37aaa81e4cd..9c965f21918b8170ca2c2d6efee067b52537698e 100644
index 81799fc159cf20344aac64cd7129240deb9a4fe8..12b476ff97603718186dd25b1f435d377841bd89 100644
--- a/lib/internal/modules/esm/resolve.js
+++ b/lib/internal/modules/esm/resolve.js
@@ -28,14 +28,13 @@ const { BuiltinModule } = require('internal/bootstrap/realm');

View File

@@ -12,10 +12,10 @@ This can be removed/refactored once Node.js upgrades to a version of V8
containing the above CL.
diff --git a/src/node.cc b/src/node.cc
index 8791119956ff93d65163d2ef424a8d821ebbdfad..ae082f2d0498e0e694e427da71078ca19086e275 100644
index 0bc086ccd1ff449c0f3fb08a972a0c45d3178f1c..b9d35e60f39d1edd910cd0fc1e57157458db93f5 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -1248,7 +1248,7 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
@@ -1244,7 +1244,7 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
result->platform_ = per_process::v8_platform.Platform();
}

View File

@@ -388,7 +388,7 @@ index bd788ec4ed88289d35798b8af8c9490a68e081a2..1a5477ba928bce93320f8056db02e1a7
function generateWrappingKeys() {
return Promise.all(Object.keys(kWrappingData).map(async (name) => {
diff --git a/test/parallel/test-x509-escaping.js b/test/parallel/test-x509-escaping.js
index c8fc4abbb108a6d6849e8452d97d29187da2ebe6..825ba4c8dce775f401080a0522565bb7a087bcc3 100644
index a5937a09cb1535778a6345885a69ca9bcecf3e96..336313d30b3329b73c04f2f6d6a5e02375a11a72 100644
--- a/test/parallel/test-x509-escaping.js
+++ b/test/parallel/test-x509-escaping.js
@@ -438,7 +438,7 @@ const { hasOpenSSL3 } = require('../common/crypto');

View File

@@ -6,10 +6,10 @@ Subject: fix: do not resolve electron entrypoints
This wastes fs cycles and can result in strange behavior if this path actually exists on disk
diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js
index e4d5579565eea07013c8260fa3c4aa3b266a4b35..fbcef937b26023e0f6f97a0e58ac7350e7616f58 100644
index a6fbcb6fd3c2413df96273d93b7339cad3f25f7a..130fe48b233691d8ee4c5d56f80d331924619008 100644
--- a/lib/internal/modules/esm/translators.js
+++ b/lib/internal/modules/esm/translators.js
@@ -407,6 +407,10 @@ function cjsPreparseModuleExports(filename, source, format) {
@@ -392,6 +392,10 @@ function cjsPreparseModuleExports(filename, source, format) {
return { module, exportNames: module[kModuleExportNames] };
}

View File

@@ -8,7 +8,7 @@ resource path. This commit ensures that the TraverseParent function
bails out if the parent path is outside of the resource path.
diff --git a/src/node_modules.cc b/src/node_modules.cc
index 04ebecc5d924f6c2fddd9992462d1ff692e1cee5..5d9a9da3a068a68c13c5c0cacfe07eec3dad8bc3 100644
index 0cd351e7aff9e293e736e0aca22e67b577eb48eb..b925434940baeeb6b06882242ca947736866d175 100644
--- a/src/node_modules.cc
+++ b/src/node_modules.cc
@@ -345,8 +345,41 @@ const BindingData::PackageConfig* BindingData::TraverseParent(

View File

@@ -8,10 +8,10 @@ an API override to replace the native `ReadFileSync` in the `modules`
binding.
diff --git a/src/env_properties.h b/src/env_properties.h
index bb874fec74c73b5de0ef6d1e2a872ebceefb11ce..6fafe2a8029c535fa98276e2d73f04ee603a7805 100644
index 454750db0113d289e7f8c8cb160e91797790572c..09786f710a88e0243bfaab10d0eca5cb2db62245 100644
--- a/src/env_properties.h
+++ b/src/env_properties.h
@@ -491,6 +491,7 @@
@@ -492,6 +492,7 @@
V(maybe_cache_generated_source_map, v8::Function) \
V(messaging_deserialize_create_object, v8::Function) \
V(message_port, v8::Object) \
@@ -20,7 +20,7 @@ index bb874fec74c73b5de0ef6d1e2a872ebceefb11ce..6fafe2a8029c535fa98276e2d73f04ee
V(performance_entry_callback, v8::Function) \
V(prepare_stack_trace_callback, v8::Function) \
diff --git a/src/node_modules.cc b/src/node_modules.cc
index 95a7b8c32693c32ca9f57e01aff9ef06b84f78b2..cecdda74847801fd5821bc0afdf0dfc9f131c44a 100644
index 3e4918a35e0d2f100f14596a3857f927b2a38c8f..ffc19850ac563082b14729e93d69695ef2c868f5 100644
--- a/src/node_modules.cc
+++ b/src/node_modules.cc
@@ -23,12 +23,14 @@ namespace modules {
@@ -118,7 +118,7 @@ index 95a7b8c32693c32ca9f57e01aff9ef06b84f78b2..cecdda74847801fd5821bc0afdf0dfc9
SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON);
SetMethod(isolate,
target,
@@ -735,6 +786,8 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
@@ -751,6 +802,8 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
void BindingData::RegisterExternalReferences(
ExternalReferenceRegistry* registry) {

View File

@@ -77,10 +77,10 @@ index 22c1e9f1ae652b033903f56f394352806ddff754..961da666a233541203b5416909fd1ff0
if (cjsModule?.[kIsExecuting]) {
const parentFilename = urlToFilename(parentURL);
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
index c1d774b9c5c4049f7ecda4e3a1faaa59dad01764..4fd58b1cea557ec122bd860e1ebc15bd04c44f78 100644
index cc1230648881d8d14ba3902fca78291c90fb79fb..edf347102fedbb28bce221defa99c37b5834024b 100644
--- a/lib/internal/modules/esm/resolve.js
+++ b/lib/internal/modules/esm/resolve.js
@@ -752,6 +752,9 @@ function packageImportsResolve(name, base, conditions) {
@@ -751,6 +751,9 @@ function packageImportsResolve(name, base, conditions) {
throw importNotDefined(name, packageJSONUrl, base);
}
@@ -90,7 +90,7 @@ index c1d774b9c5c4049f7ecda4e3a1faaa59dad01764..4fd58b1cea557ec122bd860e1ebc15bd
/**
* Resolves a package specifier to a URL.
@@ -766,6 +769,11 @@ function packageResolve(specifier, base, conditions) {
@@ -765,6 +768,11 @@ function packageResolve(specifier, base, conditions) {
return new URL('node:' + specifier);
}
@@ -103,10 +103,10 @@ index c1d774b9c5c4049f7ecda4e3a1faaa59dad01764..4fd58b1cea557ec122bd860e1ebc15bd
const packageConfig = packageJsonReader.read(packageJSONPath, { __proto__: null, specifier, base, isESM: true });
diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js
index 1716328c7a98996a8933dbaa00a1c6c3156fb2ff..30f887663bbbd9913eff35f0e6e5b8291adda120 100644
index d6c96996a900da8e7d4f7f5104312e73e72c2d62..ec76d7ffa45f49721d395e8e33be79114daa0369 100644
--- a/lib/internal/modules/esm/translators.js
+++ b/lib/internal/modules/esm/translators.js
@@ -223,7 +223,9 @@ function createCJSModuleWrap(url, translateContext, parentURL, loadCJS = loadCJS
@@ -214,7 +214,9 @@ function createCJSModuleWrap(url, translateContext, parentURL, loadCJS = loadCJS
const { exportNames, module } = cjsPreparseModuleExports(filename, source, sourceFormat);
cjsCache.set(url, module);
@@ -117,7 +117,7 @@ index 1716328c7a98996a8933dbaa00a1c6c3156fb2ff..30f887663bbbd9913eff35f0e6e5b829
if (!exportNames.has('default')) {
ArrayPrototypePush(wrapperNames, 'default');
}
@@ -325,6 +327,10 @@ translators.set('require-commonjs', (url, translateContext, parentURL) => {
@@ -313,6 +315,10 @@ translators.set('require-commonjs', (url, translateContext, parentURL) => {
return createCJSModuleWrap(url, translateContext, parentURL);
});

View File

@@ -17,7 +17,7 @@ Upstreams:
- https://github.com/nodejs/node/pull/39136
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
index 461819ce0fa732048e4365c40a86ef55d984c35f..fa55c980a9c4f373723a867fd41276d67b0b9413 100644
index 461819ce0fa732048e4365c40a86ef55d984c35f..f1c85e94cf526d0255f47c003664680d26413ec3 100644
--- a/deps/ncrypto/ncrypto.cc
+++ b/deps/ncrypto/ncrypto.cc
@@ -11,6 +11,7 @@
@@ -28,38 +28,6 @@ index 461819ce0fa732048e4365c40a86ef55d984c35f..fa55c980a9c4f373723a867fd41276d6
#if OPENSSL_VERSION_MAJOR >= 3
#include <openssl/core_names.h>
#include <openssl/params.h>
@@ -1130,7 +1131,9 @@ int64_t X509View::getValidToTime() const {
return tp;
#else
struct tm tp;
- ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
+#ifndef OPENSSL_IS_BORINGSSL
+ ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
+#endif
return PortableTimeGM(&tp);
#endif
}
@@ -1142,7 +1145,9 @@ int64_t X509View::getValidFromTime() const {
return tp;
#else
struct tm tp;
+#ifndef OPENSSL_IS_BORINGSSL
ASN1_TIME_to_tm(X509_get0_notBefore(cert_), &tp);
+#endif
return PortableTimeGM(&tp);
#endif
}
@@ -2886,10 +2891,6 @@ std::optional<uint32_t> SSLPointer::verifyPeerCertificate() const {
const char* SSLPointer::getClientHelloAlpn() const {
if (ssl_ == nullptr) return {};
#ifndef OPENSSL_IS_BORINGSSL
- const unsigned char* buf;
- size_t len;
- size_t rem;
-
if (!SSL_client_hello_get0_ext(
get(),
TLSEXT_TYPE_application_layer_protocol_negotiation,
@@ -3090,9 +3091,11 @@ const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm);
const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap);
const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap);
@@ -94,7 +62,7 @@ index 175ec8ba0f2a908ffad2ce48434aeed573b09c90..3218590ddce1e92c2a9d776f20f9fb01
Cipher() = default;
Cipher(const EVP_CIPHER* cipher) : cipher_(cipher) {}
diff --git a/node.gni b/node.gni
index e2407027ab05e59b2f0f1c213b98ea469db7a91b..c64761b730e61edcdc0e46a48699f2fd5bb1c0a6 100644
index 156fee33b3813fe4d94a1c9585f217a99dbfbd5f..8239967653fee7791800ee3292e77b91bffaaef9 100644
--- a/node.gni
+++ b/node.gni
@@ -11,7 +11,7 @@ declare_args() {
@@ -298,7 +266,7 @@ index 3ab33341806ad6c0b06b982a30a57b7b5399e38f..754ddf7b331465c56081db05d6fd2a45
// We declare another alias here to avoid having to include crypto_util.h
using EVPMDPointer = DeleteFnPtr<EVP_MD, EVP_MD_free>;
diff --git a/src/node_metadata.h b/src/node_metadata.h
index d9c533f100d25aeab1fe8589932a8ddead431258..2acab8786a8a752b17961445edeb872c2b08fdeb 100644
index 7f6268a1d83d3182b318bc95fd2ee119a2df8dea..f2ee335aa98a2465f63dafa33664197acc6f79b1 100644
--- a/src/node_metadata.h
+++ b/src/node_metadata.h
@@ -8,7 +8,7 @@

View File

@@ -28,7 +28,7 @@ index 5f1921d15bc1d3a68c35990f85e36a0e8a5b3ec4..99c6ce57c04768d125dd0a1c6bd62bca
const result = dataURLProcessor(url);
if (result === 'failure') {
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
index 4fd58b1cea557ec122bd860e1ebc15bd04c44f78..8dc8bbfe8d73e2d11edc261d6fe9f37aaa81e4cd 100644
index edf347102fedbb28bce221defa99c37b5834024b..81799fc159cf20344aac64cd7129240deb9a4fe8 100644
--- a/lib/internal/modules/esm/resolve.js
+++ b/lib/internal/modules/esm/resolve.js
@@ -25,7 +25,7 @@ const {
@@ -50,10 +50,10 @@ index 4fd58b1cea557ec122bd860e1ebc15bd04c44f78..8dc8bbfe8d73e2d11edc261d6fe9f37a
});
const { search, hash } = resolved;
diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js
index 30f887663bbbd9913eff35f0e6e5b8291adda120..e4d5579565eea07013c8260fa3c4aa3b266a4b35 100644
index ec76d7ffa45f49721d395e8e33be79114daa0369..a6fbcb6fd3c2413df96273d93b7339cad3f25f7a 100644
--- a/lib/internal/modules/esm/translators.js
+++ b/lib/internal/modules/esm/translators.js
@@ -24,7 +24,7 @@ const {
@@ -23,7 +23,7 @@ const {
const { BuiltinModule } = require('internal/bootstrap/realm');
const assert = require('internal/assert');
@@ -62,7 +62,7 @@ index 30f887663bbbd9913eff35f0e6e5b8291adda120..e4d5579565eea07013c8260fa3c4aa3b
const { dirname, extname } = require('path');
const {
assertBufferSource,
@@ -365,7 +365,7 @@ translators.set('commonjs', function commonjsStrategy(url, translateContext, par
@@ -350,7 +350,7 @@ translators.set('commonjs', function commonjsStrategy(url, translateContext, par
try {
// We still need to read the FS to detect the exports.

View File

@@ -10,10 +10,10 @@ change, it seems to introduce an incompatibility when compiling
using clang modules. Disabling them resolves the issue.
diff --git a/unofficial.gni b/unofficial.gni
index dd686d2f7c8d2f6e8d6bd13a7bf2b4b140556ba9..97e4bcfaa8aa42a5fc2b68ccdd8128eac8886532 100644
index def9a302830e493e51cc2b3588816fcbd3a1bb51..900c5e4d8a48d0725420518c923c7024518158b8 100644
--- a/unofficial.gni
+++ b/unofficial.gni
@@ -195,6 +195,10 @@ template("node_gn_build") {
@@ -197,6 +197,10 @@ template("node_gn_build") {
"CoreFoundation.framework",
"Security.framework",
]
@@ -24,7 +24,7 @@ index dd686d2f7c8d2f6e8d6bd13a7bf2b4b140556ba9..97e4bcfaa8aa42a5fc2b68ccdd8128ea
}
if (is_posix) {
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
@@ -367,6 +371,12 @@ template("node_gn_build") {
@@ -369,6 +373,12 @@ template("node_gn_build") {
include_dirs = [ "src", "tools" ]
configs += [ "//build/config/compiler:no_exit_time_destructors" ]

View File

@@ -6,7 +6,7 @@ Subject: Pass all globals through "require"
(cherry picked from commit 7d015419cb7a0ecfe6728431a4ed2056cd411d62)
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index 2b199e82fee3a9d56521b9c5cc04a20cdfd6f045..e28755a20176c2806769430910b28a0e61d7691e 100644
index 0a6788d1b848d860fa3fa3e857c7feab6f16311e..a6b01d7e143fa6ffeda6fa7723e279db7678ddd4 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -209,6 +209,13 @@ const {
@@ -23,7 +23,7 @@ index 2b199e82fee3a9d56521b9c5cc04a20cdfd6f045..e28755a20176c2806769430910b28a0e
const {
isProxy,
} = require('internal/util/types');
@@ -1799,10 +1806,12 @@ Module.prototype._compile = function(content, filename, format) {
@@ -1807,9 +1814,12 @@ Module.prototype._compile = function(content, filename, format) {
if (this[kIsMainSymbol] && getOptionValue('--inspect-brk')) {
const { callAndPauseOnStart } = internalBinding('inspector');
result = callAndPauseOnStart(compiledWrapper, thisValue, exports,
@@ -31,10 +31,10 @@ index 2b199e82fee3a9d56521b9c5cc04a20cdfd6f045..e28755a20176c2806769430910b28a0e
+ require, module, filename, dirname,
+ process, localGlobal, localBuffer);
} else {
result = ReflectApply(compiledWrapper, thisValue,
- [exports, require, module, filename, dirname]);
+ [exports, require, module, filename, dirname,
+ process, localGlobal, localBuffer]);
- result = FunctionPrototypeCall(compiledWrapper, thisValue, exports, require, module, filename, dirname);
+ result = FunctionPrototypeCall(compiledWrapper, thisValue, exports,
+ require, module, filename, dirname,
+ process, localGlobal, localBuffer);
}
this[kIsExecuting] = false;
if (requireDepth === 0) { statCache = null; }

View File

@@ -18,7 +18,7 @@ This can be removed when Node.js upgrades to a version of V8 containing CLs
from the above issue.
diff --git a/src/api/environment.cc b/src/api/environment.cc
index 53f05293bd94e159dfedf48735989e668acdd08e..d753ad6c6b49b26b86920124f7ac90c1e052638e 100644
index 5f51ad205189bd75d0d9638b1104c12b537b4e9b..8974bac7dca43294cc5cc4570f8e2e78f42aefaa 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -323,6 +323,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
@@ -102,7 +102,7 @@ index fdabe48dd7776c59298f7d972286d0d2ed062752..b5cf58cc953590493beb52abf249e33e
isolate, object, wrappable);
}
diff --git a/src/node.h b/src/node.h
index 19c34a430d095c06ccf5a988db91311d420a485a..154fb15e6c8fe985e92378cc8471aa6a9d579b07 100644
index b92a9d42da8419741c435643b7401efcb21a9e8b..6e99e8ca1b53cce05615c16c3221ae3a203f50df 100644
--- a/src/node.h
+++ b/src/node.h
@@ -603,7 +603,8 @@ NODE_EXTERN v8::Isolate* NewIsolate(
@@ -115,7 +115,7 @@ index 19c34a430d095c06ccf5a988db91311d420a485a..154fb15e6c8fe985e92378cc8471aa6a
NODE_EXTERN v8::Isolate* NewIsolate(
std::shared_ptr<ArrayBufferAllocator> allocator,
struct uv_loop_s* event_loop,
@@ -1624,9 +1625,10 @@ void RegisterSignalHandler(int signal,
@@ -1702,9 +1703,10 @@ void RegisterSignalHandler(int signal,
// work with only Node.js versions with v8::Object::Wrap() should use that
// instead.
NODE_DEPRECATED("Use v8::Object::Wrap()",
@@ -130,7 +130,7 @@ index 19c34a430d095c06ccf5a988db91311d420a485a..154fb15e6c8fe985e92378cc8471aa6a
} // namespace node
diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc
index dd6ecd1f9d82f6661b2480c0195e33515633429f..334d5cb7df7a763e0929468392dad83421cad606 100644
index 6f674df3ed0dc1b4e5cd3a249fb787a9fc98361d..90f9eb84b6835c36a91ce23d77722812ce173c0f 100644
--- a/src/node_main_instance.cc
+++ b/src/node_main_instance.cc
@@ -44,6 +44,8 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data,
@@ -143,7 +143,7 @@ index dd6ecd1f9d82f6661b2480c0195e33515633429f..334d5cb7df7a763e0929468392dad834
isolate_ =
NewIsolate(isolate_params_.get(), event_loop, platform, snapshot_data);
diff --git a/src/node_worker.cc b/src/node_worker.cc
index fcfd5fecdebd2724441eb83b498b38b11eedad25..e7d26b4c8cbb08a175084ceac51395860dc60598 100644
index fa7dc52b19119ed3d2dc407c029f56107476bc39..1acc61af0c995ddefbc00fe232b2454de77a84a3 100644
--- a/src/node_worker.cc
+++ b/src/node_worker.cc
@@ -181,6 +181,9 @@ class WorkerThreadData {

View File

@@ -121,10 +121,10 @@ index b30297eac08ad9587642b723f91d7e3b954294d4..4c5427596d1c90d3a413cdd9ff4f1151
auto backing = ArrayBuffer::NewBackingStore(
mem->data,
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index ddee7b7e40c3ee4054b2b15b75154607aa6431ed..decc3c8c966c2322f22d6bdd871514bb53882a29 100644
index e1bee00825d140232456d6dc2337420fde6bda17..04edc4ca3c0e7c2284d2822fe9f5de66ff64fda2 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -1420,7 +1420,7 @@ inline size_t CheckNumberToSize(Local<Value> number) {
@@ -1443,7 +1443,7 @@ inline size_t CheckNumberToSize(Local<Value> number) {
CHECK(value >= 0 && value < maxSize);
size_t size = static_cast<size_t>(value);
#ifdef V8_ENABLE_SANDBOX
@@ -133,6 +133,41 @@ index ddee7b7e40c3ee4054b2b15b75154607aa6431ed..decc3c8c966c2322f22d6bdd871514bb
#endif
return size;
}
@@ -1466,6 +1466,26 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
env->isolate_data()->is_building_snapshot()) {
buf = ArrayBuffer::New(isolate, size);
} else {
+#if defined(V8_ENABLE_SANDBOX)
+ // When the V8 sandbox is enabled, all array buffers must be allocated
+ // within the V8 memory cage via the V8 allocator.
+ std::unique_ptr<ArrayBuffer::Allocator> allocator(
+ ArrayBuffer::Allocator::NewDefaultAllocator());
+ void* data = allocator->AllocateUninitialized(size);
+ if (!data) [[unlikely]] {
+ THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
+ return;
+ }
+ std::unique_ptr<BackingStore> store = ArrayBuffer::NewBackingStore(
+ data,
+ size,
+ [](void* data, size_t length, void*) {
+ std::unique_ptr<ArrayBuffer::Allocator> allocator(
+ ArrayBuffer::Allocator::NewDefaultAllocator());
+ allocator->Free(data, length);
+ },
+ nullptr);
+#else
std::unique_ptr<BackingStore> store = ArrayBuffer::NewBackingStore(
isolate,
size,
@@ -1476,6 +1496,7 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
return;
}
+#endif
buf = ArrayBuffer::New(isolate, std::move(store));
}
diff --git a/src/node_i18n.cc b/src/node_i18n.cc
index 3c4f419aa29470b3280174b58680b9421b0340b5..3b24ad2a2316f89d98b067e2c13988f87a9a00d2 100644
--- a/src/node_i18n.cc

View File

@@ -12,10 +12,10 @@ See:
https://chromium-review.googlesource.com/c/v8/v8/+/6826001
diff --git a/test/fixtures/test-runner/output/describe_it.snapshot b/test/fixtures/test-runner/output/describe_it.snapshot
index 67d4af7f1b9f45d48b35c930cb1490ee019d0bdf..21d8744340c5a4c002d8c91266f46c2b66591b6e 100644
index cae467f6487ffef4fbe94da229e30c2537fe9e95..f1240a6a99dafc18ad51d413719df58b757893ab 100644
--- a/test/fixtures/test-runner/output/describe_it.snapshot
+++ b/test/fixtures/test-runner/output/describe_it.snapshot
@@ -690,6 +690,8 @@ not ok 54 - timeouts
@@ -726,6 +726,8 @@ not ok 60 - timeouts
code: 'ERR_TEST_FAILURE'
stack: |-
*
@@ -23,8 +23,8 @@ index 67d4af7f1b9f45d48b35c930cb1490ee019d0bdf..21d8744340c5a4c002d8c91266f46c2b
+ *
...
1..2
not ok 55 - successful thenable
@@ -712,6 +714,7 @@ not ok 56 - rejected thenable
not ok 61 - successful thenable
@@ -748,6 +750,7 @@ not ok 62 - rejected thenable
code: 'ERR_TEST_FAILURE'
stack: |-
*

View File

@@ -7,7 +7,7 @@ Instead of disabling the tests, flag them as flaky so they still run
but don't cause CI failures on flakes.
diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status
index 8bf4ae46f2d1d204d05adb6e951a613902f8bae2..2438ed04e3477c3bc74d02717129bf830a40c5f4 100644
index 73a866bbef8b75e9f351ad9b1d3b0fd7850fca86..a052cc735a5660cb1b8b2875bb24c205ad7ecf94 100644
--- a/test/parallel/parallel.status
+++ b/test/parallel/parallel.status
@@ -5,6 +5,16 @@ prefix parallel

View File

@@ -1 +1,2 @@
graphite_add_insertstatus_koutoforderrecording.patch
cherry-pick-7911bee5d90e.patch

View File

@@ -0,0 +1,539 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Greg Daniel <egdaniel@google.com>
Date: Wed, 11 Mar 2026 15:29:58 -0400
Subject: Make sure we are getting the correct atlas for glyph mask format.
Bug: b/491421267
Change-Id: I4eacd46599eca2df8c10a3fc894b9ce890fae1e2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1184076
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
(cherry picked from commit 0cab3e4ee34b3bca6ba7df676639d73ffe4b2135)
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1184917
diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp
index 6793512e216b00e1f8112f8e681eecf5beee8fe8..4fd0965185f8bab5a55ec63329bf6aa36ad56ed0 100644
--- a/bench/GlyphQuadFillBench.cpp
+++ b/bench/GlyphQuadFillBench.cpp
@@ -68,7 +68,7 @@ class DirectMaskGlyphVertexFillBenchmark : public Benchmark {
const sktext::gpu::AtlasSubRun* subRun =
sktext::gpu::TextBlobTools::FirstSubRun(fBlob.get());
SkASSERT_RELEASE(subRun);
- subRun->testingOnly_packedGlyphIDToGlyph(&fCache);
+ subRun->testingOnly_packedGlyphIDToGlyph(&fCache, subRun->maskFormat());
fVertices.reset(new char[subRun->vertexStride(drawMatrix) * subRun->glyphCount() * 4]);
}
diff --git a/gn/tests.gni b/gn/tests.gni
index 8ae89364ce33f62ced5e8ff5b417a0cf69a3afb1..6286969c91fa9dff8d1e83413ab5b9fd514c5ae9 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -424,6 +424,7 @@ pathops_tests_sources = [
ganesh_tests_sources = [
"$_tests/AdvancedBlendTest.cpp",
"$_tests/ApplyGammaTest.cpp",
+ "$_tests/AtlasOobTest.cpp",
"$_tests/BackendAllocationTest.cpp",
"$_tests/BackendSurfaceMutableStateTest.cpp",
"$_tests/BlendTest.cpp",
diff --git a/src/gpu/ganesh/text/GrAtlasManager.cpp b/src/gpu/ganesh/text/GrAtlasManager.cpp
index 403bfe274e56293bfe2382b02525ae742ba541a7..1e7d9aa0ce14f19e09d79544730c6aa922ae37d6 100644
--- a/src/gpu/ganesh/text/GrAtlasManager.cpp
+++ b/src/gpu/ganesh/text/GrAtlasManager.cpp
@@ -178,8 +178,7 @@ GrDrawOpAtlas::ErrorCode GrAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
}
SkASSERT(glyph != nullptr);
- MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
- MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
+ MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyph->fGlyphEntryKey.fFormat);
int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
int padding;
@@ -299,7 +298,7 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGanesh(
uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
- this->packedGlyphIDToGlyph(target->strikeCache());
+ this->packedGlyphIDToGlyph(target->strikeCache(), maskFormat);
if (fAtlasGeneration != currentAtlasGen) {
// Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
@@ -316,9 +315,10 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGanesh(
for (const Variant& variant : glyphs) {
Glyph* gpuGlyph = variant.glyph;
SkASSERT(gpuGlyph != nullptr);
-
+ SkASSERT(gpuGlyph->fGlyphEntryKey.fFormat == maskFormat);
if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
- const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
+ const SkGlyph& skGlyph =
+ *metricsAndImages.glyph(gpuGlyph->fGlyphEntryKey.fPackedID);
auto code = atlasManager->addGlyphToAtlas(
skGlyph, gpuGlyph, srcPadding, target->resourceProvider(), uploadTarget);
if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
index 1163eacd741d059b5a782112d9dbeed7080e3207..b069ba5e84bf113f3e1bcff1cd7c8e9ef570722d 100644
--- a/src/gpu/graphite/Device.cpp
+++ b/src/gpu/graphite/Device.cpp
@@ -1427,6 +1427,7 @@ void Device::drawAtlasSubRun(const sktext::gpu::AtlasSubRun* subRun,
int padding) {
return glyphs->regenerateAtlasForGraphite(begin, end, maskFormat, padding, fRecorder);
};
+
for (int subRunCursor = 0; subRunCursor < subRunEnd;) {
// For the remainder of the run, add any atlas uploads to the Recorder's TextAtlasManager
auto[ok, glyphsRegenerated] = subRun->regenerateAtlas(subRunCursor, subRunEnd,
diff --git a/src/gpu/graphite/text/TextAtlasManager.cpp b/src/gpu/graphite/text/TextAtlasManager.cpp
index 6602a76c150bff077666fb91b990d3e45d528ce2..cbb51a66846922995912c3159afba879a2487313 100644
--- a/src/gpu/graphite/text/TextAtlasManager.cpp
+++ b/src/gpu/graphite/text/TextAtlasManager.cpp
@@ -207,8 +207,7 @@ DrawAtlas::ErrorCode TextAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
}
SkASSERT(glyph != nullptr);
- MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
- MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
+ MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyph->fGlyphEntryKey.fFormat);
int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
int padding;
@@ -359,7 +358,7 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
- this->packedGlyphIDToGlyph(recorder->priv().strikeCache());
+ this->packedGlyphIDToGlyph(recorder->priv().strikeCache(), maskFormat);
if (fAtlasGeneration != currentAtlasGen) {
// Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
@@ -375,9 +374,10 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
for (const Variant& variant : glyphs) {
Glyph* gpuGlyph = variant.glyph;
SkASSERT(gpuGlyph != nullptr);
-
+ SkASSERT(gpuGlyph->fGlyphEntryKey.fFormat == maskFormat);
if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
- const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
+ const SkGlyph& skGlyph =
+ *metricsAndImages.glyph(gpuGlyph->fGlyphEntryKey.fPackedID);
auto code = atlasManager->addGlyphToAtlas(skGlyph, gpuGlyph, srcPadding);
if (code != DrawAtlas::ErrorCode::kSucceeded) {
success = code != DrawAtlas::ErrorCode::kError;
diff --git a/src/text/gpu/Glyph.h b/src/text/gpu/Glyph.h
index 821612d68cecfe9dae9518e376e09fdf233395ad..7942006a563bcab925ea2129ab6f6beea438a4c8 100644
--- a/src/text/gpu/Glyph.h
+++ b/src/text/gpu/Glyph.h
@@ -14,6 +14,25 @@
namespace sktext::gpu {
+struct GlyphEntryKey {
+ explicit GlyphEntryKey(SkPackedGlyphID id, skgpu::MaskFormat format)
+ : fPackedID(id), fFormat(format) {}
+
+ const SkPackedGlyphID fPackedID;
+ skgpu::MaskFormat fFormat;
+
+ bool operator==(const GlyphEntryKey& that) const {
+ return fPackedID == that.fPackedID && fFormat == that.fFormat;
+ }
+ bool operator!=(const GlyphEntryKey& that) const {
+ return !(*this == that);
+ }
+
+ uint32_t hash() const {
+ return fPackedID.hash();
+ }
+};
+
class Glyph {
public:
static skgpu::MaskFormat FormatFromSkGlyph(SkMask::Format format) {
@@ -34,10 +53,11 @@ public:
SkUNREACHABLE;
}
- explicit Glyph(SkPackedGlyphID packedGlyphID) : fPackedID(packedGlyphID) {}
+ explicit Glyph(SkPackedGlyphID packedGlyphID, skgpu::MaskFormat format)
+ : fGlyphEntryKey(packedGlyphID, format) {}
- const SkPackedGlyphID fPackedID;
- skgpu::AtlasLocator fAtlasLocator;
+ const GlyphEntryKey fGlyphEntryKey;
+ skgpu::AtlasLocator fAtlasLocator;
};
} // namespace sktext::gpu
diff --git a/src/text/gpu/GlyphVector.cpp b/src/text/gpu/GlyphVector.cpp
index 2a8e85f926aa547169f4b85372e9d3fb99816956..7bec7a0b77d8560d5ef978281edd7df6c45cb56f 100644
--- a/src/text/gpu/GlyphVector.cpp
+++ b/src/text/gpu/GlyphVector.cpp
@@ -99,14 +99,14 @@ SkSpan<const Glyph*> GlyphVector::glyphs() const {
// packedGlyphIDToGlyph must be run in single-threaded mode.
// If fSkStrike is not sk_sp<SkStrike> then the conversion to Glyph* has not happened.
-void GlyphVector::packedGlyphIDToGlyph(StrikeCache* cache) {
+void GlyphVector::packedGlyphIDToGlyph(StrikeCache* cache, MaskFormat maskFormat) {
if (fTextStrike == nullptr) {
SkStrike* strike = fStrikePromise.strike();
fTextStrike = cache->findOrCreateStrike(strike->strikeSpec());
// Get all the atlas locations for each glyph.
for (Variant& variant : fGlyphs) {
- variant.glyph = fTextStrike->getGlyph(variant.packedGlyphID);
+ variant.glyph = fTextStrike->getGlyph(variant.packedGlyphID, maskFormat);
}
// This must be pinned for the Atlas filling to work.
diff --git a/src/text/gpu/GlyphVector.h b/src/text/gpu/GlyphVector.h
index 42b92a93f70cc6d86d0a87dd07c2244e0da1281c..1eec6327d38fb4472b027faae68eecb9ad7509d7 100644
--- a/src/text/gpu/GlyphVector.h
+++ b/src/text/gpu/GlyphVector.h
@@ -68,7 +68,7 @@ public:
// the sub runs.
int unflattenSize() const { return GlyphVectorSize(fGlyphs.size()); }
- void packedGlyphIDToGlyph(StrikeCache* cache);
+ void packedGlyphIDToGlyph(StrikeCache* cache, skgpu::MaskFormat);
static size_t GlyphVectorSize(size_t count) {
return sizeof(Variant) * count;
diff --git a/src/text/gpu/StrikeCache.cpp b/src/text/gpu/StrikeCache.cpp
index add3127c92fdbfe56d6b56209a2235ce5a9f5acb..19df48329fd500f8682669ec96eb883b58243fdd 100644
--- a/src/text/gpu/StrikeCache.cpp
+++ b/src/text/gpu/StrikeCache.cpp
@@ -207,10 +207,11 @@ TextStrike::TextStrike(StrikeCache* strikeCache, const SkStrikeSpec& strikeSpec)
: fStrikeCache(strikeCache)
, fStrikeSpec{strikeSpec} {}
-Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID) {
- Glyph* glyph = fCache.findOrNull(packedGlyphID);
+Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID, skgpu::MaskFormat format) {
+ GlyphEntryKey localKey(packedGlyphID, format);
+ Glyph* glyph = fCache.findOrNull(localKey);
if (glyph == nullptr) {
- glyph = fAlloc.make<Glyph>(packedGlyphID);
+ glyph = fAlloc.make<Glyph>(packedGlyphID, format);
fCache.set(glyph);
fMemoryUsed += sizeof(Glyph);
if (!fRemoved) {
@@ -220,11 +221,11 @@ Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID) {
return glyph;
}
-const SkPackedGlyphID& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
- return glyph->fPackedID;
+const GlyphEntryKey& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
+ return glyph->fGlyphEntryKey;
}
-uint32_t TextStrike::HashTraits::Hash(SkPackedGlyphID key) {
+uint32_t TextStrike::HashTraits::Hash(GlyphEntryKey key) {
return key.hash();
}
diff --git a/src/text/gpu/StrikeCache.h b/src/text/gpu/StrikeCache.h
index 007c45c6c6feecba3ff031ba3939ad2402e082b9..014afd5286602e3e049d8e48ae328273e599dc41 100644
--- a/src/text/gpu/StrikeCache.h
+++ b/src/text/gpu/StrikeCache.h
@@ -13,6 +13,7 @@
#include "src/core/SkDescriptor.h"
#include "src/core/SkStrikeSpec.h"
#include "src/core/SkTHash.h"
+#include "src/gpu/AtlasTypes.h"
#include <cstddef>
#include <cstdint>
@@ -32,6 +33,7 @@ struct SkPackedGlyphID;
namespace sktext::gpu {
class Glyph;
+struct GlyphEntryKey;
class StrikeCache;
// The TextStrike manages an SkArenaAlloc for Glyphs. The SkStrike is what actually creates
@@ -43,7 +45,7 @@ public:
TextStrike(StrikeCache* strikeCache,
const SkStrikeSpec& strikeSpec);
- Glyph* getGlyph(SkPackedGlyphID);
+ Glyph* getGlyph(SkPackedGlyphID, skgpu::MaskFormat format);
const SkStrikeSpec& strikeSpec() const { return fStrikeSpec; }
const SkDescriptor& getDescriptor() const { return fStrikeSpec.descriptor(); }
@@ -54,11 +56,11 @@ private:
const SkStrikeSpec fStrikeSpec;
struct HashTraits {
- static const SkPackedGlyphID& GetKey(const Glyph* glyph);
- static uint32_t Hash(SkPackedGlyphID key);
+ static const GlyphEntryKey& GetKey(const Glyph* glyph);
+ static uint32_t Hash(GlyphEntryKey key);
};
// Map SkPackedGlyphID -> Glyph*.
- skia_private::THashTable<Glyph*, SkPackedGlyphID, HashTraits> fCache;
+ skia_private::THashTable<Glyph*, GlyphEntryKey, HashTraits> fCache;
// Store for the glyph information.
SkArenaAlloc fAlloc{512};
diff --git a/src/text/gpu/SubRunContainer.cpp b/src/text/gpu/SubRunContainer.cpp
index 3a061a2012cd99de9ee4b3674f78ae99e0385d6c..a19460c82593c6713c047ab19e71caa27e375a6d 100644
--- a/src/text/gpu/SubRunContainer.cpp
+++ b/src/text/gpu/SubRunContainer.cpp
@@ -651,8 +651,9 @@ public:
int glyphSrcPadding() const override { return 0; }
- void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const override {
- fGlyphs.packedGlyphIDToGlyph(cache);
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache,
+ skgpu::MaskFormat maskFormat) const override {
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
}
std::tuple<bool, SkRect> deviceRectAndNeedsTransform(
@@ -755,8 +756,9 @@ public:
const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
- void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
- fGlyphs.packedGlyphIDToGlyph(cache);
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache,
+ skgpu::MaskFormat maskFormat) const override {
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
}
int glyphSrcPadding() const override { return 1; }
@@ -884,8 +886,9 @@ public:
const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
- void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
- fGlyphs.packedGlyphIDToGlyph(cache);
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache,
+ skgpu::MaskFormat maskFormat) const override {
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
}
int glyphSrcPadding() const override { return SK_DistanceFieldInset; }
diff --git a/src/text/gpu/SubRunContainer.h b/src/text/gpu/SubRunContainer.h
index 2573dbb3964e9ab2cc0e276b60d4ab4f9804f0d9..4d1a3c8c2d55015d3d351d322ef039c45be2a398 100644
--- a/src/text/gpu/SubRunContainer.h
+++ b/src/text/gpu/SubRunContainer.h
@@ -167,7 +167,7 @@ public:
const VertexFiller& vertexFiller() const { return fVertexFiller; }
- virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const = 0;
+ virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache, skgpu::MaskFormat) const = 0;
protected:
const VertexFiller fVertexFiller;
diff --git a/tests/AtlasOobTest.cpp b/tests/AtlasOobTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e6fb02ee6af6543df285d8112f1a2ced5bd9ac9
--- /dev/null
+++ b/tests/AtlasOobTest.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "include/core/SkCanvas.h"
+#include "include/core/SkGraphics.h"
+#include "include/core/SkSerialProcs.h"
+#include "include/core/SkSurface.h"
+#include "include/private/chromium/SkChromeRemoteGlyphCache.h"
+#include "include/private/chromium/Slug.h"
+#include "src/core/SkDescriptor.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkTypeface_remote.h"
+#include "src/core/SkWriteBuffer.h"
+#include "src/gpu/AtlasTypes.h"
+#include "tests/CtsEnforcement.h"
+#include "tests/Test.h"
+#include "tools/ToolUtils.h"
+
+#if defined(SK_GANESH)
+#include "include/gpu/ganesh/GrDirectContext.h"
+#include "include/gpu/ganesh/SkSurfaceGanesh.h"
+#endif
+
+#if defined(SK_GRAPHITE)
+#include "include/gpu/graphite/Context.h"
+#include "include/gpu/graphite/Surface.h"
+#include "tools/graphite/GraphiteTestContext.h"
+#endif // defined(SK_GRAPHITE)
+
+#include <vector>
+#include <cstring>
+
+namespace {
+class FakeDiscardableManager : public SkStrikeClient::DiscardableHandleManager {
+public:
+ bool deleteHandle(SkDiscardableHandleId) override { return false; }
+ void notifyCacheMiss(SkStrikeClient::CacheMissType, int) override {}
+ void notifyReadFailure(const ReadFailureData&) override {}
+ void assertHandleValid(SkDiscardableHandleId) override {}
+};
+
+unsigned char kStrikeData[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x07, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0x00, 0x00, 0x00, 0x65, 0xd8, 0x50, 0xda, 0x99, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x63, 0x65, 0x72, 0x73, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x86, 0x07, 0xc2, 0x42,
+ 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x65, 0x72, 0x73, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char kDrawSlugOp[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
+ 0x00, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x86, 0x07, 0xc2, 0x42, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x65, 0x72, 0x73,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+} // namespace
+
+// TODO: We expect this test to correctly hit an SkUnreachable and then crash. That does not work
+// with our current testing framework because we have no to "expect" a crash. So for now we will
+// land this test with only the valid loop enabled, but to test this is working locally, you should
+// change the loop to have both iterations.
+static void run_atlas_oob_test(skiatest::Reporter* reporter, SkCanvas* canvas) {
+ auto discardableManager = sk_make_sp<FakeDiscardableManager>();
+ SkStrikeClient client(discardableManager, false);
+
+ // 1. Prepare Strike Data
+ if (!client.readStrikeData(kStrikeData, sizeof(kStrikeData))) {
+ REPORTER_ASSERT(reporter, false, "Failed to read initial strike data");
+ }
+
+ // 2. Prepare and Execute DrawSlug ops
+ SkPaint paint;
+ for (int idx = 0; idx < 1; ++idx) {
+// for (int idx = 0; idx < 2; ++idx) {
+ if (idx == 0) {
+ kDrawSlugOp[0x48] = (unsigned char)skgpu::MaskFormat::kARGB;
+ } else if (idx == 1) {
+ kDrawSlugOp[0x48] = (unsigned char)skgpu::MaskFormat::kA8;
+ }
+ kDrawSlugOp[0xd8] = SkMask::kARGB32_Format;
+ kDrawSlugOp[0xe0] = 0x99;
+
+ auto slug = client.deserializeSlugForTest(kDrawSlugOp, sizeof(kDrawSlugOp));
+ if (slug) {
+ slug->draw(canvas, paint);
+ }
+ }
+
+}
+
+#if defined(SK_GANESH)
+DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(Atlas_Oob_ganesh, reporter, ctxInfo, CtsEnforcement::kNextRelease) {
+ auto dContext = ctxInfo.directContext();
+ SkImageInfo info = SkImageInfo::MakeN32Premul(1024, 1024);
+ auto surface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info);
+ if (!surface) return;
+ auto canvas = surface->getCanvas();
+
+ run_atlas_oob_test(reporter, canvas);
+
+ dContext->flushAndSubmit();
+}
+#endif // defined(SK_GANESH)
+
+#if defined(SK_GRAPHITE)
+DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(Atlas_Oob_graphite, reporter, context, CtsEnforcement::kNextRelease) {
+ using namespace skgpu::graphite;
+ std::unique_ptr<Recorder> recorder = context->makeRecorder();
+ SkImageInfo info = SkImageInfo::MakeN32Premul(1024, 1024);
+ auto surface = SkSurfaces::RenderTarget(recorder.get(), info);
+ if (!surface) return;
+ auto canvas = surface->getCanvas();
+
+ run_atlas_oob_test(reporter, canvas);
+
+ std::unique_ptr<Recording> recording = recorder->snap();
+ InsertRecordingInfo recordingInfo;
+ recordingInfo.fRecording = recording.get();
+ context->insertRecording(recordingInfo);
+ context->submit();
+}
+#endif // defined(SK_GRAPHITE)

View File

@@ -9,4 +9,4 @@ refactor_use_non-deprecated_nskeyedarchiver_apis.patch
chore_turn_off_launchapplicationaturl_deprecation_errors_in_squirrel.patch
fix_crash_when_process_to_extract_zip_cannot_be_launched.patch
use_uttype_class_instead_of_deprecated_uttypeconformsto.patch
fix_clean_up_old_staged_updates_before_downloading_new_update.patch
fix_clean_up_orphaned_staged_updates_before_downloading_new_update.patch

View File

@@ -1,64 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andy Locascio <loc@anthropic.com>
Date: Tue, 6 Jan 2026 08:23:03 -0800
Subject: fix: clean up old staged updates before downloading new update
When checkForUpdates() is called while an update is already staged,
Squirrel creates a new temporary directory for the download without
cleaning up the old one. This can lead to significant disk usage if
the app keeps checking for updates without restarting.
This change adds a force parameter to pruneUpdateDirectories that
bypasses the AwaitingRelaunch state check. This is called before
creating a new temp directory, ensuring old staged updates are
cleaned up when a new download starts.
diff --git a/Squirrel/SQRLUpdater.m b/Squirrel/SQRLUpdater.m
index d156616e81e6f25a3bded30e6216b8fc311f31bc..6cd4346bf43b191147aff819cb93387e71275a46 100644
--- a/Squirrel/SQRLUpdater.m
+++ b/Squirrel/SQRLUpdater.m
@@ -543,11 +543,17 @@ - (RACSignal *)downloadBundleForUpdate:(SQRLUpdate *)update intoDirectory:(NSURL
#pragma mark File Management
- (RACSignal *)uniqueTemporaryDirectoryForUpdate {
- return [[[RACSignal
+ // Clean up any old staged update directories before creating a new one.
+ // This prevents disk usage from growing when checkForUpdates() is called
+ // multiple times without the app restarting.
+ return [[[[[self
+ pruneUpdateDirectoriesWithForce:YES]
+ ignoreValues]
+ concat:[RACSignal
defer:^{
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
return [directoryManager storageURL];
- }]
+ }]]
flattenMap:^(NSURL *storageURL) {
NSURL *updateDirectoryTemplate = [storageURL URLByAppendingPathComponent:[SQRLUpdaterUniqueTemporaryDirectoryPrefix stringByAppendingString:@"XXXXXXX"]];
char *updateDirectoryCString = strdup(updateDirectoryTemplate.path.fileSystemRepresentation);
@@ -643,7 +649,7 @@ - (BOOL)isRunningOnReadOnlyVolume {
- (RACSignal *)performHousekeeping {
return [[RACSignal
- merge:@[ [self pruneUpdateDirectories], [self truncateLogs] ]]
+ merge:@[ [self pruneUpdateDirectoriesWithForce:NO], [self truncateLogs] ]]
catch:^(NSError *error) {
NSLog(@"Error doing housekeeping: %@", error);
return [RACSignal empty];
@@ -658,11 +664,12 @@ - (RACSignal *)performHousekeeping {
///
/// Sends each removed directory then completes, or errors, on an unspecified
/// thread.
-- (RACSignal *)pruneUpdateDirectories {
+- (RACSignal *)pruneUpdateDirectoriesWithForce:(BOOL)force {
return [[[RACSignal
defer:^{
- // If we already have updates downloaded we don't wanna prune them.
- if (self.state == SQRLUpdaterStateAwaitingRelaunch) return [RACSignal empty];
+ // If we already have updates downloaded we don't wanna prune them,
+ // unless force is YES (used when starting a new download).
+ if (!force && self.state == SQRLUpdaterStateAwaitingRelaunch) return [RACSignal empty];
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
return [directoryManager storageURL];

View File

@@ -0,0 +1,130 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andy Locascio <loc@anthropic.com>
Date: Tue, 6 Jan 2026 08:23:03 -0800
Subject: fix: clean up orphaned staged updates before downloading new update
When checkForUpdates() is called while an update is already staged,
Squirrel creates a new temporary directory for the download without
cleaning up the old one. This can lead to significant disk usage if
the app keeps checking for updates without restarting.
This change adds a pruneOrphanedUpdateDirectories step before creating
a new temp directory. Unlike a blanket prune, this reads the current
ShipItState.plist and preserves the directory it references, deleting
only truly orphaned update directories. This keeps the on-disk
footprint bounded (at most 2 dirs) while ensuring quitAndInstall
remains safe to call even when a new check is in progress.
Refs https://github.com/electron/electron/issues/50200
diff --git a/Squirrel/SQRLUpdater.m b/Squirrel/SQRLUpdater.m
index d156616e81e6f25a3bded30e6216b8fc311f31bc..41856e5754228d33982db72f97f2ff241615a357 100644
--- a/Squirrel/SQRLUpdater.m
+++ b/Squirrel/SQRLUpdater.m
@@ -543,11 +543,19 @@ - (RACSignal *)downloadBundleForUpdate:(SQRLUpdate *)update intoDirectory:(NSURL
#pragma mark File Management
- (RACSignal *)uniqueTemporaryDirectoryForUpdate {
- return [[[RACSignal
+ // Clean up any orphaned update directories before creating a new one.
+ // This prevents disk usage from growing when checkForUpdates() is called
+ // multiple times without the app restarting. The currently staged update
+ // (referenced by ShipItState.plist) is always preserved so quitAndInstall
+ // remains safe to call while a new check is in progress.
+ return [[[[[self
+ pruneOrphanedUpdateDirectories]
+ ignoreValues]
+ concat:[RACSignal
defer:^{
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
return [directoryManager storageURL];
- }]
+ }]]
flattenMap:^(NSURL *storageURL) {
NSURL *updateDirectoryTemplate = [storageURL URLByAppendingPathComponent:[SQRLUpdaterUniqueTemporaryDirectoryPrefix stringByAppendingString:@"XXXXXXX"]];
char *updateDirectoryCString = strdup(updateDirectoryTemplate.path.fileSystemRepresentation);
@@ -668,25 +676,68 @@ - (RACSignal *)pruneUpdateDirectories {
return [directoryManager storageURL];
}]
flattenMap:^(NSURL *storageURL) {
- NSFileManager *manager = [[NSFileManager alloc] init];
- NSDirectoryEnumerator *enumerator = [manager enumeratorAtURL:storageURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:^(NSURL *URL, NSError *error) {
- NSLog(@"Error enumerating item %@ within directory %@: %@", URL, storageURL, error);
- return YES;
- }];
+ return [self removeUpdateDirectoriesInStorageURL:storageURL excludingURL:nil];
+ }]
+ setNameWithFormat:@"%@ -prunedUpdateDirectories", self];
+}
- return [[enumerator.rac_sequence.signal
- filter:^(NSURL *enumeratedURL) {
- NSString *name = enumeratedURL.lastPathComponent;
- return [name hasPrefix:SQRLUpdaterUniqueTemporaryDirectoryPrefix];
- }]
- doNext:^(NSURL *directoryURL) {
- NSError *error = nil;
- if (![manager removeItemAtURL:directoryURL error:&error]) {
- NSLog(@"Error removing old update directory at %@: %@", directoryURL, error.sqrl_verboseDescription);
- }
+/// Lazily removes orphaned temporary directories upon subscription, always
+/// preserving the directory currently referenced by ShipItState.plist so that
+/// quitAndInstall remains safe to call mid-check.
+///
+/// Safe to call in any state. Sends each removed directory then completes on
+/// an unspecified thread. Errors reading the staged request are swallowed
+/// (treated as "nothing staged").
+- (RACSignal *)pruneOrphanedUpdateDirectories {
+ return [[[[[SQRLShipItRequest
+ readUsingURL:self.shipItStateURL]
+ map:^(SQRLShipItRequest *request) {
+ // The request holds the URL to the staged .app bundle; its parent
+ // is the update.XXXXXXX directory we must preserve.
+ return [request.updateBundleURL URLByDeletingLastPathComponent];
+ }]
+ catch:^(NSError *error) {
+ // No staged request (or unreadable) — nothing to preserve.
+ return [RACSignal return:nil];
+ }]
+ flattenMap:^(NSURL *stagedDirectoryURL) {
+ SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
+ return [[directoryManager storageURL]
+ flattenMap:^(NSURL *storageURL) {
+ return [self removeUpdateDirectoriesInStorageURL:storageURL excludingURL:stagedDirectoryURL];
}];
}]
- setNameWithFormat:@"%@ -prunedUpdateDirectories", self];
+ setNameWithFormat:@"%@ -pruneOrphanedUpdateDirectories", self];
+}
+
+/// Shared enumerate-and-delete logic for update temp directories.
+///
+/// storageURL - The Squirrel storage root to enumerate. Must not be nil.
+/// excludedURL - Directory to skip (compared by standardized path). May be nil.
+- (RACSignal *)removeUpdateDirectoriesInStorageURL:(NSURL *)storageURL excludingURL:(NSURL *)excludedURL {
+ NSParameterAssert(storageURL != nil);
+
+ NSFileManager *manager = [[NSFileManager alloc] init];
+ NSDirectoryEnumerator *enumerator = [manager enumeratorAtURL:storageURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:^(NSURL *URL, NSError *error) {
+ NSLog(@"Error enumerating item %@ within directory %@: %@", URL, storageURL, error);
+ return YES;
+ }];
+
+ NSString *excludedPath = excludedURL.URLByStandardizingPath.path;
+
+ return [[enumerator.rac_sequence.signal
+ filter:^(NSURL *enumeratedURL) {
+ NSString *name = enumeratedURL.lastPathComponent;
+ if (![name hasPrefix:SQRLUpdaterUniqueTemporaryDirectoryPrefix]) return NO;
+ if (excludedPath != nil && [enumeratedURL.URLByStandardizingPath.path isEqualToString:excludedPath]) return NO;
+ return YES;
+ }]
+ doNext:^(NSURL *directoryURL) {
+ NSError *error = nil;
+ if (![manager removeItemAtURL:directoryURL error:&error]) {
+ NSLog(@"Error removing old update directory at %@: %@", directoryURL, error.sqrl_verboseDescription);
+ }
+ }];
}

View File

@@ -1 +1,2 @@
chore_allow_customizing_microtask_policy_per_context.patch
cherry-pick-d5b0cb2acffe.patch

View File

@@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Darius Mercadier <dmercadier@chromium.org>
Date: Wed, 25 Feb 2026 12:56:18 +0100
Subject: [M144 Merge] [maglev] fix CanElideWriteBarrier Smi recording for phis
Recording a Tagged use is not enough for 2 reasons:
* Tagged uses are sometimes ignored, in particular for loop phis
where we distinguish in-loop and out-of-loop uses.
* This Tagged use could only prevent untagging of this specific phi,
but none of its inputs. So we could have a Smi phi as input to the
current phi which gets untagged and retagged to a non-Smi, all
while the current phi doesn't get untagged.
(cherry picked from commit a54bf5cd45e5b119e2afe6019428e81c3d626fb3)
Change-Id: I9b3a2ea339f2c9d81dbb74a44425ba55d8c73871
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7604255
Auto-Submit: Darius Mercadier <dmercadier@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Darius Mercadier <dmercadier@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#105444}
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7659106
Auto-Submit: Srinivas Sista <srinivassista@chromium.org>
Reviewed-by: Rezvan Mahdavi Hezaveh <rezvan@chromium.org>
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
Reviewed-by: Deepti Gandluri <gdeepti@chromium.org>
Owners-Override: Srinivas Sista <srinivassista@chromium.org>
Cr-Commit-Position: refs/branch-heads/14.4@{#64}
Cr-Branched-From: 80acc26727d5a34e77dabeebe7c9213ec1bd4768-refs/heads/14.4.258@{#1}
Cr-Branched-From: ce7e597e90f6df3fa4b6df224bc613b80c635450-refs/heads/main@{#104020}
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
index bf6a5ab1b41d684c37dd96f7720474c6bb71a4db..c21a41d7da3394bb8f35857e0dcf49a78b218d31 100644
--- a/src/maglev/maglev-graph-builder.cc
+++ b/src/maglev/maglev-graph-builder.cc
@@ -4439,7 +4439,11 @@ bool MaglevGraphBuilder::CanElideWriteBarrier(ValueNode* object,
ValueNode* value) {
if (value->Is<RootConstant>() || value->Is<ConsStringMap>()) return true;
if (!IsEmptyNodeType(GetType(value)) && CheckType(value, NodeType::kSmi)) {
- value->MaybeRecordUseReprHint(UseRepresentation::kTagged);
+ if constexpr (SmiValuesAre31Bits()) {
+ if (Phi* value_as_phi = value->TryCast<Phi>()) {
+ value_as_phi->SetUseRequires31BitValue();
+ }
+ }
return true;
}

View File

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

View File

@@ -0,0 +1,21 @@
#!/bin/sh
# Removes the codesigning keychain created by generate-identity.sh.
# Safe to run even if generate-identity.sh was never run (each step
# is guarded).
set -eo pipefail
KEYCHAIN="electron-codesign.keychain-db"
# delete-keychain also removes it from the search list
if security list-keychains -d user | grep -q "$KEYCHAIN"; then
security delete-keychain "$KEYCHAIN"
echo "Deleted keychain: $KEYCHAIN"
else
echo "Keychain not found, nothing to delete"
fi
# Clean up working directory
rm -rf "$(dirname $0)"/.working
echo "Cleanup complete"

View File

@@ -3,6 +3,8 @@
set -eo pipefail
dir="$(dirname $0)"/.working
KEYCHAIN="electron-codesign.keychain-db"
KEYCHAIN_TEMP="$(openssl rand -hex 12)"
cleanup() {
rm -rf "$dir"
@@ -18,30 +20,16 @@ mkdir -p "$dir"
# Generate Certs
openssl req -new -newkey rsa:2048 -x509 -days 7300 -nodes -config "$(dirname $0)"/codesign.cnf -extensions extended -batch -out "$dir"/certificate.cer -keyout "$dir"/certificate.key
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$dir"/certificate.cer
sudo security import "$dir"/certificate.key -A -k /Library/Keychains/System.keychain
# restart(reload) taskgated daemon
sudo pkill -f /usr/libexec/taskgated
# macOS 15+ blocks modifications to the system keychain via SIP/TCC,
# so we use a custom user-scoped keychain instead.
# Refs https://github.com/electron/electron/issues/48182
security create-keychain -p "$KEYCHAIN_TEMP" "$KEYCHAIN"
security set-keychain-settings -t 3600 -u "$KEYCHAIN"
security unlock-keychain -p "$KEYCHAIN_TEMP" "$KEYCHAIN"
# need once
sudo security authorizationdb write system.privilege.taskport allow
# need once
DevToolsSecurity -enable
security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"')
security import "$dir"/certificate.cer -k "$KEYCHAIN" -T /usr/bin/codesign
security import "$dir"/certificate.key -k "$KEYCHAIN" -T /usr/bin/codesign -A
# openssl req -newkey rsa:2048 -nodes -keyout "$dir"/private.pem -x509 -days 1 -out "$dir"/certificate.pem -extensions extended -config "$(dirname $0)"/codesign.cnf
# openssl x509 -inform PEM -in "$dir"/certificate.pem -outform DER -out "$dir"/certificate.cer
# openssl x509 -pubkey -noout -in "$dir"/certificate.pem > "$dir"/public.key
# rm -f "$dir"/certificate.pem
# Import Certs
# security import "$dir"/certificate.cer -k $KEY_CHAIN
# security import "$dir"/private.pem -k $KEY_CHAIN
# security import "$dir"/public.key -k $KEY_CHAIN
# Generate Trust Settings
# TODO: Remove NPX
npm_config_yes=true npx ts-node "$(dirname $0)"/gen-trust.ts "$dir"/certificate.cer "$dir"/trust.xml
# Import Trust Settings
sudo security trust-settings-import -d "$dir/trust.xml"
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_TEMP" "$KEYCHAIN"

View File

@@ -2,7 +2,7 @@
set -e
valid_certs=$(security find-identity -p codesigning -v)
valid_certs=$(security find-identity -p codesigning)
if [[ $valid_certs == *"1)"* ]]; then
first_valid_cert=$(echo $valid_certs | sed 's/ \".*//' | sed 's/.* //')
echo $first_valid_cert

View File

@@ -128,6 +128,11 @@ def format_patch(repo, since):
os.path.dirname(os.path.realpath(__file__)),
'electron.gitattributes',
),
# Pin rename/copy detection to git's default so that patch output is
# deterministic regardless of local or system-level diff.renames config
# (e.g. 'copies', which would encode similar new files as copies).
'-c',
'diff.renames=true',
# Ensure it is not possible to match anything
# Disabled for now as we have consistent chunk headers
# '-c',

View File

@@ -172,14 +172,7 @@
"report/test-report-uv-handles",
"report/test-report-worker",
"report/test-report-writereport",
"sea/test-single-executable-blob-config",
"sea/test-single-executable-blob-config-errors",
"sequential/test-single-executable-application",
"sequential/test-single-executable-application-disable-experimental-sea-warning",
"sequential/test-single-executable-application-empty",
"sequential/test-single-executable-application-snapshot",
"sequential/test-single-executable-application-snapshot-and-code-cache",
"sequential/test-single-executable-application-use-code-cache",
"sea/test-single-executable",
"sequential/test-tls-connect",
"wpt/test-webcrypto",
"wasm-allocation/test-wasm-allocation"

View File

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

View File

@@ -368,6 +368,9 @@ def upload_io_to_github(release, filename, filepath, version):
for c in iter(lambda: upload_process.stdout.read(1), b""):
sys.stdout.buffer.write(c)
sys.stdout.flush()
upload_process.wait()
if upload_process.returncode != 0:
sys.exit(upload_process.returncode)
if "GITHUB_OUTPUT" in os.environ:
output_path = os.environ["GITHUB_OUTPUT"]

View File

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

View File

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

View File

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

View File

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

View File

@@ -103,6 +103,40 @@ bool Menu::IsCommandIdEnabled(int command_id) const {
return InvokeBoolMethod(this, "_isCommandIdEnabled", command_id);
}
std::u16string Menu::GetLabelForCommandId(int command_id) const {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Value> val = gin_helper::CallMethod(
isolate, const_cast<Menu*>(this), "_getLabelForCommandId", command_id);
std::u16string label;
if (!gin::ConvertFromV8(isolate, val, &label))
label.clear();
return label;
}
std::u16string Menu::GetSecondaryLabelForCommandId(int command_id) const {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Value> val =
gin_helper::CallMethod(isolate, const_cast<Menu*>(this),
"_getSecondaryLabelForCommandId", command_id);
std::u16string label;
if (!gin::ConvertFromV8(isolate, val, &label))
label.clear();
return label;
}
ui::ImageModel Menu::GetIconForCommandId(int command_id) const {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Value> val = gin_helper::CallMethod(
isolate, const_cast<Menu*>(this), "_getIconForCommandId", command_id);
gfx::Image icon;
if (!gin::ConvertFromV8(isolate, val, &icon))
icon = gfx::Image();
return ui::ImageModel::FromImage(icon);
}
bool Menu::IsCommandIdVisible(int command_id) const {
return InvokeBoolMethod(this, "_isCommandIdVisible", command_id);
}
@@ -206,10 +240,6 @@ void Menu::SetIcon(int index, const gfx::Image& image) {
model_->SetIcon(index, ui::ImageModel::FromImage(image));
}
void Menu::SetSublabel(int index, const std::u16string& sublabel) {
model_->SetSecondaryLabel(index, sublabel);
}
void Menu::SetToolTip(int index, const std::u16string& toolTip) {
model_->SetToolTip(index, toolTip);
}
@@ -291,7 +321,6 @@ void Menu::FillObjectTemplate(v8::Isolate* isolate,
.SetMethod("insertSeparator", &Menu::InsertSeparatorAt)
.SetMethod("insertSubMenu", &Menu::InsertSubMenuAt)
.SetMethod("setIcon", &Menu::SetIcon)
.SetMethod("setSublabel", &Menu::SetSublabel)
.SetMethod("setToolTip", &Menu::SetToolTip)
.SetMethod("setRole", &Menu::SetRole)
.SetMethod("setCustomType", &Menu::SetCustomType)
@@ -311,6 +340,8 @@ void Menu::FillObjectTemplate(v8::Isolate* isolate,
.SetMethod("_getAcceleratorTextAt", &Menu::GetAcceleratorTextAtForTesting)
#if BUILDFLAG(IS_MAC)
.SetMethod("_getUserAcceleratorAt", &Menu::GetUserAcceleratorAt)
.SetMethod("_simulateSubmenuCloseSequenceForTesting",
&Menu::SimulateSubmenuCloseSequenceForTesting)
#endif
.Build();
}

View File

@@ -73,6 +73,9 @@ class Menu : public gin::Wrappable<Menu>,
bool IsCommandIdChecked(int command_id) const override;
bool IsCommandIdEnabled(int command_id) const override;
bool IsCommandIdVisible(int command_id) const override;
std::u16string GetLabelForCommandId(int command_id) const override;
std::u16string GetSecondaryLabelForCommandId(int command_id) const override;
ui::ImageModel GetIconForCommandId(int command_id) const override;
bool ShouldCommandIdWorkWhenHidden(int command_id) const override;
bool GetAcceleratorForCommandIdWithParams(
int command_id,
@@ -84,6 +87,7 @@ class Menu : public gin::Wrappable<Menu>,
int command_id,
ElectronMenuModel::SharingItem* item) const override;
v8::Local<v8::Value> GetUserAcceleratorAt(int command_id) const;
virtual void SimulateSubmenuCloseSequenceForTesting();
#endif
void ExecuteCommand(int command_id, int event_flags) override;
void OnMenuWillShow(ui::SimpleMenuModel* source) override;

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