#921·obscura

cdp: Input.dispatch* commands are not read while a page-triggered navigation (Enter submit, link click) loads; the next command times out

Author: michabbbCreated Sep 8, 2026Updated Sep 16, 2026

Before you start

  • I searched existing issues and this is not a duplicate (#890 is about layout opening its own HTTP requests; this is about the connection processor not reading commands while a JS/form-triggered navigation loads).
  • I tested against the latest release or main (reproduced with the v0.2.2 stealth release binary and with main at 727cc46).
  • This is not a security vulnerability.

obscura version or commit

v0.2.2 (a1e09de6) and main 727cc46

OS and architecture

Debian 13 x86_64

How are you using obscura?

CDP server with an own CDP client (Puppeteer/Playwright-style command sequences)

Affected area

CDP server / input handling / navigation

Build variant

Render-enabled with stealth (--features render,stealth); the code path is feature-independent

Stealth mode

On (--stealth), not relevant to the behaviour

Reproduction

When a CDP command makes the page schedule a document navigation, the navigation runs inline on the connection processor and nothing is read from the connection until the new document has loaded:

  • Input.dispatchKeyEvent keyDown Enter in a <form method=get> field: the command is answered, then process_cdp_message runs check_pending_navigation and dispatches the synthetic Page.navigate inline (crates/obscura-cdp/src/server.rs, process_cdp_message). The client's next command (the matching keyUp) is not read until the result page is fetched and loaded.
  • Input.dispatchMouseEvent mouseReleased on a link or submit button: the handler itself awaits page.process_pending_navigation() (crates/obscura-cdp/src/domains/input.rs), so the mouseReleased response is delayed by the whole navigation.

Chromium answers both input commands in milliseconds and dispatches the input to the outgoing document; the navigation loads while the client keeps sending commands. With Obscura a client that uses a per-command timeout (Puppeteer's default protocol timeout, or a scraper's per-action budget) times out whenever the target page needs longer than that timeout, although nothing is wrong with the page.

Minimal reproduction (offline, one local HTTP server that answers /slow after 1.5 s):

  1. Page.navigate to <form method=get action=/slow><input id=q name=q></form>, wait for load.
  2. Runtime.evaluate document.getElementById('q').focus().
  3. Input.dispatchKeyEvent {type: keyDown, key: Enter, code: Enter, windowsVirtualKeyCode: 13} — answered immediately.
  4. Input.dispatchKeyEvent {type: keyUp, ...} — answered only after ~1.5 s (after the /slow document loaded). With a 10 s server delay and an 8 s client timeout the keyUp times out.

Same with <a href=/slow>: mousePressed is immediate, mouseReleased returns after the navigation.

Expected behavior

Input commands sent while a page-scheduled navigation is in flight are answered at once (Chromium timing), the navigation loads in the background through the same deferral loop that Page.navigate already uses, and the frame events (Page.frameStartedNavigating, Page.frameNavigated, ...) are emitted from there.

Actual behavior

The connection is not read until the navigation finished; the next command waits for the full document fetch.

Logs / additional context

Runtime.evaluate / Runtime.callFunctionOn have the same inline process_pending_navigation in crates/obscura-cdp/src/domains/runtime.rs; a navigation started from an evaluated expression still blocks that command (out of scope for the first fix, mentioned for completeness).

I have a fix ready and will open a PR referencing this issue.