fix(cdp): route navigation on exact method, not substring (#372)
The cdp_processor decided whether to take the spawn-and-defer navigation path
with `text.contains("Page.navigate")`, which also matches
`Page.navigateToHistoryEntry`. goBack / goForward (both Puppeteer and
Playwright drive Page.navigateToHistoryEntry) were misrouted into the raw
navigate path, which reads an absent `url` param and fails with
"Invalid URL: relative URL without a base", so history navigation never
reached its real handler. Any frame merely embedding the literal
"Page.navigate" (e.g. a Runtime.evaluate expression) was misrouted the same
way.
Match on the parsed CDP method instead, so only an exact Page.navigate takes
the navigation path.
Fixes #363.
This commit is contained in:
1 parent
4bc69c5209
commit
daa0d62fbd
1 file changed
+44
-1
@@ -432,7 +432,7 @@ async fn cdp_processor(
|
||||
// messages via the `process_with_interception` select loop;
|
||||
// unrelated requests get deferred only briefly and are drained
|
||||
// as soon as the nav settles.
|
||||
let is_navigation = cdp_msg.text.contains("Page.navigate");
|
||||
let is_navigation = is_navigate_method(&cdp_msg.text);
|
||||
|
||||
if is_navigation {
|
||||
process_with_interception(
|
||||
@@ -457,6 +457,18 @@ async fn cdp_processor(
|
||||
ctx.default_context.save_cookies();
|
||||
}
|
||||
|
||||
// Whether a raw CDP frame is exactly a `Page.navigate` call, and so should take
|
||||
// the spawn-and-defer navigation path. Matching on the parsed method rather than
|
||||
// a `contains("Page.navigate")` substring avoids catching
|
||||
// `Page.navigateToHistoryEntry` (goBack / goForward), which has no `url` param
|
||||
// and belongs to its own handler, or any other frame that merely embeds the
|
||||
// literal text (e.g. a `Runtime.evaluate` expression). See issue #363.
|
||||
fn is_navigate_method(text: &str) -> bool {
|
||||
serde_json::from_str::<CdpRequest>(text)
|
||||
.map(|req| req.method == "Page.navigate")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn handle_fetch_resolution(
|
||||
text: &str,
|
||||
_ctx: &mut CdpContext,
|
||||
@@ -994,3 +1006,34 @@ async fn handle_connection_ws(
|
||||
send_task.abort();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::is_navigate_method;
|
||||
|
||||
// Issue #363: only an exact Page.navigate may take the spawn-and-defer
|
||||
// navigation path. A substring match also caught Page.navigateToHistoryEntry
|
||||
// (goBack / goForward), which has no `url` param, so it was misrouted into
|
||||
// the raw-navigate path and failed with "Invalid URL" instead of reaching
|
||||
// its real handler.
|
||||
#[test]
|
||||
fn only_exact_page_navigate_routes_as_navigation() {
|
||||
assert!(is_navigate_method(
|
||||
r#"{"id":1,"method":"Page.navigate","params":{"url":"https://example.com"}}"#
|
||||
));
|
||||
assert!(!is_navigate_method(
|
||||
r#"{"id":2,"method":"Page.navigateToHistoryEntry","params":{"entryId":0}}"#
|
||||
));
|
||||
}
|
||||
|
||||
// A Runtime.evaluate whose expression merely contains the literal
|
||||
// "Page.navigate" must not be misrouted, and malformed input is not a
|
||||
// navigation.
|
||||
#[test]
|
||||
fn unrelated_methods_do_not_route_as_navigation() {
|
||||
assert!(!is_navigate_method(
|
||||
r#"{"id":3,"method":"Runtime.evaluate","params":{"expression":"'Page.navigate'"}}"#
|
||||
));
|
||||
assert!(!is_navigate_method("not json"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user