[WebDriver: Release Action] Fix panic by work around buggy spec (#37624)

1. Narrow the lifetime of `input_cancel_list` to avoid Runtime multiple
BorrowMut error.
2. Work around the buggy spec by removing matching item in
`input_cancel_list` when dispatch `keyUp` and `mouseUp`. See
https://github.com/servo/servo/issues/37579#issuecomment-2990762713

Testing: All WebDriver WPT test.
Fixes: #37579

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
This commit is contained in:
Euclid Ye 2025-06-23 16:33:18 +08:00 committed by GitHub
parent 795367c751
commit 5e252d0ef6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 39 additions and 24 deletions

View file

@ -327,6 +327,22 @@ impl Handler {
fn dispatch_keyup_action(&self, source_id: &str, action: &KeyUpAction) {
let session = self.session().unwrap();
// Remove the last matching keyUp from `[input_cancel_list]` due to bugs in spec
// See https://github.com/w3c/webdriver/issues/1905 &&
// https://github.com/servo/servo/issues/37579#issuecomment-2990762713
{
let mut input_cancel_list = session.input_cancel_list.borrow_mut();
if let Some(pos) = input_cancel_list.iter().rposition(|(id, item)| {
id == source_id &&
matches!(item,
ActionItem::Key(KeyActionItem::Key(KeyAction::Up(KeyUpAction { value })))
if *value == action.value )
}) {
info!("dispatch_keyup_action: removing last matching keyup from input_cancel_list");
input_cancel_list.remove(pos);
}
}
let raw_key = action.value.chars().next().unwrap();
let mut input_state_table = session.input_state_table.borrow_mut();
let key_input_state = match input_state_table.get_mut(source_id).unwrap() {
@ -396,6 +412,24 @@ impl Handler {
}
pointer_input_state.pressed.remove(&action.button);
// Remove matching pointerUp(must be unique) from `[input_cancel_list]` due to bugs in spec
// See https://github.com/w3c/webdriver/issues/1905 &&
// https://github.com/servo/servo/issues/37579#issuecomment-2990762713
{
let mut input_cancel_list = session.input_cancel_list.borrow_mut();
if let Some(pos) = input_cancel_list.iter().position(|(id, item)| {
id == source_id &&
matches!(item, ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Up(
PointerUpAction { button, .. },
))) if *button == action.button )
}) {
info!(
"dispatch_pointerup_action: removing matching pointerup from input_cancel_list"
);
input_cancel_list.remove(pos);
}
}
self.increment_num_pending_actions();
let msg_id = self.current_action_id.get();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(

View file

@ -64,7 +64,7 @@ use webdriver::response::{
};
use webdriver::server::{self, Session, SessionTeardownKind, WebDriverHandler};
use crate::actions::{ActionItem, ActionsByTick, InputSourceState, PointerInputState};
use crate::actions::{ActionItem, InputSourceState, PointerInputState};
#[derive(Default)]
pub struct WebDriverMessageIdGenerator {
@ -1758,8 +1758,10 @@ impl Handler {
// only one command can run at a time, so this will never block."
// 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
let undo_actions = session
.input_cancel_list
.borrow_mut()
.drain(..)
.rev()
.map(|(id, action_item)| HashMap::from([(id, action_item)]))