fix(cdp): five Playwright drop-in compatibility patches
Brings Obscura's CDP server in line with what Playwright's `chromium.connectOverCDP()` expects. Each fix is small and surgical; together they make `connect → newContext → newPage → goto → page.title()` work end-to-end against `https://example.com`. 1. **server.rs — emit pending events before command response.** Chromium CDP semantics require side-effect events (e.g. `Target.targetCreated` + `Target.attachedToTarget` from `Target.createTarget`) to arrive *before* the originating command's response. Playwright's wire reader assumes this ordering — it resolves the response promise on the response and then synchronously looks up the freshly-created session. Reversing the order makes that lookup miss and the page hang at `newPage()`. 2. **domains/browser.rs — accept Browser.setWindowBounds as a no-op.** Playwright calls `Browser.setWindowBounds` during teardown. Returning "Unknown Browser method" causes Playwright to treat the target as broken and tear it down mid-test, surfacing as "Frame has been detached" on the next `page.title()`. 3. **obscura-browser/page.rs — frame_id == target_id for main frame.** Was `format!("{id}.1", id)`. Playwright's frame manager indexes frames by frame id and looks up the main frame by target id, so the `.1` suffix made every main-frame lookup miss. 4. **domains/page.rs + dispatch.rs — track isolated worlds and re-emit them post-navigation.** Playwright registers a utility world via `Page.createIsolatedWorld` and expects `Runtime.executionContextCreated` for each registered world after every navigation. Without re-emission, the utility-world handle goes stale post-`goto` and `page.title()` hangs waiting for the world's execution context. Falls back to the legacy `__puppeteer_utility_world__24.40.0` name when no worlds have been registered, preserving prior Puppeteer support. 5. **obscura-js/runtime.rs — strip trailing semicolons before wrapping.** Playwright's utility-script expression is an IIFE ending in `})();`. Both `wrap_expression` (CDP `Runtime.evaluate`) and `evaluate_for_cdp` (the `meta_code` IIFE) embed the user expression inside `({EXPR})` / `return ({EXPR});`. With the trailing `;` left in, this produces `({...;})` — a SyntaxError. Parse errors are not catchable, so the surrounding try/catch never fires and the call silently returns `undefined`, surfacing as `page.title() === ""`. Verified end-to-end against `https://example.com`: [probe] connect OK [probe] newContext OK [probe] newPage OK [probe] goto OK [probe] title = Example Domain
This commit is contained in:
1 parent
b00a1f20e7
commit
9e3e1e4ef7
6 files changed
+77
-10
No files matched your search
@@ -17,6 +17,14 @@ pub struct CdpContext {
|
||||
page_counter: u32,
|
||||
pub preload_scripts: Vec<(String, String)>, // (identifier, source)
|
||||
pub preload_counter: u32,
|
||||
// World names registered via Page.createIsolatedWorld. After every
|
||||
// navigation Obscura clears execution contexts (via
|
||||
// Runtime.executionContextsCleared) and must re-emit a
|
||||
// Runtime.executionContextCreated for each registered world, otherwise
|
||||
// Playwright/Puppeteer hang waiting for their utility world to come
|
||||
// back. Stored as plain Strings (not by-page) — for now we only model
|
||||
// a single page in CdpContext anyway.
|
||||
pub isolated_worlds: Vec<String>,
|
||||
pub fetch_intercept: FetchInterceptState,
|
||||
pub intercept_tx: Option<tokio::sync::mpsc::UnboundedSender<InterceptedRequest>>,
|
||||
}
|
||||
@@ -38,6 +46,7 @@ impl CdpContext {
|
||||
preload_counter: 0,
|
||||
fetch_intercept: FetchInterceptState::new(),
|
||||
intercept_tx: None,
|
||||
isolated_worlds: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user