Skip to content

cdp: serve /json target list and create the target for flat-protocol clients - #3352

Open
arrufat wants to merge 7 commits into
mainfrom
cdp-json-targets
Open

cdp: serve /json target list and create the target for flat-protocol clients#3352
arrufat wants to merge 7 commits into
mainfrom
cdp-json-targets

Conversation

@arrufat

@arrufat arrufat commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

What

  • /json and /json/list return a one-element target list instead of []; new /json/new (GET+PUT), /json/activate/* and /json/close/* endpoints. The listed target is synthetic (built at server init, before any browser context exists); /json/new?url and /json/close are accepted no-ops that log not_implemented.
  • Flat-protocol clients (chrome-remote-interface, pychrome) list /json, connect to the webSocketDebuggerUrl, and send Page.*/Runtime.* directly with no Target handshake. Previously every such command failed with BrowserContextNotLoaded. Page-scoped handlers now obtain their context through Command.requireBrowserContext, which for a sessionless command creates the browser context, target and attached session — reusing Target.createTarget's setup, extracted into setupTarget. Attaching is required because page events (including the Page.navigate response) are only emitted to an attached session.
  • Teardown methods (X.disable, Page.close) on a connection with no context succeed as no-ops via Command.teardownBrowserContext instead of creating a page. Handlers that resolve ids minted by an earlier command keep returning BrowserContextNotLoaded, since a fresh context can never satisfy them.
  • Routing in Handshake.handleHttpRequest ignores the query string and one trailing slash (as Chrome does) and doesn't wait on a PUT body. HTTP responses share Handshake.buildResponse.

Why

Target-listing drivers could not connect at all. Session-based drivers (Puppeteer, Playwright, chromedp) are unaffected: their commands either carry a session id or go through the Target/Browser domains, neither of which creates a page implicitly. Handlers that never needed a context (Page.enable, Page.getFrameTree) are unchanged.

Three tests that asserted BrowserContextNotLoaded for sessionless page commands were updated to the new behavior.

@arrufat
arrufat marked this pull request as draft August 30, 2026 21:36
@arrufat

arrufat commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Converting this to a draft because it is really a "do we want this surface?" question rather than a bug fix, and I would rather have your call on it. @karlseguin @krichprollsch what do you think?

Demand: honestly, none on record. No issue mentions pychrome or chrome-remote-interface, and nobody has reported the BrowserContextNotLoaded failure a flat client hits today. The beneficiaries would be low-level CDP clients that list /json and drive a page directly without the Target handshake — chrome-remote-interface, pychrome, curl+websocat scripting. Real, but niche next to Puppeteer/Playwright.

Cost: the /json listing is trivial. The implicit target creation is not free: a new code path in dispatchCommand that creates browser state on its own, an isPageDomain list to maintain, sessionless Page.enable now creating the target instead of returning the startup stub, and three tests rewritten.

Both or neither: the two halves are coupled. Shipping only the /json list would be worse than today — clients would go from failing fast ("no targets") to connecting and then failing with an opaque BrowserContextNotLoaded.

If no, I will close this and leave /json returning [] as the honest fail-fast; the branch stays around for when a real user shows up. If yes, it is ready for review as is.

@karlseguin

Copy link
Copy Markdown
Collaborator

At the very least, I'd like to merge #3209 and #3329 first. They've been a bit of a pain to keep rebased.

…clients

/json and /json/list returned an empty array and /json/new did not exist,
so target-listing clients (chrome-remote-interface, pychrome, Selenium 4)
found nothing to connect to. Return a one-element list; add /json/new
(GET+PUT), /json/activate/* and /json/close/*.

Listing alone is not enough: those clients connect to the advertised
webSocketDebuggerUrl and drive Page/Runtime directly, without the
Target.createBrowserContext/createTarget/attachToTarget handshake, and
every command failed with BrowserContextNotLoaded. The dispatcher now
runs Target.createTarget's setup (extracted into setupTarget) plus an
attach on the first sessionless command to a page-scoped domain.
Browser-level domains never trigger it, so session-based drivers keep
creating their context explicitly.
# Conflicts:
#	src/cdp/Connection.zig
#	src/server/Server.zig
#	src/server/cdp/CDP.zig
#	src/server/cdp/domains/network.zig
#	src/server/cdp/testing.zig
@arrufat
arrufat marked this pull request as ready for review September 1, 2026 05:36
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