#807·camoufox

Humanized moves overrun their duration, and toggle prefs outlive their flag in persistent profiles

Author: coffeegrind123Created Sep 28, 2026Updated Sep 28, 2026

Describe the bug:

Two separate bugs, both found in a real deployment:

1. A humanized move takes its plan plus one ack round trip per point. sendTrajectoryAcked() (additions/juggler/input/MouseDispatch.js) sleeps each point's pause and only then dispatches and waits for the renderer's ack. The ack's latency is added to every point, so the move runs longer than the humanize cap it was scaled to. The busier the page, the bigger the overrun. On a page whose main thread is busy 19 ms in every 20, moves capped at humanize=0.5 took 0.54–0.71 s (medians 587–607 ms over three runs of eleven moves).

2. block_webrtc, block_images and disable_coop stay on after the flag is removed. The launchers write these prefs only when the flag is on: media.peerconnection.enabled=false, permissions.default.image=2 and browser.tabs.remote.useCrossOriginOpenerPolicy=false (pythonlib/camoufox/utils.py, typescript/src/utils.ts). A persistent context writes launch prefs into the profile's user.js, and Firefox saves them into prefs.js. A later launch without the flag writes nothing for that pref, so the saved value stays.

A persistent profile that was once launched with block_webrtc=True still had user_pref("media.peerconnection.enabled", false); in its prefs.js long after the flag was removed. BrowserScan reported "WebRTC: disabled", which stock Firefox never does.

Screenshots:

None.

To Reproduce:

1. Time a humanized move on a busy page:

python
from camoufox.sync_api import Camoufox

with Camoufox(humanize=0.5, headless=True) as browser:
    page = browser.new_page()
    page.set_content('<body style="margin:0;width:1400px;height:800px"></body>')
    page.evaluate("""
        setInterval(() => { const t = performance.now(); while (performance.now() - t < 19); }, 20);
        window.moves = []; addEventListener("mousemove", () => moves.push(performance.now()));
    """)
    page.mouse.move(20, 20)
    n = page.evaluate("moves.length")
    page.mouse.move(1000, 600)
    t = page.evaluate(f"moves.slice({n})")
    print(round(t[-1] - t[0]), "ms for a move capped at 500 ms")

2.

  1. Launch a persistent context once with block_webrtc=True.
  2. Launch it again without the flag.
  3. Evaluate typeof RTCPeerConnection, or read prefs.js in the profile.

Version:

main at e92caec.