Implement webdriver release action (#37484)

- Implement webdriver release action.
- Improve `Input Cancel List`.

Testing: `./mach test-wpt -r --product servodriver
./tests/wpt/tests/webdriver/tests/classic/release_actions/sequence.py`

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
This commit is contained in:
batu_hoang 2025-06-19 15:03:38 +08:00 committed by GitHub
parent 212ce933e5
commit 49be5ca05a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 57 additions and 23 deletions

View file

@ -31,7 +31,7 @@ static WHEELSCROLL_INTERVAL: u64 = 17;
// In the spec, `action item` refers to a plain JSON object. // In the spec, `action item` refers to a plain JSON object.
// However, we use the name ActionItem here // However, we use the name ActionItem here
// to be consistent with type names from webdriver crate. // to be consistent with type names from webdriver crate.
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub(crate) enum ActionItem { pub(crate) enum ActionItem {
Null(NullActionItem), Null(NullActionItem),
Key(KeyActionItem), Key(KeyActionItem),
@ -222,7 +222,6 @@ impl Handler {
}, },
ActionItem::Key(KeyActionItem::Key(KeyAction::Down(keydown_action))) => { ActionItem::Key(KeyActionItem::Key(KeyAction::Down(keydown_action))) => {
self.dispatch_keydown_action(input_id, keydown_action); self.dispatch_keydown_action(input_id, keydown_action);
// Step 9. If subtype is "keyDown", append a copy of action // Step 9. If subtype is "keyDown", append a copy of action
// object with the subtype property changed to "keyUp" to // object with the subtype property changed to "keyUp" to
// input state's input cancel list. // input state's input cancel list.
@ -230,11 +229,12 @@ impl Handler {
.unwrap() .unwrap()
.input_cancel_list .input_cancel_list
.borrow_mut() .borrow_mut()
.push(ActionItem::Key(KeyActionItem::Key(KeyAction::Up( .push((
KeyUpAction { input_id.clone(),
ActionItem::Key(KeyActionItem::Key(KeyAction::Up(KeyUpAction {
value: keydown_action.value.clone(), value: keydown_action.value.clone(),
}, }))),
)))); ));
}, },
ActionItem::Key(KeyActionItem::Key(KeyAction::Up(keyup_action))) => { ActionItem::Key(KeyActionItem::Key(KeyAction::Up(keyup_action))) => {
self.dispatch_keyup_action(input_id, keyup_action); self.dispatch_keyup_action(input_id, keyup_action);
@ -246,18 +246,22 @@ impl Handler {
pointer_down_action, pointer_down_action,
))) => { ))) => {
self.dispatch_pointerdown_action(input_id, pointer_down_action); self.dispatch_pointerdown_action(input_id, pointer_down_action);
// Step 10. If subtype is "pointerDown", append a copy of action // Step 10. If subtype is "pointerDown", append a copy of action
// object with the subtype property changed to "pointerUp" to // object with the subtype property changed to "pointerUp" to
// input state's input cancel list. // input state's input cancel list.
self.session().unwrap().input_cancel_list.borrow_mut().push( self.session()
ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Up( .unwrap()
PointerUpAction { .input_cancel_list
button: pointer_down_action.button, .borrow_mut()
..Default::default() .push((
}, input_id.clone(),
))), ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Up(
); PointerUpAction {
button: pointer_down_action.button,
..Default::default()
},
))),
));
}, },
ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Move( ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Move(
pointer_move_action, pointer_move_action,

View file

@ -64,7 +64,7 @@ use webdriver::response::{
}; };
use webdriver::server::{self, Session, SessionTeardownKind, WebDriverHandler}; use webdriver::server::{self, Session, SessionTeardownKind, WebDriverHandler};
use crate::actions::{ActionItem, InputSourceState, PointerInputState}; use crate::actions::{ActionItem, ActionsByTick, InputSourceState, PointerInputState};
#[derive(Default)] #[derive(Default)]
pub struct WebDriverMessageIdGenerator { pub struct WebDriverMessageIdGenerator {
@ -143,11 +143,19 @@ pub fn start_server(port: u16, constellation_chan: Sender<EmbedderToConstellatio
} }
/// Represents the current WebDriver session and holds relevant session state. /// Represents the current WebDriver session and holds relevant session state.
/// Currently, only 1 webview is supported per session.
/// So only there is only 1 InputState.
pub struct WebDriverSession { pub struct WebDriverSession {
/// <https://www.w3.org/TR/webdriver2/#dfn-session-id>
id: Uuid, id: Uuid,
browsing_context_id: BrowsingContextId,
/// <https://www.w3.org/TR/webdriver2/#dfn-current-top-level-browsing-context>
webview_id: WebViewId, webview_id: WebViewId,
/// <https://www.w3.org/TR/webdriver2/#dfn-current-browsing-context>
browsing_context_id: BrowsingContextId,
/// <https://www.w3.org/TR/webdriver2/#dfn-window-handles>
window_handles: HashMap<WebViewId, String>, window_handles: HashMap<WebViewId, String>,
/// Time to wait for injected scripts to run before interrupting them. A [`None`] value /// Time to wait for injected scripts to run before interrupting them. A [`None`] value
@ -171,7 +179,7 @@ pub struct WebDriverSession {
input_state_table: RefCell<HashMap<String, InputSourceState>>, input_state_table: RefCell<HashMap<String, InputSourceState>>,
/// <https://w3c.github.io/webdriver/#dfn-input-cancel-list> /// <https://w3c.github.io/webdriver/#dfn-input-cancel-list>
input_cancel_list: RefCell<Vec<ActionItem>>, input_cancel_list: RefCell<Vec<(String, ActionItem)>>,
} }
impl WebDriverSession { impl WebDriverSession {
@ -201,6 +209,8 @@ impl WebDriverSession {
} }
} }
/// The `Handler` for the WebDriver server.
/// Currently, only 1 session is supported.
struct Handler { struct Handler {
/// The threaded receiver on which we can block for a load-status. /// The threaded receiver on which we can block for a load-status.
/// It will receive messages sent on the load_status_sender, /// It will receive messages sent on the load_status_sender,
@ -1614,12 +1624,35 @@ impl Handler {
/// <https://w3c.github.io/webdriver/#dfn-release-actions> /// <https://w3c.github.io/webdriver/#dfn-release-actions>
fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> { fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
// TODO: The previous implementation of this function was different from the spec.
// Need to re-implement this to match the spec.
let session = self.session()?; let session = self.session()?;
// Step 1. If session's current browsing context is no longer open, // Step 1. If session's current browsing context is no longer open,
// return error with error code no such window. // return error with error code no such window.
self.verify_browsing_context_is_open(session.browsing_context_id)?; self.verify_browsing_context_is_open(session.browsing_context_id)?;
// TODO: Step 2. User prompts. No user prompt implemented yet.
// TODO: Step 3. Skip for now because webdriver holds only one session.
// TODO: Step 4. Actions options are not used yet.
// TODO: Step 5. Skip for now because webdriver holds only one session.
// Step 6. Let undo actions be input cancel list in reverse order.
let mut input_cancel_list = session.input_cancel_list.borrow_mut();
let undo_actions: ActionsByTick = input_cancel_list
.drain(..)
.rev()
.map(|action| {
let mut map = HashMap::new();
map.insert(action.0, action.1);
map
})
.collect();
// Step 7. Dispatch undo actions with current browsing context.
if let Err(err) = self.dispatch_actions(undo_actions, session.browsing_context_id) {
return Err(WebDriverError::new(err, "Failed to dispatch undo actions"));
}
// Step 8. Reset the input state of session's current top-level browsing context.
session.input_state_table.borrow_mut().clear(); session.input_state_table.borrow_mut().clear();
Ok(WebDriverResponse::Void) Ok(WebDriverResponse::Void)

View file

@ -1,3 +0,0 @@
[sequence.py]
[test_release_char_sequence_sends_keyup_events_in_reverse]
expected: FAIL