From 0ccaa7e1a9e9bf9472d869576019b9cda350ad87 Mon Sep 17 00:00:00 2001 From: Pyfisch Date: Sat, 6 Oct 2018 17:35:45 +0200 Subject: [PATCH] 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 --- Cargo.lock | 23 +- components/compositing/Cargo.toml | 1 + components/compositing/lib.rs | 1 + components/compositing/windowing.rs | 7 +- components/constellation/Cargo.toml | 1 + components/constellation/constellation.rs | 16 +- components/constellation/lib.rs | 1 + components/embedder_traits/Cargo.toml | 1 + components/embedder_traits/lib.rs | 8 +- components/malloc_size_of/Cargo.toml | 2 + components/malloc_size_of/lib.rs | 15 + components/msg/Cargo.toml | 1 - components/msg/constellation_msg.rs | 147 ----- components/msg/lib.rs | 2 - components/script/Cargo.toml | 1 + components/script/dom/document.rs | 103 ++- components/script/dom/keyboardevent.rs | 761 +++------------------- components/script/lib.rs | 1 + components/script/script_thread.rs | 7 +- components/script/textinput.rs | 174 +++-- components/script_traits/Cargo.toml | 1 + components/script_traits/lib.rs | 12 +- components/servo/lib.rs | 12 +- components/webdriver_server/Cargo.toml | 1 + components/webdriver_server/keys.rs | 263 +++----- components/webdriver_server/lib.rs | 8 +- ports/libsimpleservo/src/api.rs | 2 +- ports/servo/Cargo.toml | 1 + ports/servo/browser.rs | 246 +++---- ports/servo/glutin_app/keyutils.rs | 427 ++++++------ ports/servo/glutin_app/window.rs | 90 +-- ports/servo/non_android_main.rs | 1 + tests/unit/script/Cargo.toml | 2 +- tests/unit/script/lib.rs | 2 +- tests/unit/script/textinput.rs | 25 +- 35 files changed, 762 insertions(+), 1604 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35c513184b9..7c2eb5a4c63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -512,6 +512,7 @@ dependencies = [ "gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", @@ -548,6 +549,7 @@ dependencies = [ "gfx_traits 0.0.1", "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "layout_traits 0.0.1", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "metrics 0.0.1", @@ -900,6 +902,7 @@ name = "embedder_traits" version = "0.0.1" dependencies = [ "ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", @@ -1731,6 +1734,15 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "keyboard-types" +version = "0.4.0-servo" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "khronos_api" version = "2.1.0" @@ -2018,6 +2030,7 @@ dependencies = [ "hashglobe 0.1.0", "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", "hyper_serde 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "mozjs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.20.0", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2270,7 +2283,6 @@ dependencies = [ name = "msg" version = "0.0.1" dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", "malloc_size_of_derive 0.0.1", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2847,7 +2859,7 @@ dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3009,6 +3021,7 @@ dependencies = [ "ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "jstraceable_derive 0.0.1", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3111,7 +3124,7 @@ name = "script_tests" version = "0.0.1" dependencies = [ "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "msg 0.0.1", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "script 0.0.1", "servo_url 0.0.1", ] @@ -3130,6 +3143,7 @@ dependencies = [ "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", "hyper_serde 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", "malloc_size_of_derive 0.0.1", @@ -3218,6 +3232,7 @@ dependencies = [ "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libservo 0.0.1", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4049,6 +4064,7 @@ dependencies = [ "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", @@ -4476,6 +4492,7 @@ dependencies = [ "checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" "checksum jpeg-decoder 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0dfe27a6c0dabd772d0f9b9f8701c4ca12c4d1eebcadf2be1f6f70396f6a1434" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum keyboard-types 0.4.0-servo (registry+https://github.com/rust-lang/crates.io-index)" = "4bfd54c7c93e29a8a7ef177160b9898a479e31220918493fbfab49744008d172" "checksum khronos_api 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ef23fcc4059260c5936f638c9805ebfc87cb172fa6661d130cba7f97d58f55" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index 3f2096d9557..994599ed095 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -21,6 +21,7 @@ gleam = {version = "0.6", optional = true} image = "0.19" ipc-channel = "0.11" libc = "0.2" +keyboard-types = {version = "0.4.0-serde", features = ["serde"]} log = "0.4" msg = {path = "../msg"} net_traits = {path = "../net_traits"} diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index 6b8bfb5aaf7..c5005a6a425 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -12,6 +12,7 @@ extern crate gleam; #[cfg(feature = "gleam")] extern crate image; extern crate ipc_channel; +extern crate keyboard_types; extern crate libc; #[macro_use] extern crate log; diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 2e773e37aa7..47a8a4e6b5f 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -8,7 +8,8 @@ use embedder_traits::EventLoopWaker; use euclid::TypedScale; #[cfg(feature = "gleam")] use gleam::gl; -use msg::constellation_msg::{Key, KeyModifiers, KeyState, TopLevelBrowsingContextId, TraversalDirection}; +use keyboard_types::KeyboardEvent; +use msg::constellation_msg::{TopLevelBrowsingContextId, TraversalDirection}; use script_traits::{MouseButton, TouchEventType, TouchId}; use servo_geometry::{DeviceIndependentPixel, DeviceUintLength}; use servo_url::ServoUrl; @@ -70,7 +71,7 @@ pub enum WindowEvent { /// Sent when the user quits the application Quit, /// Sent when a key input state changes - KeyEvent(Option, Key, KeyState, KeyModifiers), + Keyboard(KeyboardEvent), /// Sent when Ctr+R/Apple+R is called to reload the current page. Reload(TopLevelBrowsingContextId), /// Create a new top level browsing context @@ -94,7 +95,7 @@ impl Debug for WindowEvent { WindowEvent::Idle => write!(f, "Idle"), WindowEvent::Refresh => write!(f, "Refresh"), WindowEvent::Resize => write!(f, "Resize"), - WindowEvent::KeyEvent(..) => write!(f, "Key"), + WindowEvent::Keyboard(..) => write!(f, "Keyboard"), WindowEvent::LoadUrl(..) => write!(f, "LoadUrl"), WindowEvent::MouseWindowEventClass(..) => write!(f, "Mouse"), WindowEvent::MouseWindowMoveEventClass(..) => write!(f, "MouseMove"), diff --git a/components/constellation/Cargo.toml b/components/constellation/Cargo.toml index 599f9f7e6e0..d3569f3b7fe 100644 --- a/components/constellation/Cargo.toml +++ b/components/constellation/Cargo.toml @@ -25,6 +25,7 @@ gfx_traits = {path = "../gfx_traits"} hyper = "0.10" ipc-channel = "0.11" layout_traits = {path = "../layout_traits"} +keyboard-types = {version = "0.4.0-serde", features = ["serde"]} log = "0.4" metrics = {path = "../metrics"} msg = {path = "../msg"} diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 4513058be02..a54ac7fed7f 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -111,10 +111,10 @@ use gfx_traits::Epoch; use ipc_channel::{Error as IpcError}; use ipc_channel::ipc::{self, IpcSender, IpcReceiver}; use ipc_channel::router::ROUTER; +use keyboard_types::KeyboardEvent; use layout_traits::LayoutThreadFactory; use log::{Log, Level, LevelFilter, Metadata, Record}; use msg::constellation_msg::{BrowsingContextId, PipelineId, HistoryStateId, TopLevelBrowsingContextId}; -use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection}; use net_traits::{self, IpcSend, FetchResponseMsg, ResourceThreads}; use net_traits::pub_domains::reg_host; @@ -972,8 +972,8 @@ where }); let _ = resp_chan.send(focus_browsing_context); }, - FromCompositorMsg::KeyEvent(ch, key, state, modifiers) => { - self.handle_key_msg(ch, key, state, modifiers); + FromCompositorMsg::Keyboard(key_event) => { + self.handle_key_msg(key_event); }, // Load a new page from a typed url // If there is already a pending page (self.pending_changes), it will not be overridden; @@ -2622,12 +2622,12 @@ where session_history.replace_history_state(pipeline_id, history_state_id, url); } - fn handle_key_msg(&mut self, ch: Option, key: Key, state: KeyState, mods: KeyModifiers) { + fn handle_key_msg(&mut self, event: KeyboardEvent) { // Send to the focused browsing contexts' current pipeline. If it // doesn't exist, fall back to sending to the compositor. match self.focused_browsing_context_id { Some(browsing_context_id) => { - let event = CompositorEvent::KeyEvent(ch, key, state, mods); + let event = CompositorEvent::KeyboardEvent(event); let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { Some(ctx) => ctx.pipeline_id, None => return warn!( @@ -2647,7 +2647,7 @@ where } }, None => { - let event = (None, EmbedderMsg::KeyEvent(ch, key, state, mods)); + let event = (None, EmbedderMsg::Keyboard(event)); self.embedder_proxy.clone().send(event); }, } @@ -2942,8 +2942,8 @@ where Some(pipeline) => pipeline.event_loop.clone(), None => return warn!("Pipeline {} SendKeys after closure.", pipeline_id), }; - for (key, mods, state) in cmd { - let event = CompositorEvent::KeyEvent(None, key, state, mods); + for event in cmd { + let event = CompositorEvent::KeyboardEvent(event); let control_msg = ConstellationControlMsg::SendEvent(pipeline_id, event); if let Err(e) = event_loop.send(control_msg) { return self.handle_send_error(pipeline_id, e); diff --git a/components/constellation/lib.rs b/components/constellation/lib.rs index 1e0e9ed8ed4..f5e23ba3c09 100644 --- a/components/constellation/lib.rs +++ b/components/constellation/lib.rs @@ -22,6 +22,7 @@ extern crate gfx; extern crate gfx_traits; extern crate hyper; extern crate ipc_channel; +extern crate keyboard_types; extern crate layout_traits; #[macro_use] extern crate log; diff --git a/components/embedder_traits/Cargo.toml b/components/embedder_traits/Cargo.toml index da187d7d370..e125376e8c2 100644 --- a/components/embedder_traits/Cargo.toml +++ b/components/embedder_traits/Cargo.toml @@ -14,6 +14,7 @@ tests = [] [dependencies] ipc-channel = "0.11" +keyboard-types = {version = "0.4.0-serde", features = ["serde"]} lazy_static = "1" log = "0.4" msg = {path = "../msg"} diff --git a/components/embedder_traits/lib.rs b/components/embedder_traits/lib.rs index 6bb9281b7d2..48c231ca3cd 100644 --- a/components/embedder_traits/lib.rs +++ b/components/embedder_traits/lib.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ extern crate ipc_channel; +extern crate keyboard_types; #[macro_use] extern crate lazy_static; #[macro_use] @@ -18,7 +19,8 @@ extern crate webrender_api; pub mod resources; use ipc_channel::ipc::IpcSender; -use msg::constellation_msg::{InputMethodType, Key, KeyModifiers, KeyState, TopLevelBrowsingContextId}; +use keyboard_types::KeyboardEvent; +use msg::constellation_msg::{InputMethodType, TopLevelBrowsingContextId}; use servo_channel::{Receiver, Sender}; use servo_url::ServoUrl; use std::fmt::{Debug, Error, Formatter}; @@ -93,7 +95,7 @@ pub enum EmbedderMsg { /// Wether or not to unload a document AllowUnload(IpcSender), /// Sends an unconsumed key event back to the embedder. - KeyEvent(Option, Key, KeyState, KeyModifiers), + Keyboard(KeyboardEvent), /// Changes the cursor. SetCursor(CursorKind), /// A favicon was detected @@ -134,7 +136,7 @@ impl Debug for EmbedderMsg { EmbedderMsg::Alert(..) => write!(f, "Alert"), EmbedderMsg::AllowUnload(..) => write!(f, "AllowUnload"), EmbedderMsg::AllowNavigation(..) => write!(f, "AllowNavigation"), - EmbedderMsg::KeyEvent(..) => write!(f, "KeyEvent"), + EmbedderMsg::Keyboard(..) => write!(f, "Keyboard"), EmbedderMsg::SetCursor(..) => write!(f, "SetCursor"), EmbedderMsg::NewFavicon(..) => write!(f, "NewFavicon"), EmbedderMsg::HeadParsed => write!(f, "HeadParsed"), diff --git a/components/malloc_size_of/Cargo.toml b/components/malloc_size_of/Cargo.toml index cd05b5d50c0..6907118f58a 100644 --- a/components/malloc_size_of/Cargo.toml +++ b/components/malloc_size_of/Cargo.toml @@ -12,6 +12,7 @@ path = "lib.rs" servo = [ "hyper", "hyper_serde", + "keyboard-types", "mozjs", "serde", "serde_bytes", @@ -30,6 +31,7 @@ euclid = "0.19" hashglobe = { path = "../hashglobe" } hyper = { version = "0.10", optional = true } hyper_serde = { version = "0.8", optional = true } +keyboard-types = {version = "0.4.0-serde", features = ["serde"], optional = true} mozjs = { version = "0.9.0", optional = true } selectors = { path = "../selectors" } serde = { version = "1.0.27", optional = true } diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs index 72b7f81595a..df2b5a442dd 100644 --- a/components/malloc_size_of/lib.rs +++ b/components/malloc_size_of/lib.rs @@ -52,6 +52,8 @@ extern crate hyper; #[cfg(feature = "servo")] extern crate hyper_serde; #[cfg(feature = "servo")] +extern crate keyboard_types; +#[cfg(feature = "servo")] extern crate mozjs as js; extern crate selectors; #[cfg(feature = "servo")] @@ -957,6 +959,19 @@ malloc_size_of_is_0!(webrender_api::StickyOffsetBounds); #[cfg(feature = "webrender_api")] malloc_size_of_is_0!(webrender_api::TransformStyle); +#[cfg(feature = "servo")] +impl MallocSizeOf for keyboard_types::Key { + fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + match self { + keyboard_types::Key::Character(ref s) => s.size_of(ops), + _ => 0, + } + } +} + +#[cfg(feature = "servo")] +malloc_size_of_is_0!(keyboard_types::Modifiers); + #[cfg(feature = "servo")] impl MallocSizeOf for xml5ever::QualName { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { diff --git a/components/msg/Cargo.toml b/components/msg/Cargo.toml index 86a9dac0aa9..bfe3483113b 100644 --- a/components/msg/Cargo.toml +++ b/components/msg/Cargo.toml @@ -12,7 +12,6 @@ test = false doctest = false [dependencies] -bitflags = "1.0" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = { path = "../malloc_size_of_derive" } serde = "1.0.60" diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 46697ad7645..c315a63ee44 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -10,153 +10,6 @@ use std::fmt; use std::num::NonZeroU32; use webrender_api; -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub enum KeyState { - Pressed, - Released, - Repeated, -} - -//N.B. Based on the glutin key enum -#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] -pub enum Key { - Space, - Apostrophe, - Comma, - Minus, - Period, - Slash, - Num0, - Num1, - Num2, - Num3, - Num4, - Num5, - Num6, - Num7, - Num8, - Num9, - Semicolon, - Equal, - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - LeftBracket, - Backslash, - RightBracket, - GraveAccent, - World1, - World2, - - Escape, - Enter, - Tab, - Backspace, - Insert, - Delete, - Right, - Left, - Down, - Up, - PageUp, - PageDown, - Home, - End, - CapsLock, - ScrollLock, - NumLock, - PrintScreen, - Pause, - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - F25, - Kp0, - Kp1, - Kp2, - Kp3, - Kp4, - Kp5, - Kp6, - Kp7, - Kp8, - Kp9, - KpDecimal, - KpDivide, - KpMultiply, - KpSubtract, - KpAdd, - KpEnter, - KpEqual, - LeftShift, - LeftControl, - LeftAlt, - LeftSuper, - RightShift, - RightControl, - RightAlt, - RightSuper, - Menu, - - NavigateBackward, - NavigateForward, -} - -bitflags! { - #[derive(Deserialize, Serialize)] - pub struct KeyModifiers: u8 { - const NONE = 0x00; - const SHIFT = 0x01; - const CONTROL = 0x02; - const ALT = 0x04; - const SUPER = 0x08; - } -} - #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum TraversalDirection { Forward(usize), diff --git a/components/msg/lib.rs b/components/msg/lib.rs index ec9619bab05..3e515e294f8 100644 --- a/components/msg/lib.rs +++ b/components/msg/lib.rs @@ -4,8 +4,6 @@ #![deny(unsafe_code)] -#[macro_use] -extern crate bitflags; #[macro_use] extern crate malloc_size_of; #[macro_use] diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index ca859bb472a..88353ab9023 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -59,6 +59,7 @@ image = "0.19" ipc-channel = "0.11" itertools = "0.7.6" jstraceable_derive = {path = "../jstraceable_derive"} +keyboard-types = {version = "0.4.0-serde", features = ["serde"]} lazy_static = "1" libc = "0.2" log = "0.4" diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index d68f085af83..72d74b61ae4 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -61,7 +61,7 @@ use dom::htmlimageelement::HTMLImageElement; use dom::htmlmetaelement::HTMLMetaElement; use dom::htmlscriptelement::{HTMLScriptElement, ScriptResult}; use dom::htmltitleelement::HTMLTitleElement; -use dom::keyboardevent::KeyboardEvent; +use dom::keyboardevent::{KeyboardEvent, key_keycode}; use dom::location::Location; use dom::messageevent::MessageEvent; use dom::mouseevent::MouseEvent; @@ -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}; @@ -1350,36 +1351,39 @@ impl Document { /// The entry point for all key processing for web content pub fn dispatch_key_event( &self, - ch: Option, - key: Key, - state: KeyState, - modifiers: KeyModifiers, + keyboard_event: ::keyboard_types::KeyboardEvent, ) { let focused = self.get_focused_element(); let body = self.GetBody(); + let printable = match keyboard_event.key { + Key::Character(_) => true, + _ => false, + }; + let target = match (&focused, &body) { (&Some(ref focused), _) => focused.upcast(), (&None, &Some(ref body)) => body.upcast(), (&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); + match keyboard_event.state { + KeyState::Down => "keydown", + KeyState::Up => "keyup", + }.to_owned()); + let char_code = if let Key::Character(ref c) = keyboard_event.key { + let mut chars = c.chars(); + let n = chars.next().map(|c| c as u32); + if chars.next().is_some() { + None + } else { + n + } + } else { + None + }; let keyevent = KeyboardEvent::new( &self.window, ev_type, @@ -1387,29 +1391,21 @@ impl Document { 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, + char_code, + key_keycode(&keyboard_event.key), ); let event = keyevent.upcast::(); 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() && - cancel_state != EventDefault::Prevented - { + if keyboard_event.state != KeyState::Up && printable && cancel_state != EventDefault::Prevented { // https://w3c.github.io/uievents/#keypress-event-order let event = KeyboardEvent::new( &self.window, @@ -1418,18 +1414,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, + None, 0, ); let ev = event.upcast::(); @@ -1438,7 +1429,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 +1437,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 - 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::(); if let Some(el) = maybe_elem { synthetic_click_activation( @@ -1459,11 +1450,15 @@ impl Document { ActivationSource::NotFromClick, ) } - }, - Key::Enter if state == KeyState::Released => { + } + Key::Enter if keyboard_event.state == KeyState::Up => { let maybe_elem = target.downcast::(); 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); } } diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index 81986eaf5b2..46345ea739e 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -4,7 +4,7 @@ use dom::bindings::cell::DomRefCell; use dom::bindings::codegen::Bindings::KeyboardEventBinding; -use dom::bindings::codegen::Bindings::KeyboardEventBinding::{KeyboardEventConstants, KeyboardEventMethods}; +use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; use dom::bindings::error::Fallible; use dom::bindings::inheritance::Castable; @@ -15,23 +15,20 @@ use dom::event::Event; use dom::uievent::UIEvent; use dom::window::Window; use dom_struct::dom_struct; -use msg::constellation_msg::{Key, KeyModifiers}; -use std::borrow::Cow; +use keyboard_types::{Key, Modifiers}; use std::cell::Cell; unsafe_no_jsmanaged_fields!(Key); +unsafe_no_jsmanaged_fields!(Modifiers); #[dom_struct] pub struct KeyboardEvent { uievent: UIEvent, - key: Cell>, - key_string: DomRefCell, + key: DomRefCell, + typed_key: DomRefCell, code: DomRefCell, location: Cell, - ctrl: Cell, - alt: Cell, - shift: Cell, - meta: Cell, + modifiers: Cell, repeat: Cell, is_composing: Cell, char_code: Cell>, @@ -43,14 +40,11 @@ impl KeyboardEvent { fn new_inherited() -> KeyboardEvent { KeyboardEvent { uievent: UIEvent::new_inherited(), - key: Cell::new(None), - key_string: DomRefCell::new(DOMString::new()), + key: DomRefCell::new(DOMString::new()), + typed_key: DomRefCell::new(Key::Unidentified), code: DomRefCell::new(DOMString::new()), location: Cell::new(0), - ctrl: Cell::new(false), - alt: Cell::new(false), - shift: Cell::new(false), - meta: Cell::new(false), + modifiers: Cell::new(Modifiers::empty()), repeat: Cell::new(false), is_composing: Cell::new(false), char_code: Cell::new(None), @@ -74,17 +68,12 @@ impl KeyboardEvent { cancelable: bool, view: Option<&Window>, _detail: i32, - ch: Option, - key: Option, - key_string: DOMString, + key: Key, code: DOMString, location: u32, repeat: bool, is_composing: bool, - ctrl_key: bool, - alt_key: bool, - shift_key: bool, - meta_key: bool, + modifiers: Modifiers, char_code: Option, key_code: u32, ) -> DomRoot { @@ -94,22 +83,18 @@ impl KeyboardEvent { can_bubble, cancelable, view, - key_string, + DOMString::from(key.to_string()), location, DOMString::new(), repeat, DOMString::new(), ); - ev.key.set(key); + *ev.typed_key.borrow_mut() = key; *ev.code.borrow_mut() = code; - ev.ctrl.set(ctrl_key); - ev.alt.set(alt_key); - ev.shift.set(shift_key); - ev.meta.set(meta_key); - ev.char_code.set(char_code); - ev.printable.set(ch); - ev.key_code.set(key_code); + ev.modifiers.set(modifiers); ev.is_composing.set(is_composing); + ev.char_code.set(char_code); + ev.key_code.set(key_code); ev } @@ -118,6 +103,11 @@ impl KeyboardEvent { type_: DOMString, init: &KeyboardEventBinding::KeyboardEventInit, ) -> Fallible> { + let mut modifiers = Modifiers::empty(); + modifiers.set(Modifiers::CONTROL, init.parent.ctrlKey); + modifiers.set(Modifiers::ALT, init.parent.altKey); + modifiers.set(Modifiers::SHIFT, init.parent.shiftKey); + modifiers.set(Modifiers::META, init.parent.metaKey); let event = KeyboardEvent::new( window, type_, @@ -125,32 +115,18 @@ impl KeyboardEvent { init.parent.parent.parent.cancelable, init.parent.parent.view.r(), init.parent.parent.detail, - None, - key_from_string(&init.key, init.location), - init.key.clone(), + Key::Unidentified, init.code.clone(), init.location, init.repeat, init.isComposing, - init.parent.ctrlKey, - init.parent.altKey, - init.parent.shiftKey, - init.parent.metaKey, + modifiers, None, 0, ); + *event.key.borrow_mut() = init.key.clone(); Ok(event) } - - pub fn key_properties(ch: Option, key: Key, mods: KeyModifiers) -> KeyEventProperties { - KeyEventProperties { - key_string: key_value(ch, key, mods), - code: code_value(key), - location: key_location(key), - char_code: ch.map(|ch| ch as u32), - key_code: key_keycode(key), - } - } } impl KeyboardEvent { @@ -158,650 +134,63 @@ impl KeyboardEvent { self.printable.get() } - pub fn get_key(&self) -> Option { - self.key.get().clone() + pub fn key(&self) -> Key { + self.typed_key.borrow().clone() } - pub fn get_key_modifiers(&self) -> KeyModifiers { - let mut result = KeyModifiers::empty(); - if self.shift.get() { - result = result | KeyModifiers::SHIFT; - } - if self.ctrl.get() { - result = result | KeyModifiers::CONTROL; - } - if self.alt.get() { - result = result | KeyModifiers::ALT; - } - if self.meta.get() { - result = result | KeyModifiers::SUPER; - } - result - } -} - -// https://w3c.github.io/uievents-key/#key-value-tables -pub fn key_value(ch: Option, key: Key, mods: KeyModifiers) -> Cow<'static, str> { - if let Some(ch) = ch { - return Cow::from(format!("{}", ch)); - } - - let shift = mods.contains(KeyModifiers::SHIFT); - Cow::from(match key { - Key::Space => " ", - Key::Apostrophe if shift => "\"", - Key::Apostrophe => "'", - Key::Comma if shift => "<", - Key::Comma => ",", - Key::Minus if shift => "_", - Key::Minus => "-", - Key::Period if shift => ">", - Key::Period => ".", - Key::Slash if shift => "?", - Key::Slash => "/", - Key::GraveAccent if shift => "~", - Key::GraveAccent => "`", - Key::Num0 if shift => ")", - Key::Num0 => "0", - Key::Num1 if shift => "!", - Key::Num1 => "1", - Key::Num2 if shift => "@", - Key::Num2 => "2", - Key::Num3 if shift => "#", - Key::Num3 => "3", - Key::Num4 if shift => "$", - Key::Num4 => "4", - Key::Num5 if shift => "%", - Key::Num5 => "5", - Key::Num6 if shift => "^", - Key::Num6 => "6", - Key::Num7 if shift => "&", - Key::Num7 => "7", - Key::Num8 if shift => "*", - Key::Num8 => "8", - Key::Num9 if shift => "(", - Key::Num9 => "9", - Key::Semicolon if shift => ":", - Key::Semicolon => ";", - Key::Equal if shift => "+", - Key::Equal => "=", - Key::A if shift => "A", - Key::A => "a", - Key::B if shift => "B", - Key::B => "b", - Key::C if shift => "C", - Key::C => "c", - Key::D if shift => "D", - Key::D => "d", - Key::E if shift => "E", - Key::E => "e", - Key::F if shift => "F", - Key::F => "f", - Key::G if shift => "G", - Key::G => "g", - Key::H if shift => "H", - Key::H => "h", - Key::I if shift => "I", - Key::I => "i", - Key::J if shift => "J", - Key::J => "j", - Key::K if shift => "K", - Key::K => "k", - Key::L if shift => "L", - Key::L => "l", - Key::M if shift => "M", - Key::M => "m", - Key::N if shift => "N", - Key::N => "n", - Key::O if shift => "O", - Key::O => "o", - Key::P if shift => "P", - Key::P => "p", - Key::Q if shift => "Q", - Key::Q => "q", - Key::R if shift => "R", - Key::R => "r", - Key::S if shift => "S", - Key::S => "s", - Key::T if shift => "T", - Key::T => "t", - Key::U if shift => "U", - Key::U => "u", - Key::V if shift => "V", - Key::V => "v", - Key::W if shift => "W", - Key::W => "w", - Key::X if shift => "X", - Key::X => "x", - Key::Y if shift => "Y", - Key::Y => "y", - Key::Z if shift => "Z", - Key::Z => "z", - Key::LeftBracket if shift => "{", - Key::LeftBracket => "[", - Key::Backslash if shift => "|", - Key::Backslash => "\\", - Key::RightBracket if shift => "}", - Key::RightBracket => "]", - Key::World1 => "Unidentified", - Key::World2 => "Unidentified", - Key::Escape => "Escape", - Key::Enter => "Enter", - Key::Tab => "Tab", - Key::Backspace => "Backspace", - Key::Insert => "Insert", - Key::Delete => "Delete", - Key::Right => "ArrowRight", - Key::Left => "ArrowLeft", - Key::Down => "ArrowDown", - Key::Up => "ArrowUp", - Key::PageUp => "PageUp", - Key::PageDown => "PageDown", - Key::Home => "Home", - Key::End => "End", - Key::CapsLock => "CapsLock", - Key::ScrollLock => "ScrollLock", - Key::NumLock => "NumLock", - Key::PrintScreen => "PrintScreen", - Key::Pause => "Pause", - Key::F1 => "F1", - Key::F2 => "F2", - Key::F3 => "F3", - Key::F4 => "F4", - Key::F5 => "F5", - Key::F6 => "F6", - Key::F7 => "F7", - Key::F8 => "F8", - Key::F9 => "F9", - Key::F10 => "F10", - Key::F11 => "F11", - Key::F12 => "F12", - Key::F13 => "F13", - Key::F14 => "F14", - Key::F15 => "F15", - Key::F16 => "F16", - Key::F17 => "F17", - Key::F18 => "F18", - Key::F19 => "F19", - Key::F20 => "F20", - Key::F21 => "F21", - Key::F22 => "F22", - Key::F23 => "F23", - Key::F24 => "F24", - Key::F25 => "F25", - Key::Kp0 => "0", - Key::Kp1 => "1", - Key::Kp2 => "2", - Key::Kp3 => "3", - Key::Kp4 => "4", - Key::Kp5 => "5", - Key::Kp6 => "6", - Key::Kp7 => "7", - Key::Kp8 => "8", - Key::Kp9 => "9", - Key::KpDecimal => ".", - Key::KpDivide => "/", - Key::KpMultiply => "*", - Key::KpSubtract => "-", - Key::KpAdd => "+", - Key::KpEnter => "Enter", - Key::KpEqual => "=", - Key::LeftShift => "Shift", - Key::LeftControl => "Control", - Key::LeftAlt => "Alt", - Key::LeftSuper => "Super", - Key::RightShift => "Shift", - Key::RightControl => "Control", - Key::RightAlt => "Alt", - Key::RightSuper => "Super", - Key::Menu => "ContextMenu", - Key::NavigateForward => "BrowserForward", - Key::NavigateBackward => "BrowserBack", - }) -} - -fn key_from_string(key_string: &str, location: u32) -> Option { - match key_string { - " " => Some(Key::Space), - "\"" => Some(Key::Apostrophe), - "'" => Some(Key::Apostrophe), - "<" => Some(Key::Comma), - "," => Some(Key::Comma), - "_" => Some(Key::Minus), - "-" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Minus), - ">" => Some(Key::Period), - "." if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Period), - "?" => Some(Key::Slash), - "/" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Slash), - "~" => Some(Key::GraveAccent), - "`" => Some(Key::GraveAccent), - ")" => Some(Key::Num0), - "0" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num0), - "!" => Some(Key::Num1), - "1" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num1), - "@" => Some(Key::Num2), - "2" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num2), - "#" => Some(Key::Num3), - "3" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num3), - "$" => Some(Key::Num4), - "4" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num4), - "%" => Some(Key::Num5), - "5" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num5), - "^" => Some(Key::Num6), - "6" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num6), - "&" => Some(Key::Num7), - "7" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num7), - "*" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num8), - "8" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num8), - "(" => Some(Key::Num9), - "9" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num9), - ":" => Some(Key::Semicolon), - ";" => Some(Key::Semicolon), - "+" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Equal), - "=" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Equal), - "A" => Some(Key::A), - "a" => Some(Key::A), - "B" => Some(Key::B), - "b" => Some(Key::B), - "C" => Some(Key::C), - "c" => Some(Key::C), - "D" => Some(Key::D), - "d" => Some(Key::D), - "E" => Some(Key::E), - "e" => Some(Key::E), - "F" => Some(Key::F), - "f" => Some(Key::F), - "G" => Some(Key::G), - "g" => Some(Key::G), - "H" => Some(Key::H), - "h" => Some(Key::H), - "I" => Some(Key::I), - "i" => Some(Key::I), - "J" => Some(Key::J), - "j" => Some(Key::J), - "K" => Some(Key::K), - "k" => Some(Key::K), - "L" => Some(Key::L), - "l" => Some(Key::L), - "M" => Some(Key::M), - "m" => Some(Key::M), - "N" => Some(Key::N), - "n" => Some(Key::N), - "O" => Some(Key::O), - "o" => Some(Key::O), - "P" => Some(Key::P), - "p" => Some(Key::P), - "Q" => Some(Key::Q), - "q" => Some(Key::Q), - "R" => Some(Key::R), - "r" => Some(Key::R), - "S" => Some(Key::S), - "s" => Some(Key::S), - "T" => Some(Key::T), - "t" => Some(Key::T), - "U" => Some(Key::U), - "u" => Some(Key::U), - "V" => Some(Key::V), - "v" => Some(Key::V), - "W" => Some(Key::W), - "w" => Some(Key::W), - "X" => Some(Key::X), - "x" => Some(Key::X), - "Y" => Some(Key::Y), - "y" => Some(Key::Y), - "Z" => Some(Key::Z), - "z" => Some(Key::Z), - "{" => Some(Key::LeftBracket), - "[" => Some(Key::LeftBracket), - "|" => Some(Key::Backslash), - "\\" => Some(Key::Backslash), - "}" => Some(Key::RightBracket), - "]" => Some(Key::RightBracket), - "Escape" => Some(Key::Escape), - "Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => { - Some(Key::Enter) - }, - "Tab" => Some(Key::Tab), - "Backspace" => Some(Key::Backspace), - "Insert" => Some(Key::Insert), - "Delete" => Some(Key::Delete), - "ArrowRight" => Some(Key::Right), - "ArrowLeft" => Some(Key::Left), - "ArrowDown" => Some(Key::Down), - "ArrowUp" => Some(Key::Up), - "PageUp" => Some(Key::PageUp), - "PageDown" => Some(Key::PageDown), - "Home" => Some(Key::Home), - "End" => Some(Key::End), - "CapsLock" => Some(Key::CapsLock), - "ScrollLock" => Some(Key::ScrollLock), - "NumLock" => Some(Key::NumLock), - "PrintScreen" => Some(Key::PrintScreen), - "Pause" => Some(Key::Pause), - "F1" => Some(Key::F1), - "F2" => Some(Key::F2), - "F3" => Some(Key::F3), - "F4" => Some(Key::F4), - "F5" => Some(Key::F5), - "F6" => Some(Key::F6), - "F7" => Some(Key::F7), - "F8" => Some(Key::F8), - "F9" => Some(Key::F9), - "F10" => Some(Key::F10), - "F11" => Some(Key::F11), - "F12" => Some(Key::F12), - "F13" => Some(Key::F13), - "F14" => Some(Key::F14), - "F15" => Some(Key::F15), - "F16" => Some(Key::F16), - "F17" => Some(Key::F17), - "F18" => Some(Key::F18), - "F19" => Some(Key::F19), - "F20" => Some(Key::F20), - "F21" => Some(Key::F21), - "F22" => Some(Key::F22), - "F23" => Some(Key::F23), - "F24" => Some(Key::F24), - "F25" => Some(Key::F25), - "0" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp0), - "1" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp1), - "2" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp2), - "3" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp3), - "4" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp4), - "5" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp5), - "6" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp6), - "7" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp7), - "8" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp8), - "9" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp9), - "." if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpDecimal), - "/" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpDivide), - "*" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpMultiply), - "-" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpSubtract), - "+" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpAdd), - "Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => { - Some(Key::KpEnter) - }, - "=" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpEqual), - "Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => { - Some(Key::LeftShift) - }, - "Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => { - Some(Key::LeftControl) - }, - "Alt" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftAlt), - "Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => { - Some(Key::LeftSuper) - }, - "Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => { - Some(Key::RightShift) - }, - "Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => { - Some(Key::RightControl) - }, - "Alt" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightAlt), - "Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => { - Some(Key::RightSuper) - }, - "ContextMenu" => Some(Key::Menu), - "BrowserForward" => Some(Key::NavigateForward), - "BrowserBack" => Some(Key::NavigateBackward), - _ => None, - } -} - -// https://w3c.github.io/uievents-code/#code-value-tables -fn code_value(key: Key) -> &'static str { - match key { - Key::Space => "Space", - Key::Apostrophe => "Quote", - Key::Comma => "Comma", - Key::Minus => "Minus", - Key::Period => "Period", - Key::Slash => "Slash", - Key::GraveAccent => "Backquote", - Key::Num0 => "Digit0", - Key::Num1 => "Digit1", - Key::Num2 => "Digit2", - Key::Num3 => "Digit3", - Key::Num4 => "Digit4", - Key::Num5 => "Digit5", - Key::Num6 => "Digit6", - Key::Num7 => "Digit7", - Key::Num8 => "Digit8", - Key::Num9 => "Digit9", - Key::Semicolon => "Semicolon", - Key::Equal => "Equal", - Key::A => "KeyA", - Key::B => "KeyB", - Key::C => "KeyC", - Key::D => "KeyD", - Key::E => "KeyE", - Key::F => "KeyF", - Key::G => "KeyG", - Key::H => "KeyH", - Key::I => "KeyI", - Key::J => "KeyJ", - Key::K => "KeyK", - Key::L => "KeyL", - Key::M => "KeyM", - Key::N => "KeyN", - Key::O => "KeyO", - Key::P => "KeyP", - Key::Q => "KeyQ", - Key::R => "KeyR", - Key::S => "KeyS", - Key::T => "KeyT", - Key::U => "KeyU", - Key::V => "KeyV", - Key::W => "KeyW", - Key::X => "KeyX", - Key::Y => "KeyY", - Key::Z => "KeyZ", - Key::LeftBracket => "BracketLeft", - Key::Backslash => "Backslash", - Key::RightBracket => "BracketRight", - - Key::World1 | Key::World2 => panic!("unknown char code for {:?}", key), - - Key::Escape => "Escape", - Key::Enter => "Enter", - Key::Tab => "Tab", - Key::Backspace => "Backspace", - Key::Insert => "Insert", - Key::Delete => "Delete", - Key::Right => "ArrowRight", - Key::Left => "ArrowLeft", - Key::Down => "ArrowDown", - Key::Up => "ArrowUp", - Key::PageUp => "PageUp", - Key::PageDown => "PageDown", - Key::Home => "Home", - Key::End => "End", - Key::CapsLock => "CapsLock", - Key::ScrollLock => "ScrollLock", - Key::NumLock => "NumLock", - Key::PrintScreen => "PrintScreen", - Key::Pause => "Pause", - Key::F1 => "F1", - Key::F2 => "F2", - Key::F3 => "F3", - Key::F4 => "F4", - Key::F5 => "F5", - Key::F6 => "F6", - Key::F7 => "F7", - Key::F8 => "F8", - Key::F9 => "F9", - Key::F10 => "F10", - Key::F11 => "F11", - Key::F12 => "F12", - Key::F13 => "F13", - Key::F14 => "F14", - Key::F15 => "F15", - Key::F16 => "F16", - Key::F17 => "F17", - Key::F18 => "F18", - Key::F19 => "F19", - Key::F20 => "F20", - Key::F21 => "F21", - Key::F22 => "F22", - Key::F23 => "F23", - Key::F24 => "F24", - Key::F25 => "F25", - Key::Kp0 => "Numpad0", - Key::Kp1 => "Numpad1", - Key::Kp2 => "Numpad2", - Key::Kp3 => "Numpad3", - Key::Kp4 => "Numpad4", - Key::Kp5 => "Numpad5", - Key::Kp6 => "Numpad6", - Key::Kp7 => "Numpad7", - Key::Kp8 => "Numpad8", - Key::Kp9 => "Numpad9", - Key::KpDecimal => "NumpadDecimal", - Key::KpDivide => "NumpadDivide", - Key::KpMultiply => "NumpadMultiply", - Key::KpSubtract => "NumpadSubtract", - Key::KpAdd => "NumpadAdd", - Key::KpEnter => "NumpadEnter", - Key::KpEqual => "NumpadEqual", - Key::LeftShift | Key::RightShift => "Shift", - Key::LeftControl | Key::RightControl => "Control", - Key::LeftAlt | Key::RightAlt => "Alt", - Key::LeftSuper | Key::RightSuper => "Super", - Key::Menu => "ContextMenu", - - Key::NavigateForward => "BrowserForward", - Key::NavigateBackward => "BrowserBackward", - } -} - -fn key_location(key: Key) -> u32 { - match key { - Key::Kp0 | - Key::Kp1 | - Key::Kp2 | - Key::Kp3 | - Key::Kp4 | - Key::Kp5 | - Key::Kp6 | - Key::Kp7 | - Key::Kp8 | - Key::Kp9 | - Key::KpDecimal | - Key::KpDivide | - Key::KpMultiply | - Key::KpSubtract | - Key::KpAdd | - Key::KpEnter | - Key::KpEqual => KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD, - - Key::LeftShift | Key::LeftAlt | Key::LeftControl | Key::LeftSuper => { - KeyboardEventConstants::DOM_KEY_LOCATION_LEFT - }, - - Key::RightShift | Key::RightAlt | Key::RightControl | Key::RightSuper => { - KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT - }, - - _ => KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD, + pub fn modifiers(&self) -> Modifiers { + self.modifiers.get() } } // https://w3c.github.io/uievents/#legacy-key-models -fn key_keycode(key: Key) -> u32 { +pub fn key_keycode(key: &Key) -> u32 { match key { // https://w3c.github.io/uievents/#legacy-key-models Key::Backspace => 8, Key::Tab => 9, Key::Enter => 13, - Key::LeftShift | Key::RightShift => 16, - Key::LeftControl | Key::RightControl => 17, - Key::LeftAlt | Key::RightAlt => 18, + Key::Shift => 16, + Key::Control => 17, + Key::Alt => 18, Key::CapsLock => 20, Key::Escape => 27, - Key::Space => 32, Key::PageUp => 33, Key::PageDown => 34, Key::End => 35, Key::Home => 36, - Key::Left => 37, - Key::Up => 38, - Key::Right => 39, - Key::Down => 40, + Key::ArrowLeft => 37, + Key::ArrowUp => 38, + Key::ArrowRight => 39, + Key::ArrowDown => 40, Key::Delete => 46, - - // https://w3c.github.io/uievents/#optionally-fixed-virtual-key-codes - Key::Semicolon => 186, - Key::Equal => 187, - Key::Comma => 188, - Key::Minus => 189, - Key::Period => 190, - Key::Slash => 191, - Key::LeftBracket => 219, - Key::Backslash => 220, - Key::RightBracket => 221, - Key::Apostrophe => 222, - - //§ B.2.1.3 - Key::Num0 | - Key::Num1 | - Key::Num2 | - Key::Num3 | - Key::Num4 | - Key::Num5 | - Key::Num6 | - Key::Num7 | - Key::Num8 | - Key::Num9 => key as u32 - Key::Num0 as u32 + '0' as u32, - - //§ B.2.1.4 - Key::A | - Key::B | - Key::C | - Key::D | - Key::E | - Key::F | - Key::G | - Key::H | - Key::I | - Key::J | - Key::K | - Key::L | - Key::M | - Key::N | - Key::O | - Key::P | - Key::Q | - Key::R | - Key::S | - Key::T | - Key::U | - Key::V | - Key::W | - Key::X | - Key::Y | - Key::Z => key as u32 - Key::A as u32 + 'A' as u32, + Key::Character(ref c) if c.len() == 1 => match c.chars().next().unwrap() { + ' ' => 32, + //§ B.2.1.3 + '0'...'9' => c.as_bytes()[0] as u32, + //§ B.2.1.4 + 'a'...'z' => c.to_ascii_uppercase().as_bytes()[0] as u32, + 'A'...'Z' => c.as_bytes()[0] as u32, + // https://w3c.github.io/uievents/#optionally-fixed-virtual-key-codes + ';' | ':' => 186, + '=' | '+' => 187, + ',' | '<' => 188, + '-' | '_' => 189, + '.' | '>' => 190, + '/' | '?' => 191, + '`' | '~' => 192, + '[' | '{' => 219, + '\\' | '|' => 220, + ']' | '}' => 221, + '\'' | '\"' => 222, + _ => 0, + }, //§ B.2.1.8 _ => 0, } } -#[derive(MallocSizeOf)] -pub struct KeyEventProperties { - pub key_string: Cow<'static, str>, - pub code: &'static str, - pub location: u32, - pub char_code: Option, - pub key_code: u32, -} - -impl KeyEventProperties { - pub fn is_printable(&self) -> bool { - self.char_code.is_some() - } -} - impl KeyboardEventMethods for KeyboardEvent { // https://w3c.github.io/uievents/#widl-KeyboardEvent-initKeyboardEvent fn InitKeyboardEvent( @@ -822,14 +211,14 @@ impl KeyboardEventMethods for KeyboardEvent { self.upcast::() .InitUIEvent(type_arg, can_bubble_arg, cancelable_arg, view_arg, 0); - *self.key_string.borrow_mut() = key_arg; + *self.key.borrow_mut() = key_arg; self.location.set(location_arg); self.repeat.set(repeat); } // https://w3c.github.io/uievents/#widl-KeyboardEvent-key fn Key(&self) -> DOMString { - self.key_string.borrow().clone() + self.key.borrow().clone() } // https://w3c.github.io/uievents/#widl-KeyboardEvent-code @@ -844,22 +233,22 @@ impl KeyboardEventMethods for KeyboardEvent { // https://w3c.github.io/uievents/#widl-KeyboardEvent-ctrlKey fn CtrlKey(&self) -> bool { - self.ctrl.get() + self.modifiers.get().contains(Modifiers::CONTROL) } // https://w3c.github.io/uievents/#widl-KeyboardEvent-shiftKey fn ShiftKey(&self) -> bool { - self.shift.get() + self.modifiers.get().contains(Modifiers::SHIFT) } // https://w3c.github.io/uievents/#widl-KeyboardEvent-altKey fn AltKey(&self) -> bool { - self.alt.get() + self.modifiers.get().contains(Modifiers::ALT) } // https://w3c.github.io/uievents/#widl-KeyboardEvent-metaKey fn MetaKey(&self) -> bool { - self.meta.get() + self.modifiers.get().contains(Modifiers::META) } // https://w3c.github.io/uievents/#widl-KeyboardEvent-repeat @@ -874,15 +263,21 @@ impl KeyboardEventMethods for KeyboardEvent { // https://w3c.github.io/uievents/#dom-keyboardevent-getmodifierstate fn GetModifierState(&self, key_arg: DOMString) -> bool { - match &*key_arg { - "Ctrl" => self.CtrlKey(), - "Alt" => self.AltKey(), - "Shift" => self.ShiftKey(), - "Meta" => self.MetaKey(), - "AltGraph" | "CapsLock" | "NumLock" | "ScrollLock" | "Accel" | "Fn" | "FnLock" | - "Hyper" | "OS" | "Symbol" | "SymbolLock" => false, //FIXME - _ => false, - } + self.modifiers.get().contains(match &*key_arg { + "Alt" => Modifiers::ALT, + "AltGraph" => Modifiers::ALT_GRAPH, + "CapsLock" => Modifiers::CAPS_LOCK, + "Control" => Modifiers::CONTROL, + "Fn" => Modifiers::FN, + "FnLock" => Modifiers::FN_LOCK, + "Meta" => Modifiers::META, + "NumLock" => Modifiers::NUM_LOCK, + "ScrollLock" => Modifiers::SCROLL_LOCK, + "Shift" => Modifiers::SHIFT, + "Symbol" => Modifiers::SYMBOL, + "SymbolLock" => Modifiers::SYMBOL_LOCK, + _ => return false, + }) } // https://w3c.github.io/uievents/#widl-KeyboardEvent-charCode diff --git a/components/script/lib.rs b/components/script/lib.rs index 097f69d7808..38cf0cafbe7 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -55,6 +55,7 @@ extern crate image; extern crate ipc_channel; #[macro_use] extern crate jstraceable_derive; +extern crate keyboard_types; #[macro_use] extern crate lazy_static; extern crate libc; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 748b6fc63e4..75db9b29bf5 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -96,7 +96,7 @@ use script_traits::{ProgressiveWebMetricType, Painter, ScriptMsg, ScriptThreadFa use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg}; use script_traits::{TimerSource, TouchEventType, TouchId, UntrustedNodeAddress}; use script_traits::{UpdatePipelineIdReason, WindowSizeData, WindowSizeType}; -use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent}; +use script_traits::CompositorEvent::{KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent}; use script_traits::webdriver_msg::WebDriverScriptCommand; use serviceworkerjob::{Job, JobQueue}; use servo_atoms::Atom; @@ -2809,6 +2809,7 @@ impl ScriptThread { } } }, + TouchEvent(event_type, identifier, point, node_address) => { let touch_result = self.handle_touch_event( pipeline_id, @@ -2834,12 +2835,12 @@ impl ScriptThread { } }, - KeyEvent(ch, key, state, modifiers) => { + KeyboardEvent(key_event) => { let document = match { self.documents.borrow().find_document(pipeline_id) } { Some(document) => document, None => return warn!("Message sent to closed pipeline {}.", pipeline_id), }; - document.dispatch_key_event(ch, key, state, modifiers); + document.dispatch_key_event(key_event); }, } } diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 7f462ba43fb..dfa469c1a08 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -7,7 +7,7 @@ use clipboard_provider::ClipboardProvider; use dom::bindings::str::DOMString; use dom::keyboardevent::KeyboardEvent; -use msg::constellation_msg::{Key, KeyModifiers}; +use keyboard_types::{Key, KeyState, Modifiers, ShortcutMatcher}; use std::borrow::ToOwned; use std::cmp::{max, min}; use std::default::Default; @@ -130,17 +130,11 @@ pub enum Direction { Backward, } -/// Was the keyboard event accompanied by the standard control modifier, -/// i.e. cmd on Mac OS or ctrl on other platforms. +// Some shortcuts use Cmd on Mac and Control on other systems. #[cfg(target_os = "macos")] -fn is_control_key(mods: KeyModifiers) -> bool { - mods.contains(KeyModifiers::SUPER) && !mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) -} - +pub const CMD_OR_CONTROL: Modifiers = Modifiers::META; #[cfg(not(target_os = "macos"))] -fn is_control_key(mods: KeyModifiers) -> bool { - mods.contains(KeyModifiers::CONTROL) && !mods.contains(KeyModifiers::SUPER | KeyModifiers::ALT) -} +pub const CMD_OR_CONTROL: Modifiers = Modifiers::CONTROL; /// The length in bytes of the first n characters in a UTF-8 string. /// @@ -685,155 +679,139 @@ impl TextInput { /// Process a given `KeyboardEvent` and return an action for the caller to execute. pub fn handle_keydown(&mut self, event: &KeyboardEvent) -> KeyReaction { - if let Some(key) = event.get_key() { - self.handle_keydown_aux(event.printable(), key, event.get_key_modifiers()) - } else { - KeyReaction::Nothing - } + let key = event.key(); + let mods = event.modifiers(); + self.handle_keydown_aux(key, mods, cfg!(target_os = "macos")) } + // This function exists for easy unit testing. + // To test Mac OS shortcuts on other systems a flag is passed. pub fn handle_keydown_aux( &mut self, - printable: Option, key: Key, - mods: KeyModifiers, + mut mods: Modifiers, + macos: bool, ) -> KeyReaction { - let maybe_select = if mods.contains(KeyModifiers::SHIFT) { + let maybe_select = if mods.contains(Modifiers::SHIFT) { Selection::Selected } else { Selection::NotSelected }; - - match (printable, key) { - (_, Key::B) if mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) => { + mods.remove(Modifiers::SHIFT); + ShortcutMatcher::new(KeyState::Down, key.clone(), mods) + .shortcut(Modifiers::CONTROL | Modifiers::ALT, 'B', || { self.adjust_horizontal_by_word(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - }, - (_, Key::F) if mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) => { + }) + .shortcut(Modifiers::CONTROL | Modifiers::ALT, 'F', || { self.adjust_horizontal_by_word(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - }, - (_, Key::A) if mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) => { + }) + .shortcut(Modifiers::CONTROL | Modifiers::ALT, 'A', || { self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - }, - (_, Key::E) if mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT) => { + }) + .shortcut(Modifiers::CONTROL | Modifiers::ALT, 'E', || { self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - }, - #[cfg(target_os = "macos")] - (None, Key::A) if mods == KeyModifiers::CONTROL => - { + }) + .optional_shortcut(macos, Modifiers::CONTROL, 'A', || { self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - } - #[cfg(target_os = "macos")] - (None, Key::E) if mods == KeyModifiers::CONTROL => - { + }) + .optional_shortcut(macos, Modifiers::CONTROL, 'E', || { self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - } - (_, Key::A) if is_control_key(mods) => { + }) + .shortcut(CMD_OR_CONTROL, 'A', || { self.select_all(); KeyReaction::RedrawSelection - }, - (_, Key::C) if is_control_key(mods) => { + }) + .shortcut(CMD_OR_CONTROL, 'C', || { if let Some(text) = self.get_selection_text() { self.clipboard_provider.set_clipboard_contents(text); } KeyReaction::DispatchInput - }, - (_, Key::V) if is_control_key(mods) => { + }) + .shortcut(CMD_OR_CONTROL, 'V', || { let contents = self.clipboard_provider.clipboard_contents(); self.insert_string(contents); KeyReaction::DispatchInput - }, - (Some(c), _) => { - self.insert_char(c); - KeyReaction::DispatchInput - }, - (None, Key::Delete) => { + }) + .shortcut(Modifiers::empty(), Key::Delete, || { self.delete_char(Direction::Forward); KeyReaction::DispatchInput - }, - (None, Key::Backspace) => { + }) + .shortcut(Modifiers::empty(), Key::Backspace, || { self.delete_char(Direction::Backward); KeyReaction::DispatchInput - }, - #[cfg(target_os = "macos")] - (None, Key::Left) if mods.contains(KeyModifiers::SUPER) => - { + }) + .optional_shortcut(macos, Modifiers::META, Key::ArrowLeft, || { self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - } - #[cfg(target_os = "macos")] - (None, Key::Right) if mods.contains(KeyModifiers::SUPER) => - { + }) + .optional_shortcut(macos, Modifiers::META, Key::ArrowRight, || { self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - } - #[cfg(target_os = "macos")] - (None, Key::Up) if mods.contains(KeyModifiers::SUPER) => - { + }) + .optional_shortcut(macos, Modifiers::META, Key::ArrowUp, || { self.adjust_horizontal_to_limit(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - } - #[cfg(target_os = "macos")] - (None, Key::Down) if mods.contains(KeyModifiers::SUPER) => - { + }) + .optional_shortcut(macos, Modifiers::META, Key::ArrowDown, || { self.adjust_horizontal_to_limit(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - } - (None, Key::Left) if mods.contains(KeyModifiers::ALT) => { + }) + .shortcut(Modifiers::ALT, Key::ArrowLeft, || { self.adjust_horizontal_by_word(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - }, - (None, Key::Right) if mods.contains(KeyModifiers::ALT) => { + }) + .shortcut(Modifiers::ALT, Key::ArrowRight, || { self.adjust_horizontal_by_word(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - }, - (None, Key::Left) => { + }) + .shortcut(Modifiers::empty(), Key::ArrowLeft, || { self.adjust_horizontal_by_one(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - }, - (None, Key::Right) => { + }) + .shortcut(Modifiers::empty(), Key::ArrowRight, || { self.adjust_horizontal_by_one(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - }, - (None, Key::Up) => { + }) + .shortcut(Modifiers::empty(), Key::ArrowUp, || { self.adjust_vertical(-1, maybe_select); KeyReaction::RedrawSelection - }, - (None, Key::Down) => { + }) + .shortcut(Modifiers::empty(), Key::ArrowDown, || { self.adjust_vertical(1, maybe_select); KeyReaction::RedrawSelection - }, - (None, Key::Enter) | (None, Key::KpEnter) => self.handle_return(), - (None, Key::Home) => { - #[cfg(not(target_os = "macos"))] - { - self.edit_point.index = 0; - } + }) + .shortcut(Modifiers::empty(), Key::Enter, || self.handle_return()) + .optional_shortcut(macos, Modifiers::empty(), Key::Home, || { + self.edit_point.index = 0; KeyReaction::RedrawSelection - }, - (None, Key::End) => { - #[cfg(not(target_os = "macos"))] - { - self.edit_point.index = self.current_line_length(); - self.assert_ok_selection(); - } + }) + .optional_shortcut(macos, Modifiers::empty(), Key::End, || { + self.edit_point.index = self.current_line_length(); + self.assert_ok_selection(); KeyReaction::RedrawSelection - }, - (None, Key::PageUp) => { + }) + .shortcut(Modifiers::empty(), Key::PageUp, || { self.adjust_vertical(-28, maybe_select); KeyReaction::RedrawSelection - }, - (None, Key::PageDown) => { + }) + .shortcut(Modifiers::empty(), Key::PageDown, || { self.adjust_vertical(28, maybe_select); KeyReaction::RedrawSelection - }, - _ => KeyReaction::Nothing, - } + }) + .otherwise(|| { + if let Key::Character(ref c) = key { + self.insert_string(c.as_str()); + return KeyReaction::DispatchInput; + } + KeyReaction::Nothing + }) + .unwrap() } /// Whether the content is empty. diff --git a/components/script_traits/Cargo.toml b/components/script_traits/Cargo.toml index 42a46ac3bdf..9b47eb9a7b2 100644 --- a/components/script_traits/Cargo.toml +++ b/components/script_traits/Cargo.toml @@ -20,6 +20,7 @@ gfx_traits = {path = "../gfx_traits"} hyper = "0.10" hyper_serde = "0.8" ipc-channel = "0.11" +keyboard-types = {version = "0.4.0-serde", features = ["serde"]} libc = "0.2" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = { path = "../malloc_size_of_derive" } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 6c087690b5f..94b015fa543 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -19,6 +19,7 @@ extern crate gfx_traits; extern crate hyper; extern crate hyper_serde; extern crate ipc_channel; +extern crate keyboard_types; extern crate libc; #[macro_use] extern crate malloc_size_of; @@ -50,8 +51,9 @@ use hyper::header::Headers; use hyper::method::Method; use ipc_channel::{Error as IpcError}; use ipc_channel::ipc::{IpcReceiver, IpcSender}; +use keyboard_types::KeyboardEvent; use libc::c_void; -use msg::constellation_msg::{BrowsingContextId, HistoryStateId, Key, KeyModifiers, KeyState, PipelineId}; +use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId}; use msg::constellation_msg::{PipelineNamespaceId, TraversalDirection, TopLevelBrowsingContextId}; use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads}; use net_traits::image::base::Image; @@ -477,7 +479,7 @@ pub enum CompositorEvent { Option, ), /// A key was pressed. - KeyEvent(Option, Key, KeyState, KeyModifiers), + KeyboardEvent(KeyboardEvent), } /// Requests a TimerEvent-Message be sent after the given duration. @@ -706,7 +708,7 @@ pub enum WebDriverCommandMsg { /// of a browsing context. ScriptCommand(BrowsingContextId, WebDriverScriptCommand), /// Act as if keys were pressed in the browsing context with the given ID. - SendKeys(BrowsingContextId, Vec<(Key, KeyModifiers, KeyState)>), + SendKeys(BrowsingContextId, Vec), /// Set the window size. SetWindowSize( TopLevelBrowsingContextId, @@ -734,7 +736,7 @@ pub enum ConstellationMsg { /// Query the constellation to see if the current compositor output is stable IsReadyToSaveImage(HashMap), /// Inform the constellation of a key event. - KeyEvent(Option, Key, KeyState, KeyModifiers), + Keyboard(KeyboardEvent), /// Request to load a page. LoadUrl(TopLevelBrowsingContextId, ServoUrl), /// Request to traverse the joint session history of the provided browsing context. @@ -778,7 +780,7 @@ impl fmt::Debug for ConstellationMsg { GetPipeline(..) => "GetPipeline", GetFocusTopLevelBrowsingContext(..) => "GetFocusTopLevelBrowsingContext", IsReadyToSaveImage(..) => "IsReadyToSaveImage", - KeyEvent(..) => "KeyEvent", + Keyboard(..) => "Keyboard", LoadUrl(..) => "LoadUrl", TraverseHistory(..) => "TraverseHistory", WindowSize(..) => "WindowSize", diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 9bd7623df47..722a3d8a558 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -109,7 +109,7 @@ use webvr::{WebVRThread, WebVRCompositorHandler}; pub use gleam::gl; pub use servo_config as config; pub use servo_url as url; -pub use msg::constellation_msg::{KeyState, TopLevelBrowsingContextId as BrowserId}; +pub use msg::constellation_msg::{TopLevelBrowsingContextId as BrowserId}; /// The in-process interface to Servo. /// @@ -316,10 +316,10 @@ where } }, - WindowEvent::KeyEvent(ch, key, state, modifiers) => { - let msg = ConstellationMsg::KeyEvent(ch, key, state, modifiers); + WindowEvent::Keyboard(key_event) => { + let msg = ConstellationMsg::Keyboard(key_event); if let Err(e) = self.constellation_chan.send(msg) { - warn!("Sending key event to constellation failed ({:?}).", e); + warn!("Sending keyboard event to constellation failed ({:?}).", e); } }, @@ -395,12 +395,12 @@ where (_, ShutdownState::ShuttingDown) => {}, ( - EmbedderMsg::KeyEvent(ch, key, state, modified), + EmbedderMsg::Keyboard(key_event), ShutdownState::NotShuttingDown, ) => { let event = ( top_level_browsing_context, - EmbedderMsg::KeyEvent(ch, key, state, modified), + EmbedderMsg::Keyboard(key_event), ); self.embedder_events.push(event); }, diff --git a/components/webdriver_server/Cargo.toml b/components/webdriver_server/Cargo.toml index 959cc07b868..3cbe306cbfa 100644 --- a/components/webdriver_server/Cargo.toml +++ b/components/webdriver_server/Cargo.toml @@ -16,6 +16,7 @@ euclid = "0.19" hyper = "0.10" image = "0.19" ipc-channel = "0.11" +keyboard-types = {version = "0.4.0-serde", features = ["serde"]} log = "0.4" msg = {path = "../msg"} net_traits = {path = "../net_traits"} diff --git a/components/webdriver_server/keys.rs b/components/webdriver_server/keys.rs index 72fdb8b32ac..212a3b56815 100644 --- a/components/webdriver_server/keys.rs +++ b/components/webdriver_server/keys.rs @@ -2,184 +2,105 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use msg::constellation_msg::{Key, KeyState, KeyModifiers}; +use keyboard_types::{Key, KeyboardEvent}; -/// Takes a character and returns an Option containing a tuple of the -/// corresponding keycode and whether shift is required. This is -/// currently pretty much ascii-only and the webdriver spec isn't -/// entirely clear on how to deal with characters outside this -/// range. Returns None if no key corresponding to the character is -/// matched. -fn key_from_char(key_string: &char) -> Option<(Key, bool)> { - match *key_string { - ' ' => Some((Key::Space, false)), - '\'' => Some((Key::Apostrophe, true)), - '\"' => Some((Key::Apostrophe, false)), - '<' => Some((Key::Comma, true)), - ',' => Some((Key::Comma, false)), - '_' => Some((Key::Minus, true)), - '-' => Some((Key::Minus, false)), - '>' => Some((Key::Period, true)), - '.' => Some((Key::Period, false)), - '?' => Some((Key::Slash, true)), - '/' => Some((Key::Slash, false)), - '~' => Some((Key::GraveAccent, true)), - '`' => Some((Key::GraveAccent, false)), - ')' => Some((Key::Num0, true)), - '0' => Some((Key::Num0, false)), - '!' => Some((Key::Num1, true)), - '1' => Some((Key::Num1, false)), - '@' => Some((Key::Num2, true)), - '2' => Some((Key::Num2, false)), - '#' => Some((Key::Num3, true)), - '3' => Some((Key::Num3, false)), - '$' => Some((Key::Num4, true)), - '4' => Some((Key::Num4, false)), - '%' => Some((Key::Num5, true)), - '5' => Some((Key::Num5, false)), - '^' => Some((Key::Num6, true)), - '6' => Some((Key::Num6, false)), - '&' => Some((Key::Num7, true)), - '7' => Some((Key::Num7, false)), - '*' => Some((Key::Num8, true)), - '8' => Some((Key::Num8, false)), - '(' => Some((Key::Num9, true)), - '9' => Some((Key::Num9, false)), - ':' => Some((Key::Semicolon, true)), - ';' => Some((Key::Semicolon, false)), - '+' => Some((Key::Equal, true)), - '=' => Some((Key::Equal, false)), - 'A' => Some((Key::A, true)), - 'a' => Some((Key::A, false)), - 'B' => Some((Key::B, true)), - 'b' => Some((Key::B, false)), - 'C' => Some((Key::C, true)), - 'c' => Some((Key::C, false)), - 'D' => Some((Key::D, true)), - 'd' => Some((Key::D, false)), - 'E' => Some((Key::E, true)), - 'e' => Some((Key::E, false)), - 'F' => Some((Key::F, true)), - 'f' => Some((Key::F, false)), - 'G' => Some((Key::G, true)), - 'g' => Some((Key::G, false)), - 'H' => Some((Key::H, true)), - 'h' => Some((Key::H, false)), - 'I' => Some((Key::I, true)), - 'i' => Some((Key::I, false)), - 'J' => Some((Key::J, true)), - 'j' => Some((Key::J, false)), - 'K' => Some((Key::K, true)), - 'k' => Some((Key::K, false)), - 'L' => Some((Key::L, true)), - 'l' => Some((Key::L, false)), - 'M' => Some((Key::M, true)), - 'm' => Some((Key::M, false)), - 'N' => Some((Key::N, true)), - 'n' => Some((Key::N, false)), - 'O' => Some((Key::O, true)), - 'o' => Some((Key::O, false)), - 'P' => Some((Key::P, true)), - 'p' => Some((Key::P, false)), - 'Q' => Some((Key::Q, true)), - 'q' => Some((Key::Q, false)), - 'R' => Some((Key::R, true)), - 'r' => Some((Key::R, false)), - 'S' => Some((Key::S, true)), - 's' => Some((Key::S, false)), - 'T' => Some((Key::T, true)), - 't' => Some((Key::T, false)), - 'U' => Some((Key::U, true)), - 'u' => Some((Key::U, false)), - 'V' => Some((Key::V, true)), - 'v' => Some((Key::V, false)), - 'W' => Some((Key::W, true)), - 'w' => Some((Key::W, false)), - 'X' => Some((Key::X, true)), - 'x' => Some((Key::X, false)), - 'Y' => Some((Key::Y, true)), - 'y' => Some((Key::Y, false)), - 'Z' => Some((Key::Z, true)), - 'z' => Some((Key::Z, false)), - '{' => Some((Key::LeftBracket, true)), - '[' => Some((Key::LeftBracket, false)), - '|' => Some((Key::Backslash, true)), - '\\' => Some((Key::Backslash, false)), - '}' => Some((Key::RightBracket, true)), - ']' => Some((Key::RightBracket, false)), - '\u{E000}' => None, - '\u{E001}' => None, - '\u{E002}' => None, - '\u{E003}' => Some((Key::Backspace, false)), - '\u{E004}' => Some((Key::Tab, false)), - '\u{E005}' => None, - '\u{E006}' => Some((Key::Enter, false)), // This is supposed to be the Return key - '\u{E007}' => Some((Key::Enter, false)), - '\u{E008}' => Some((Key::LeftShift, false)), - '\u{E009}' => Some((Key::LeftShift, false)), - '\u{E00A}' => Some((Key::LeftAlt, false)), - '\u{E00B}' => Some((Key::Pause, false)), - '\u{E00C}' => Some((Key::Escape, false)), - '\u{E00D}' => Some((Key::Space, false)), - '\u{E00E}' => Some((Key::PageUp, false)), - '\u{E00F}' => Some((Key::PageDown, false)), - '\u{E010}' => Some((Key::End, false)), - '\u{E011}' => Some((Key::Home, false)), - '\u{E012}' => Some((Key::Right, false)), - '\u{E013}' => Some((Key::Left, false)), - '\u{E014}' => Some((Key::Down, false)), - '\u{E015}' => Some((Key::Up, false)), - '\u{E016}' => Some((Key::Insert, false)), - '\u{E017}' => Some((Key::Delete, false)), - '\u{E018}' => Some((Key::Semicolon, false)), - '\u{E019}' => Some((Key::Equal, false)), - '\u{E01A}' => Some((Key::Kp0, false)), - '\u{E01B}' => Some((Key::Kp1, false)), - '\u{E01C}' => Some((Key::Kp2, false)), - '\u{E01D}' => Some((Key::Kp3, false)), - '\u{E01E}' => Some((Key::Kp4, false)), - '\u{E01F}' => Some((Key::Kp5, false)), - '\u{E020}' => Some((Key::Kp6, false)), - '\u{E021}' => Some((Key::Kp7, false)), - '\u{E022}' => Some((Key::Kp8, false)), - '\u{E023}' => Some((Key::Kp9, false)), - '\u{E024}' => Some((Key::KpMultiply, false)), - '\u{E025}' => Some((Key::KpAdd, false)), - '\u{E026}' => Some((Key::KpEnter, false)), - '\u{E027}' => Some((Key::KpSubtract, false)), - '\u{E028}' => Some((Key::KpDecimal, false)), - '\u{E029}' => Some((Key::KpDivide, false)), - '\u{E031}' => Some((Key::F1, false)), - '\u{E032}' => Some((Key::F2, false)), - '\u{E033}' => Some((Key::F3, false)), - '\u{E034}' => Some((Key::F4, false)), - '\u{E035}' => Some((Key::F5, false)), - '\u{E036}' => Some((Key::F6, false)), - '\u{E037}' => Some((Key::F7, false)), - '\u{E038}' => Some((Key::F8, false)), - '\u{E039}' => Some((Key::F9, false)), - '\u{E03A}' => Some((Key::F10, false)), - '\u{E03B}' => Some((Key::F11, false)), - '\u{E03C}' => Some((Key::F12, false)), - '\u{E03D}' => None, - '\u{E040}' => None, - _ => None, +// spec: https://w3c.github.io/webdriver/#keyboard-actions +// normalised (sic) as in british spelling +fn get_normalised_key_value(key: char) -> Key { + match key { + '\u{E000}' => Key::Unidentified, + '\u{E001}' => Key::Cancel, + '\u{E002}' => Key::Help, + '\u{E003}' => Key::Backspace, + '\u{E004}' => Key::Tab, + '\u{E005}' => Key::Clear, + // FIXME(pyfisch): spec says "Return" + '\u{E006}' => Key::Enter, + '\u{E007}' => Key::Enter, + '\u{E008}' => Key::Shift, + '\u{E009}' => Key::Control, + '\u{E00A}' => Key::Alt, + '\u{E00B}' => Key::Pause, + '\u{E00C}' => Key::Escape, + '\u{E00D}' => Key::Character(" ".to_string()), + '\u{E00E}' => Key::PageUp, + '\u{E00F}' => Key::PageDown, + '\u{E010}' => Key::End, + '\u{E011}' => Key::Home, + '\u{E012}' => Key::ArrowLeft, + '\u{E013}' => Key::ArrowUp, + '\u{E014}' => Key::ArrowRight, + '\u{E015}' => Key::ArrowDown, + '\u{E016}' => Key::Insert, + '\u{E017}' => Key::Delete, + '\u{E018}' => Key::Character(";".to_string()), + '\u{E019}' => Key::Character("=".to_string()), + '\u{E01A}' => Key::Character("0".to_string()), + '\u{E01B}' => Key::Character("1".to_string()), + '\u{E01C}' => Key::Character("2".to_string()), + '\u{E01D}' => Key::Character("3".to_string()), + '\u{E01E}' => Key::Character("4".to_string()), + '\u{E01F}' => Key::Character("5".to_string()), + '\u{E020}' => Key::Character("6".to_string()), + '\u{E021}' => Key::Character("7".to_string()), + '\u{E022}' => Key::Character("8".to_string()), + '\u{E023}' => Key::Character("9".to_string()), + '\u{E024}' => Key::Character("*".to_string()), + '\u{E025}' => Key::Character("+".to_string()), + '\u{E026}' => Key::Character(",".to_string()), + '\u{E027}' => Key::Character("-".to_string()), + '\u{E028}' => Key::Character(".".to_string()), + '\u{E029}' => Key::Character("/".to_string()), + '\u{E031}' => Key::F1, + '\u{E032}' => Key::F2, + '\u{E033}' => Key::F3, + '\u{E034}' => Key::F4, + '\u{E035}' => Key::F5, + '\u{E036}' => Key::F6, + '\u{E037}' => Key::F7, + '\u{E038}' => Key::F8, + '\u{E039}' => Key::F9, + '\u{E03A}' => Key::F10, + '\u{E03B}' => Key::F11, + '\u{E03C}' => Key::F12, + '\u{E03D}' => Key::Meta, + '\u{E040}' => Key::ZenkakuHankaku, + '\u{E050}' => Key::Shift, + '\u{E051}' => Key::Control, + '\u{E052}' => Key::Alt, + '\u{E053}' => Key::Meta, + '\u{E054}' => Key::PageUp, + '\u{E055}' => Key::PageDown, + '\u{E056}' => Key::End, + '\u{E057}' => Key::Home, + '\u{E058}' => Key::ArrowLeft, + '\u{E059}' => Key::ArrowUp, + '\u{E05A}' => Key::ArrowRight, + '\u{E05B}' => Key::ArrowDown, + '\u{E05C}' => Key::Insert, + '\u{E05D}' => Key::Delete, + _ => Key::Character(key.to_string()), } } -pub fn keycodes_to_keys(key_codes: &str) -> Result, String> { +pub fn keycodes_to_keys(key_codes: &str) -> Vec { let mut rv = vec![]; for char_code in key_codes.chars() { - let (key, with_shift) = - key_from_char(&char_code).ok_or(format!("Unsupported character code {}", char_code))?; - let modifiers = if with_shift { - KeyModifiers::SHIFT - } else { - KeyModifiers::empty() + // TODO(pyfisch): compute code, location, modifiers according to spec + let key = get_normalised_key_value(char_code); + let mut event = KeyboardEvent { + state: ::keyboard_types::KeyState::Down, + key, + code: ::keyboard_types::Code::Unidentified, + location: ::keyboard_types::Location::Standard, + modifiers: ::keyboard_types::Modifiers::empty(), + repeat: false, + is_composing: false, }; - rv.push((key, modifiers, KeyState::Pressed)); - rv.push((key, modifiers, KeyState::Released)); + rv.push(event.clone()); + event.state = ::keyboard_types::KeyState::Up; + rv.push(event); } - Ok(rv) + rv } diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index b4c61a1ddce..8778346b17e 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -12,6 +12,7 @@ extern crate euclid; extern crate hyper; extern crate image; extern crate ipc_channel; +extern crate keyboard_types; #[macro_use] extern crate log; extern crate msg; @@ -995,12 +996,7 @@ impl Handler { )) })?; - let keys = keycodes_to_keys(&keys.text).or_else(|_| { - Err(WebDriverError::new( - ErrorStatus::UnsupportedOperation, - "Failed to convert keycodes", - )) - })?; + let keys = keycodes_to_keys(&keys.text); // TODO: there's a race condition caused by the focus command and the // send keys command being two separate messages, diff --git a/ports/libsimpleservo/src/api.rs b/ports/libsimpleservo/src/api.rs index d8ed898cc53..f946b0681cc 100644 --- a/ports/libsimpleservo/src/api.rs +++ b/ports/libsimpleservo/src/api.rs @@ -408,7 +408,7 @@ impl ServoGlue { EmbedderMsg::SelectFiles(..) | EmbedderMsg::MoveTo(..) | EmbedderMsg::ResizeTo(..) | - EmbedderMsg::KeyEvent(..) | + EmbedderMsg::Keyboard(..) | EmbedderMsg::SetCursor(..) | EmbedderMsg::NewFavicon(..) | EmbedderMsg::HeadParsed | diff --git a/ports/servo/Cargo.toml b/ports/servo/Cargo.toml index 298dae298f9..f26d470ef9e 100644 --- a/ports/servo/Cargo.toml +++ b/ports/servo/Cargo.toml @@ -40,6 +40,7 @@ crossbeam-channel = "0.2" euclid = "0.19" gleam = "0.6" glutin = "0.18" +keyboard-types = {version = "0.4.0-serde", features = ["serde"]} lazy_static = "1" libservo = {path = "../../components/servo"} log = "0.4" diff --git a/ports/servo/browser.rs b/ports/servo/browser.rs index dee5055f449..f2a0f9eaafe 100644 --- a/ports/servo/browser.rs +++ b/ports/servo/browser.rs @@ -5,10 +5,11 @@ use euclid::{TypedPoint2D, TypedVector2D}; use glutin_app::keyutils::{CMD_OR_CONTROL, CMD_OR_ALT}; use glutin_app::window::{Window, LINE_HEIGHT}; +use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher}; use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent}; use servo::embedder_traits::{EmbedderMsg, FilterPattern}; -use servo::msg::constellation_msg::{Key, TopLevelBrowsingContextId as BrowserId}; -use servo::msg::constellation_msg::{KeyModifiers, KeyState, TraversalDirection}; +use servo::msg::constellation_msg::{TopLevelBrowsingContextId as BrowserId}; +use servo::msg::constellation_msg::TraversalDirection; use servo::net_traits::pub_domains::is_reg_domain; use servo::script_traits::TouchEventType; use servo::servo_config::opts; @@ -70,8 +71,8 @@ impl Browser { pub fn handle_window_events(&mut self, events: Vec) { for event in events { match event { - WindowEvent::KeyEvent(ch, key, state, mods) => { - self.handle_key_from_window(ch, key, state, mods); + WindowEvent::Keyboard(key_event) => { + self.handle_key_from_window(key_event); }, event => { self.event_queue.push(event); @@ -85,22 +86,14 @@ impl Browser { } /// Handle key events before sending them to Servo. - fn handle_key_from_window( - &mut self, - ch: Option, - key: Key, - state: KeyState, - mods: KeyModifiers, - ) { - let pressed = state == KeyState::Pressed; - // We don't match the state in the parent `match` because we don't want to do anything - // on KeyState::Released when it's a combo that we handle on Pressed. For example, - // if we catch Alt-Left on pressed, we don't want the Release event to be sent to Servo. - match (mods, ch, key, self.browser_id) { - (CMD_OR_CONTROL, _, Key::R, Some(id)) => if pressed { - self.event_queue.push(WindowEvent::Reload(id)); - }, - (CMD_OR_CONTROL, _, Key::L, Some(id)) => if pressed { + fn handle_key_from_window(&mut self, key_event: KeyboardEvent) { + ShortcutMatcher::from_event(key_event.clone()) + .shortcut(CMD_OR_CONTROL, 'R', || { + if let Some(id) = self.browser_id { + self.event_queue.push(WindowEvent::Reload(id)); + } + }) + .shortcut(CMD_OR_CONTROL, 'L', || { let url: String = if let Some(ref current_url) = self.current_url { current_url.to_string() } else { @@ -110,163 +103,130 @@ impl Browser { let input = tinyfiledialogs::input_box(title, title, &url); if let Some(input) = input { if let Some(url) = sanitize_url(&input) { - self.event_queue.push(WindowEvent::LoadUrl(id, url)); + if let Some(id) = self.browser_id { + self.event_queue.push(WindowEvent::LoadUrl(id, url)); + } } } - }, - (CMD_OR_CONTROL, _, Key::Q, _) => if pressed { + }) + .shortcut(CMD_OR_CONTROL, 'Q', || { self.event_queue.push(WindowEvent::Quit); - }, - (_, Some('3'), _, _) if mods ^ KeyModifiers::CONTROL == KeyModifiers::SHIFT => { - if pressed { - self.event_queue.push(WindowEvent::CaptureWebRender); + }) + .shortcut(Modifiers::CONTROL, Key::F9, || { + self.event_queue.push(WindowEvent::CaptureWebRender) + }) + .shortcut(Modifiers::CONTROL, Key::F10, || { + self.event_queue.push(WindowEvent::ToggleWebRenderDebug( + WebRenderDebugOption::RenderTargetDebug, + )); + }) + .shortcut(Modifiers::CONTROL, Key::F11, || { + self.event_queue.push(WindowEvent::ToggleWebRenderDebug( + WebRenderDebugOption::TextureCacheDebug, + )); + }) + .shortcut(Modifiers::CONTROL, Key::F12, || { + self.event_queue.push(WindowEvent::ToggleWebRenderDebug( + WebRenderDebugOption::Profiler, + )); + }) + .shortcut(CMD_OR_ALT, Key::ArrowRight, || { + if let Some(id) = self.browser_id { + let event = WindowEvent::Navigation(id, TraversalDirection::Forward(1)); + self.event_queue.push(event); } - }, - (KeyModifiers::CONTROL, None, Key::F10, _) => if pressed { - let event = - WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::RenderTargetDebug); - self.event_queue.push(event); - }, - (KeyModifiers::CONTROL, None, Key::F11, _) => if pressed { - let event = - WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::TextureCacheDebug); - self.event_queue.push(event); - }, - (KeyModifiers::CONTROL, None, Key::F12, _) => if pressed { - let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::Profiler); - self.event_queue.push(event); - }, - (CMD_OR_ALT, None, Key::Right, Some(id)) | - (KeyModifiers::NONE, None, Key::NavigateForward, Some(id)) => if pressed { - let event = WindowEvent::Navigation(id, TraversalDirection::Forward(1)); - self.event_queue.push(event); - }, - (CMD_OR_ALT, None, Key::Left, Some(id)) | - (KeyModifiers::NONE, None, Key::NavigateBackward, Some(id)) => if pressed { - let event = WindowEvent::Navigation(id, TraversalDirection::Back(1)); - self.event_queue.push(event); - }, - (KeyModifiers::NONE, None, Key::Escape, _) => if pressed { + }) + .shortcut(CMD_OR_ALT, Key::ArrowLeft, || { + if let Some(id) = self.browser_id { + let event = WindowEvent::Navigation(id, TraversalDirection::Back(1)); + self.event_queue.push(event); + } + }) + .shortcut(Modifiers::empty(), Key::Escape, || { self.event_queue.push(WindowEvent::Quit); - }, - _ => { - self.platform_handle_key(ch, key, mods, state); - }, - } + }) + .otherwise(|| self.platform_handle_key(key_event)); } #[cfg(not(target_os = "win"))] - fn platform_handle_key( - &mut self, - ch: Option, - key: Key, - mods: KeyModifiers, - state: KeyState, - ) { - let pressed = state == KeyState::Pressed; - match (mods, key, self.browser_id) { - (CMD_OR_CONTROL, Key::LeftBracket, Some(id)) => if pressed { - let event = WindowEvent::Navigation(id, TraversalDirection::Back(1)); - self.event_queue.push(event); - }, - (CMD_OR_CONTROL, Key::RightBracket, Some(id)) => if pressed { - let event = WindowEvent::Navigation(id, TraversalDirection::Back(1)); - self.event_queue.push(event); - }, - _ => { - self.event_queue - .push(WindowEvent::KeyEvent(ch, key, state, mods)); - }, + fn platform_handle_key(&mut self, key_event: KeyboardEvent) { + if let Some(id) = self.browser_id { + if let Some(event) = ShortcutMatcher::from_event(key_event.clone()) + .shortcut(CMD_OR_CONTROL, '[', || { + WindowEvent::Navigation(id, TraversalDirection::Back(1)) + }) + .shortcut(CMD_OR_CONTROL, ']', || { + WindowEvent::Navigation(id, TraversalDirection::Forward(1)) + }) + .otherwise(|| WindowEvent::Keyboard(key_event)) + { + self.event_queue.push(event) + } } } #[cfg(target_os = "win")] - fn platform_handle_key( - &mut self, - _ch: Option, - _key: Key, - _mods: KeyModifiers, - _state: KeyState, - ) { - } + fn platform_handle_key(&mut self, _key_event: KeyboardEvent) {} /// Handle key events after they have been handled by Servo. - fn handle_key_from_servo( - &mut self, - _: Option, - ch: Option, - key: Key, - state: KeyState, - mods: KeyModifiers, - ) { - if state == KeyState::Released { - return; - } - - match (mods, ch, key) { - (CMD_OR_CONTROL, Some('='), _) | (CMD_OR_CONTROL, Some('+'), _) => { - self.event_queue.push(WindowEvent::Zoom(1.1)); - }, - (_, Some('='), _) if mods == (CMD_OR_CONTROL | KeyModifiers::SHIFT) => { - self.event_queue.push(WindowEvent::Zoom(1.1)); - }, - (CMD_OR_CONTROL, Some('-'), _) => { - self.event_queue.push(WindowEvent::Zoom(1.0 / 1.1)); - }, - (CMD_OR_CONTROL, Some('0'), _) => { - self.event_queue.push(WindowEvent::ResetZoom); - }, - - (KeyModifiers::NONE, None, Key::PageDown) => { + fn handle_key_from_servo(&mut self, _: Option, event: KeyboardEvent) { + ShortcutMatcher::from_event(event) + .shortcut(CMD_OR_CONTROL, '=', || { + self.event_queue.push(WindowEvent::Zoom(1.1)) + }) + .shortcut(CMD_OR_CONTROL, '+', || { + self.event_queue.push(WindowEvent::Zoom(1.1)) + }) + .shortcut(CMD_OR_CONTROL, '-', || { + self.event_queue.push(WindowEvent::Zoom(1.0 / 1.1)) + }) + .shortcut(CMD_OR_CONTROL, '0', || { + self.event_queue.push(WindowEvent::ResetZoom) + }) + .shortcut(Modifiers::empty(), Key::PageDown, || { let scroll_location = ScrollLocation::Delta(TypedVector2D::new( 0.0, -self.window.page_height() + 2.0 * LINE_HEIGHT, )); self.scroll_window_from_key(scroll_location, TouchEventType::Move); - }, - (KeyModifiers::NONE, None, Key::PageUp) => { + }) + .shortcut(Modifiers::empty(), Key::PageUp, || { let scroll_location = ScrollLocation::Delta(TypedVector2D::new( 0.0, self.window.page_height() - 2.0 * LINE_HEIGHT, )); self.scroll_window_from_key(scroll_location, TouchEventType::Move); - }, - - (KeyModifiers::NONE, None, Key::Home) => { + }) + .shortcut(Modifiers::empty(), Key::Home, || { self.scroll_window_from_key(ScrollLocation::Start, TouchEventType::Move); - }, - - (KeyModifiers::NONE, None, Key::End) => { + }) + .shortcut(Modifiers::empty(), Key::End, || { self.scroll_window_from_key(ScrollLocation::End, TouchEventType::Move); - }, - - (KeyModifiers::NONE, None, Key::Up) => { + }) + .shortcut(Modifiers::empty(), Key::ArrowUp, || { self.scroll_window_from_key( ScrollLocation::Delta(TypedVector2D::new(0.0, 3.0 * LINE_HEIGHT)), TouchEventType::Move, ); - }, - (KeyModifiers::NONE, None, Key::Down) => { + }) + .shortcut(Modifiers::empty(), Key::ArrowDown, || { self.scroll_window_from_key( ScrollLocation::Delta(TypedVector2D::new(0.0, -3.0 * LINE_HEIGHT)), TouchEventType::Move, ); - }, - (KeyModifiers::NONE, None, Key::Left) => { + }) + .shortcut(Modifiers::empty(), Key::ArrowLeft, || { self.scroll_window_from_key( ScrollLocation::Delta(TypedVector2D::new(LINE_HEIGHT, 0.0)), TouchEventType::Move, ); - }, - (KeyModifiers::NONE, None, Key::Right) => { + }) + .shortcut(Modifiers::empty(), Key::ArrowRight, || { self.scroll_window_from_key( ScrollLocation::Delta(TypedVector2D::new(-LINE_HEIGHT, 0.0)), TouchEventType::Move, ); - }, - - _ => {}, - } + }); } fn scroll_window_from_key(&mut self, scroll_location: ScrollLocation, phase: TouchEventType) { @@ -311,7 +271,8 @@ impl Browser { &message, MessageBoxIcon::Warning, ); - }).unwrap() + }) + .unwrap() .join() .expect("Thread spawning failed"); } @@ -350,8 +311,8 @@ impl Browser { self.event_queue .push(WindowEvent::SelectBrowser(new_browser_id)); }, - EmbedderMsg::KeyEvent(ch, key, state, modified) => { - self.handle_key_from_servo(browser_id, ch, key, state, modified); + EmbedderMsg::Keyboard(key_event) => { + self.handle_key_from_servo(browser_id, key_event); }, EmbedderMsg::SetCursor(cursor) => { self.window.set_cursor(cursor); @@ -438,7 +399,8 @@ fn platform_get_selected_devices(devices: Vec) -> Option { }, None => None, } - }).unwrap() + }) + .unwrap() .join() .expect("Thread spawning failed") } @@ -480,7 +442,8 @@ fn get_selected_files(patterns: Vec, multiple_files: bool) -> Opt let file = tinyfiledialogs::open_file_dialog(picker_name, "", filter_opt); file.map(|x| vec![x]) } - }).unwrap() + }) + .unwrap() .join() .expect("Thread spawning failed") } @@ -495,7 +458,8 @@ fn sanitize_url(request: &str) -> Option { } else { None } - }).or_else(|| { + }) + .or_else(|| { PREFS .get("shell.searchpage") .as_string() diff --git a/ports/servo/glutin_app/keyutils.rs b/ports/servo/glutin_app/keyutils.rs index 2c685cf10e5..a1700e71f23 100644 --- a/ports/servo/glutin_app/keyutils.rs +++ b/ports/servo/glutin_app/keyutils.rs @@ -2,215 +2,34 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use servo::msg::constellation_msg::{self, Key, KeyModifiers}; +use keyboard_types::{Code, Key, Modifiers, Location}; use winit::VirtualKeyCode; // Some shortcuts use Cmd on Mac and Control on other systems. #[cfg(target_os = "macos")] -pub const CMD_OR_CONTROL: KeyModifiers = KeyModifiers::SUPER; +pub const CMD_OR_CONTROL: Modifiers = Modifiers::META; #[cfg(not(target_os = "macos"))] -pub const CMD_OR_CONTROL: KeyModifiers = KeyModifiers::CONTROL; +pub const CMD_OR_CONTROL: Modifiers = Modifiers::CONTROL; // Some shortcuts use Cmd on Mac and Alt on other systems. #[cfg(target_os = "macos")] -pub const CMD_OR_ALT: KeyModifiers = KeyModifiers::SUPER; +pub const CMD_OR_ALT: Modifiers = Modifiers::META; #[cfg(not(target_os = "macos"))] -pub const CMD_OR_ALT: KeyModifiers = KeyModifiers::ALT; +pub const CMD_OR_ALT: Modifiers = Modifiers::ALT; -pub fn char_to_script_key(c: char) -> Option { - match c { - ' ' => Some(Key::Space), - '"' => Some(Key::Apostrophe), - '\'' => Some(Key::Apostrophe), - '<' => Some(Key::Comma), - ',' => Some(Key::Comma), - '_' => Some(Key::Minus), - '-' => Some(Key::Minus), - '>' => Some(Key::Period), - '.' => Some(Key::Period), - '?' => Some(Key::Slash), - '/' => Some(Key::Slash), - '~' => Some(Key::GraveAccent), - '`' => Some(Key::GraveAccent), - ')' => Some(Key::Num0), - '0' => Some(Key::Num0), - '!' => Some(Key::Num1), - '1' => Some(Key::Num1), - '@' => Some(Key::Num2), - '2' => Some(Key::Num2), - '#' => Some(Key::Num3), - '3' => Some(Key::Num3), - '$' => Some(Key::Num4), - '4' => Some(Key::Num4), - '%' => Some(Key::Num5), - '5' => Some(Key::Num5), - '^' => Some(Key::Num6), - '6' => Some(Key::Num6), - '&' => Some(Key::Num7), - '7' => Some(Key::Num7), - '*' => Some(Key::Num8), - '8' => Some(Key::Num8), - '(' => Some(Key::Num9), - '9' => Some(Key::Num9), - ':' => Some(Key::Semicolon), - ';' => Some(Key::Semicolon), - '+' => Some(Key::Equal), - '=' => Some(Key::Equal), - 'A' => Some(Key::A), - 'a' => Some(Key::A), - 'B' => Some(Key::B), - 'b' => Some(Key::B), - 'C' => Some(Key::C), - 'c' => Some(Key::C), - 'D' => Some(Key::D), - 'd' => Some(Key::D), - 'E' => Some(Key::E), - 'e' => Some(Key::E), - 'F' => Some(Key::F), - 'f' => Some(Key::F), - 'G' => Some(Key::G), - 'g' => Some(Key::G), - 'H' => Some(Key::H), - 'h' => Some(Key::H), - 'I' => Some(Key::I), - 'i' => Some(Key::I), - 'J' => Some(Key::J), - 'j' => Some(Key::J), - 'K' => Some(Key::K), - 'k' => Some(Key::K), - 'L' => Some(Key::L), - 'l' => Some(Key::L), - 'M' => Some(Key::M), - 'm' => Some(Key::M), - 'N' => Some(Key::N), - 'n' => Some(Key::N), - 'O' => Some(Key::O), - 'o' => Some(Key::O), - 'P' => Some(Key::P), - 'p' => Some(Key::P), - 'Q' => Some(Key::Q), - 'q' => Some(Key::Q), - 'R' => Some(Key::R), - 'r' => Some(Key::R), - 'S' => Some(Key::S), - 's' => Some(Key::S), - 'T' => Some(Key::T), - 't' => Some(Key::T), - 'U' => Some(Key::U), - 'u' => Some(Key::U), - 'V' => Some(Key::V), - 'v' => Some(Key::V), - 'W' => Some(Key::W), - 'w' => Some(Key::W), - 'X' => Some(Key::X), - 'x' => Some(Key::X), - 'Y' => Some(Key::Y), - 'y' => Some(Key::Y), - 'Z' => Some(Key::Z), - 'z' => Some(Key::Z), - '{' => Some(Key::LeftBracket), - '[' => Some(Key::LeftBracket), - '|' => Some(Key::Backslash), - '\\' => Some(Key::Backslash), - '}' => Some(Key::RightBracket), - ']' => Some(Key::RightBracket), - _ => None, - } -} - -pub fn winit_key_to_script_key(key: VirtualKeyCode) -> Result { +pub fn get_servo_key_from_winit_key(key: Option) -> Key { use winit::VirtualKeyCode::*; - // TODO(negge): add more key mappings - Ok(match key { - A => Key::A, - B => Key::B, - C => Key::C, - D => Key::D, - E => Key::E, - F => Key::F, - G => Key::G, - H => Key::H, - I => Key::I, - J => Key::J, - K => Key::K, - L => Key::L, - M => Key::M, - N => Key::N, - O => Key::O, - P => Key::P, - Q => Key::Q, - R => Key::R, - S => Key::S, - T => Key::T, - U => Key::U, - V => Key::V, - W => Key::W, - X => Key::X, - Y => Key::Y, - Z => Key::Z, - - Numpad0 => Key::Kp0, - Numpad1 => Key::Kp1, - Numpad2 => Key::Kp2, - Numpad3 => Key::Kp3, - Numpad4 => Key::Kp4, - Numpad5 => Key::Kp5, - Numpad6 => Key::Kp6, - Numpad7 => Key::Kp7, - Numpad8 => Key::Kp8, - Numpad9 => Key::Kp9, - - Key0 => Key::Num0, - Key1 => Key::Num1, - Key2 => Key::Num2, - Key3 => Key::Num3, - Key4 => Key::Num4, - Key5 => Key::Num5, - Key6 => Key::Num6, - Key7 => Key::Num7, - Key8 => Key::Num8, - Key9 => Key::Num9, - - Return => Key::Enter, - Space => Key::Space, + // TODO: figure out how to map NavigateForward, NavigateBackward + // TODO: map the remaining keys if possible + let key = if let Some(key) = key { + key + } else { + return Key::Unidentified; + }; + match key { + // printable: Key1 to Key0 + // printable: A to Z Escape => Key::Escape, - Equals => Key::Equal, - Minus => Key::Minus, - Back => Key::Backspace, - PageDown => Key::PageDown, - PageUp => Key::PageUp, - - Insert => Key::Insert, - Home => Key::Home, - Delete => Key::Delete, - End => Key::End, - - Left => Key::Left, - Up => Key::Up, - Right => Key::Right, - Down => Key::Down, - - LShift => Key::LeftShift, - LControl => Key::LeftControl, - LAlt => Key::LeftAlt, - LWin => Key::LeftSuper, - RShift => Key::RightShift, - RControl => Key::RightControl, - RAlt => Key::RightAlt, - RWin => Key::RightSuper, - - Apostrophe => Key::Apostrophe, - Backslash => Key::Backslash, - Comma => Key::Comma, - Grave => Key::GraveAccent, - LBracket => Key::LeftBracket, - Period => Key::Period, - RBracket => Key::RightBracket, - Semicolon => Key::Semicolon, - Slash => Key::Slash, - Tab => Key::Tab, - Subtract => Key::Minus, - F1 => Key::F1, F2 => Key::F2, F3 => Key::F3, @@ -223,23 +42,201 @@ pub fn winit_key_to_script_key(key: VirtualKeyCode) -> Result Key::F10, F11 => Key::F11, F12 => Key::F12, - - NavigateBackward => Key::NavigateBackward, - NavigateForward => Key::NavigateForward, - _ => return Err(()), - }) -} - -pub fn is_printable(key_code: VirtualKeyCode) -> bool { - use winit::VirtualKeyCode::*; - match key_code { - Escape | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | F14 | - F15 | Snapshot | Scroll | Pause | Insert | Home | Delete | End | PageDown | PageUp | - Left | Up | Right | Down | Back | LAlt | LControl | LShift | LWin | Mail | - MediaSelect | MediaStop | Mute | MyComputer | NavigateForward | NavigateBackward | - NextTrack | NoConvert | PlayPause | Power | PrevTrack | RAlt | RControl | RShift | - RWin | Sleep | Stop | VolumeDown | VolumeUp | Wake | WebBack | WebFavorites | - WebForward | WebHome | WebRefresh | WebSearch | WebStop => false, - _ => true, + // F13 to F15 are not mapped + Snapshot => Key::PrintScreen, + // Scroll not mapped + Pause => Key::Pause, + Insert => Key::Insert, + Home => Key::Home, + Delete => Key::Delete, + End => Key::End, + PageDown => Key::PageDown, + PageUp => Key::PageUp, + Left => Key::ArrowLeft, + Up => Key::ArrowUp, + Right => Key::ArrowRight, + Down => Key::ArrowDown, + Back => Key::Backspace, + Return => Key::Enter, + // printable: Space + Compose => Key::Compose, + // Caret not mapped + Numlock => Key::NumLock, + // printable: Numpad0 to Numpad9 + // AbntC1 and AbntC2 not mapped + // printable: Add, Apostrophe, + // Apps, At, Ax not mapped + // printable: Backslash, + Calculator => Key::LaunchApplication2, + Capital => Key::CapsLock, + // printable: Colon, Comma, + Convert => Key::Convert, + // not mapped: Decimal, + // printable: Divide, Equals, Grave, + Kana => Key::KanaMode, + Kanji => Key::KanjiMode, + LAlt => Key::Alt, + // printable: LBracket, + LControl => Key::Control, + LShift => Key::Shift, + LWin => Key::Meta, + Mail => Key::LaunchMail, + // not mapped: MediaSelect, + MediaStop => Key::MediaStop, + // printable: Minus, Multiply, + Mute => Key::AudioVolumeMute, + MyComputer => Key::LaunchApplication1, + // not mapped: NavigateForward, NavigateBackward + NextTrack => Key::MediaTrackNext, + NoConvert => Key::NonConvert, + // printable: NumpadComma, NumpadEnter, NumpadEquals, + // not mapped: OEM102, + // printable: Period, + PlayPause => Key::MediaPlayPause, + Power => Key::Power, + PrevTrack => Key::MediaTrackPrevious, + RAlt => Key::Alt, + // printable RBracket + RControl => Key::Control, + RShift => Key::Shift, + RWin => Key::Meta, + // printable Semicolon, Slash + Sleep => Key::Standby, + // not mapped: Stop, + // printable Subtract, + // not mapped: Sysrq, + Tab => Key::Tab, + // printable: Underline, + // not mapped: Unlabeled, + VolumeDown => Key::AudioVolumeDown, + VolumeUp => Key::AudioVolumeUp, + Wake => Key::WakeUp, + WebBack => Key::BrowserBack, + WebFavorites => Key::BrowserFavorites, + WebForward => Key::BrowserForward, + WebHome => Key::BrowserHome, + WebRefresh => Key::BrowserRefresh, + WebSearch => Key::BrowserSearch, + WebStop => Key::BrowserStop, + // printable Yen, + Copy => Key::Copy, + Paste => Key::Paste, + Cut => Key::Cut, + _ => Key::Unidentified, } } + +pub fn get_servo_location_from_winit_key(key: Option) -> Location { + use winit::VirtualKeyCode::*; + // TODO: add more numpad keys + let key = if let Some(key) = key { + key + } else { + return Location::Standard; + }; + match key { + LShift | LControl | LAlt | LWin => Location::Left, + RShift | RControl | RAlt | RWin => Location::Right, + Numpad0 | Numpad1 | Numpad2 | Numpad3 | Numpad4 | Numpad5 | Numpad6 | Numpad7 | + Numpad8 | Numpad9 => Location::Numpad, + NumpadComma | NumpadEnter | NumpadEquals => Location::Numpad, + _ => Location::Standard, + } +} + +#[cfg(target_os = "linux")] +pub fn get_servo_code_from_scancode(scancode: u32) -> Code { + // TODO: Map more codes + use keyboard_types::Code::*; + match scancode { + 1 => Escape, + 2 => Digit1, + 3 => Digit2, + 4 => Digit3, + 5 => Digit4, + 6 => Digit5, + 7 => Digit6, + 8 => Digit7, + 9 => Digit8, + 10 => Digit9, + 11 => Digit0, + + 14 => Backspace, + 15 => Tab, + 16 => KeyQ, + 17 => KeyW, + 18 => KeyE, + 19 => KeyR, + 20 => KeyT, + 21 => KeyY, + 22 => KeyU, + 23 => KeyI, + 24 => KeyO, + 25 => KeyP, + 26 => BracketLeft, + 27 => BracketRight, + 28 => Enter, + + 30 => KeyA, + 31 => KeyS, + 32 => KeyD, + 33 => KeyF, + 34 => KeyG, + 35 => KeyH, + 36 => KeyJ, + 37 => KeyK, + 38 => KeyL, + 39 => Semicolon, + 40 => Quote, + + 42 => ShiftLeft, + 43 => Backslash, + 44 => KeyZ, + 45 => KeyX, + 46 => KeyC, + 47 => KeyV, + 48 => KeyB, + 49 => KeyN, + 50 => KeyM, + 51 => Comma, + 52 => Period, + 53 => Slash, + 54 => ShiftRight, + + 57 => Space, + + 59 => F1, + 60 => F2, + 61 => F3, + 62 => F4, + 63 => F5, + 64 => F6, + 65 => F7, + 66 => F8, + 67 => F9, + 68 => F10, + + 87 => F11, + 88 => F12, + + 103 => ArrowUp, + 104 => PageUp, + 105 => ArrowLeft, + 106 => ArrowRight, + + 102 => Home, + 107 => End, + 108 => ArrowDown, + 109 => PageDown, + 110 => Insert, + 111 => Delete, + + _ => Unidentified, + } +} + +#[cfg(not(target_os = "linux"))] +pub fn get_servo_code_from_scancode(_scancode: u32) -> Code { + // TODO: Implement for Windows and Mac OS + Code::Unidentified +} diff --git a/ports/servo/glutin_app/window.rs b/ports/servo/glutin_app/window.rs index 628f6103d54..c47d313cc9b 100644 --- a/ports/servo/glutin_app/window.rs +++ b/ports/servo/glutin_app/window.rs @@ -9,12 +9,12 @@ use euclid::{Length, TypedPoint2D, TypedVector2D, TypedScale, TypedSize2D}; use gdi32; use gleam::gl; use glutin::{Api, ContextBuilder, GlContext, GlRequest, GlWindow}; +use keyboard_types::{Key, KeyboardEvent, Modifiers as KeyModifiers, KeyState}; #[cfg(any(target_os = "linux", target_os = "macos"))] use osmesa_sys; use servo::compositing::windowing::{AnimationState, MouseWindowEvent, WindowEvent}; use servo::compositing::windowing::{EmbedderCoordinates, WindowMethods}; use servo::embedder_traits::EventLoopWaker; -use servo::msg::constellation_msg::{Key, KeyState, KeyModifiers}; use servo::script_traits::TouchEventType; use servo::servo_config::opts; use servo::servo_geometry::DeviceIndependentPixel; @@ -151,7 +151,7 @@ pub struct Window { event_queue: RefCell>, mouse_pos: Cell>, key_modifiers: Cell, - last_pressed_key: Cell>, + last_pressed: Cell>, animation_state: Cell, fullscreen: Cell, gl: Rc, @@ -280,7 +280,7 @@ impl Window { mouse_pos: Cell::new(TypedPoint2D::new(0, 0)), key_modifiers: Cell::new(KeyModifiers::empty()), - last_pressed_key: Cell::new(None), + last_pressed: Cell::new(None), gl: gl.clone(), animation_state: Cell::new(AnimationState::Idle), fullscreen: Cell::new(false), @@ -408,56 +408,68 @@ impl Window { GlRequest::Specific(Api::OpenGlEs, (3, 0)) } - fn handle_received_character(&self, ch: char) { - let last_key = if let Some(key) = self.last_pressed_key.get() { - key + fn handle_received_character(&self, mut ch: char) { + info!("winit received character: {:?}", ch); + if ch.is_control() { + if ch as u8 >= 32 { + return; + } + // shift ASCII control characters to lowercase + ch = (ch as u8 + 96) as char; + } + let mut event = if let Some(event) = self.last_pressed.replace(None) { + event } else { return; }; - - self.last_pressed_key.set(None); - - let (key, ch) = if let Some(key) = keyutils::char_to_script_key(ch) { - (key, Some(ch)) - } else { - (last_key, None) - }; - - let modifiers = self.key_modifiers.get(); - let event = WindowEvent::KeyEvent(ch, key, KeyState::Pressed, modifiers); - self.event_queue.borrow_mut().push(event); + event.key = Key::Character(ch.to_string()); + self.event_queue + .borrow_mut() + .push(WindowEvent::Keyboard(event)); } fn toggle_keyboard_modifiers(&self, mods: ModifiersState) { self.toggle_modifier(KeyModifiers::CONTROL, mods.ctrl); self.toggle_modifier(KeyModifiers::SHIFT, mods.shift); self.toggle_modifier(KeyModifiers::ALT, mods.alt); - self.toggle_modifier(KeyModifiers::SUPER, mods.logo); + self.toggle_modifier(KeyModifiers::META, mods.logo); } fn handle_keyboard_input( &self, + scancode: u32, element_state: ElementState, - code: VirtualKeyCode, + code: Option, mods: ModifiersState, ) { + info!( + "winit keyboard input: {}, {:?}, {:?}, {:?}", + scancode, element_state, code, mods + ); self.toggle_keyboard_modifiers(mods); - - if let Ok(key) = keyutils::winit_key_to_script_key(code) { - let state = match element_state { - ElementState::Pressed => KeyState::Pressed, - ElementState::Released => KeyState::Released, - }; - if element_state == ElementState::Pressed && keyutils::is_printable(code) { - // If pressed and printable, we expect a ReceivedCharacter event. - self.last_pressed_key.set(Some(key)); - } else { - self.last_pressed_key.set(None); - let modifiers = self.key_modifiers.get(); - self.event_queue - .borrow_mut() - .push(WindowEvent::KeyEvent(None, key, state, modifiers)); - } + let key = keyutils::get_servo_key_from_winit_key(code); + let location = keyutils::get_servo_location_from_winit_key(code); + let state = match element_state { + ElementState::Pressed => KeyState::Down, + ElementState::Released => KeyState::Up, + }; + let event = KeyboardEvent { + state, + key: key.clone(), + code: keyutils::get_servo_code_from_scancode(scancode), + location, + modifiers: self.key_modifiers.get(), + repeat: false, + is_composing: false, + }; + if element_state == ElementState::Pressed && key == Key::Unidentified { + // If pressed and probably printable, we expect a ReceivedCharacter event. + self.last_pressed.set(Some(event)); + } else if key != Key::Unidentified { + self.last_pressed.set(None); + self.event_queue + .borrow_mut() + .push(WindowEvent::Keyboard(event)); } } @@ -472,15 +484,15 @@ impl Window { winit::WindowEvent::KeyboardInput { input: winit::KeyboardInput { + scancode, state, - virtual_keycode: Some(virtual_keycode), + virtual_keycode, modifiers, - .. }, .. }, .. - } => self.handle_keyboard_input(state, virtual_keycode, modifiers), + } => self.handle_keyboard_input(scancode, state, virtual_keycode, modifiers), Event::WindowEvent { event: winit::WindowEvent::MouseInput { state, button, .. }, .. diff --git a/ports/servo/non_android_main.rs b/ports/servo/non_android_main.rs index 50ba92f2130..091f88e8f3d 100644 --- a/ports/servo/non_android_main.rs +++ b/ports/servo/non_android_main.rs @@ -8,6 +8,7 @@ extern crate euclid; extern crate gdi32; extern crate gleam; extern crate glutin; +extern crate keyboard_types; #[macro_use] extern crate lazy_static; #[cfg(any(target_os = "linux", target_os = "macos"))] diff --git a/tests/unit/script/Cargo.toml b/tests/unit/script/Cargo.toml index 81907fe1bc4..a0d1418fb84 100644 --- a/tests/unit/script/Cargo.toml +++ b/tests/unit/script/Cargo.toml @@ -10,6 +10,6 @@ path = "lib.rs" [dependencies] euclid = "0.19" -msg = {path = "../../../components/msg"} +keyboard-types = "0.4.0-servo" script = {path = "../../../components/script"} servo_url = {path = "../../../components/url"} diff --git a/tests/unit/script/lib.rs b/tests/unit/script/lib.rs index 069417a5911..f8ffcaee26f 100644 --- a/tests/unit/script/lib.rs +++ b/tests/unit/script/lib.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #[cfg(test)] extern crate euclid; -#[cfg(test)] extern crate msg; +#[cfg(test)] extern crate keyboard_types; #[cfg(test)] extern crate script; #[cfg(test)] extern crate servo_url; diff --git a/tests/unit/script/textinput.rs b/tests/unit/script/textinput.rs index 3880d641d85..0f4467d81ca 100644 --- a/tests/unit/script/textinput.rs +++ b/tests/unit/script/textinput.rs @@ -7,7 +7,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use msg::constellation_msg::{Key, KeyModifiers}; +use keyboard_types::{Key, Modifiers}; use script::clipboard_provider::DummyClipboardContext; use script::test::DOMString; use script::textinput::{TextInput, TextPoint, Selection, Lines, Direction, SelectionDirection}; @@ -420,34 +420,33 @@ fn test_textinput_adjust_horizontal_to_line_end() { } #[test] -#[cfg(target_os = "macos")] fn test_navigation_keyboard_shortcuts() { let mut textinput = text_input(Lines::Multiple, "hello áéc"); // Test that CMD + Right moves to the end of the current line. - textinput.handle_keydown_aux(None, Key::Right, KeyModifiers::SUPER); + textinput.handle_keydown_aux(Key::ArrowRight, Modifiers::META, true); assert_eq!(textinput.edit_point().index, 11); // Test that CMD + Right moves to the beginning of the current line. - textinput.handle_keydown_aux(None, Key::Left, KeyModifiers::SUPER); + textinput.handle_keydown_aux(Key::ArrowLeft, Modifiers::META, true); assert_eq!(textinput.edit_point().index, 0); // Test that CTRL + ALT + E moves to the end of the current line also. - textinput.handle_keydown_aux(None, Key::E, KeyModifiers::CONTROL | KeyModifiers::ALT); + textinput.handle_keydown_aux(Key::Character("e".to_owned()), Modifiers::CONTROL | Modifiers::ALT, true); assert_eq!(textinput.edit_point().index, 11); // Test that CTRL + ALT + A moves to the beginning of the current line also. - textinput.handle_keydown_aux(None, Key::A, KeyModifiers::CONTROL | KeyModifiers::ALT); + textinput.handle_keydown_aux(Key::Character("a".to_owned()), Modifiers::CONTROL | Modifiers::ALT, true); assert_eq!(textinput.edit_point().index, 0); // Test that ALT + Right moves to the end of the word. - textinput.handle_keydown_aux(None, Key::Right, KeyModifiers::ALT); + textinput.handle_keydown_aux(Key::ArrowRight, Modifiers::ALT, true); assert_eq!(textinput.edit_point().index, 5); // Test that CTRL + ALT + F moves to the end of the word also. - textinput.handle_keydown_aux(None, Key::F, KeyModifiers::CONTROL | KeyModifiers::ALT); + textinput.handle_keydown_aux(Key::Character("f".to_owned()), Modifiers::CONTROL | Modifiers::ALT, true); assert_eq!(textinput.edit_point().index, 11); // Test that ALT + Left moves to the end of the word. - textinput.handle_keydown_aux(None, Key::Left, KeyModifiers::ALT); + textinput.handle_keydown_aux(Key::ArrowLeft, Modifiers::ALT, true); assert_eq!(textinput.edit_point().index, 6); // Test that CTRL + ALT + B moves to the end of the word also. - textinput.handle_keydown_aux(None, Key::B, KeyModifiers::CONTROL | KeyModifiers::ALT); + textinput.handle_keydown_aux(Key::Character("b".to_owned()), Modifiers::CONTROL | Modifiers::ALT, true); assert_eq!(textinput.edit_point().index, 0); } @@ -507,9 +506,9 @@ fn test_textinput_set_content() { #[test] fn test_clipboard_paste() { #[cfg(target_os = "macos")] - const MODIFIERS: KeyModifiers = KeyModifiers::SUPER; + const MODIFIERS: Modifiers = Modifiers::META; #[cfg(not(target_os = "macos"))] - const MODIFIERS: KeyModifiers = KeyModifiers::CONTROL; + const MODIFIERS: Modifiers = Modifiers::CONTROL; let mut textinput = TextInput::new(Lines::Single, DOMString::from("defg"), @@ -519,7 +518,7 @@ fn test_clipboard_paste() { SelectionDirection::None); assert_eq!(textinput.get_content(), "defg"); assert_eq!(textinput.edit_point().index, 0); - textinput.handle_keydown_aux(Some('v'), Key::V, MODIFIERS); + textinput.handle_keydown_aux(Key::Character("v".to_owned()), MODIFIERS, false); assert_eq!(textinput.get_content(), "abcdefg"); }