Synchronize dispatch_actions in WebDriver (#36932)

Implement missing synchronization in `dispatch_actions` of `WebDriver`.
https://w3c.github.io/webdriver/#dispatching-actions

> The user agent event loop has spun enough times to process the DOM
events generated by the last invocation of the >[dispatch tick
actions](https://w3c.github.io/webdriver/#dfn-dispatch-tick-actions)
steps.

- Add a way for `ScriptThread` to notify `WebDriver` about the
completion of input commands.
- Add a `webdriver_id` field for `InputEvent`. `ScriptThread` uses it to
distinguish WebDriver events and sends notification.

Tests:
`./mach test-wpt --product servodriver -r
tests\wpt\tests\webdriver\tests\classic\element_click\events.py` pass if
`hit_testing` pass. Check
[issue](https://github.com/servo/servo/issues/36676#issuecomment-2882917136)

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
batu_hoang 2025-05-21 19:03:04 +08:00 committed by GitHub
parent 3a527d784b
commit f52fa9b672
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 471 additions and 140 deletions

View file

@ -10,6 +10,7 @@ use base::id::{PipelineId, WebViewId};
use crossbeam_channel::Sender;
use embedder_traits::{
AnimationState, EventLoopWaker, MouseButton, MouseButtonAction, TouchEventResult,
WebDriverMessageId,
};
use euclid::Rect;
use ipc_channel::ipc::IpcSender;
@ -101,9 +102,16 @@ pub enum CompositorMsg {
/// The load of a page has completed
LoadComplete(WebViewId),
/// WebDriver mouse button event
WebDriverMouseButtonEvent(WebViewId, MouseButtonAction, MouseButton, f32, f32),
WebDriverMouseButtonEvent(
WebViewId,
MouseButtonAction,
MouseButton,
f32,
f32,
WebDriverMessageId,
),
/// WebDriver mouse move event
WebDriverMouseMoveEvent(WebViewId, f32, f32),
WebDriverMouseMoveEvent(WebViewId, f32, f32, WebDriverMessageId),
// Webdriver wheel scroll event
WebDriverWheelScrollEvent(WebViewId, f32, f32, f64, f64),

View file

@ -17,6 +17,7 @@ use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, Worke
use embedder_traits::{
AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError,
JavaScriptEvaluationId, MediaSessionEvent, TouchEventResult, ViewportDetails,
WebDriverMessageId,
};
use euclid::default::Size2D as UntypedSize2D;
use http::{HeaderMap, Method};
@ -652,6 +653,8 @@ pub enum ScriptToConstellationMessage {
JavaScriptEvaluationId,
Result<JSValue, JavaScriptEvaluationError>,
),
/// Notify the completion of a webdriver command.
WebDriverInputComplete(WebDriverMessageId),
}
impl fmt::Debug for ScriptToConstellationMessage {

View file

@ -8,6 +8,8 @@ use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use webrender_api::units::DevicePoint;
use crate::WebDriverMessageId;
/// An input event that is sent from the embedder to Servo.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum InputEvent {
@ -42,6 +44,38 @@ impl InputEvent {
InputEvent::Wheel(event) => Some(event.point),
}
}
pub fn webdriver_message_id(&self) -> Option<WebDriverMessageId> {
match self {
InputEvent::EditingAction(..) => None,
InputEvent::Gamepad(..) => None,
InputEvent::Ime(..) => None,
InputEvent::Keyboard(..) => None,
InputEvent::MouseButton(event) => event.webdriver_id,
InputEvent::MouseMove(event) => event.webdriver_id,
InputEvent::Touch(..) => None,
InputEvent::Wheel(..) => None,
}
}
pub fn with_webdriver_message_id(self, webdriver_id: Option<WebDriverMessageId>) -> Self {
match self {
InputEvent::EditingAction(..) => {},
InputEvent::Gamepad(..) => {},
InputEvent::Ime(..) => {},
InputEvent::Keyboard(..) => {},
InputEvent::MouseButton(mut event) => {
event.webdriver_id = webdriver_id;
},
InputEvent::MouseMove(mut event) => {
event.webdriver_id = webdriver_id;
},
InputEvent::Touch(..) => {},
InputEvent::Wheel(..) => {},
};
self
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
@ -49,6 +83,18 @@ pub struct MouseButtonEvent {
pub action: MouseButtonAction,
pub button: MouseButton,
pub point: DevicePoint,
webdriver_id: Option<WebDriverMessageId>,
}
impl MouseButtonEvent {
pub fn new(action: MouseButtonAction, button: MouseButton, point: DevicePoint) -> Self {
Self {
action,
button,
point,
webdriver_id: None,
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
@ -102,6 +148,16 @@ pub enum MouseButtonAction {
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct MouseMoveEvent {
pub point: DevicePoint,
webdriver_id: Option<WebDriverMessageId>,
}
impl MouseMoveEvent {
pub fn new(point: DevicePoint) -> Self {
Self {
point,
webdriver_id: None,
}
}
}
/// The type of input represented by a multi-touch event.

View file

@ -24,6 +24,9 @@ use webrender_api::units::DeviceIntSize;
use crate::{MouseButton, MouseButtonAction};
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct WebDriverMessageId(pub usize);
/// Messages to the constellation originating from the WebDriver server.
#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverCommandMsg {
@ -41,9 +44,23 @@ pub enum WebDriverCommandMsg {
/// Act as if keys were pressed or release in the browsing context with the given ID.
KeyboardAction(BrowsingContextId, KeyboardEvent),
/// Act as if the mouse was clicked in the browsing context with the given ID.
MouseButtonAction(WebViewId, MouseButtonAction, MouseButton, f32, f32),
MouseButtonAction(
WebViewId,
MouseButtonAction,
MouseButton,
f32,
f32,
WebDriverMessageId,
IpcSender<WebDriverCommandResponse>,
),
/// Act as if the mouse was moved in the browsing context with the given ID.
MouseMoveAction(WebViewId, f32, f32),
MouseMoveAction(
WebViewId,
f32,
f32,
WebDriverMessageId,
IpcSender<WebDriverCommandResponse>,
),
/// Act as if the mouse wheel is scrolled in the browsing context given the given ID.
WheelScrollAction(WebViewId, f32, f32, f64, f64),
/// Set the window size.
@ -188,6 +205,11 @@ pub enum WebDriverFrameId {
Parent,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct WebDriverCommandResponse {
pub id: WebDriverMessageId,
}
#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverLoadStatus {
Complete,