Page.createIsolatedWorld and do_navigate both hardcoded context id 100 for every isolated world emission. After the second navigation Playwright expected a fresh incrementing id (101, 102) while obscura kept re-using 100. Runtime.evaluate then failed with 'Cannot find context with specified id: 101' because the id was never registered in valid_context_ids and the client's bookkeeping had diverged from the server's. Added next_isolated_context_id counter on CdpContext (starts at 100) and a next_isolated_context() helper that claims an id, registers it in valid_context_ids, and increments. Both call sites now use it.
This commit is contained in:
1 parent
191c5ac7a2
commit
682431f766
2 files changed
+46
-15
No files matched your search
@@ -35,6 +35,14 @@ pub struct CdpContext {
|
|||||||
// "Cannot find context with specified id" CDP error and unblocking the
|
// "Cannot find context with specified id" CDP error and unblocking the
|
||||||
// Playwright locator path described in issue #51.
|
// Playwright locator path described in issue #51.
|
||||||
pub valid_context_ids: HashSet<i64>,
|
pub valid_context_ids: HashSet<i64>,
|
||||||
|
// Monotonic counter for isolated-world execution context ids. Issue #192:
|
||||||
|
// both `Page.createIsolatedWorld` and `do_navigate` used to hardcode id
|
||||||
|
// 100, so re-creating a context (e.g. Playwright opening a second page or
|
||||||
|
// re-navigating) silently emitted the same id twice and the client's
|
||||||
|
// bookkeeping diverged from the server's. Each fresh isolated context now
|
||||||
|
// claims and increments from this counter so the ids real Chrome would
|
||||||
|
// emit (incrementing, never reused) are mirrored.
|
||||||
|
pub next_isolated_context_id: i64,
|
||||||
pub fetch_intercept: FetchInterceptState,
|
pub fetch_intercept: FetchInterceptState,
|
||||||
pub intercept_tx: Option<tokio::sync::mpsc::UnboundedSender<InterceptedRequest>>,
|
pub intercept_tx: Option<tokio::sync::mpsc::UnboundedSender<InterceptedRequest>>,
|
||||||
}
|
}
|
||||||
@@ -124,9 +132,19 @@ impl CdpContext {
|
|||||||
intercept_tx: None,
|
intercept_tx: None,
|
||||||
isolated_worlds: Vec::new(),
|
isolated_worlds: Vec::new(),
|
||||||
valid_context_ids,
|
valid_context_ids,
|
||||||
|
next_isolated_context_id: 100,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Claim the next isolated-world execution context id and register it as
|
||||||
|
/// valid for `Runtime.evaluate`/`callFunctionOn`. Issue #192.
|
||||||
|
pub fn next_isolated_context(&mut self) -> i64 {
|
||||||
|
let id = self.next_isolated_context_id;
|
||||||
|
self.next_isolated_context_id += 1;
|
||||||
|
self.valid_context_ids.insert(id);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_page(&mut self) -> String {
|
pub fn create_page(&mut self) -> String {
|
||||||
self.page_counter += 1;
|
self.page_counter += 1;
|
||||||
let page_id = format!("page-{}", self.page_counter);
|
let page_id = format!("page-{}", self.page_counter);
|
||||||
|
|||||||
@@ -121,11 +121,18 @@ async fn do_navigate(
|
|||||||
} else {
|
} else {
|
||||||
ctx.isolated_worlds.clone()
|
ctx.isolated_worlds.clone()
|
||||||
};
|
};
|
||||||
for (idx, world_name) in world_names.iter().enumerate() {
|
// Issue #192: real Chrome emits a fresh, monotonically increasing
|
||||||
let world_ctx_id = 100 + idx as u32;
|
// executionContextId on every re-creation. Reusing ids 100, 101, …
|
||||||
|
// across navigations made Playwright's client-side counter and our
|
||||||
|
// server-side `valid_context_ids` diverge after the first nav, so
|
||||||
|
// Runtime.evaluate failed with "Cannot find context with specified
|
||||||
|
// id: 101". Each post-nav isolated world now claims a fresh id from
|
||||||
|
// the same counter that backs Page.createIsolatedWorld.
|
||||||
|
for world_name in &world_names {
|
||||||
|
let world_ctx_id = ctx.next_isolated_context();
|
||||||
phase1.push(CdpEvent {
|
phase1.push(CdpEvent {
|
||||||
method: "Runtime.executionContextCreated".into(),
|
method: "Runtime.executionContextCreated".into(),
|
||||||
params: json!({"context": {"id": world_ctx_id, "origin": page_url, "name": world_name, "uniqueId": format!("ctx-isolated-nav-{}-{}", page_id, idx), "auxData": {"isDefault": false, "type": "isolated", "frameId": frame_id}}}),
|
params: json!({"context": {"id": world_ctx_id, "origin": page_url, "name": world_name, "uniqueId": format!("ctx-isolated-nav-{}-{}", page_id, world_ctx_id), "auxData": {"isDefault": false, "type": "isolated", "frameId": frame_id}}}),
|
||||||
session_id: es.clone(),
|
session_id: es.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -236,14 +243,17 @@ pub async fn handle(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
"createIsolatedWorld" => {
|
"createIsolatedWorld" => {
|
||||||
let page = ctx.get_session_page(session_id).ok_or("No page for session")?;
|
let (frame_id_param, world_name, page_url, page_id) = {
|
||||||
let frame_id_param = params.get("frameId").and_then(|v| v.as_str())
|
let page = ctx.get_session_page(session_id).ok_or("No page for session")?;
|
||||||
.unwrap_or(&page.frame_id).to_string();
|
(
|
||||||
let world_name = params.get("worldName").and_then(|v| v.as_str())
|
params.get("frameId").and_then(|v| v.as_str())
|
||||||
.unwrap_or("").to_string();
|
.unwrap_or(&page.frame_id).to_string(),
|
||||||
let page_url = page.url_string();
|
params.get("worldName").and_then(|v| v.as_str())
|
||||||
let page_id = page.id.clone();
|
.unwrap_or("").to_string(),
|
||||||
let context_id: i64 = 100;
|
page.url_string(),
|
||||||
|
page.id.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
// Track this world so Page.navigate can re-emit a context for it
|
// Track this world so Page.navigate can re-emit a context for it
|
||||||
// post-navigation. Without this, Playwright (and Puppeteer)
|
// post-navigation. Without this, Playwright (and Puppeteer)
|
||||||
// hang in any operation that uses the utility world — including
|
// hang in any operation that uses the utility world — including
|
||||||
@@ -252,9 +262,12 @@ pub async fn handle(
|
|||||||
if !world_name.is_empty() && !ctx.isolated_worlds.contains(&world_name) {
|
if !world_name.is_empty() && !ctx.isolated_worlds.contains(&world_name) {
|
||||||
ctx.isolated_worlds.push(world_name.clone());
|
ctx.isolated_worlds.push(world_name.clone());
|
||||||
}
|
}
|
||||||
// Register the isolated world's id so Runtime.evaluate /
|
// Issue #192: every isolated world emission gets a fresh id from
|
||||||
// Runtime.callFunctionOn accept it as a valid contextId (#51).
|
// the monotonic counter and is registered as a valid contextId.
|
||||||
ctx.valid_context_ids.insert(context_id);
|
// Reusing id 100 across navigations made Playwright's bookkeeping
|
||||||
|
// diverge (it expected 101 on the second nav) and Runtime.evaluate
|
||||||
|
// failed with "Cannot find context with specified id: 101".
|
||||||
|
let context_id = ctx.next_isolated_context();
|
||||||
|
|
||||||
ctx.pending_events.push(CdpEvent {
|
ctx.pending_events.push(CdpEvent {
|
||||||
method: "Runtime.executionContextCreated".to_string(),
|
method: "Runtime.executionContextCreated".to_string(),
|
||||||
@@ -263,7 +276,7 @@ pub async fn handle(
|
|||||||
"id": context_id,
|
"id": context_id,
|
||||||
"origin": page_url,
|
"origin": page_url,
|
||||||
"name": world_name,
|
"name": world_name,
|
||||||
"uniqueId": format!("ctx-isolated-{}", page_id),
|
"uniqueId": format!("ctx-isolated-{}-{}", page_id, context_id),
|
||||||
"auxData": {
|
"auxData": {
|
||||||
"isDefault": false,
|
"isDefault": false,
|
||||||
"type": "isolated",
|
"type": "isolated",
|
||||||
|
|||||||
Reference in new issue
Block a user