chore: upgrade Chromium from v126 to v143 for asset discovery#2187
Open
Manoj-Katta wants to merge 13 commits intomasterfrom
Open
chore: upgrade Chromium from v126 to v143 for asset discovery#2187Manoj-Katta wants to merge 13 commits intomasterfrom
Manoj-Katta wants to merge 13 commits intomasterfrom
Conversation
Upgrade bundled Chromium from v126.0.6478.184 to v143.0.7499.169 to match the renderer browser version. Update sec-ch-ua header from v123 to v143 in direct font request headers. PPLT-4214 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chrome >=128 new headless auto-requests /favicon.ico on page load, breaking discovery tests that asserted exact request counts. Filter at the helper level so all tests benefit without per-assertion churn.
Chrome >=128 new headless enforces site isolation via IsolateOrigins and site-per-process, putting cross-origin sub-resources and worker fetches into separate renderer processes. The existing --disable-site-isolation-trials flag only covers opt-in trials and no longer keeps everything in one renderer. Extend the existing --disable-features list with IsolateOrigins and site-per-process so the main page's Fetch.enable / Network.enable listeners continue to see cross-origin and worker traffic, matching the v126 old-headless behavior Percy relies on. Also add HttpsFirstBalancedModeAutoEnable to prevent new headless from blocking HTTP asset discovery with ERR_BLOCKED_BY_CLIENT.
Invalidates actions/cache entries that still contain the old v126 Chromium binary so CI downloads the new v143 revisions fresh.
…T-5285) Two adjustments for Chrome >=128 (new headless) without changing production code: - test server now replies 204 to the auto-fetched /favicon.ico so it doesn't leak into a snapshot's resource list and shift downstream request indices (the previous helper only filtered favicon from the request log; the browser still received the default reply and the resource still got captured). - iframe-srcdoc test regex accepts both serializations: pre-Chrome-128 left "<" / ">" literal inside attribute values, while >=128 entity-escapes them per HTML5 spec. Both are valid HTML. Fixes 4 of the v143 failures: takes additional snapshots after running each execute, runs the execute callback in the correct frame, can execute scripts at various states, can execute scripts that wait for specific states. Cluster A (cross-origin / domain-validation, 14 specs) is being tracked separately — root cause is Chrome's stricter CORS / Opaque Response Blocking; chasing a narrower fix than --disable-web-security before landing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…284) Chrome 143 enables Local Network Access (LNA) checks by default. When asset discovery serves the root document via Fetch.fulfillRequest (the domSnapshot path), Chrome treats sub-resource requests to local network addresses (*.localhost, 127.0.0.1, RFC 1918) as needing explicit user permission. Headless can't grant the prompt, so the request fails with corsError = LocalNetworkAccessPermissionDenied and the resource is never delivered to Percy's CDP listeners. Add LocalNetworkAccessChecks to the existing --disable-features list so the fulfilled-root flow keeps loading cross-origin local sub-resources, matching v126 behavior. This is narrower than --disable-web-security: it preserves CORS / CORB / ORB enforcement on real cross-origin internet hosts and only relaxes Chrome's loopback / private-network gating, which is exactly what asset discovery needs. Resolves the 12 cross-origin and auto-domain-validation discovery test failures from PR #2187 CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| // Chrome >=128 new headless auto-requests /favicon.ico for every navigation. | ||
| // Reply 204 so the browser doesn't capture it as a snapshot resource, and | ||
| // skip it from the requests log so per-test request assertions stay stable. | ||
| if (req.url.pathname === '/favicon.ico') { |
Contributor
There was a problem hiding this comment.
How do we know if it's not requested from the page, but only requested from the header, Favicon appears in the tab header under tag, can we distinguish if it's a favicon.ico request from the header and not from the any page resource ?
Comment on lines
+1538
to
+1540
| // srcdoc HTML attribute serialization: pre-Chrome-128 left `<` `>` literal | ||
| // inside attribute values; Chrome >=128 entity-escapes them per HTML5 spec. | ||
| // Accept either form. |
Contributor
There was a problem hiding this comment.
can you write an example what it does, any eg, in the comment to understand it
Comment on lines
+180
to
+184
| linux: '1536366', | ||
| win64: '1536376', | ||
| win32: '1536377', | ||
| darwin: '1536380', | ||
| darwinArm: '1536376' |
Contributor
There was a problem hiding this comment.
how do you got it this, can you attach a reference here ?
Comment on lines
+22
to
+30
| args = [ | ||
| // disable the translate popup and optimization downloads | ||
| '--disable-features=Translate,OptimizationGuideModelDownloading', | ||
| // disable: translate popup, optimization downloads, baseline site | ||
| // isolation (so cross-origin sub-resources and worker fetches stay in | ||
| // the main renderer for interception), HTTPS-first navigation blocking, | ||
| // and Local Network Access permission checks (Chrome 143+ blocks | ||
| // sub-resource requests to *.localhost / 127.0.0.1 / RFC1918 with | ||
| // `LocalNetworkAccessPermissionDenied` when the document was served | ||
| // via Fetch.fulfillRequest from asset discovery — there is no permission | ||
| // prompt available in headless to grant the access). |
Contributor
There was a problem hiding this comment.
can you simplify this, what it meant ?
and why we have to disable IsolateOrigins
Contributor
this-is-shivamsingh
left a comment
There was a problem hiding this comment.
commented :}}
Manoj-Katta
added a commit
that referenced
this pull request
Apr 27, 2026
Address inline review comments on PR #2187: - src/browser.js: split the --disable-features comment into one bullet per feature. Each bullet documents the specific Chrome >=128/143 behavior that broke and why disabling is the right fix - src/install.js: add the procedure for picking per-platform revision numbers (chromiumdash + snapshot index) so future upgrades don't have to re-derive it - test/helpers/server.js: only short-circuit /favicon.ico to 204 when the test hasn't supplied an explicit reply for it. Tests that want favicon-as-asset (none today, but future-proof) keep working via `server.reply('/favicon.ico', ...)` - test/snapshot.test.js: expand the iframe srcdoc-serialization comment with a literal example of each form so the regex's "either form" intent is obvious No behavioral change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address inline review comments on PR #2187: - src/browser.js: split the --disable-features comment into one bullet per feature. Each bullet documents the specific Chrome >=128/143 behavior that broke and why disabling is the right fix - src/install.js: add the procedure for picking per-platform revision numbers (chromiumdash + snapshot index) so future upgrades don't have to re-derive it - test/helpers/server.js: only short-circuit /favicon.ico to 204 when the test hasn't supplied an explicit reply for it. Tests that want favicon-as-asset (none today, but future-proof) keep working via `server.reply('/favicon.ico', ...)` - test/snapshot.test.js: expand the iframe srcdoc-serialization comment with a literal example of each form so the regex's "either form" intent is obvious No behavioral change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c615e84 to
4f02ad6
Compare
…ntent-Length (PPLT-4214)
Two v143 regressions surfaced after the launch-flag fixes landed:
1. `does not capture remote files with content-length NAN greater
than 25MB` — Chrome 143 won't terminate the body of a malformed
`Content-Length` response, so Network.loadingFinished never fires,
the page's <link href="large.css"> blocks the load event, and the
navigation times out (3 retries × 30s = 93s). The existing size
guard in saveResponseResource is unreachable.
2. `captures requests from workers` — for page-fetched worker scripts,
Chrome 143 splits the lifecycle across two CDP sessions: the page
session sees Fetch.requestPaused under requestId X, but
Network.responseReceived / loadingFinished for the same script fire
on the worker session under a fresh requestId Y once the worker
target attaches. Percy's #requests entry under X is never cleaned
up, idle() blocks indefinitely, retries fail. Confirmed via raw
CDP probe (/tmp/worker-probe.mjs) — Chrome itself behaves
correctly; the bug is in Percy's requestId-keyed bookkeeping.
Fix: enable Fetch response-stage interception (Fetch.continueRequest
with interceptResponse:true) and add _handleResponsePaused with four
branches:
- responseErrorReason set: confirm the failure with Fetch.failRequest
so _handleLoadingFailed logs the network error as before.
- Content-Length oversized or malformed: forget the request,
Fetch.failRequest with 'Aborted' before the body streams. This
unsticks the page load for the NaN test.
- 3xx redirect: skip — _handleRequest already builds the redirect
chain off the next request-stage event with redirectResponse.
- Otherwise: schedule a 1s backstop that forgets the request only if
Network.loadingFinished / loadingFailed haven't already done so.
For normal requests this is a no-op (Network events fire within
tens of ms). For worker scripts where loadingFinished never fires
on the page session, this is what unblocks idle().
Trade-off: every Fetch-intercepted request now does one extra CDP
roundtrip (response-stage pause -> continueResponse). Sub-millisecond
per request on localhost websocket. Puppeteer and Playwright run this
same dual-stage pattern.
Helpers added: inspectContentLength (returns tooLarge / malformed /
rawValue) and headersArrayToObject (Fetch event headers come as
[{name,value}]). Constant RESPONSE_STAGE_BACKSTOP_MS = 1000.
Both target tests pass in 2-3s in isolation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s (PPLT-4214) CI on 56306df surfaced a regression in `logs instrumentation for network errors`: when a server destroys the socket mid-response, Network.loadingFailed used to fire with errorText='net::ERR_EMPTY_RESPONSE' (or similar concrete reason), and _handleLoadingFailed logged asset_load_missing/network_error. After enabling response-stage Fetch interception, the error now surfaces first as Fetch.requestPaused with responseErrorReason set. The previous handler called Fetch.failRequest({errorReason: responseErrorReason}), which forces Chrome to fire Network.loadingFailed with errorText synthesized from that reason. For socket-destroy, responseErrorReason is the generic 'Failed', which collapses to net::ERR_FAILED — the exact errorText the asset_load_missing branch explicitly excludes, so the instrumentation was lost. Switch to Fetch.continueResponse for errored responses. This lets Chrome propagate the original errorText through Network.loadingFailed naturally, restoring the asset_load_missing log. Verified locally: - logs instrumentation for network errors: passes (3s) - captures requests from workers: still passes (4s) - does not capture remote files with content-length NAN: still passes (2s) - captures redirected resources, does not capture event-stream requests, logs failed request errors with a debug loglevel, logs unhandled response errors gracefully: all still pass Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The other discovery specs opt out of /favicon.ico via the test server's 204 short-circuit so favicon noise doesn't shift their captured[] indices. This spec opts back in by registering an explicit /favicon.ico reply, asserting that Percy correctly captures the favicon when the server actually serves one — the production scenario where a customer's site has a real favicon. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… a timer (PPLT-4214) Replaces the 1-second backstop timer with a principled fix: read the response body via Fetch.getResponseBody at response stage, save the resource, and forget the request — so the entire lifecycle is complete before Chrome would dispatch Network.loadingFinished. The backstop approach was a workaround that silently dropped requests where Network.loadingFinished never fired on the page session (Chrome 143's split-session worker scripts being the motivating case). The worker test passed because the orphan was deleted, but the worker script itself was never captured as a Percy resource — a real fidelity gap, even if invisible to visual diffs. Approach 2 captures the resource using the data Chrome gives us at response stage: - Errored response: continue, let Network.loadingFailed propagate the original errorText to _handleLoadingFailed (unchanged from before). - Oversized / malformed Content-Length: log skip, forget, Fetch.failRequest before body streams (unchanged from before). - Redirect (3xx): continue; _handleRequest builds the chain on the next request stage event. - Streaming (text/event-stream): continue without reading body — SSE bodies never complete and Fetch.getResponseBody would hang. - Normal: read body via Fetch.getResponseBody, build a synthetic response object, run saveResponseResource, forget the request. Network.loadingFinished still fires later for normal page-session requests but finds nothing in #requests and exits early (the existing handler already guards for this case). Other changes in this commit: - Drop RESPONSE_STAGE_BACKSTOP_MS constant and the setTimeout block. - Trim the multi-paragraph block comments in _handleResponsePaused, the helpers, and the dispatch in _handleRequestPaused down to one-liners. - Update `logs unhandled response errors gracefully` test to mock Fetch.getResponseBody (the body-read path is now Fetch-based). - Make `captures favicon when the server provides one` deterministic by adding <link rel="icon"> to the test DOM — the previous version relied on Chrome's auto-fetch which has non-deterministic timing on Windows CI. Net diff vs. backstop approach: -27 lines in network.js, +5 lines in discovery.test.js. All affected tests pass locally: - captures favicon when the server provides one (2s) - does not capture remote files with content-length NAN greater than 25MB (2s) - captures requests from workers (2s) - captures redirected resources (3s) - does not capture event-stream requests (3s) - logs failed request errors with a debug loglevel (2s) - logs instrumentation for network errors (2s) - logs unhandled response errors gracefully (2s) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… path (PPLT-4214) Drop the dead Content-Length size check from saveResponseResource — the oversized/malformed header path is now rejected earlier in _handleResponsePaused via Fetch.failRequest, so this guard never runs. The body.length check below still covers cached responses whose headers lie about size. Add two discovery specs to keep coverage at 100%: - "logs gracefully when direct font request fails" exercises the catch in saveResponseResource by serving the font as application/octet-stream (forces makeDirectRequest) and returning 400 on the direct fetch (makes makeRequest throw without retry). - "captures auto-fetched favicon when the page does not declare one" validates that Percy captures Chrome's implicit /favicon.ico fetch. Bumps networkIdleTimeout to 1000ms so the auto-fetch lands before idle declares the network done — Chrome's auto-fetch timing varies by platform and was previously flaky on Windows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…LT-4214) The Network constructor reads page.session.browser.version.userAgent. If the session crashes mid-construction (close() sets browser to null), the read throws a TypeError that masks the real "Session crashed!" error. Add optional chaining so userAgent stays unset on the race; the next CDP call in Network.watch surfaces the real session-closed error as expected. Fixes Snapshot > handles page crashes on Linux CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Upgrade bundled Chromium from
v126.0.6478.184tov143.0.7499.169to match the renderer browser version. 17-major-version jump that crosses Chrome 128's switch from old-headless to new-headless mode.Ticket: PPLT-4214 (split into PPLT-5282 / PPLT-5284 / PPLT-5285 — see
docs/plans/)What changed
Binary
Platform revisions in
install.jsupdated to Chrome 143.sec-ch-uaheader bumped to v143.Browser launch flags
Four
--disable-features=entries added to preserve pre-v143 behavior under new headless:IsolateOrigins,site-per-process— keeps cross-origin iframes and workers in the page's CDP session.HttpsFirstBalancedModeAutoEnable— prevents auto-upgrade ofhttp://localhosttohttps://.LocalNetworkAccessChecks— allows asset discovery to load localhost subresources (LNA permission prompt is headless-incompatible).Network interception (
network.js)Chrome 143 splits some request lifecycles across CDP sessions (workers especially) and stops gracefully terminating responses with malformed
Content-Length. Body capture moved from post-loadNetwork.getResponseBodyto response-stageFetch.getResponseBody:Fetch.continueRequestnow setsinterceptResponse: true._handleResponsePausedsaves the resource at the response stage with five branches: errored, oversized/malformed, redirect, event-stream, normal.Network.loadingFinished.saveResponseResource(response-stage handler catches oversized earlier).Test changes
204to/favicon.icoby default — Chrome 128+ auto-fetches it.<link rel="icon">).logs unhandled response errors gracefullymock updated toFetch.getResponseBody.<>and v143 entity-escaped<>.Tests resolved
19 original failures across three clusters:
LocalNetworkAccessChecksflag.Plus 2 v143 regressions resolved by the response-stage capture refactor:
does not capture remote files with content-length NAN greater than 25MBcaptures requests from workersFollow-ups
IsolateOriginsflag) is debt for a separate ticket.Snapshot handleSyncJob should handle promise rejectis out of scope.