Skip to content

feat(google): rewrite /search language params pre-request via a DNR redirect rule#249

Open
rejifald wants to merge 2 commits into
mainfrom
feat/google-dnr-prerequest-rewrite
Open

feat(google): rewrite /search language params pre-request via a DNR redirect rule#249
rejifald wants to merge 2 commits into
mainfrom
feat/google-dnr-prerequest-rewrite

Conversation

@rejifald

Copy link
Copy Markdown
Owner

What

Two commits:

1. feat(google): rewrite /search language params pre-request via a DNR redirect rule (ef5aab0)

The content-script searchParams rewrite acts only after the raw entry request has been served: one wasted page load per omnibox/homepage search, and the raw request — carrying Chrome's opaque gs_lcrp token — seeds the server-side pinned candidate set that can intersect with the lr filter down to zero organic results (the empty-SERP class #208 mitigates after the fact).

A dynamic declarativeNetRequest redirect rule (id 2) now rewrites the URL inside the network stack before the request leaves the browser:

  • queryTransform adds hl + the pipe-joined lr and removes the strip/scrub token tiers plus the enumerated gs_* family (removeParams has no prefix matching).
  • Condition mirrors the site-rule gates: GOOGLE_REQUEST_DOMAINS (new @movar/host-match export derived from the isGoogleHost suffix set), an RE2 regexFilter for /search with a real q, main_frame only, allowlist/snooze excluded; regenerated on every settings/pause/snooze change alongside the Accept-Language rule.
  • The transform is idempotent and browsers skip same-URL redirects, so already-clean SERP-internal navigations are matched-but-skipped no-ops (Firefox documents the skip; Chrome is pinned by e2e).
  • Safari is compile-time excluded (open queryTransform bugs) and keeps the content-script fallback, which stays everywhere as layer 1 — a dnr.test.ts parity test pins both layers to byte-identical URLs. The feat(google): empty-SERP detect-and-retry fallback (finding #1) #208 empty-SERP detect-and-retry stays as layer 2 for pins seeded by vectors the rule never sees.

DNR rewrites log no CorrectionEvent (same as the Accept-Language rule), so the live google fixture drops its correction expectation. Store privacy forms, deployment checklist, and docs (google-search-url-params.md prevention-layer section, pitfalls.md #4) updated. Changeset included (@movar/extension minor).

2. test(e2e): retry serviceWorker fixture seeding across MV3 SW restarts (269b8d7)

Under machine load the serviceWorker fixture flaked in two ways: waitForEvent('serviceworker') inherited the 5s actionTimeout (too tight for a cold SW boot on a starved host), and the storage clear+seed evaluate ran once against whatever SW instance came first — Chrome can tear down and restart a just-installed MV3 service worker, leaving that evaluate to reject with "Target … closed" or hang until the 30s test timeout.

The fixture now waits with an explicit 15s budget, prefers the newest listed instance, excludes instances already seen to die, and runs the (idempotent) clear+seed raced against a 5s deadline with up to 3 attempts against a re-acquired live worker. Downstream fixtures get the worker that actually served the seed; the per-test storage-isolation contract is unchanged.

Verified: popup.behavior + options.behavior at --repeat-each=10 --workers=6, 150/150 twice under 12-concurrent-Chromium load (the flake's home spec, toolbar-icon.behavior, lives on main and isn't on this branch).

Reviewer notes

  • Branch is ~30 commits behind main — expect the usual merge-train rebase + metrics regen before landing.
  • Metrics regen rides in 269b8d7 (LOC badge +102; health score 81/B unchanged).
  • ~40 locally generated darwin visual baselines are deliberately not included — Playwright first-run byproducts; Linux baselines are authoritative on CI, and only the onboarding darwin set is tracked by convention.

🤖 Generated with Claude Code

rejifald and others added 2 commits July 8, 2026 15:30
…edirect rule

The content-script searchParams rewrite acts only after the raw entry
request has been served: one wasted page load per entry search, and the
raw request — carrying Chrome's opaque gs_lcrp omnibox token — seeds the
server-side pinned candidate set that can zero out results under the lr
filter (the case #208's detect-and-retry mitigates after the fact).

A dynamic declarativeNetRequest redirect rule (id 2) now rewrites the URL
inside the network stack before the request leaves the browser:
queryTransform adds hl + the pipe-joined lr and removes the strip/scrub
token tiers (plus the enumerated gs_* family — removeParams has no prefix
matching). Condition mirrors the site-rule gates: GOOGLE_REQUEST_DOMAINS
(new @movar/host-match export derived from the isGoogleHost suffix set),
an RE2 regexFilter for /search + a real q param, main_frame only,
allowlist/snooze excluded; regenerated on every settings/pause/snooze
change alongside the Accept-Language rule. The transform is idempotent
and browsers skip same-URL redirects, so already-clean SERP-internal
navigations are matched-but-skipped no-ops (Firefox documents the skip;
Chrome is pinned by e2e). Safari is compile-time excluded (open
queryTransform bugs) and keeps the content-script fallback, which stays
everywhere as layer 1 — a dnr.test.ts parity test pins both layers to
byte-identical URLs. The #208 empty-results retry stays as layer 2 for
pins seeded by vectors the rule never sees.

DNR rewrites log no CorrectionEvent (like the Accept-Language rule), so
the live google fixture drops its correction expectation; store forms,
deployment checklist, and docs (google-search-url-params.md "prevention
layer" section, pitfalls.md entry #4) updated to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Under machine load the serviceWorker fixture flaked in two ways: the
`serviceworker` waitForEvent inherited the 5s actionTimeout — too tight
for a cold SW boot on a starved host — and the storage clear+seed
evaluate ran once against whatever instance Playwright returned first.
Chrome tears down and restarts a just-installed MV3 service worker at
will, so that evaluate could reject with "Target ... closed" or hang
with no error until the 30s test timeout killed the context.

waitForServiceWorker now waits with an explicit 15s budget, prefers the
newest listed instance, and skips instances already seen to die. The
clear+seed is one idempotent evaluate raced against a 5s deadline and
retried (3 attempts) against a re-acquired live worker; explicit closed
errors blacklist the dead instance, deadline timeouts retry without
blacklisting so a healthy-but-starved worker gets another chance. The
fixture hands downstream fixtures the worker that actually served the
seed. Per-test storage-isolation contract is unchanged.

Verified: popup.behavior + options.behavior at --repeat-each=10
--workers=6, 150/150 twice (the flake's home spec, toolbar-icon
.behavior, lives on main and isn't on this branch). Metrics regen
rides along (LOC badge +102).

Co-Authored-By: Claude Opus 4.8 <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.

1 participant