[webdriver] Add synchronization for wheel action (#37260)

Implement action synchronization for wheel event. Previously only done
for pointer here https://github.com/servo/servo/pull/36932.

Testing:
`tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py`

---------

Signed-off-by: PotatoCP <kenzieradityatirtarahardja18@gmail.com>
This commit is contained in:
Kenzie Raditya Tirtarahardja 2025-06-11 17:03:08 +08:00 committed by GitHub
parent 5114e24db1
commit 15eadb56a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 89 additions and 34 deletions

View file

@ -673,7 +673,7 @@ impl IOCompositor {
let point = dppx.transform_point(Point2D::new(x, y)); let point = dppx.transform_point(Point2D::new(x, y));
webview_renderer.dispatch_point_input_event( webview_renderer.dispatch_point_input_event(
InputEvent::MouseButton(MouseButtonEvent::new(action, button, point)) InputEvent::MouseButton(MouseButtonEvent::new(action, button, point))
.with_webdriver_message_id(Some(message_id)), .with_webdriver_message_id(message_id),
); );
}, },
@ -686,11 +686,18 @@ impl IOCompositor {
let point = dppx.transform_point(Point2D::new(x, y)); let point = dppx.transform_point(Point2D::new(x, y));
webview_renderer.dispatch_point_input_event( webview_renderer.dispatch_point_input_event(
InputEvent::MouseMove(MouseMoveEvent::new(point)) InputEvent::MouseMove(MouseMoveEvent::new(point))
.with_webdriver_message_id(Some(message_id)), .with_webdriver_message_id(message_id),
); );
}, },
CompositorMsg::WebDriverWheelScrollEvent(webview_id, x, y, delta_x, delta_y) => { CompositorMsg::WebDriverWheelScrollEvent(
webview_id,
x,
y,
delta_x,
delta_y,
message_id,
) => {
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
warn!("Handling input event for unknown webview: {webview_id}"); warn!("Handling input event for unknown webview: {webview_id}");
return; return;
@ -705,8 +712,10 @@ impl IOCompositor {
let point = dppx.transform_point(Point2D::new(x, y)); let point = dppx.transform_point(Point2D::new(x, y));
let scroll_delta = let scroll_delta =
dppx.transform_vector(Vector2D::new(delta_x as f32, delta_y as f32)); dppx.transform_vector(Vector2D::new(delta_x as f32, delta_y as f32));
webview_renderer webview_renderer.dispatch_point_input_event(
.dispatch_point_input_event(InputEvent::Wheel(WheelEvent { delta, point })); InputEvent::Wheel(WheelEvent::new(delta, point))
.with_webdriver_message_id(message_id),
);
webview_renderer.on_webdriver_wheel_action(scroll_delta, point); webview_renderer.on_webdriver_wheel_action(scroll_delta, point);
}, },

View file

@ -4899,10 +4899,20 @@ where
webview_id, x, y, msg_id, webview_id, x, y, msg_id,
)); ));
}, },
WebDriverCommandMsg::WheelScrollAction(webview, x, y, delta_x, delta_y) => { WebDriverCommandMsg::WheelScrollAction(
webview_id,
x,
y,
delta_x,
delta_y,
msg_id,
response_sender,
) => {
self.webdriver.input_command_response_sender = Some(response_sender);
self.compositor_proxy self.compositor_proxy
.send(CompositorMsg::WebDriverWheelScrollEvent( .send(CompositorMsg::WebDriverWheelScrollEvent(
webview, x, y, delta_x, delta_y, webview_id, x, y, delta_x, delta_y, msg_id,
)); ));
}, },
WebDriverCommandMsg::TakeScreenshot(webview_id, rect, response_sender) => { WebDriverCommandMsg::TakeScreenshot(webview_id, rect, response_sender) => {

View file

@ -111,12 +111,12 @@ pub enum CompositorMsg {
MouseButton, MouseButton,
f32, f32,
f32, f32,
WebDriverMessageId, Option<WebDriverMessageId>,
), ),
/// WebDriver mouse move event /// WebDriver mouse move event
WebDriverMouseMoveEvent(WebViewId, f32, f32, WebDriverMessageId), WebDriverMouseMoveEvent(WebViewId, f32, f32, Option<WebDriverMessageId>),
// Webdriver wheel scroll event // Webdriver wheel scroll event
WebDriverWheelScrollEvent(WebViewId, f32, f32, f64, f64), WebDriverWheelScrollEvent(WebViewId, f32, f32, f64, f64, Option<WebDriverMessageId>),
/// Inform WebRender of the existence of this pipeline. /// Inform WebRender of the existence of this pipeline.
SendInitialTransaction(WebRenderPipelineId), SendInitialTransaction(WebRenderPipelineId),

View file

@ -54,7 +54,7 @@ impl InputEvent {
InputEvent::MouseButton(event) => event.webdriver_id, InputEvent::MouseButton(event) => event.webdriver_id,
InputEvent::MouseMove(event) => event.webdriver_id, InputEvent::MouseMove(event) => event.webdriver_id,
InputEvent::Touch(..) => None, InputEvent::Touch(..) => None,
InputEvent::Wheel(..) => None, InputEvent::Wheel(event) => event.webdriver_id,
} }
} }
@ -71,7 +71,9 @@ impl InputEvent {
event.webdriver_id = webdriver_id; event.webdriver_id = webdriver_id;
}, },
InputEvent::Touch(..) => {}, InputEvent::Touch(..) => {},
InputEvent::Wheel(..) => {}, InputEvent::Wheel(ref mut event) => {
event.webdriver_id = webdriver_id;
},
}; };
self self
@ -275,6 +277,17 @@ pub struct WheelDelta {
pub struct WheelEvent { pub struct WheelEvent {
pub delta: WheelDelta, pub delta: WheelDelta,
pub point: DevicePoint, pub point: DevicePoint,
webdriver_id: Option<WebDriverMessageId>,
}
impl WheelEvent {
pub fn new(delta: WheelDelta, point: DevicePoint) -> Self {
WheelEvent {
delta,
point,
webdriver_id: None,
}
}
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]

View file

@ -50,7 +50,8 @@ pub enum WebDriverCommandMsg {
MouseButton, MouseButton,
f32, f32,
f32, f32,
WebDriverMessageId, // Should never be None.
Option<WebDriverMessageId>,
IpcSender<WebDriverCommandResponse>, IpcSender<WebDriverCommandResponse>,
), ),
/// Act as if the mouse was moved in the browsing context with the given ID. /// Act as if the mouse was moved in the browsing context with the given ID.
@ -58,11 +59,23 @@ pub enum WebDriverCommandMsg {
WebViewId, WebViewId,
f32, f32,
f32, f32,
WebDriverMessageId, // None if it's not the last `perform_pointer_move` since we only
// expect one response from constellation for each tick actions.
Option<WebDriverMessageId>,
IpcSender<WebDriverCommandResponse>, IpcSender<WebDriverCommandResponse>,
), ),
/// Act as if the mouse wheel is scrolled in the browsing context given the given ID. /// Act as if the mouse wheel is scrolled in the browsing context given the given ID.
WheelScrollAction(WebViewId, f32, f32, f64, f64), WheelScrollAction(
WebViewId,
f32,
f32,
f64,
f64,
// None if it's not the last `perform_wheel_scroll` since we only
// expect one response from constellation for each tick actions.
Option<WebDriverMessageId>,
IpcSender<WebDriverCommandResponse>,
),
/// Set the window size. /// Set the window size.
SetWindowSize(WebViewId, DeviceIntSize, IpcSender<Size2D<f32, CSSPixel>>), SetWindowSize(WebViewId, DeviceIntSize, IpcSender<Size2D<f32, CSSPixel>>),
/// Take a screenshot of the window. /// Take a screenshot of the window.

View file

@ -156,12 +156,16 @@ impl Handler {
&self, &self,
tick_actions: &TickActions, tick_actions: &TickActions,
) -> Result<(), ErrorStatus> { ) -> Result<(), ErrorStatus> {
// TODO: Add matches! for wheel and key actions // TODO: Add matches! for key actions
// after implmenting webdriver id for wheel and key events. // after implmenting webdriver id for key events.
let count_non_null_actions_in_tick = tick_actions let count_non_null_actions_in_tick = tick_actions
.iter() .iter()
.filter(|(_, action)| { .filter(|(_, action)| {
matches!(action, ActionItem::Pointer(PointerActionItem::Pointer(_))) matches!(
action,
ActionItem::Pointer(PointerActionItem::Pointer(_)) |
ActionItem::Wheel(WheelActionItem::Wheel(_))
)
}) })
.count(); .count();
@ -176,7 +180,7 @@ impl Handler {
let current_waiting_id = self let current_waiting_id = self
.current_action_id .current_action_id
.get() .get()
.expect("Current id should be set before dispat_actions_inner is called"); .expect("Current id should be set before dispatch_actions_inner is called");
if current_waiting_id != response.id { if current_waiting_id != response.id {
dbg!("Dispatch actions completed with wrong id in response"); dbg!("Dispatch actions completed with wrong id in response");
@ -345,7 +349,7 @@ impl Handler {
} }
pointer_input_state.pressed.insert(action.button); pointer_input_state.pressed.insert(action.button);
let msg_id = self.current_action_id.get().unwrap(); let msg_id = self.current_action_id.get();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction( let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
session.webview_id, session.webview_id,
MouseButtonAction::Down, MouseButtonAction::Down,
@ -375,7 +379,7 @@ impl Handler {
} }
pointer_input_state.pressed.remove(&action.button); pointer_input_state.pressed.remove(&action.button);
let msg_id = self.current_action_id.get().unwrap(); let msg_id = self.current_action_id.get();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction( let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
session.webview_id, session.webview_id,
MouseButtonAction::Up, MouseButtonAction::Up,
@ -496,9 +500,15 @@ impl Handler {
let current_y = pointer_input_state.y; let current_y = pointer_input_state.y;
// Step 7 // Step 7
if x != current_x || y != current_y { // Actually "last" should not be checked here based on spec.
// However, we need to send the webdriver id at the final perform.
if x != current_x || y != current_y || last {
// Step 7.2 // Step 7.2
let msg_id = self.current_action_id.get().unwrap(); let msg_id = if last {
self.current_action_id.get()
} else {
None
};
let cmd_msg = WebDriverCommandMsg::MouseMoveAction( let cmd_msg = WebDriverCommandMsg::MouseMoveAction(
session.webview_id, session.webview_id,
x as f32, x as f32,
@ -627,14 +637,23 @@ impl Handler {
}; };
// Step 5 // Step 5
if delta_x != 0 || delta_y != 0 { // Actually "last" should not be checked here based on spec.
// However, we need to send the webdriver id at the final perform.
if delta_x != 0 || delta_y != 0 || last {
// Perform implementation-specific action dispatch steps // Perform implementation-specific action dispatch steps
let msg_id = if last {
self.current_action_id.get()
} else {
None
};
let cmd_msg = WebDriverCommandMsg::WheelScrollAction( let cmd_msg = WebDriverCommandMsg::WheelScrollAction(
session.webview_id, session.webview_id,
x as f32, x as f32,
y as f32, y as f32,
delta_x as f64, delta_x as f64,
delta_y as f64, delta_y as f64,
msg_id,
self.constellation_sender.clone(),
); );
self.constellation_chan self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg)) .send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))

View file

@ -603,7 +603,7 @@ impl WindowPortsMethods for Window {
let phase = winit_phase_to_touch_event_type(phase); let phase = winit_phase_to_touch_event_type(phase);
// Send events // Send events
webview.notify_input_event(InputEvent::Wheel(WheelEvent { delta, point })); webview.notify_input_event(InputEvent::Wheel(WheelEvent::new(delta, point)));
webview.notify_scroll_event( webview.notify_scroll_event(
scroll_location, scroll_location,
self.webview_relative_mouse_point.get().to_i32(), self.webview_relative_mouse_point.get().to_i32(),

View file

@ -1,13 +1,4 @@
[wheel.py] [wheel.py]
[test_scroll_not_scrollable]
expected: FAIL
[test_scroll_scrollable_overflow]
expected: FAIL
[test_scroll_iframe]
expected: FAIL
[test_scroll_shadow_tree[outer-open\]] [test_scroll_shadow_tree[outer-open\]]
expected: FAIL expected: FAIL