fix(timeout): cap defensive CDP guards so click can't hang multi-minute
Reported: a single `click @ref` could hang 5+ minutes, with multiple queued click invocations adding up to 7+ minutes — worst case 30s timeout × 3 CDP calls × N parallel processes: - verify_ref_identity (Accessibility.getPartialAXTree) → default 30s - resolveNode / getBoxModel → default 30s - wait_for_paint_settled (Runtime.evaluate awaitPromise) → default 30s The latter two are best-effort defenses added in fork.3-5 to fix SPA race / DOM-reuse bugs. They should never block a real click for 30s — the unguarded code path was always faster than the guarded path-that-hangs. - verify_ref_identity capped at 1s (skips check on timeout) - wait_for_paint_settled capped at 500ms (skips wait on timeout) Both skip-on-timeout intentionally: the worst case is the click behaves like fork.2 (race-prone but fast), which is strictly better than the user pkilling stuck processes. Also rewrites the misleading "Chrome 144+ chrome://inspect tip" in the auto-connect failure message — the toggle exposes target discovery only, not the /json/version HTTP API the auto-connect flow expects (verified by user: lsof shows :9222 listening but curl /json/version returns 404).
This commit is contained in:
@@ -1607,8 +1607,9 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
|
||||
To let agent-browser work with your existing Chrome (recommended):\n\
|
||||
{}\n\n\
|
||||
Or start a standalone browser with: agent-browser --launch open <url>\n\n\
|
||||
Tip: On Chrome 144+, you can enable CDP without restarting:\n\
|
||||
Open chrome://inspect/#remote-debugging and toggle it on.",
|
||||
Note: chrome://inspect/#remote-debugging only enables remote *target discovery* — \
|
||||
it does NOT expose the standard CDP HTTP API on /json/version. \
|
||||
A full restart with --remote-debugging-port=<port> is required.",
|
||||
chrome_relaunch_hint(),
|
||||
));
|
||||
}
|
||||
@@ -2135,8 +2136,9 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
|
||||
To let agent-browser work with your existing Chrome (recommended):\n\
|
||||
{}\n\n\
|
||||
Or start a standalone browser with: agent-browser --launch open <url>\n\n\
|
||||
Tip: On Chrome 144+, you can enable CDP without restarting:\n\
|
||||
Open chrome://inspect/#remote-debugging and toggle it on.",
|
||||
Note: chrome://inspect/#remote-debugging only enables remote *target discovery* — \
|
||||
it does NOT expose the standard CDP HTTP API on /json/version. \
|
||||
A full restart with --remote-debugging-port=<port> is required.",
|
||||
chrome_relaunch_hint(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -396,9 +396,23 @@ async fn verify_ref_identity(
|
||||
"backendNodeId": backend_node_id,
|
||||
"fetchRelatives": false,
|
||||
});
|
||||
let resp: Result<GetFullAXTreeResult, String> = client
|
||||
.send_command_typed("Accessibility.getPartialAXTree", ¶ms, Some(session_id))
|
||||
.await;
|
||||
// Tight 1s timeout: this is a defensive guard, not a critical path.
|
||||
// The default 30s CDP timeout was the dominant factor in the
|
||||
// "click hangs 5+ minutes" report — three CDP calls (verify +
|
||||
// resolveNode + paint-settle) at 30s each, multiplied by parallel
|
||||
// click invocations queueing on the daemon, totalled multi-minute
|
||||
// user-visible hangs. Cap our own helper so a stuck AX query
|
||||
// doesn't make `click` worse than the no-guard version was.
|
||||
let resp: Result<GetFullAXTreeResult, String> = match tokio::time::timeout(
|
||||
std::time::Duration::from_secs(1),
|
||||
client.send_command_typed("Accessibility.getPartialAXTree", ¶ms, Some(session_id)),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(r) => r,
|
||||
// Timeout: skip identity verification rather than block the click.
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
let Ok(tree) = resp else {
|
||||
// Node likely gone; let the box-model call fail and trigger fallback.
|
||||
return Ok(());
|
||||
|
||||
@@ -903,8 +903,15 @@ async fn wait_for_paint_settled(client: &CdpClient, session_id: &str) {
|
||||
requestAnimationFrame(() => \
|
||||
requestAnimationFrame(() => \
|
||||
queueMicrotask(() => resolve(true)))))";
|
||||
let _ = client
|
||||
.send_command_typed::<_, Value>(
|
||||
// Tight 500ms timeout. RAF normally fires at 16ms, two RAFs total ~33ms.
|
||||
// If the tab is hidden / throttled / page is doing something pathological
|
||||
// and RAF doesn't fire in 500ms, we'd rather return now than stall the
|
||||
// user's click. Without this cap, a stuck RAF inherited the default 30s
|
||||
// CDP timeout and was the main contributor to the "click hangs 5+ min"
|
||||
// user report.
|
||||
let _ = tokio::time::timeout(
|
||||
std::time::Duration::from_millis(500),
|
||||
client.send_command_typed::<_, Value>(
|
||||
"Runtime.evaluate",
|
||||
&EvaluateParams {
|
||||
expression: script.to_string(),
|
||||
@@ -912,6 +919,7 @@ async fn wait_for_paint_settled(client: &CdpClient, session_id: &str) {
|
||||
await_promise: Some(true),
|
||||
},
|
||||
Some(session_id),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user