Three behaviours a page could detect, changed in one breaking release:
- **Animations run on stock timing.** no-css-animations.patch finished every
finite animation at once by default, and any page could read it:
`el.animate(frames, 1000).effect.getComputedTiming().duration` was 0, and a
500ms transition reported 0. Measured on v152.0.4-beta.31. The speedup is
now an opt-in, `instantAnimations: True`, which raises a LeakWarning.
disableInstantAnimations is gone.
- **speak() on a spoofed voice works like a real voice.** It fired `error`
after 3ms unless voices:fakeCompletion was set, and then start and end in
the same tick. It now starts and ends after the text's duration at ~150
words per minute. Both voices:fakeCompletion keys are gone, and so is a
debug line printed to stderr on every call.
- **Keys removed:**
- battery:* and window.scrollMinX/Y: Firefox keeps getBattery() and
scrollMin* chrome-only, so no page could read them.
- window.scrollMaxX/Y, screen.pageXOffset/pageYOffset,
window.history.length and document.body.client*: each pinned a live value
to a constant, so scrolling, navigating or re-laying out never changed it.
fpgen.yml mapped pageYOffset, so about 15% of identities froze
window.scrollY at a non-zero value.
- The body keys' role as an undocumented alias for window.innerWidth/Height
in browser-init and in the launcher.
- MaskConfig::GetInt32Rect, which only the body keys used.
New guards, both of which fail on v152.0.4-beta.31:
tests/patches/animation-timing.py and tests/patches/spoofed-voice-speaks.py.
The decisions are recorded as animations-run-on-stock-timing and
spoofed-voices-speak. Every patch applies cleanly to a fresh tree, and the
result builds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes the new_page() hang from #666, and restores the pythonlib/ + settings/
halves of #637-#647 that were dropped when those PRs were consolidated into #666
(that PR only carried patches/ + additions/, so these never actually landed).
## new_page() hangs when window.outer* is spoofed (#666)
The outer-size hijack in browser-init.patch pinned the chrome documentElement to
the spoofed size. That caps .browserStack, which caps the content viewport, so
the content window can never reach the size Juggler asks for in
updateViewportSize() -- and awaitViewportDimensions awaits exact equality with
no timeout, so it deadlocks rather than erroring. The second new_page() hung
forever and took the context with it.
The pin was never load-bearing: GetOuterWidth/GetOuterHeight already consult
MaskConfig unconditionally (fingerprint-injection.patch), so window.outerWidth is
spoofed in C++ regardless of the real chrome window size. Resizing is enough.
Measured on the official v152.0.4-beta.26 build (headless):
config before after
none pass pass
inner pass pass
outer HANG pass
both HANG pass (iw:360 ih:740 ow:360 oh:800 -- exact)
This corrects the diagnosis in #666, which blamed the inner+outer combination and
the `!(outerWidth || outerHeight)` guard. outer* ALONE is sufficient to hang, and
dropping inner* does not help, so that guard is not the culprit.
Also fixed driver-side: Playwright's implicit 1280x720 viewport is what asks for
the impossible size, so the driver now defaults to no_viewport when the config
spoofs any window dimension. That fixes the hang on already-released builds
without a rebuild. An explicit viewport=/no_viewport= from the caller wins.
## WebRTC ICE prefs (#538)
#666 merged the C++ half of the WebRTC fix but not the prefs, so the shipped
build still has no_host=true and none of the proxy_only prefs.
proxy_only_if_behind_proxy is the pref that actually stops the real-IP leak: it
prevents a UDP STUN request routing around a TCP proxy. no_host=false keeps the
stock two-candidate shape, which obfuscate_host_addresses makes leak-free.
## Also restored from the consolidation
- fix(proxy): dom.security.https_first rewrote http:// before the launch-arg
proxy filter saw it, breaking CONNECT-only proxies (#638).
- fix(stealth): speech-voice spoofing + stop leaking host voices (#646).
- fix(stealth): clamp inner <= outer <= avail <= screen; BrowserForge can emit
impossible geometries that leak as tells (#647).
Refs: https://github.com/daijro/camoufox/pull/666
Refs: https://github.com/daijro/camoufox/issues/538