[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::{ use embedder_traits::{
AnimationState, CompositorHitTestResult, Cursor, EmbedderMsg, EmbedderProxy, AnimationState, CompositorHitTestResult, Cursor, EmbedderMsg, EmbedderProxy,
FocusSequenceNumber, ImeEvent, InputEvent, JSValue, JavaScriptEvaluationError, FocusSequenceNumber, ImeEvent, InputEvent, JSValue, JavaScriptEvaluationError,
JavaScriptEvaluationId, MediaSessionActionType, MediaSessionEvent, MediaSessionPlaybackState, JavaScriptEvaluationId, KeyboardEvent, MediaSessionActionType, MediaSessionEvent,
MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverCommandMsg, MediaSessionPlaybackState, MouseButton, MouseButtonAction, MouseButtonEvent, Theme,
WebDriverCommandResponse, WebDriverLoadStatus, ViewportDetails, WebDriverCommandMsg, WebDriverCommandResponse, WebDriverLoadStatus,
}; };
use euclid::Size2D; use euclid::Size2D;
use euclid::default::Size2D as UntypedSize2D; 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::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use keyboard_types::webdriver::Event as WebDriverInputEvent; 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 log::{debug, error, info, trace, warn};
use media::WindowGLContext; use media::WindowGLContext;
use net_traits::pub_domains::reg_host; use net_traits::pub_domains::reg_host;
@ -3034,13 +3034,13 @@ where
} }
fn update_active_keybord_modifiers(&mut self, event: &KeyboardEvent) { 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 // `KeyboardEvent::modifiers` contains the pre-existing modifiers before this key was
// either pressed or released, but `active_keyboard_modifiers` should track the subsequent // 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 // state. If this event will update that state, we need to ensure that we are tracking what
// the event changes. // the event changes.
let modified_modifier = match event.key { let modified_modifier = match event.event.key {
Key::Alt => Modifiers::ALT, Key::Alt => Modifiers::ALT,
Key::AltGraph => Modifiers::ALT_GRAPH, Key::AltGraph => Modifiers::ALT_GRAPH,
Key::CapsLock => Modifiers::CAPS_LOCK, Key::CapsLock => Modifiers::CAPS_LOCK,
@ -3059,7 +3059,7 @@ where
Key::Super => Modifiers::META, Key::Super => Modifiers::META,
_ => return, _ => return,
}; };
match event.state { match event.event.state {
KeyState::Down => self.active_keyboard_modifiers.insert(modified_modifier), KeyState::Down => self.active_keyboard_modifiers.insert(modified_modifier),
KeyState::Up => self.active_keyboard_modifiers.remove(modified_modifier), KeyState::Up => self.active_keyboard_modifiers.remove(modified_modifier),
} }
@ -4840,7 +4840,7 @@ where
pressed_mouse_buttons: self.pressed_mouse_buttons, pressed_mouse_buttons: self.pressed_mouse_buttons,
active_keyboard_modifiers: event.modifiers, active_keyboard_modifiers: event.modifiers,
hit_test_result: None, hit_test_result: None,
event: InputEvent::Keyboard(event), event: InputEvent::Keyboard(KeyboardEvent::new(event)),
}, },
WebDriverInputEvent::Composition(event) => ConstellationInputEvent { WebDriverInputEvent::Composition(event) => ConstellationInputEvent {
pressed_mouse_buttons: self.pressed_mouse_buttons, 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) { let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
Some(browsing_context) => browsing_context.pipeline_id, Some(browsing_context) => browsing_context.pipeline_id,
None => { None => {
@ -4866,13 +4873,15 @@ where
Some(pipeline) => pipeline.event_loop.clone(), Some(pipeline) => pipeline.event_loop.clone(),
None => return warn!("{}: KeyboardAction after closure", pipeline_id), 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( let control_msg = ScriptThreadMessage::SendInputEvent(
pipeline_id, pipeline_id,
ConstellationInputEvent { ConstellationInputEvent {
pressed_mouse_buttons: self.pressed_mouse_buttons, pressed_mouse_buttons: self.pressed_mouse_buttons,
active_keyboard_modifiers: event.modifiers, active_keyboard_modifiers: key_event.modifiers,
hit_test_result: None, hit_test_result: None,
event: InputEvent::Keyboard(event), event,
}, },
); );
if let Err(e) = event_loop.send(control_msg) { 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 /// The entry point for all key processing for web content
pub(crate) fn dispatch_key_event( pub(crate) fn dispatch_key_event(
&self, &self,
keyboard_event: ::keyboard_types::KeyboardEvent, keyboard_event: ::embedder_traits::KeyboardEvent,
can_gc: CanGc, can_gc: CanGc,
) { ) {
let focused = self.get_focused_element(); let focused = self.get_focused_element();
@ -2379,19 +2379,19 @@ impl Document {
let keyevent = KeyboardEvent::new( let keyevent = KeyboardEvent::new(
&self.window, &self.window,
DOMString::from(keyboard_event.state.to_string()), DOMString::from(keyboard_event.event.state.to_string()),
true, true,
true, true,
Some(&self.window), Some(&self.window),
0, 0,
keyboard_event.key.clone(), keyboard_event.event.key.clone(),
DOMString::from(keyboard_event.code.to_string()), DOMString::from(keyboard_event.event.code.to_string()),
keyboard_event.location as u32, keyboard_event.event.location as u32,
keyboard_event.repeat, keyboard_event.event.repeat,
keyboard_event.is_composing, keyboard_event.event.is_composing,
keyboard_event.modifiers, keyboard_event.event.modifiers,
0, 0,
keyboard_event.key.legacy_keycode(), keyboard_event.event.key.legacy_keycode(),
can_gc, can_gc,
); );
let event = keyevent.upcast::<Event>(); let event = keyevent.upcast::<Event>();
@ -2402,9 +2402,9 @@ impl Document {
// it MUST prevent the respective beforeinput and input // it MUST prevent the respective beforeinput and input
// (and keypress if supported) events from being generated // (and keypress if supported) events from being generated
// TODO: keypress should be deprecated and superceded by beforeinput // TODO: keypress should be deprecated and superceded by beforeinput
if keyboard_event.state == KeyState::Down && if keyboard_event.event.state == KeyState::Down &&
is_character_value_key(&(keyboard_event.key)) && is_character_value_key(&(keyboard_event.event.key)) &&
!keyboard_event.is_composing && !keyboard_event.event.is_composing &&
cancel_state != EventDefault::Prevented cancel_state != EventDefault::Prevented
{ {
// https://w3c.github.io/uievents/#keypress-event-order // https://w3c.github.io/uievents/#keypress-event-order
@ -2415,13 +2415,13 @@ impl Document {
true, true,
Some(&self.window), Some(&self.window),
0, 0,
keyboard_event.key.clone(), keyboard_event.event.key.clone(),
DOMString::from(keyboard_event.code.to_string()), DOMString::from(keyboard_event.event.code.to_string()),
keyboard_event.location as u32, keyboard_event.event.location as u32,
keyboard_event.repeat, keyboard_event.event.repeat,
keyboard_event.is_composing, keyboard_event.event.is_composing,
keyboard_event.modifiers, keyboard_event.event.modifiers,
keyboard_event.key.legacy_charcode(), keyboard_event.event.key.legacy_charcode(),
0, 0,
can_gc, can_gc,
); );
@ -2439,8 +2439,8 @@ impl Document {
// however *when* we do it is up to us. // 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 // 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 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=27337
if (keyboard_event.key == Key::Enter || keyboard_event.code == Code::Space) && if (keyboard_event.event.key == Key::Enter || keyboard_event.event.code == Code::Space) &&
keyboard_event.state == KeyState::Up keyboard_event.event.state == KeyState::Up
{ {
if let Some(elem) = target.downcast::<Element>() { if let Some(elem) = target.downcast::<Element>() {
elem.upcast::<Node>() elem.upcast::<Node>()

View file

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

View file

@ -8,12 +8,11 @@ use base::id::PipelineId;
use constellation_traits::EmbedderToConstellationMessage; use constellation_traits::EmbedderToConstellationMessage;
use embedder_traits::{ use embedder_traits::{
AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern, AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern,
GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent, Notification, GamepadHapticEffectType, InputMethodType, KeyboardEvent, LoadStatus, MediaSessionEvent,
PermissionFeature, RgbColor, ScreenGeometry, SelectElementOptionOrOptgroup, SimpleDialog, Notification, PermissionFeature, RgbColor, ScreenGeometry, SelectElementOptionOrOptgroup,
WebResourceRequest, WebResourceResponse, WebResourceResponseMsg, SimpleDialog, WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
}; };
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use keyboard_types::KeyboardEvent;
use serde::Serialize; use serde::Serialize;
use url::Url; use url::Url;
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize}; 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 * 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/. */ * 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 log::error;
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -53,7 +53,7 @@ impl InputEvent {
InputEvent::EditingAction(..) => None, InputEvent::EditingAction(..) => None,
InputEvent::Gamepad(..) => None, InputEvent::Gamepad(..) => None,
InputEvent::Ime(..) => None, InputEvent::Ime(..) => None,
InputEvent::Keyboard(..) => None, InputEvent::Keyboard(event) => event.webdriver_id,
InputEvent::MouseButton(event) => event.webdriver_id, InputEvent::MouseButton(event) => event.webdriver_id,
InputEvent::MouseMove(event) => event.webdriver_id, InputEvent::MouseMove(event) => event.webdriver_id,
InputEvent::Touch(..) => None, InputEvent::Touch(..) => None,
@ -67,7 +67,9 @@ impl InputEvent {
InputEvent::EditingAction(..) => {}, InputEvent::EditingAction(..) => {},
InputEvent::Gamepad(..) => {}, InputEvent::Gamepad(..) => {},
InputEvent::Ime(..) => {}, InputEvent::Ime(..) => {},
InputEvent::Keyboard(..) => {}, InputEvent::Keyboard(ref mut event) => {
event.webdriver_id = webdriver_id;
},
InputEvent::MouseButton(ref mut event) => { InputEvent::MouseButton(ref mut event) => {
event.webdriver_id = webdriver_id; 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)] #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct MouseButtonEvent { pub struct MouseButtonEvent {
pub action: MouseButtonAction, pub action: MouseButtonAction,

View file

@ -25,7 +25,6 @@ use crossbeam_channel::Sender;
use euclid::{Scale, Size2D}; use euclid::{Scale, Size2D};
use http::{HeaderMap, Method, StatusCode}; use http::{HeaderMap, Method, StatusCode};
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
pub use keyboard_types::{KeyboardEvent, Modifiers};
use log::warn; use log::warn;
use malloc_size_of::malloc_size_of_is_0; use malloc_size_of::malloc_size_of_is_0;
use malloc_size_of_derive::MallocSizeOf; 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. /// Act as if keys were pressed in the browsing context with the given ID.
SendKeys(BrowsingContextId, Vec<WebDriverInputEvent>), SendKeys(BrowsingContextId, Vec<WebDriverInputEvent>),
/// Act as if keys were pressed or release in the browsing context with the given ID. /// 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. /// Act as if the mouse was clicked in the browsing context with the given ID.
MouseButtonAction( MouseButtonAction(
WebViewId, WebViewId,

View file

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

View file

@ -227,6 +227,9 @@ struct Handler {
current_action_id: Cell<Option<WebDriverMessageId>>, current_action_id: Cell<Option<WebDriverMessageId>>,
resize_timeout: u32, resize_timeout: u32,
/// Number of pending actions of which WebDriver is waiting for responses.
num_pending_actions: Cell<u32>,
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -459,9 +462,16 @@ impl Handler {
id_generator: WebDriverMessageIdGenerator::new(), id_generator: WebDriverMessageIdGenerator::new(),
current_action_id: Cell::new(None), current_action_id: Cell::new(None),
resize_timeout: 500, 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> { fn focus_webview_id(&self) -> WebDriverResult<WebViewId> {
debug!("Getting focused context."); debug!("Getting focused context.");
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();

View file

@ -8,7 +8,7 @@ use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use euclid::Vector2D; use euclid::Vector2D;
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher}; use keyboard_types::{Key, Modifiers, ShortcutMatcher};
use log::{error, info}; use log::{error, info};
use servo::base::id::WebViewId; use servo::base::id::WebViewId;
use servo::config::{opts, pref}; use servo::config::{opts, pref};
@ -17,8 +17,8 @@ use servo::webrender_api::ScrollLocation;
use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize}; use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize};
use servo::{ use servo::{
AllowOrDenyRequest, AuthenticationRequest, FilterPattern, FormControl, GamepadHapticEffectType, AllowOrDenyRequest, AuthenticationRequest, FilterPattern, FormControl, GamepadHapticEffectType,
LoadStatus, PermissionRequest, Servo, ServoDelegate, ServoError, SimpleDialog, TouchEventType, KeyboardEvent, LoadStatus, PermissionRequest, Servo, ServoDelegate, ServoError, SimpleDialog,
WebView, WebViewBuilder, WebViewDelegate, TouchEventType, WebView, WebViewBuilder, WebViewDelegate,
}; };
use url::Url; 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. /// 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) { fn handle_overridable_key_bindings(&self, webview: ::servo::WebView, event: KeyboardEvent) {
let origin = webview.rect().min.ceil().to_i32(); let origin = webview.rect().min.ceil().to_i32();
ShortcutMatcher::from_event(event) ShortcutMatcher::from_event(event.event)
.shortcut(CMD_OR_CONTROL, '=', || { .shortcut(CMD_OR_CONTROL, '=', || {
webview.set_zoom(1.1); 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. // no keyboard event is emitted. A dummy event is created in this case.
(KeyboardEvent::default(), None) (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 // 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 // to infer that it's related to this character and set the event
// properties appropriately. // properties appropriately.
if let Some(key_code) = key_code { if let Some(key_code) = key_code {
self.keys_down self.keys_down
.borrow_mut() .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. // If pressed and probably printable, we expect a ReceivedCharacter event.
// Wait for that to be received and don't queue any event right now. // Wait for that to be received and don't queue any event right now.
self.last_pressed self.last_pressed
.set(Some((keyboard_event, Some(winit_event.logical_key)))); .set(Some((keyboard_event, Some(winit_event.logical_key))));
return; 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 release and probably printable, this is following a ReceiverCharacter event.
if let Some(key) = self.keys_down.borrow_mut().remove(&winit_event.logical_key) { 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); self.last_pressed.set(None);
let xr_poses = self.xr_window_poses.borrow(); let xr_poses = self.xr_window_poses.borrow();
for xr_window_pose in &*xr_poses { for xr_window_pose in &*xr_poses {
@ -284,7 +288,7 @@ impl Window {
}; };
let mut handled = true; 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, 'R', || focused_webview.reload())
.shortcut(CMD_OR_CONTROL, 'W', || { .shortcut(CMD_OR_CONTROL, 'W', || {
state.close_webview(focused_webview.id()); state.close_webview(focused_webview.id());
@ -828,14 +832,14 @@ impl servo::webxr::glwindow::GlWindow for XRWindow {
impl XRWindowPose { impl XRWindowPose {
fn handle_xr_translation(&self, input: &KeyboardEvent) { fn handle_xr_translation(&self, input: &KeyboardEvent) {
if input.state != KeyState::Down { if input.event.state != KeyState::Down {
return; return;
} }
const NORMAL_TRANSLATE: f32 = 0.1; const NORMAL_TRANSLATE: f32 = 0.1;
const QUICK_TRANSLATE: f32 = 1.0; const QUICK_TRANSLATE: f32 = 1.0;
let mut x = 0.0; let mut x = 0.0;
let mut z = 0.0; let mut z = 0.0;
match input.key { match input.event.key {
Key::Character(ref k) => match &**k { Key::Character(ref k) => match &**k {
"w" => z = -NORMAL_TRANSLATE, "w" => z = -NORMAL_TRANSLATE,
"W" => z = -QUICK_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 * 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/. */ * 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::event::{ElementState, KeyEvent};
use winit::keyboard::{ use winit::keyboard::{
Key as WinitKey, KeyCode, KeyLocation as WinitKeyLocation, ModifiersState, NamedKey, 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 { pub fn keyboard_event_from_winit(key_event: &KeyEvent, state: ModifiersState) -> KeyboardEvent {
KeyboardEvent { KeyboardEvent::new_without_event(
state: KeyState::from_winit_key_event(key_event), KeyState::from_winit_key_event(key_event),
key: Key::from_winit_key_event(key_event), Key::from_winit_key_event(key_event),
code: Code::from_winit_key_event(key_event), Code::from_winit_key_event(key_event),
location: Location::from_winit_key_event(key_event), Location::from_winit_key_event(key_event),
modifiers: keyboard_modifiers_from_winit_modifiers(state), keyboard_modifiers_from_winit_modifiers(state),
repeat: false, false,
is_composing: false, false,
} )
} }

View file

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

View file

@ -5,9 +5,6 @@
[test_key_down_closes_browsing_context] [test_key_down_closes_browsing_context]
expected: FAIL expected: FAIL
[test_backspace_erases_keys]
expected: FAIL
[test_element_in_shadow_tree[outer-open\]] [test_element_in_shadow_tree[outer-open\]]
expected: FAIL 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] [key_special_keys.py]
[test_codepoint_keys_behave_correctly[\\U0001f60d\]]
expected: FAIL
[test_codepoint_keys_behave_correctly[\\u0ba8\\u0bbf\]] [test_codepoint_keys_behave_correctly[\\u0ba8\\u0bbf\]]
expected: FAIL expected: FAIL