[WebDriver] Add synchronization for key action (#37403)

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

---------

Signed-off-by: PotatoCP <kenzieradityatirtarahardja18@gmail.com>
This commit is contained in:
Kenzie Raditya Tirtarahardja 2025-06-18 15:26:44 +08:00 committed by GitHub
parent f97cdb4d12
commit cdc8b45965
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 171 additions and 118 deletions

View file

@ -130,9 +130,9 @@ use embedder_traits::user_content_manager::UserContentManager;
use embedder_traits::{
AnimationState, CompositorHitTestResult, Cursor, EmbedderMsg, EmbedderProxy,
FocusSequenceNumber, ImeEvent, InputEvent, JSValue, JavaScriptEvaluationError,
JavaScriptEvaluationId, MediaSessionActionType, MediaSessionEvent, MediaSessionPlaybackState,
MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverCommandMsg,
WebDriverCommandResponse, WebDriverLoadStatus,
JavaScriptEvaluationId, KeyboardEvent, MediaSessionActionType, MediaSessionEvent,
MediaSessionPlaybackState, MouseButton, MouseButtonAction, MouseButtonEvent, Theme,
ViewportDetails, WebDriverCommandMsg, WebDriverCommandResponse, WebDriverLoadStatus,
};
use euclid::Size2D;
use euclid::default::Size2D as UntypedSize2D;
@ -141,7 +141,7 @@ use ipc_channel::Error as IpcError;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use keyboard_types::webdriver::Event as WebDriverInputEvent;
use keyboard_types::{Key, KeyState, KeyboardEvent, Modifiers};
use keyboard_types::{Key, KeyState, Modifiers};
use log::{debug, error, info, trace, warn};
use media::WindowGLContext;
use net_traits::pub_domains::reg_host;
@ -3034,13 +3034,13 @@ where
}
fn update_active_keybord_modifiers(&mut self, event: &KeyboardEvent) {
self.active_keyboard_modifiers = event.modifiers;
self.active_keyboard_modifiers = event.event.modifiers;
// `KeyboardEvent::modifiers` contains the pre-existing modifiers before this key was
// either pressed or released, but `active_keyboard_modifiers` should track the subsequent
// state. If this event will update that state, we need to ensure that we are tracking what
// the event changes.
let modified_modifier = match event.key {
let modified_modifier = match event.event.key {
Key::Alt => Modifiers::ALT,
Key::AltGraph => Modifiers::ALT_GRAPH,
Key::CapsLock => Modifiers::CAPS_LOCK,
@ -3059,7 +3059,7 @@ where
Key::Super => Modifiers::META,
_ => return,
};
match event.state {
match event.event.state {
KeyState::Down => self.active_keyboard_modifiers.insert(modified_modifier),
KeyState::Up => self.active_keyboard_modifiers.remove(modified_modifier),
}
@ -4840,7 +4840,7 @@ where
pressed_mouse_buttons: self.pressed_mouse_buttons,
active_keyboard_modifiers: event.modifiers,
hit_test_result: None,
event: InputEvent::Keyboard(event),
event: InputEvent::Keyboard(KeyboardEvent::new(event)),
},
WebDriverInputEvent::Composition(event) => ConstellationInputEvent {
pressed_mouse_buttons: self.pressed_mouse_buttons,
@ -4855,7 +4855,14 @@ where
}
}
},
WebDriverCommandMsg::KeyboardAction(browsing_context_id, event) => {
WebDriverCommandMsg::KeyboardAction(
browsing_context_id,
key_event,
msg_id,
response_sender,
) => {
self.webdriver.input_command_response_sender = Some(response_sender);
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
Some(browsing_context) => browsing_context.pipeline_id,
None => {
@ -4866,13 +4873,15 @@ where
Some(pipeline) => pipeline.event_loop.clone(),
None => return warn!("{}: KeyboardAction after closure", pipeline_id),
};
let event = InputEvent::Keyboard(KeyboardEvent::new(key_event.clone()))
.with_webdriver_message_id(msg_id);
let control_msg = ScriptThreadMessage::SendInputEvent(
pipeline_id,
ConstellationInputEvent {
pressed_mouse_buttons: self.pressed_mouse_buttons,
active_keyboard_modifiers: event.modifiers,
active_keyboard_modifiers: key_event.modifiers,
hit_test_result: None,
event: InputEvent::Keyboard(event),
event,
},
);
if let Err(e) = event_loop.send(control_msg) {

View file

@ -2365,7 +2365,7 @@ impl Document {
/// The entry point for all key processing for web content
pub(crate) fn dispatch_key_event(
&self,
keyboard_event: ::keyboard_types::KeyboardEvent,
keyboard_event: ::embedder_traits::KeyboardEvent,
can_gc: CanGc,
) {
let focused = self.get_focused_element();
@ -2379,19 +2379,19 @@ impl Document {
let keyevent = KeyboardEvent::new(
&self.window,
DOMString::from(keyboard_event.state.to_string()),
DOMString::from(keyboard_event.event.state.to_string()),
true,
true,
Some(&self.window),
0,
keyboard_event.key.clone(),
DOMString::from(keyboard_event.code.to_string()),
keyboard_event.location as u32,
keyboard_event.repeat,
keyboard_event.is_composing,
keyboard_event.modifiers,
keyboard_event.event.key.clone(),
DOMString::from(keyboard_event.event.code.to_string()),
keyboard_event.event.location as u32,
keyboard_event.event.repeat,
keyboard_event.event.is_composing,
keyboard_event.event.modifiers,
0,
keyboard_event.key.legacy_keycode(),
keyboard_event.event.key.legacy_keycode(),
can_gc,
);
let event = keyevent.upcast::<Event>();
@ -2402,9 +2402,9 @@ impl Document {
// it MUST prevent the respective beforeinput and input
// (and keypress if supported) events from being generated
// TODO: keypress should be deprecated and superceded by beforeinput
if keyboard_event.state == KeyState::Down &&
is_character_value_key(&(keyboard_event.key)) &&
!keyboard_event.is_composing &&
if keyboard_event.event.state == KeyState::Down &&
is_character_value_key(&(keyboard_event.event.key)) &&
!keyboard_event.event.is_composing &&
cancel_state != EventDefault::Prevented
{
// https://w3c.github.io/uievents/#keypress-event-order
@ -2415,13 +2415,13 @@ impl Document {
true,
Some(&self.window),
0,
keyboard_event.key.clone(),
DOMString::from(keyboard_event.code.to_string()),
keyboard_event.location as u32,
keyboard_event.repeat,
keyboard_event.is_composing,
keyboard_event.modifiers,
keyboard_event.key.legacy_charcode(),
keyboard_event.event.key.clone(),
DOMString::from(keyboard_event.event.code.to_string()),
keyboard_event.event.location as u32,
keyboard_event.event.repeat,
keyboard_event.event.is_composing,
keyboard_event.event.modifiers,
keyboard_event.event.key.legacy_charcode(),
0,
can_gc,
);
@ -2439,8 +2439,8 @@ impl Document {
// however *when* we do it is up to us.
// Here, we're dispatching it after the key event so the script has a chance to cancel it
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27337
if (keyboard_event.key == Key::Enter || keyboard_event.code == Code::Space) &&
keyboard_event.state == KeyState::Up
if (keyboard_event.event.key == Key::Enter || keyboard_event.event.code == Code::Space) &&
keyboard_event.event.state == KeyState::Up
{
if let Some(elem) = target.downcast::<Element>() {
elem.upcast::<Node>()

View file

@ -85,7 +85,9 @@ use gleam::gl::RENDERER;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use javascript_evaluator::JavaScriptEvaluator;
pub use keyboard_types::*;
pub use keyboard_types::{
Code, CompositionEvent, CompositionState, Key, KeyState, Location, Modifiers,
};
use layout::LayoutFactoryImpl;
use log::{Log, Metadata, Record, debug, warn};
use media::{GlApi, NativeDisplay, WindowGLContext};

View file

@ -8,12 +8,11 @@ use base::id::PipelineId;
use constellation_traits::EmbedderToConstellationMessage;
use embedder_traits::{
AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern,
GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent, Notification,
PermissionFeature, RgbColor, ScreenGeometry, SelectElementOptionOrOptgroup, SimpleDialog,
WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
GamepadHapticEffectType, InputMethodType, KeyboardEvent, LoadStatus, MediaSessionEvent,
Notification, PermissionFeature, RgbColor, ScreenGeometry, SelectElementOptionOrOptgroup,
SimpleDialog, WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
};
use ipc_channel::ipc::IpcSender;
use keyboard_types::KeyboardEvent;
use serde::Serialize;
use url::Url;
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use keyboard_types::{CompositionEvent, KeyboardEvent};
use keyboard_types::{Code, CompositionEvent, Key, KeyState, Location, Modifiers};
use log::error;
use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
@ -53,7 +53,7 @@ impl InputEvent {
InputEvent::EditingAction(..) => None,
InputEvent::Gamepad(..) => None,
InputEvent::Ime(..) => None,
InputEvent::Keyboard(..) => None,
InputEvent::Keyboard(event) => event.webdriver_id,
InputEvent::MouseButton(event) => event.webdriver_id,
InputEvent::MouseMove(event) => event.webdriver_id,
InputEvent::Touch(..) => None,
@ -67,7 +67,9 @@ impl InputEvent {
InputEvent::EditingAction(..) => {},
InputEvent::Gamepad(..) => {},
InputEvent::Ime(..) => {},
InputEvent::Keyboard(..) => {},
InputEvent::Keyboard(ref mut event) => {
event.webdriver_id = webdriver_id;
},
InputEvent::MouseButton(ref mut event) => {
event.webdriver_id = webdriver_id;
},
@ -85,6 +87,51 @@ impl InputEvent {
}
}
/// Recreate KeyboardEvent from keyboard_types to pair it with webdriver_id,
/// which is used for webdriver action synchronization.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct KeyboardEvent {
pub event: ::keyboard_types::KeyboardEvent,
webdriver_id: Option<WebDriverMessageId>,
}
impl KeyboardEvent {
pub fn new(keyboard_event: ::keyboard_types::KeyboardEvent) -> Self {
Self {
event: keyboard_event,
webdriver_id: None,
}
}
pub fn new_without_event(
state: KeyState,
key: Key,
code: Code,
location: Location,
modifiers: Modifiers,
repeat: bool,
is_composing: bool,
) -> Self {
Self::new(::keyboard_types::KeyboardEvent {
state,
key,
code,
location,
modifiers,
repeat,
is_composing,
})
}
pub fn from_state_and_key(state: KeyState, key: Key) -> Self {
Self::new(::keyboard_types::KeyboardEvent {
state,
key,
..::keyboard_types::KeyboardEvent::default()
})
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct MouseButtonEvent {
pub action: MouseButtonAction,

View file

@ -25,7 +25,6 @@ use crossbeam_channel::Sender;
use euclid::{Scale, Size2D};
use http::{HeaderMap, Method, StatusCode};
use ipc_channel::ipc::IpcSender;
pub use keyboard_types::{KeyboardEvent, Modifiers};
use log::warn;
use malloc_size_of::malloc_size_of_is_0;
use malloc_size_of_derive::MallocSizeOf;

View file

@ -42,7 +42,13 @@ pub enum WebDriverCommandMsg {
/// Act as if keys were pressed in the browsing context with the given ID.
SendKeys(BrowsingContextId, Vec<WebDriverInputEvent>),
/// Act as if keys were pressed or release in the browsing context with the given ID.
KeyboardAction(BrowsingContextId, KeyboardEvent),
KeyboardAction(
BrowsingContextId,
KeyboardEvent,
// Should never be None.
Option<WebDriverMessageId>,
IpcSender<WebDriverCommandResponse>,
),
/// Act as if the mouse was clicked in the browsing context with the given ID.
MouseButtonAction(
WebViewId,

View file

@ -157,7 +157,7 @@ impl Handler {
// Step 1.4. Wait for
// The user agent event loop has spun enough times to process the DOM events
// generated by the last invocation of the dispatch tick actions steps.
self.wait_for_user_agent_handling_complete(tick_actions)?;
self.wait_for_user_agent_handling_complete()?;
// At least tick duration milliseconds have passed.
let elapsed = now.elapsed();
if elapsed.as_millis() < tick_duration as u128 {
@ -171,29 +171,13 @@ impl Handler {
Ok(())
}
fn wait_for_user_agent_handling_complete(
&self,
tick_actions: &TickActions,
) -> Result<(), ErrorStatus> {
// TODO: Add matches! for key actions
// after implmenting webdriver id for key events.
let count_non_null_actions_in_tick = tick_actions
.iter()
.filter(|(_, action)| {
matches!(
action,
ActionItem::Pointer(PointerActionItem::Pointer(_)) |
ActionItem::Wheel(WheelActionItem::Wheel(_))
)
})
.count();
fn wait_for_user_agent_handling_complete(&self) -> Result<(), ErrorStatus> {
// To ensure we wait for all events to be processed, only the last event
// in each tick action step holds the message id.
// Whenever a new event is generated, the message id is passed to it.
//
// Wait for count_non_null_actions_in_tick number of responses
for _ in 0..count_non_null_actions_in_tick {
// Wait for num_pending_actions number of responses
for _ in 0..self.num_pending_actions.get() {
match self.constellation_receiver.recv() {
Ok(response) => {
let current_waiting_id = self
@ -213,6 +197,8 @@ impl Handler {
};
}
self.num_pending_actions.set(0);
Ok(())
}
@ -320,9 +306,13 @@ impl Handler {
let keyboard_event = key_input_state.dispatch_keydown(raw_key);
// Step 12
self.increment_num_pending_actions();
let msg_id = self.current_action_id.get();
let cmd_msg = WebDriverCommandMsg::KeyboardAction(
self.session().unwrap().browsing_context_id,
keyboard_event,
msg_id,
self.constellation_sender.clone(),
);
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
@ -342,9 +332,13 @@ impl Handler {
if let Some(keyboard_event) = key_input_state.dispatch_keyup(raw_key) {
// Step 12
self.increment_num_pending_actions();
let msg_id = self.current_action_id.get();
let cmd_msg = WebDriverCommandMsg::KeyboardAction(
self.session().unwrap().browsing_context_id,
keyboard_event,
msg_id,
self.constellation_sender.clone(),
);
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
@ -367,6 +361,7 @@ impl Handler {
}
pointer_input_state.pressed.insert(action.button);
self.increment_num_pending_actions();
let msg_id = self.current_action_id.get();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
session.webview_id,
@ -397,6 +392,7 @@ impl Handler {
}
pointer_input_state.pressed.remove(&action.button);
self.increment_num_pending_actions();
let msg_id = self.current_action_id.get();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
session.webview_id,
@ -523,6 +519,7 @@ impl Handler {
if x != current_x || y != current_y || last {
// Step 7.2
let msg_id = if last {
self.increment_num_pending_actions();
self.current_action_id.get()
} else {
None
@ -660,6 +657,7 @@ impl Handler {
if delta_x != 0 || delta_y != 0 || last {
// Perform implementation-specific action dispatch steps
let msg_id = if last {
self.increment_num_pending_actions();
self.current_action_id.get()
} else {
None

View file

@ -227,6 +227,9 @@ struct Handler {
current_action_id: Cell<Option<WebDriverMessageId>>,
resize_timeout: u32,
/// Number of pending actions of which WebDriver is waiting for responses.
num_pending_actions: Cell<u32>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
@ -459,9 +462,16 @@ impl Handler {
id_generator: WebDriverMessageIdGenerator::new(),
current_action_id: Cell::new(None),
resize_timeout: 500,
num_pending_actions: Cell::new(0),
}
}
fn increment_num_pending_actions(&self) {
// Increase the num_pending_actions by one every time we dispatch non null actions.
self.num_pending_actions
.set(self.num_pending_actions.get() + 1);
}
fn focus_webview_id(&self) -> WebDriverResult<WebViewId> {
debug!("Getting focused context.");
let (sender, receiver) = ipc::channel().unwrap();

View file

@ -8,7 +8,7 @@ use std::path::PathBuf;
use std::rc::Rc;
use euclid::Vector2D;
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
use keyboard_types::{Key, Modifiers, ShortcutMatcher};
use log::{error, info};
use servo::base::id::WebViewId;
use servo::config::{opts, pref};
@ -17,8 +17,8 @@ use servo::webrender_api::ScrollLocation;
use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize};
use servo::{
AllowOrDenyRequest, AuthenticationRequest, FilterPattern, FormControl, GamepadHapticEffectType,
LoadStatus, PermissionRequest, Servo, ServoDelegate, ServoError, SimpleDialog, TouchEventType,
WebView, WebViewBuilder, WebViewDelegate,
KeyboardEvent, LoadStatus, PermissionRequest, Servo, ServoDelegate, ServoError, SimpleDialog,
TouchEventType, WebView, WebViewBuilder, WebViewDelegate,
};
use url::Url;
@ -314,7 +314,7 @@ impl RunningAppState {
/// Handle servoshell key bindings that may have been prevented by the page in the focused webview.
fn handle_overridable_key_bindings(&self, webview: ::servo::WebView, event: KeyboardEvent) {
let origin = webview.rect().min.ceil().to_i32();
ShortcutMatcher::from_event(event)
ShortcutMatcher::from_event(event.event)
.shortcut(CMD_OR_CONTROL, '=', || {
webview.set_zoom(1.1);
})

View file

@ -184,16 +184,16 @@ impl Window {
// no keyboard event is emitted. A dummy event is created in this case.
(KeyboardEvent::default(), None)
};
event.key = Key::Character(character.to_string());
event.event.key = Key::Character(character.to_string());
if event.state == KeyState::Down {
if event.event.state == KeyState::Down {
// Ensure that when we receive a keyup event from winit, we are able
// to infer that it's related to this character and set the event
// properties appropriately.
if let Some(key_code) = key_code {
self.keys_down
.borrow_mut()
.insert(key_code, event.key.clone());
.insert(key_code, event.event.key.clone());
}
}
@ -223,20 +223,24 @@ impl Window {
}
}
if keyboard_event.state == KeyState::Down && keyboard_event.key == Key::Unidentified {
if keyboard_event.event.state == KeyState::Down &&
keyboard_event.event.key == Key::Unidentified
{
// If pressed and probably printable, we expect a ReceivedCharacter event.
// Wait for that to be received and don't queue any event right now.
self.last_pressed
.set(Some((keyboard_event, Some(winit_event.logical_key))));
return;
} else if keyboard_event.state == KeyState::Up && keyboard_event.key == Key::Unidentified {
} else if keyboard_event.event.state == KeyState::Up &&
keyboard_event.event.key == Key::Unidentified
{
// If release and probably printable, this is following a ReceiverCharacter event.
if let Some(key) = self.keys_down.borrow_mut().remove(&winit_event.logical_key) {
keyboard_event.key = key;
keyboard_event.event.key = key;
}
}
if keyboard_event.key != Key::Unidentified {
if keyboard_event.event.key != Key::Unidentified {
self.last_pressed.set(None);
let xr_poses = self.xr_window_poses.borrow();
for xr_window_pose in &*xr_poses {
@ -284,7 +288,7 @@ impl Window {
};
let mut handled = true;
ShortcutMatcher::from_event(key_event.clone())
ShortcutMatcher::from_event(key_event.event.clone())
.shortcut(CMD_OR_CONTROL, 'R', || focused_webview.reload())
.shortcut(CMD_OR_CONTROL, 'W', || {
state.close_webview(focused_webview.id());
@ -828,14 +832,14 @@ impl servo::webxr::glwindow::GlWindow for XRWindow {
impl XRWindowPose {
fn handle_xr_translation(&self, input: &KeyboardEvent) {
if input.state != KeyState::Down {
if input.event.state != KeyState::Down {
return;
}
const NORMAL_TRANSLATE: f32 = 0.1;
const QUICK_TRANSLATE: f32 = 1.0;
let mut x = 0.0;
let mut z = 0.0;
match input.key {
match input.event.key {
Key::Character(ref k) => match &**k {
"w" => z = -NORMAL_TRANSLATE,
"W" => z = -QUICK_TRANSLATE,

View file

@ -2,7 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers};
use keyboard_types::{Code, Key, KeyState, Location, Modifiers};
use servo::KeyboardEvent;
use winit::event::{ElementState, KeyEvent};
use winit::keyboard::{
Key as WinitKey, KeyCode, KeyLocation as WinitKeyLocation, ModifiersState, NamedKey,
@ -583,13 +584,13 @@ fn keyboard_modifiers_from_winit_modifiers(mods: ModifiersState) -> Modifiers {
}
pub fn keyboard_event_from_winit(key_event: &KeyEvent, state: ModifiersState) -> KeyboardEvent {
KeyboardEvent {
state: KeyState::from_winit_key_event(key_event),
key: Key::from_winit_key_event(key_event),
code: Code::from_winit_key_event(key_event),
location: Location::from_winit_key_event(key_event),
modifiers: keyboard_modifiers_from_winit_modifiers(state),
repeat: false,
is_composing: false,
}
KeyboardEvent::new_without_event(
KeyState::from_winit_key_event(key_event),
Key::from_winit_key_event(key_event),
Code::from_winit_key_event(key_event),
Location::from_winit_key_event(key_event),
keyboard_modifiers_from_winit_modifiers(state),
false,
false,
)
}

View file

@ -626,22 +626,14 @@ impl RunningAppState {
}
pub fn key_down(&self, key: Key) {
let key_event = KeyboardEvent {
state: KeyState::Down,
key,
..KeyboardEvent::default()
};
let key_event = KeyboardEvent::from_state_and_key(KeyState::Down, key);
self.active_webview()
.notify_input_event(InputEvent::Keyboard(key_event));
self.perform_updates();
}
pub fn key_up(&self, key: Key) {
let key_event = KeyboardEvent {
state: KeyState::Up,
key,
..KeyboardEvent::default()
};
let key_event = KeyboardEvent::from_state_and_key(KeyState::Up, key);
self.active_webview()
.notify_input_event(InputEvent::Keyboard(key_event));
self.perform_updates();
@ -653,22 +645,20 @@ impl RunningAppState {
return;
}
let active_webview = self.active_webview();
active_webview.notify_input_event(InputEvent::Keyboard(KeyboardEvent {
state: KeyState::Down,
key: Key::Process,
..KeyboardEvent::default()
}));
active_webview.notify_input_event(InputEvent::Keyboard(KeyboardEvent::from_state_and_key(
KeyState::Down,
Key::Process,
)));
active_webview.notify_input_event(InputEvent::Ime(ImeEvent::Composition(
CompositionEvent {
state: CompositionState::End,
data: text,
},
)));
active_webview.notify_input_event(InputEvent::Keyboard(KeyboardEvent {
state: KeyState::Up,
key: Key::Process,
..KeyboardEvent::default()
}));
active_webview.notify_input_event(InputEvent::Keyboard(KeyboardEvent::from_state_and_key(
KeyState::Up,
Key::Process,
)));
self.perform_updates();
}

View file

@ -5,9 +5,6 @@
[test_key_down_closes_browsing_context]
expected: FAIL
[test_backspace_erases_keys]
expected: FAIL
[test_element_in_shadow_tree[outer-open\]]
expected: FAIL

View file

@ -1,6 +0,0 @@
[key_shortcuts.py]
[test_mod_a_mod_c_right_mod_v_pastes_text]
expected: FAIL
[test_mod_a_mod_x_deletes_all_text]
expected: FAIL

View file

@ -1,7 +1,4 @@
[key_special_keys.py]
[test_codepoint_keys_behave_correctly[\\U0001f60d\]]
expected: FAIL
[test_codepoint_keys_behave_correctly[\\u0ba8\\u0bbf\]]
expected: FAIL