mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Auto merge of #21881 - pyfisch:keyboard-types, r=paulrouget
Use keyboard-types crate Have embedders send DOM keys to servo and use a strongly typed KeyboardEvent from the W3C UI Events spec. All keyboard handling now uses the new types. Introduce a ShortcutMatcher to recognize key bindings. Shortcuts are now recognized in a uniform way. Updated the winit port. Updated webdriver integration. part of #20331 What this PR does: * allow the use non-ASCII keyboards for text input * decouple keyboard event "key" from "code" (key meaning vs location) What this PR does not do: * completely improve keyboard events send from winit and webdriver * add support for CompositionEvent or IME Notes: * The winit embedder does not send keyup events for printable keys (this is a regression) * keyboard-types is on crates.io because I believe it to be useful outside of servo. If you prefer I can add a copy in this repo. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21881) <!-- Reviewable:end -->
This commit is contained in:
commit
9a0404ac5f
35 changed files with 747 additions and 1673 deletions
|
@ -100,9 +100,10 @@ use hyper_serde::Serde;
|
|||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use js::jsapi::{JSContext, JSObject, JSRuntime};
|
||||
use js::jsapi::JS_GetRuntime;
|
||||
use keyboard_types::{Key, KeyState, Modifiers};
|
||||
use metrics::{InteractiveFlag, InteractiveMetrics, InteractiveWindow, ProfilerMetadataFactory, ProgressiveWebMetric};
|
||||
use mime::{Mime, TopLevel, SubLevel};
|
||||
use msg::constellation_msg::{BrowsingContextId, Key, KeyModifiers, KeyState};
|
||||
use msg::constellation_msg::BrowsingContextId;
|
||||
use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy};
|
||||
use net_traits::CookieSource::NonHTTP;
|
||||
use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl};
|
||||
|
@ -769,10 +770,12 @@ impl Document {
|
|||
// Step 1 is not handled here; the fragid is already obtained by the calling function
|
||||
// Step 2: Simply use None to indicate the top of the document.
|
||||
// Step 3 & 4
|
||||
percent_decode(fragid.as_bytes()).decode_utf8().ok()
|
||||
// Step 5
|
||||
percent_decode(fragid.as_bytes())
|
||||
.decode_utf8()
|
||||
.ok()
|
||||
// Step 5
|
||||
.and_then(|decoded_fragid| self.get_element_by_id(&Atom::from(decoded_fragid)))
|
||||
// Step 6
|
||||
// Step 6
|
||||
.or_else(|| self.get_anchor_by_name(fragid))
|
||||
// Step 7 & 8
|
||||
}
|
||||
|
@ -805,7 +808,8 @@ impl Document {
|
|||
rect.origin.x.to_nearest_px() as f32,
|
||||
rect.origin.y.to_nearest_px() as f32,
|
||||
)
|
||||
}).or_else(|| {
|
||||
})
|
||||
.or_else(|| {
|
||||
if fragment.is_empty() || fragment.eq_ignore_ascii_case("top") {
|
||||
// FIXME(stshine): this should be the origin of the stacking context space,
|
||||
// which may differ under the influence of writing mode.
|
||||
|
@ -1348,13 +1352,7 @@ impl Document {
|
|||
}
|
||||
|
||||
/// The entry point for all key processing for web content
|
||||
pub fn dispatch_key_event(
|
||||
&self,
|
||||
ch: Option<char>,
|
||||
key: Key,
|
||||
state: KeyState,
|
||||
modifiers: KeyModifiers,
|
||||
) {
|
||||
pub fn dispatch_key_event(&self, keyboard_event: ::keyboard_types::KeyboardEvent) {
|
||||
let focused = self.get_focused_element();
|
||||
let body = self.GetBody();
|
||||
|
||||
|
@ -1364,50 +1362,29 @@ impl Document {
|
|||
(&None, &None) => self.window.upcast(),
|
||||
};
|
||||
|
||||
let ctrl = modifiers.contains(KeyModifiers::CONTROL);
|
||||
let alt = modifiers.contains(KeyModifiers::ALT);
|
||||
let shift = modifiers.contains(KeyModifiers::SHIFT);
|
||||
let meta = modifiers.contains(KeyModifiers::SUPER);
|
||||
|
||||
let is_composing = false;
|
||||
let is_repeating = state == KeyState::Repeated;
|
||||
let ev_type = DOMString::from(
|
||||
match state {
|
||||
KeyState::Pressed | KeyState::Repeated => "keydown",
|
||||
KeyState::Released => "keyup",
|
||||
}.to_owned(),
|
||||
);
|
||||
|
||||
let props = KeyboardEvent::key_properties(ch, key, modifiers);
|
||||
|
||||
let keyevent = KeyboardEvent::new(
|
||||
&self.window,
|
||||
ev_type,
|
||||
DOMString::from(keyboard_event.state.to_string()),
|
||||
true,
|
||||
true,
|
||||
Some(&self.window),
|
||||
0,
|
||||
ch,
|
||||
Some(key),
|
||||
DOMString::from(props.key_string.clone()),
|
||||
DOMString::from(props.code),
|
||||
props.location,
|
||||
is_repeating,
|
||||
is_composing,
|
||||
ctrl,
|
||||
alt,
|
||||
shift,
|
||||
meta,
|
||||
None,
|
||||
props.key_code,
|
||||
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,
|
||||
0,
|
||||
keyboard_event.key.legacy_keycode(),
|
||||
);
|
||||
let event = keyevent.upcast::<Event>();
|
||||
event.fire(target);
|
||||
let mut cancel_state = event.get_cancel_state();
|
||||
|
||||
// https://w3c.github.io/uievents/#keys-cancelable-keys
|
||||
if state != KeyState::Released &&
|
||||
props.is_printable() &&
|
||||
if keyboard_event.state == KeyState::Down &&
|
||||
keyboard_event.key.legacy_charcode() != 0 &&
|
||||
cancel_state != EventDefault::Prevented
|
||||
{
|
||||
// https://w3c.github.io/uievents/#keypress-event-order
|
||||
|
@ -1418,18 +1395,13 @@ impl Document {
|
|||
true,
|
||||
Some(&self.window),
|
||||
0,
|
||||
ch,
|
||||
Some(key),
|
||||
DOMString::from(props.key_string),
|
||||
DOMString::from(props.code),
|
||||
props.location,
|
||||
is_repeating,
|
||||
is_composing,
|
||||
ctrl,
|
||||
alt,
|
||||
shift,
|
||||
meta,
|
||||
props.char_code,
|
||||
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(),
|
||||
0,
|
||||
);
|
||||
let ev = event.upcast::<Event>();
|
||||
|
@ -1438,7 +1410,7 @@ impl Document {
|
|||
}
|
||||
|
||||
if cancel_state == EventDefault::Allowed {
|
||||
let msg = EmbedderMsg::KeyEvent(ch, key, state, modifiers);
|
||||
let msg = EmbedderMsg::Keyboard(keyboard_event.clone());
|
||||
self.send_to_embedder(msg);
|
||||
|
||||
// This behavior is unspecced
|
||||
|
@ -1446,8 +1418,10 @@ 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
|
||||
match key {
|
||||
Key::Space if state == KeyState::Released => {
|
||||
match keyboard_event.key {
|
||||
Key::Character(ref letter)
|
||||
if letter == " " && keyboard_event.state == KeyState::Up =>
|
||||
{
|
||||
let maybe_elem = target.downcast::<Element>();
|
||||
if let Some(el) = maybe_elem {
|
||||
synthetic_click_activation(
|
||||
|
@ -1459,11 +1433,15 @@ impl Document {
|
|||
ActivationSource::NotFromClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
Key::Enter if state == KeyState::Released => {
|
||||
}
|
||||
Key::Enter if keyboard_event.state == KeyState::Up => {
|
||||
let maybe_elem = target.downcast::<Element>();
|
||||
if let Some(el) = maybe_elem {
|
||||
if let Some(a) = el.as_maybe_activatable() {
|
||||
let ctrl = keyboard_event.modifiers.contains(Modifiers::CONTROL);
|
||||
let alt = keyboard_event.modifiers.contains(Modifiers::ALT);
|
||||
let shift = keyboard_event.modifiers.contains(Modifiers::SHIFT);
|
||||
let meta = keyboard_event.modifiers.contains(Modifiers::META);
|
||||
a.implicit_submission(ctrl, alt, shift, meta);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue