Skip to content

chore: upgrade Chromium from v126 to v143 for asset discovery#2187

Open
Manoj-Katta wants to merge 13 commits intomasterfrom
PPLT-4214/upgrade-chromium-v143
Open

chore: upgrade Chromium from v126 to v143 for asset discovery#2187
Manoj-Katta wants to merge 13 commits intomasterfrom
PPLT-4214/upgrade-chromium-v143

Conversation

@Manoj-Katta
Copy link
Copy Markdown
Contributor

@Manoj-Katta Manoj-Katta commented Apr 17, 2026

What this does

Upgrade bundled Chromium from v126.0.6478.184 to v143.0.7499.169 to 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.js updated to Chrome 143. sec-ch-ua header 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 of http://localhost to https://.
  • 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-load Network.getResponseBody to response-stage Fetch.getResponseBody:

  • Fetch.continueRequest now sets interceptResponse: true.
  • New _handleResponsePaused saves the resource at the response stage with five branches: errored, oversized/malformed, redirect, event-stream, normal.
  • Resource lifecycle completes on the page session without depending on Network.loadingFinished.
  • Removed the dead Content-Length size guard inside saveResponseResource (response-stage handler catches oversized earlier).

Test changes

  • Test server replies 204 to /favicon.ico by default — Chrome 128+ auto-fetches it.
  • New test: explicit favicon capture (<link rel="icon">).
  • New test: auto-fetched favicon capture (no link, bumped idle timeout for cross-platform determinism).
  • New test: graceful logging on direct font fetch failure (covers the post-refactor catch).
  • logs unhandled response errors gracefully mock updated to Fetch.getResponseBody.
  • iframe srcdoc regex accepts both v126 literal <> and v143 entity-escaped &lt;&gt;.

Tests resolved

19 original failures across three clusters:

  • Cluster 1 — favicon auto-fetch (3 specs): test server 204s favicon by default.
  • Cluster 2 — cross-origin / domain validation (12 specs): LocalNetworkAccessChecks flag.
  • Cluster 3 — execute callback / iframe (4 specs): favicon 204 + iframe srcdoc regex.

Plus 2 v143 regressions resolved by the response-stage capture refactor:

  • does not capture remote files with content-length NAN greater than 25MB
  • captures requests from workers

Follow-ups

  • Multi-session handling (proper alternative to the IsolateOrigins flag) is debt for a separate ticket.
  • Pre-existing flake Snapshot handleSyncJob should handle promise reject is out of scope.

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>
@Manoj-Katta Manoj-Katta requested a review from a team as a code owner April 17, 2026 05:50
Manoj-Katta and others added 5 commits April 19, 2026 12:46
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>
Comment thread packages/core/test/helpers/server.js Outdated
// 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') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 thread packages/core/test/snapshot.test.js Outdated
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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do you got it this, can you attach a reference here ?

Comment thread packages/core/src/browser.js Outdated
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).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you simplify this, what it meant ?
and why we have to disable IsolateOrigins

Copy link
Copy Markdown
Contributor

@this-is-shivamsingh this-is-shivamsingh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@Manoj-Katta Manoj-Katta force-pushed the PPLT-4214/upgrade-chromium-v143 branch from c615e84 to 4f02ad6 Compare April 27, 2026 12:17
Manoj-Katta and others added 6 commits April 28, 2026 00:25
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants