Skip to content

Commit c615e84

Browse files
Manoj-Kattaclaude
andcommitted
chore(core): document Chrome 143 upgrade decisions per PR review
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) <[email protected]>
1 parent 5ba42dc commit c615e84

4 files changed

Lines changed: 51 additions & 17 deletions

File tree

packages/core/src/browser.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,28 @@ export class Browser extends EventEmitter {
2020
#lastid = 0;
2121

2222
args = [
23-
// disable: translate popup, optimization downloads, baseline site
24-
// isolation (so cross-origin sub-resources and worker fetches stay in
25-
// the main renderer for interception), HTTPS-first navigation blocking,
26-
// and Local Network Access permission checks (Chrome 143+ blocks
27-
// sub-resource requests to *.localhost / 127.0.0.1 / RFC1918 with
28-
// `LocalNetworkAccessPermissionDenied` when the document was served
29-
// via Fetch.fulfillRequest from asset discovery — there is no permission
30-
// prompt available in headless to grant the access).
23+
// Disable a set of Chrome features that break Percy's asset-discovery
24+
// assumptions in Chrome >=128 new-headless (which became the default when
25+
// we upgraded from v126 to v143):
26+
// - Translate, OptimizationGuideModelDownloading: chatty background
27+
// features that aren't relevant for headless asset capture
28+
// - site-per-process: baseline cross-site renderer-process isolation
29+
// (default-on in v143). Without this off, cross-origin sub-resources
30+
// run in a different renderer process / CDP target than the page,
31+
// so Percy's page-session Network/Fetch listeners never see their
32+
// events
33+
// - IsolateOrigins: stricter, per-origin variant of site-per-process;
34+
// the existing `--disable-site-isolation-trials` flag below only
35+
// covers *opt-in trials* and doesn't override the baseline
36+
// - HttpsFirstBalancedModeAutoEnable: Chrome 143+ auto-upgrades HTTP
37+
// navigation to HTTPS and blocks otherwise with ERR_BLOCKED_BY_CLIENT,
38+
// which would break HTTP asset discovery (local dev, CI, staging URLs)
39+
// - LocalNetworkAccessChecks: Chrome 143+ requires explicit user
40+
// permission for sub-resource requests to *.localhost / 127.0.0.1 /
41+
// RFC1918. When asset discovery serves the root via
42+
// Fetch.fulfillRequest, sub-resource requests fail with
43+
// LocalNetworkAccessPermissionDenied. Headless has no UI to grant
44+
// the permission, so disable the check.
3145
'--disable-features=Translate,OptimizationGuideModelDownloading,IsolateOrigins,site-per-process,HttpsFirstBalancedModeAutoEnable,LocalNetworkAccessChecks',
3246
// disable several subsystems which run network requests in the background
3347
'--disable-background-networking',

packages/core/src/install.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,18 @@ export function chromium({
175175
});
176176
}
177177

178-
// default chromium revisions corresponds to v143.0.7499.169
178+
// Default Chromium revisions, pinned to Chrome 143.0.7499.169 (branch base
179+
// position 1536371). Per-platform numbers below are the *closest available*
180+
// revision for each OS — Chromium snapshots aren't always published at the
181+
// exact base position, so pick the nearest one whose archive exists.
182+
//
183+
// To pick numbers for a future upgrade:
184+
// 1. Find the target Chrome version + branch base position at
185+
// https://chromiumdash.appspot.com/releases?platform=Mac
186+
// 2. Find the closest available revision per platform via the snapshots index
187+
// https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html
188+
// (folders: Linux_x64/, Mac/, Mac_Arm/, Win_x64/, Win/)
189+
// 3. Verify each archive exists before pinning the number here.
179190
chromium.revisions = {
180191
linux: '1536366',
181192
win64: '1536376',

packages/core/test/helpers/server.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,18 @@ export function createTestServer({ default: defaultReply, ...replies }, port = 8
2828
server.route(async (req, res, next) => {
2929
let pathname = req.url.pathname;
3030
if (req.url.search) pathname += req.url.search;
31-
// Chrome >=128 new headless auto-requests /favicon.ico for every navigation.
32-
// Reply 204 so the browser doesn't capture it as a snapshot resource, and
33-
// skip it from the requests log so per-test request assertions stay stable.
34-
if (req.url.pathname === '/favicon.ico') {
31+
let reply = replies[pathname] || defaultReply;
32+
// Chrome >=128 new-headless auto-fetches /favicon.ico on every navigation.
33+
// We can't distinguish that from a page-issued <link rel="icon"> request —
34+
// both arrive at the test server identically. So if the test didn't
35+
// explicitly set a /favicon.ico reply (`server.reply('/favicon.ico', ...)`),
36+
// fall back to a 204 instead of letting the catch-all default reply turn
37+
// it into a captured resource. Tests that *do* care about favicon-as-asset
38+
// continue to work because their explicit reply takes precedence.
39+
if (req.url.pathname === '/favicon.ico' && !replies['/favicon.ico']) {
3540
return res.writeHead(204).end();
3641
}
3742
server.requests.push(req.body ? [pathname, req.body, req.headers] : [pathname, req.headers]);
38-
let reply = replies[pathname] || defaultReply;
3943
return reply ? await reply(req, res) : next();
4044
});
4145

packages/core/test/snapshot.test.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,9 +1535,14 @@ describe('Snapshot', () => {
15351535

15361536
await percy.idle();
15371537

1538-
// srcdoc HTML attribute serialization: pre-Chrome-128 left `<` `>` literal
1539-
// inside attribute values; Chrome >=128 entity-escapes them per HTML5 spec.
1540-
// Accept either form.
1538+
// The iframe's srcdoc attribute holds serialized child-frame HTML. Pre
1539+
// Chrome-128 (old headless) left `<` and `>` literal inside the attribute
1540+
// value, so the captured DOM looked like:
1541+
// <iframe ... srcdoc="<!DOCTYPE html>...<p>Foo</p>...">
1542+
// Chrome >=128 (new headless) entity-escapes special chars per HTML5
1543+
// spec, so the same DOM looks like:
1544+
// <iframe ... srcdoc="&lt;!DOCTYPE html&gt;...&lt;p&gt;Foo&lt;/p&gt;...">
1545+
// Both render identically in any browser. Accept either serialization.
15411546
expect(Buffer.from((
15421547
api.requests['/builds/123/resources'][0]
15431548
.body.data.attributes['base64-content']

0 commit comments

Comments
 (0)