mirror of
https://github.com/servo/servo.git
synced 2025-07-22 14:53:49 +01:00
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:
parent
3a527d784b
commit
f52fa9b672
13 changed files with 471 additions and 140 deletions
|
@ -10,11 +10,12 @@ mod actions;
|
|||
mod capabilities;
|
||||
|
||||
use std::borrow::ToOwned;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::io::Cursor;
|
||||
use std::net::{SocketAddr, SocketAddrV4};
|
||||
use std::time::Duration;
|
||||
use std::{env, fmt, mem, process, thread};
|
||||
use std::{env, fmt, process, thread};
|
||||
|
||||
use base::id::{BrowsingContextId, WebViewId};
|
||||
use base64::Engine;
|
||||
|
@ -23,8 +24,9 @@ use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection};
|
|||
use cookie::{CookieBuilder, Expiration};
|
||||
use crossbeam_channel::{Receiver, Sender, after, select, unbounded};
|
||||
use embedder_traits::{
|
||||
MouseButton, WebDriverCommandMsg, WebDriverCookieError, WebDriverFrameId, WebDriverJSError,
|
||||
WebDriverJSResult, WebDriverJSValue, WebDriverLoadStatus, WebDriverScriptCommand,
|
||||
MouseButton, WebDriverCommandMsg, WebDriverCommandResponse, WebDriverCookieError,
|
||||
WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue, WebDriverLoadStatus,
|
||||
WebDriverMessageId, WebDriverScriptCommand,
|
||||
};
|
||||
use euclid::{Rect, Size2D};
|
||||
use http::method::Method;
|
||||
|
@ -43,8 +45,8 @@ use servo_url::ServoUrl;
|
|||
use style_traits::CSSPixel;
|
||||
use uuid::Uuid;
|
||||
use webdriver::actions::{
|
||||
ActionSequence, PointerDownAction, PointerMoveAction, PointerOrigin, PointerType,
|
||||
PointerUpAction,
|
||||
ActionSequence, ActionsType, PointerAction, PointerActionItem, PointerActionParameters,
|
||||
PointerDownAction, PointerMoveAction, PointerOrigin, PointerType, PointerUpAction,
|
||||
};
|
||||
use webdriver::capabilities::CapabilitiesMatching;
|
||||
use webdriver::command::{
|
||||
|
@ -64,6 +66,26 @@ use webdriver::server::{self, Session, SessionTeardownKind, WebDriverHandler};
|
|||
|
||||
use crate::actions::{InputSourceState, PointerInputState};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WebDriverMessageIdGenerator {
|
||||
counter: Cell<usize>,
|
||||
}
|
||||
|
||||
impl WebDriverMessageIdGenerator {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
counter: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a unique ID.
|
||||
pub fn next(&self) -> WebDriverMessageId {
|
||||
let id = self.counter.get();
|
||||
self.counter.set(id + 1);
|
||||
WebDriverMessageId(id)
|
||||
}
|
||||
}
|
||||
|
||||
fn extension_routes() -> Vec<(Method, &'static str, ServoExtensionRoute)> {
|
||||
vec![
|
||||
(
|
||||
|
@ -145,10 +167,11 @@ pub struct WebDriverSession {
|
|||
|
||||
unhandled_prompt_behavior: String,
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-input-state-table
|
||||
input_state_table: HashMap<String, InputSourceState>,
|
||||
// https://w3c.github.io/webdriver/#dfn-input-cancel-list
|
||||
input_cancel_list: Vec<ActionSequence>,
|
||||
/// <https://w3c.github.io/webdriver/#dfn-input-state-map>
|
||||
input_state_table: RefCell<HashMap<String, InputSourceState>>,
|
||||
|
||||
/// <https://w3c.github.io/webdriver/#dfn-input-cancel-list>
|
||||
input_cancel_list: RefCell<Vec<ActionSequence>>,
|
||||
}
|
||||
|
||||
impl WebDriverSession {
|
||||
|
@ -172,8 +195,8 @@ impl WebDriverSession {
|
|||
strict_file_interactability: false,
|
||||
unhandled_prompt_behavior: "dismiss and notify".to_string(),
|
||||
|
||||
input_state_table: HashMap::new(),
|
||||
input_cancel_list: Vec::new(),
|
||||
input_state_table: RefCell::new(HashMap::new()),
|
||||
input_cancel_list: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,8 +210,22 @@ struct Handler {
|
|||
/// for it to send us a load-status. Messages sent on it
|
||||
/// will be forwarded to the load_status_receiver.
|
||||
load_status_sender: IpcSender<WebDriverLoadStatus>,
|
||||
|
||||
session: Option<WebDriverSession>,
|
||||
|
||||
/// The channel for sending Webdriver messages to the constellation.
|
||||
constellation_chan: Sender<EmbedderToConstellationMessage>,
|
||||
|
||||
/// The IPC sender which we can clone and pass along to the constellation
|
||||
constellation_sender: IpcSender<WebDriverCommandResponse>,
|
||||
|
||||
/// Receiver notification from the constellation when a command is completed
|
||||
constellation_receiver: IpcReceiver<WebDriverCommandResponse>,
|
||||
|
||||
id_generator: WebDriverMessageIdGenerator,
|
||||
|
||||
current_action_id: Cell<Option<WebDriverMessageId>>,
|
||||
|
||||
resize_timeout: u32,
|
||||
}
|
||||
|
||||
|
@ -409,11 +446,18 @@ impl Handler {
|
|||
let (load_status_sender, receiver) = ipc::channel().unwrap();
|
||||
let (sender, load_status_receiver) = unbounded();
|
||||
ROUTER.route_ipc_receiver_to_crossbeam_sender(receiver, sender);
|
||||
|
||||
let (constellation_sender, constellation_receiver) = ipc::channel().unwrap();
|
||||
|
||||
Handler {
|
||||
load_status_sender,
|
||||
load_status_receiver,
|
||||
session: None,
|
||||
constellation_chan,
|
||||
constellation_sender,
|
||||
constellation_receiver,
|
||||
id_generator: WebDriverMessageIdGenerator::new(),
|
||||
current_action_id: Cell::new(None),
|
||||
resize_timeout: 500,
|
||||
}
|
||||
}
|
||||
|
@ -1445,18 +1489,13 @@ impl Handler {
|
|||
}
|
||||
|
||||
fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
|
||||
let input_cancel_list = {
|
||||
let session = self.session_mut()?;
|
||||
session.input_cancel_list.reverse();
|
||||
mem::take(&mut session.input_cancel_list)
|
||||
};
|
||||
|
||||
let input_cancel_list = self.session().unwrap().input_cancel_list.borrow();
|
||||
if let Err(error) = self.dispatch_actions(&input_cancel_list) {
|
||||
return Err(WebDriverError::new(error, ""));
|
||||
}
|
||||
|
||||
let session = self.session_mut()?;
|
||||
session.input_state_table = HashMap::new();
|
||||
let session = self.session()?;
|
||||
session.input_state_table.borrow_mut().clear();
|
||||
|
||||
Ok(WebDriverResponse::Void)
|
||||
}
|
||||
|
@ -1614,7 +1653,7 @@ impl Handler {
|
|||
let id = Uuid::new_v4().to_string();
|
||||
|
||||
// Step 8.1
|
||||
self.session_mut()?.input_state_table.insert(
|
||||
self.session_mut()?.input_state_table.borrow_mut().insert(
|
||||
id.clone(),
|
||||
InputSourceState::Pointer(PointerInputState::new(&PointerType::Mouse)),
|
||||
);
|
||||
|
@ -1645,19 +1684,31 @@ impl Handler {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
// Step 8.16 Dispatch a list of actions with input state,
|
||||
// actions, session's current browsing context, and actions options.
|
||||
if let Err(error) =
|
||||
self.dispatch_pointermove_action(&id, &pointer_move_action, 0)
|
||||
{
|
||||
return Err(WebDriverError::new(error, ""));
|
||||
}
|
||||
let action_sequence = ActionSequence {
|
||||
id: id.clone(),
|
||||
actions: ActionsType::Pointer {
|
||||
parameters: PointerActionParameters {
|
||||
pointer_type: PointerType::Mouse,
|
||||
},
|
||||
actions: vec![
|
||||
PointerActionItem::Pointer(PointerAction::Move(
|
||||
pointer_move_action,
|
||||
)),
|
||||
PointerActionItem::Pointer(PointerAction::Down(
|
||||
pointer_down_action,
|
||||
)),
|
||||
PointerActionItem::Pointer(PointerAction::Up(pointer_up_action)),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
self.dispatch_pointerdown_action(&id, &pointer_down_action);
|
||||
self.dispatch_pointerup_action(&id, &pointer_up_action);
|
||||
let _ = self.dispatch_actions(&[action_sequence]);
|
||||
|
||||
// Step 8.17 Remove an input source with input state and input id.
|
||||
self.session_mut()?.input_state_table.remove(&id);
|
||||
self.session_mut()?
|
||||
.input_state_table
|
||||
.borrow_mut()
|
||||
.remove(&id);
|
||||
|
||||
// Step 13
|
||||
Ok(WebDriverResponse::Void)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue