fix: include buttons bitmask in drag mouseMoved events (#1087)
* fix: include buttons bitmask in drag mouseMoved events The drag handler was omitting the `buttons` field from every `mouseMoved` event dispatched during the move phase. Without it the browser sees `event.buttons === 0`, meaning no button is held, so `dragstart`/`dragover`/`drop` never fire and the drop target never receives the element. Fix: - Add `"buttons": 1` (left-button mask) to each `mouseMoved` sent while the button is held. - Add `"buttons": 1` to `mousePressed` and `"buttons": 0` to `mouseReleased`, consistent with how `dispatch_click` handles the same fields in interaction.rs. - Correct the parity-test fixture for `drag`, which was supplying a `selector` key instead of the `source` key that `handle_drag` reads. - Add an e2e test (`e2e_drag_action_sends_buttons_during_move`) that drives the high-level `drag` action against the existing `html5_drag_probe` fixture and asserts that `mousemove` events carry `buttons == 1` and that `dragstart` fires on the source element. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * style: fix rustfmt formatting in e2e drag test --------- Co-authored-by: wangjingjing <wangjingjing.99@bytedance.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
wangjingjing
ctate
parent
6c93480d0d
commit
1f4b6b9d7a
@@ -5448,12 +5448,13 @@ async fn handle_drag(cmd: &Value, state: &mut DaemonState) -> Result<Value, Stri
|
|||||||
mgr.client
|
mgr.client
|
||||||
.send_command(
|
.send_command(
|
||||||
"Input.dispatchMouseEvent",
|
"Input.dispatchMouseEvent",
|
||||||
Some(json!({ "type": "mousePressed", "x": sx, "y": sy, "button": "left", "clickCount": 1 })),
|
Some(json!({ "type": "mousePressed", "x": sx, "y": sy, "button": "left", "buttons": 1, "clickCount": 1 })),
|
||||||
Some(&source_session_id),
|
Some(&source_session_id),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Move in steps to target
|
// Move in steps to target, keeping the left button held (buttons: 1) so
|
||||||
|
// that the browser sees a drag rather than a plain pointer move.
|
||||||
let steps = 10;
|
let steps = 10;
|
||||||
for i in 1..=steps {
|
for i in 1..=steps {
|
||||||
let cx = sx + (tx - sx) * (i as f64) / (steps as f64);
|
let cx = sx + (tx - sx) * (i as f64) / (steps as f64);
|
||||||
@@ -5461,7 +5462,7 @@ async fn handle_drag(cmd: &Value, state: &mut DaemonState) -> Result<Value, Stri
|
|||||||
mgr.client
|
mgr.client
|
||||||
.send_command(
|
.send_command(
|
||||||
"Input.dispatchMouseEvent",
|
"Input.dispatchMouseEvent",
|
||||||
Some(json!({ "type": "mouseMoved", "x": cx, "y": cy })),
|
Some(json!({ "type": "mouseMoved", "x": cx, "y": cy, "button": "left", "buttons": 1 })),
|
||||||
Some(&target_session_id),
|
Some(&target_session_id),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -5472,7 +5473,7 @@ async fn handle_drag(cmd: &Value, state: &mut DaemonState) -> Result<Value, Stri
|
|||||||
mgr.client
|
mgr.client
|
||||||
.send_command(
|
.send_command(
|
||||||
"Input.dispatchMouseEvent",
|
"Input.dispatchMouseEvent",
|
||||||
Some(json!({ "type": "mouseReleased", "x": tx, "y": ty, "button": "left", "clickCount": 1 })),
|
Some(json!({ "type": "mouseReleased", "x": tx, "y": ty, "button": "left", "buttons": 0, "clickCount": 1 })),
|
||||||
Some(&target_session_id),
|
Some(&target_session_id),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -1615,6 +1615,73 @@ async fn e2e_mouse_drag_reaches_pointer_capture_target() {
|
|||||||
assert_success(&resp);
|
assert_success(&resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[ignore]
|
||||||
|
async fn e2e_drag_action_sends_buttons_during_move() {
|
||||||
|
let mut state = DaemonState::new();
|
||||||
|
|
||||||
|
let resp = execute_command(
|
||||||
|
&json!({ "id": "1", "action": "launch", "headless": true }),
|
||||||
|
&mut state,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_success(&resp);
|
||||||
|
|
||||||
|
let resp = execute_command(
|
||||||
|
&json!({
|
||||||
|
"id": "2",
|
||||||
|
"action": "navigate",
|
||||||
|
"url": native_test_fixture_url("html5_drag_probe")
|
||||||
|
}),
|
||||||
|
&mut state,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_success(&resp);
|
||||||
|
|
||||||
|
let resp = execute_command(
|
||||||
|
&json!({
|
||||||
|
"id": "3",
|
||||||
|
"action": "drag",
|
||||||
|
"source": "#source",
|
||||||
|
"target": "#dest"
|
||||||
|
}),
|
||||||
|
&mut state,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_success(&resp);
|
||||||
|
assert_eq!(get_data(&resp)["dragged"].as_bool(), Some(true));
|
||||||
|
|
||||||
|
let resp = execute_command(
|
||||||
|
&json!({ "id": "4", "action": "evaluate", "script": "window.__html5DragProbe" }),
|
||||||
|
&mut state,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_success(&resp);
|
||||||
|
|
||||||
|
let probe = &get_data(&resp)["result"];
|
||||||
|
let events = probe["events"]
|
||||||
|
.as_array()
|
||||||
|
.expect("html5 drag probe should expose events");
|
||||||
|
|
||||||
|
// The mousemove events emitted while the button is held should carry
|
||||||
|
// buttons == 1 so the browser recognises the gesture as a drag.
|
||||||
|
assert!(
|
||||||
|
events
|
||||||
|
.iter()
|
||||||
|
.any(|event| { event["type"] == "mousemove" && event["buttons"].as_i64() == Some(1) }),
|
||||||
|
"Expected at least one mousemove with buttons == 1 during drag"
|
||||||
|
);
|
||||||
|
|
||||||
|
// dragstart must fire on the source element.
|
||||||
|
assert!(
|
||||||
|
events.iter().any(|event| event["type"] == "dragstart"),
|
||||||
|
"Expected dragstart to fire on the source element"
|
||||||
|
);
|
||||||
|
|
||||||
|
let resp = execute_command(&json!({ "id": "99", "action": "close" }), &mut state).await;
|
||||||
|
assert_success(&resp);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// State save/load, state management
|
// State save/load, state management
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ fn minimal_command(action: &str, id: &str) -> Value {
|
|||||||
obj.insert("script".to_string(), json!("h => h"));
|
obj.insert("script".to_string(), json!("h => h"));
|
||||||
}
|
}
|
||||||
"drag" => {
|
"drag" => {
|
||||||
obj.insert("selector".to_string(), json!("body"));
|
obj.insert("source".to_string(), json!("body"));
|
||||||
obj.insert("target".to_string(), json!("body"));
|
obj.insert("target".to_string(), json!("body"));
|
||||||
}
|
}
|
||||||
"swipe" => {
|
"swipe" => {
|
||||||
|
|||||||
Reference in New Issue
Block a user