From cd0fbc02c2ee70dfc58d23fb4f48edb94c2cdcac Mon Sep 17 00:00:00 2001 From: zhangyang-crazy-one <740884666@qq.com> Date: Mon, 22 Jun 2026 19:01:40 +0800 Subject: [PATCH] fix(cdp): pass clickCount to MouseEvent.detail + fix char handler event loop (#316) (#317) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 1: clickCount parameter was read with underscore prefix and silently discarded. MouseEvent constructor never received the detail field, so triple-click (clickCount:3) was always treated as a single click by the DOM — inputs never selected their full text. Fix: rename _click_count to click_count, pass it as detail in both mousedown and click MouseEvent constructors. Bug 2: page.type() intermittently dropped the last character because the char handler's quote-escaping order was wrong (single-quote before backslash), and the V8 event loop wasnt pumped after dispatching the input event. Fix: fix escaping order (backslash first, then single-quote), and add page.settle(50) after the char handler's evaluate to pump the event loop. --- crates/obscura-cdp/src/domains/input.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/obscura-cdp/src/domains/input.rs b/crates/obscura-cdp/src/domains/input.rs index e633d73..1e0010a 100644 --- a/crates/obscura-cdp/src/domains/input.rs +++ b/crates/obscura-cdp/src/domains/input.rs @@ -14,7 +14,7 @@ pub async fn handle( let x = params.get("x").and_then(|v| v.as_f64()).unwrap_or(0.0); let y = params.get("y").and_then(|v| v.as_f64()).unwrap_or(0.0); let _button = params.get("button").and_then(|v| v.as_str()).unwrap_or("left"); - let _click_count = params.get("clickCount").and_then(|v| v.as_u64()).unwrap_or(1); + let click_count = params.get("clickCount").and_then(|v| v.as_u64()).unwrap_or(1); if event_type == "mousePressed" { if let Some(page) = ctx.get_session_page_mut(session_id) { @@ -23,9 +23,9 @@ pub async fn handle( var target = (document.elementFromPoint && document.elementFromPoint({x},{y})) || globalThis.__obscura_click_target || document.activeElement || document.body;\ if (!target) return;\ globalThis.__obscura_click_target = target;\ - var evt = globalThis.__obscura_markTrusted(new MouseEvent('mousedown', {{bubbles:true,cancelable:true,clientX:{x},clientY:{y},button:0}}));\ + var evt = globalThis.__obscura_markTrusted(new MouseEvent('mousedown', {{bubbles:true,cancelable:true,clientX:{x},clientY:{y},button:0,detail:{click_count}}}));\ target.dispatchEvent(evt);\ - var click = globalThis.__obscura_markTrusted(new MouseEvent('click', {{bubbles:true,cancelable:true,clientX:{x},clientY:{y},button:0}}));\ + var click = globalThis.__obscura_markTrusted(new MouseEvent('click', {{bubbles:true,cancelable:true,clientX:{x},clientY:{y},button:0,detail:{click_count}}}));\ var cancelled = !target.dispatchEvent(click);\ if (!cancelled) {{\ var link = target.closest ? target.closest('a[href]') : null;\ @@ -51,7 +51,7 @@ pub async fn handle( }}\ }}\ }})()", - x = x, y = y, + x = x, y = y, click_count = click_count, ); page.evaluate(&code); page.process_pending_navigation().await.map_err(|e| e.to_string())?; @@ -155,6 +155,7 @@ pub async fn handle( } "char" => { if !text.is_empty() { + let escaped_text = text.replace('\\', "\\\\").replace('\'', "\\'"); let js = format!( "(function() {{\ var target = document.activeElement;\ @@ -163,9 +164,11 @@ pub async fn handle( target.dispatchEvent(globalThis.__obscura_markTrusted(new Event('input', {{bubbles:true}})));\ }}\ }})()", - text = text.replace('\'', "\\'").replace('\\', "\\\\"), + text = escaped_text, ); page.evaluate(&js); + // Pump event loop so Angular change detection picks up the input + page.settle(50).await; } } _ => {}