Skip to content

Playwright Firefox boots the engine: the pref its build lags, and the error that hid it - #103

Merged
lannbot merged 1 commit into
mainfrom
fix/playwright-firefox
Aug 23, 2026
Merged

Playwright Firefox boots the engine: the pref its build lags, and the error that hid it#103
lannbot merged 1 commit into
mainfrom
fix/playwright-firefox

Conversation

@lannbot

@lannbot lannbot commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Two bugs wearing one symptom (the solo page dying opaquely under headless Playwright Firefox):

  • The boot failure: Playwright 1.57.0's Firefox build ships JSPI disabled where release Firefox has it on — which is exactly why the owner's phone runs the app while automation died. Measured both ways (WebAssembly.Suspending undefined by default; function with javascript.options.wasm_js_promise_integration=true), and everything else the device store needs was measured present under Gecko: module SharedWorkers, OPFS via createWritable, Web Locks, non-extractable CryptoKeys through IndexedDB. The harness sets the pref — restoring a release default the automation build lags, not granting the page anything real users lack — and e2e-deps probes with the same pref so it can't certify a browser the suite would refuse.
  • The opacity: the banner was honest all along (the engine's needs-JSPI refusal reached it). The bare Error was Juggler rendering console.error(errorObject) as the word "Error" — lossless under CDP, total loss under Juggler. Boot logging now leads with the string, and the worker refuses early with a PlatformError (no-jspi) whose sentence names the missing capability ("this browser cannot run the engine: it has no WebAssembly JS Promise Integration…") instead of whichever import happened to suspend first. Probed in the SharedWorker, the realm that instantiates — JSPI availability is per-global.
  • The gate: firefox-smoke, the suite's first non-Chromium scenario — reads the five platform facts out of the page (a silently-lapsed pref cannot become a green lie), runs the T0 anchor path, survives a reload. Runner grows per-scenario engine selection with lazy launch, ordered last so earlier failures never pay for a second browser.
  • CI: the browser cache key gains -ff — the cache saves only on a miss, so the grown browser set under the old key would have re-downloaded Firefox ~90MB on every run forever.

Gates: e2e 24/24, invariants 9/9, devstore matrix ALL PASS.

… error that hid it

Two bugs wearing one symptom.

THE BOOT FAILURE: Playwright 1.57.0's Firefox build (144) ships
WebAssembly JS Promise Integration DISABLED where release Firefox has
it on — which is why the owner's phone runs the app while headless
Firefox died at device setup. Everything else the device store needs
was measured present under Gecko: module SharedWorkers, OPFS
(createWritable; no sync access handles, which the store never asks
for), Web Locks, non-extractable CryptoKeys through IndexedDB. The
harness now launches Firefox with
javascript.options.wasm_js_promise_integration=true — restoring a
release default the automation build lags, not granting the page
anything a real user's browser lacks — and e2e-deps probes with the
same pref so the recipe cannot certify a browser the suite would
refuse.

THE OPACITY: the banner was honest all along (the engine's own
needs-JSPI refusal reached it); the bare 'Error' was Juggler rendering
a console.error(errorObject) to an out-of-process observer as the word
Error and nothing else — a logging idiom that is lossless under CDP and
total loss under Juggler. Boot logging now leads with the string. And
the refusal got sharper: the worker probes WebAssembly.Suspending at
bringUpEngine — in the SharedWorker, the realm that instantiates, since
JSPI availability is per-global — and refuses with a PlatformError
(code no-jspi) whose sentence names the missing capability instead of
whichever import happened to suspend first.

THE GATE: firefox-smoke — the suite's first non-Chromium scenario. It
reads the five platform facts out of the page (so a pref that silently
stops applying cannot become a green lie), runs the T0 anchor path, and
survives a reload. The runner grew per-scenario engine selection with
lazy launch; Firefox is ordered last so earlier failures never pay for
a second browser. Deliberately not a second matrix: the visor
ceremonies are engine-independent.

The CI browser cache key gains a -ff suffix: the cache saves only on a
miss, so the grown browser set under the old key would have hit forever
with a chromium-only archive and re-downloaded Firefox every run.

Gates: e2e 24/24 (firefox-smoke 3 acts), invariants 9/9, devstore
matrix ALL PASS, deno checks clean.
@lannbot
lannbot merged commit 3910cc3 into main Aug 23, 2026
3 checks passed
@lannbot
lannbot deleted the fix/playwright-firefox branch August 23, 2026 19:00
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