fix(cdp): read and validate contextId in Runtime.evaluate / callFunctionOn (#51) (#149)

Fixes #51. `Runtime.evaluate` and `Runtime.callFunctionOn` previously
ignored the `contextId` / `executionContextId` parameter entirely, so
clients targeting an isolated world (e.g. Playwright's locator path,
which calls `Page.createIsolatedWorld` → contextId 100 → evaluates the
UtilityScript against that id) silently fell through to the default
context, breaking the contract advertised via
Runtime.executionContextCreated.

Track the set of advertised execution context ids on CdpContext
(pre-seeded with 1 and 2 — the default-frame contexts emitted by
Runtime.enable and post-navigation) and extend it from
Page.createIsolatedWorld. Both handlers now reject unknown ids with
`Cannot find context with specified id: <id>` — matching the wording
real Chrome returns — and log accepted ids at debug for observability.

Obscura still routes every JS call against the page's single V8 isolate,
so this PR is spec-compliance scaffolding rather than full multi-world
execution. Follow-up work to physically isolate worlds can build on the
same valid_context_ids registry.
This commit is contained in:
Ben Younes authored and GitHub committed 2026-05-14 09:15:19 +00:00
1 parent f3c783f6f5
commit 2ff0f8fe1d
3 files changed
+173 -2

No files matched your search

+4 -1
View File
@@ -177,7 +177,7 @@ pub async fn handle(
.unwrap_or("").to_string();
let page_url = page.url_string();
let page_id = page.id.clone();
let context_id = 100;
let context_id: i64 = 100;
// Track this world so Page.navigate can re-emit a context for it
// post-navigation. Without this, Playwright (and Puppeteer)
// hang in any operation that uses the utility world — including
@@ -186,6 +186,9 @@ pub async fn handle(
if !world_name.is_empty() && !ctx.isolated_worlds.contains(&world_name) {
ctx.isolated_worlds.push(world_name.clone());
}
// Register the isolated world's id so Runtime.evaluate /
// Runtime.callFunctionOn accept it as a valid contextId (#51).
ctx.valid_context_ids.insert(context_id);
ctx.pending_events.push(CdpEvent {
method: "Runtime.executionContextCreated".to_string(),