Auto merge of #21881 - pyfisch:keyboard-types, r=paulrouget

Use keyboard-types crate

Have embedders send DOM keys to servo and use a strongly typed KeyboardEvent
from the W3C UI Events spec. All keyboard handling now uses the new types.

Introduce a ShortcutMatcher to recognize key bindings. Shortcuts are now
recognized in a uniform way.

Updated the winit port.
Updated webdriver integration.

part of #20331

What this PR does:

* allow the use non-ASCII keyboards for text input
* decouple keyboard event "key" from "code" (key meaning vs location)

What this PR does not do:

* completely improve keyboard events send from winit and webdriver
* add support for CompositionEvent or IME

Notes:

* The winit embedder does not send keyup events for printable keys (this is a regression)
* keyboard-types is on crates.io because I believe it to be useful outside of servo. If you prefer I can add a copy in this repo.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21881)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-10-17 05:36:08 -04:00 committed by GitHub
commit 9a0404ac5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 747 additions and 1673 deletions

21
Cargo.lock generated
View file

@ -498,6 +498,7 @@ dependencies = [
"gleam 0.6.2 (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.2-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",
@ -534,6 +535,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.2-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",
@ -895,6 +897,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.2-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",
@ -1726,6 +1729,15 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "keyboard-types"
version = "0.4.2-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"
@ -2013,6 +2025,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.2-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)",
@ -2249,7 +2262,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)",
@ -2981,6 +2993,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.2-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)",
@ -3084,7 +3097,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.2-servo (registry+https://github.com/rust-lang/crates.io-index)",
"script 0.0.1",
"servo_url 0.0.1",
]
@ -3103,6 +3116,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.2-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",
@ -3191,6 +3205,7 @@ dependencies = [
"gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.6.2 (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.2-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)",
@ -4032,6 +4047,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.2-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",
@ -4458,6 +4474,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.2-servo (registry+https://github.com/rust-lang/crates.io-index)" = "75082c134a78e0fc2232d2f30bf3dfdea1cd28591846b85a73b4b46cd776b482"
"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"

View file

@ -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.2-servo", features = ["serde"]}
log = "0.4"
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}

View file

@ -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;

View file

@ -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<char>, 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"),

View file

@ -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.2-servo", features = ["serde"]}
log = "0.4"
metrics = {path = "../metrics"}
msg = {path = "../msg"}

View file

@ -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<char>, 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);

View file

@ -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;

View file

@ -11,6 +11,7 @@ path = "lib.rs"
[dependencies]
ipc-channel = "0.11"
keyboard-types = {version = "0.4.2-servo", features = ["serde"]}
lazy_static = "1"
log = "0.4"
msg = {path = "../msg"}

View file

@ -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<bool>),
/// Sends an unconsumed key event back to the embedder.
KeyEvent(Option<char>, 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"),

View file

@ -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.2-servo", features = ["serde"], optional = true}
mozjs = { version = "0.9.0", optional = true }
selectors = { path = "../selectors" }
serde = { version = "1.0.27", optional = true }

View file

@ -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")]
@ -955,6 +957,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 {

View file

@ -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"

View file

@ -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),

View file

@ -4,8 +4,6 @@
#![deny(unsafe_code)]
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate malloc_size_of;
#[macro_use]

View file

@ -58,6 +58,7 @@ image = "0.19"
ipc-channel = "0.11"
itertools = "0.7.6"
jstraceable_derive = {path = "../jstraceable_derive"}
keyboard-types = {version = "0.4.2-servo", features = ["serde"]}
lazy_static = "1"
libc = "0.2"
log = "0.4"

View file

@ -100,9 +100,10 @@ use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::{JSContext, JSObject, JSRuntime};
use js::jsapi::JS_GetRuntime;
use keyboard_types::{Key, KeyState, Modifiers};
use metrics::{InteractiveFlag, InteractiveMetrics, InteractiveWindow, ProfilerMetadataFactory, ProgressiveWebMetric};
use mime::{Mime, TopLevel, SubLevel};
use msg::constellation_msg::{BrowsingContextId, Key, KeyModifiers, KeyState};
use msg::constellation_msg::BrowsingContextId;
use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy};
use net_traits::CookieSource::NonHTTP;
use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl};
@ -769,10 +770,12 @@ impl Document {
// Step 1 is not handled here; the fragid is already obtained by the calling function
// Step 2: Simply use None to indicate the top of the document.
// Step 3 & 4
percent_decode(fragid.as_bytes()).decode_utf8().ok()
// Step 5
percent_decode(fragid.as_bytes())
.decode_utf8()
.ok()
// Step 5
.and_then(|decoded_fragid| self.get_element_by_id(&Atom::from(decoded_fragid)))
// Step 6
// Step 6
.or_else(|| self.get_anchor_by_name(fragid))
// Step 7 & 8
}
@ -805,7 +808,8 @@ impl Document {
rect.origin.x.to_nearest_px() as f32,
rect.origin.y.to_nearest_px() as f32,
)
}).or_else(|| {
})
.or_else(|| {
if fragment.is_empty() || fragment.eq_ignore_ascii_case("top") {
// FIXME(stshine): this should be the origin of the stacking context space,
// which may differ under the influence of writing mode.
@ -1348,13 +1352,7 @@ impl Document {
}
/// The entry point for all key processing for web content
pub fn dispatch_key_event(
&self,
ch: Option<char>,
key: Key,
state: KeyState,
modifiers: KeyModifiers,
) {
pub fn dispatch_key_event(&self, keyboard_event: ::keyboard_types::KeyboardEvent) {
let focused = self.get_focused_element();
let body = self.GetBody();
@ -1364,50 +1362,29 @@ impl Document {
(&None, &None) => self.window.upcast(),
};
let ctrl = modifiers.contains(KeyModifiers::CONTROL);
let alt = modifiers.contains(KeyModifiers::ALT);
let shift = modifiers.contains(KeyModifiers::SHIFT);
let meta = modifiers.contains(KeyModifiers::SUPER);
let is_composing = false;
let is_repeating = state == KeyState::Repeated;
let ev_type = DOMString::from(
match state {
KeyState::Pressed | KeyState::Repeated => "keydown",
KeyState::Released => "keyup",
}.to_owned(),
);
let props = KeyboardEvent::key_properties(ch, key, modifiers);
let keyevent = KeyboardEvent::new(
&self.window,
ev_type,
DOMString::from(keyboard_event.state.to_string()),
true,
true,
Some(&self.window),
0,
ch,
Some(key),
DOMString::from(props.key_string.clone()),
DOMString::from(props.code),
props.location,
is_repeating,
is_composing,
ctrl,
alt,
shift,
meta,
None,
props.key_code,
keyboard_event.key.clone(),
DOMString::from(keyboard_event.code.to_string()),
keyboard_event.location as u32,
keyboard_event.repeat,
keyboard_event.is_composing,
keyboard_event.modifiers,
0,
keyboard_event.key.legacy_keycode(),
);
let event = keyevent.upcast::<Event>();
event.fire(target);
let mut cancel_state = event.get_cancel_state();
// https://w3c.github.io/uievents/#keys-cancelable-keys
if state != KeyState::Released &&
props.is_printable() &&
if keyboard_event.state == KeyState::Down &&
keyboard_event.key.legacy_charcode() != 0 &&
cancel_state != EventDefault::Prevented
{
// https://w3c.github.io/uievents/#keypress-event-order
@ -1418,18 +1395,13 @@ impl Document {
true,
Some(&self.window),
0,
ch,
Some(key),
DOMString::from(props.key_string),
DOMString::from(props.code),
props.location,
is_repeating,
is_composing,
ctrl,
alt,
shift,
meta,
props.char_code,
keyboard_event.key.clone(),
DOMString::from(keyboard_event.code.to_string()),
keyboard_event.location as u32,
keyboard_event.repeat,
keyboard_event.is_composing,
keyboard_event.modifiers,
keyboard_event.key.legacy_charcode(),
0,
);
let ev = event.upcast::<Event>();
@ -1438,7 +1410,7 @@ impl Document {
}
if cancel_state == EventDefault::Allowed {
let msg = EmbedderMsg::KeyEvent(ch, key, state, modifiers);
let msg = EmbedderMsg::Keyboard(keyboard_event.clone());
self.send_to_embedder(msg);
// This behavior is unspecced
@ -1446,8 +1418,10 @@ impl Document {
// however *when* we do it is up to us.
// Here, we're dispatching it after the key event so the script has a chance to cancel it
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27337
match key {
Key::Space if state == KeyState::Released => {
match keyboard_event.key {
Key::Character(ref letter)
if letter == " " && keyboard_event.state == KeyState::Up =>
{
let maybe_elem = target.downcast::<Element>();
if let Some(el) = maybe_elem {
synthetic_click_activation(
@ -1459,11 +1433,15 @@ impl Document {
ActivationSource::NotFromClick,
)
}
},
Key::Enter if state == KeyState::Released => {
}
Key::Enter if keyboard_event.state == KeyState::Up => {
let maybe_elem = target.downcast::<Element>();
if let Some(el) = maybe_elem {
if let Some(a) = el.as_maybe_activatable() {
let ctrl = keyboard_event.modifiers.contains(Modifiers::CONTROL);
let alt = keyboard_event.modifiers.contains(Modifiers::ALT);
let shift = keyboard_event.modifiers.contains(Modifiers::SHIFT);
let meta = keyboard_event.modifiers.contains(Modifiers::META);
a.implicit_submission(ctrl, alt, shift, meta);
}
}

View file

@ -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,47 +15,39 @@ 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<Option<Key>>,
key_string: DomRefCell<DOMString>,
key: DomRefCell<DOMString>,
typed_key: DomRefCell<Key>,
code: DomRefCell<DOMString>,
location: Cell<u32>,
ctrl: Cell<bool>,
alt: Cell<bool>,
shift: Cell<bool>,
meta: Cell<bool>,
modifiers: Cell<Modifiers>,
repeat: Cell<bool>,
is_composing: Cell<bool>,
char_code: Cell<Option<u32>>,
char_code: Cell<u32>,
key_code: Cell<u32>,
printable: Cell<Option<char>>,
}
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),
char_code: Cell::new(0),
key_code: Cell::new(0),
printable: Cell::new(None),
}
}
@ -74,18 +66,13 @@ impl KeyboardEvent {
cancelable: bool,
view: Option<&Window>,
_detail: i32,
ch: Option<char>,
key: Option<Key>,
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,
char_code: Option<u32>,
modifiers: Modifiers,
char_code: u32,
key_code: u32,
) -> DomRoot<KeyboardEvent> {
let ev = KeyboardEvent::new_uninitialized(window);
@ -94,22 +81,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 +101,11 @@ impl KeyboardEvent {
type_: DOMString,
init: &KeyboardEventBinding::KeyboardEventInit,
) -> Fallible<DomRoot<KeyboardEvent>> {
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,680 +113,27 @@ 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,
None,
modifiers,
0,
0,
);
*event.key.borrow_mut() = init.key.clone();
Ok(event)
}
pub fn key_properties(ch: Option<char>, 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 {
pub fn printable(&self) -> Option<char> {
self.printable.get()
pub fn key(&self) -> Key {
self.typed_key.borrow().clone()
}
pub fn get_key(&self) -> Option<Key> {
self.key.get().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<char>, 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<Key> {
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,
}
}
// https://w3c.github.io/uievents/#legacy-key-models
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::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::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,
//§ 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<u32>,
pub key_code: u32,
}
impl KeyEventProperties {
pub fn is_printable(&self) -> bool {
self.char_code.is_some()
pub fn modifiers(&self) -> Modifiers {
self.modifiers.get()
}
}
@ -822,14 +157,14 @@ impl KeyboardEventMethods for KeyboardEvent {
self.upcast::<UIEvent>()
.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 +179,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,20 +209,26 @@ 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
fn CharCode(&self) -> u32 {
self.char_code.get().unwrap_or(0)
self.char_code.get()
}
// https://w3c.github.io/uievents/#widl-KeyboardEvent-keyCode
@ -897,7 +238,11 @@ impl KeyboardEventMethods for KeyboardEvent {
// https://w3c.github.io/uievents/#widl-KeyboardEvent-which
fn Which(&self) -> u32 {
self.char_code.get().unwrap_or(self.KeyCode())
if self.char_code.get() != 0 {
self.char_code.get()
} else {
self.key_code.get()
}
}
// https://dom.spec.whatwg.org/#dom-event-istrusted

View file

@ -54,6 +54,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;

View file

@ -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;
@ -2823,6 +2823,7 @@ impl ScriptThread {
}
}
},
TouchEvent(event_type, identifier, point, node_address) => {
let touch_result = self.handle_touch_event(
pipeline_id,
@ -2848,12 +2849,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);
},
}
}

View file

@ -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<T: ClipboardProvider> TextInput<T> {
/// 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<char>,
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.

View file

@ -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.2-servo", features = ["serde"]}
libc = "0.2"
malloc_size_of = { path = "../malloc_size_of" }
malloc_size_of_derive = { path = "../malloc_size_of_derive" }

View file

@ -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<UntrustedNodeAddress>,
),
/// A key was pressed.
KeyEvent(Option<char>, Key, KeyState, KeyModifiers),
KeyboardEvent(KeyboardEvent),
}
/// Requests a TimerEvent-Message be sent after the given duration.
@ -708,7 +710,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<KeyboardEvent>),
/// Set the window size.
SetWindowSize(
TopLevelBrowsingContextId,
@ -736,7 +738,7 @@ pub enum ConstellationMsg {
/// Query the constellation to see if the current compositor output is stable
IsReadyToSaveImage(HashMap<PipelineId, Epoch>),
/// Inform the constellation of a key event.
KeyEvent(Option<char>, 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.
@ -780,7 +782,7 @@ impl fmt::Debug for ConstellationMsg {
GetPipeline(..) => "GetPipeline",
GetFocusTopLevelBrowsingContext(..) => "GetFocusTopLevelBrowsingContext",
IsReadyToSaveImage(..) => "IsReadyToSaveImage",
KeyEvent(..) => "KeyEvent",
Keyboard(..) => "Keyboard",
LoadUrl(..) => "LoadUrl",
TraverseHistory(..) => "TraverseHistory",
WindowSize(..) => "WindowSize",

View file

@ -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.
///
@ -320,10 +320,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);
}
},
@ -399,12 +399,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);
},

View file

@ -16,6 +16,7 @@ euclid = "0.19"
hyper = "0.10"
image = "0.19"
ipc-channel = "0.11"
keyboard-types = {version = "0.4.2-servo", features = ["serde"]}
log = "0.4"
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}

View file

@ -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<Vec<(Key, KeyModifiers, KeyState)>, String> {
pub fn keycodes_to_keys(key_codes: &str) -> Vec<KeyboardEvent> {
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
}

View file

@ -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,

View file

@ -408,7 +408,7 @@ impl ServoGlue {
EmbedderMsg::SelectFiles(..) |
EmbedderMsg::MoveTo(..) |
EmbedderMsg::ResizeTo(..) |
EmbedderMsg::KeyEvent(..) |
EmbedderMsg::Keyboard(..) |
EmbedderMsg::SetCursor(..) |
EmbedderMsg::NewFavicon(..) |
EmbedderMsg::HeadParsed |

View file

@ -40,6 +40,7 @@ crossbeam-channel = "0.2"
euclid = "0.19"
gleam = "0.6"
glutin = "0.18"
keyboard-types = {version = "0.4.2-servo", features = ["serde"]}
lazy_static = "1"
libservo = {path = "../../components/servo"}
log = "0.4"

View file

@ -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<WindowEvent>) {
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<char>,
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<char>,
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<char>,
_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<BrowserId>,
ch: Option<char>,
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<BrowserId>, 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<String>) -> Option<String> {
},
None => None,
}
}).unwrap()
})
.unwrap()
.join()
.expect("Thread spawning failed")
}
@ -480,7 +442,8 @@ fn get_selected_files(patterns: Vec<FilterPattern>, 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<ServoUrl> {
} else {
None
}
}).or_else(|| {
})
.or_else(|| {
PREFS
.get("shell.searchpage")
.as_string()

View file

@ -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 winit::VirtualKeyCode;
use keyboard_types::{Code, Key, KeyboardEvent, KeyState, Modifiers, Location};
use winit::{ElementState, KeyboardInput, ModifiersState, 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<constellation_msg::Key> {
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<constellation_msg::Key, ()> {
fn get_servo_key_from_winit_key(key: Option<VirtualKeyCode>) -> 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,226 @@ pub fn winit_key_to_script_key(key: VirtualKeyCode) -> Result<constellation_msg:
F10 => 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,
}
}
fn get_servo_location_from_winit_key(key: Option<VirtualKeyCode>) -> 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")]
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"))]
fn get_servo_code_from_scancode(_scancode: u32) -> Code {
// TODO: Implement for Windows and Mac OS
Code::Unidentified
}
fn get_modifiers(mods: ModifiersState) -> Modifiers {
let mut modifiers = Modifiers::empty();
modifiers.set(Modifiers::CONTROL, mods.ctrl);
modifiers.set(Modifiers::SHIFT, mods.shift);
modifiers.set(Modifiers::ALT, mods.alt);
modifiers.set(Modifiers::META, mods.logo);
modifiers
}
pub fn keyboard_event_from_winit(input: KeyboardInput) -> KeyboardEvent {
info!("winit keyboard input: {:?}", input);
KeyboardEvent {
state: match input.state {
ElementState::Pressed => KeyState::Down,
ElementState::Released => KeyState::Up,
},
key: get_servo_key_from_winit_key(input.virtual_keycode),
code: get_servo_code_from_scancode(input.scancode),
location: get_servo_location_from_winit_key(input.virtual_keycode),
modifiers: get_modifiers(input.modifiers),
repeat: false,
is_composing: false,
}
}

View file

@ -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, 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;
@ -31,13 +31,13 @@ use std::rc::Rc;
use std::sync::Arc;
use std::thread;
use std::time;
use super::keyutils;
use super::keyutils::keyboard_event_from_winit;
#[cfg(target_os = "windows")]
use user32;
#[cfg(target_os = "windows")]
use winapi;
use winit;
use winit::{ElementState, Event, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
use winit::{ElementState, Event, MouseButton, MouseScrollDelta, TouchPhase, KeyboardInput};
use winit::dpi::{LogicalPosition, LogicalSize, PhysicalSize};
#[cfg(target_os = "macos")]
use winit::os::macos::{ActivationPolicy, WindowBuilderExt};
@ -150,8 +150,7 @@ pub struct Window {
mouse_down_point: Cell<TypedPoint2D<i32, DevicePixel>>,
event_queue: RefCell<Vec<WindowEvent>>,
mouse_pos: Cell<TypedPoint2D<i32, DevicePixel>>,
key_modifiers: Cell<KeyModifiers>,
last_pressed_key: Cell<Option<Key>>,
last_pressed: Cell<Option<KeyboardEvent>>,
animation_state: Cell<AnimationState>,
fullscreen: Cell<bool>,
gl: Rc<gl::Gl>,
@ -276,11 +275,8 @@ impl Window {
event_queue: RefCell::new(vec![]),
mouse_down_button: Cell::new(None),
mouse_down_point: Cell::new(TypedPoint2D::new(0, 0)),
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 +404,46 @@ 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 if ch.is_ascii() {
// Some keys like Backspace emit a control character in winit
// but they are already dealt with in handle_keyboard_input
// so just ignore the character.
return
} else {
return;
// For combined characters like the letter e with an acute accent
// no keyboard event is emitted. A dummy event is created in this case.
KeyboardEvent::default()
};
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);
}
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);
event.key = Key::Character(ch.to_string());
self.event_queue
.borrow_mut()
.push(WindowEvent::Keyboard(event));
}
fn handle_keyboard_input(
&self,
element_state: ElementState,
code: VirtualKeyCode,
mods: ModifiersState,
input: KeyboardInput,
) {
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 event = keyboard_event_from_winit(input);
if event.state == KeyState::Down && event.key == Key::Unidentified {
// If pressed and probably printable, we expect a ReceivedCharacter event.
self.last_pressed.set(Some(event));
} else if event.key != Key::Unidentified {
self.last_pressed.set(None);
self.event_queue
.borrow_mut()
.push(WindowEvent::Keyboard(event));
}
}
@ -470,17 +456,11 @@ impl Window {
Event::WindowEvent {
event:
winit::WindowEvent::KeyboardInput {
input:
winit::KeyboardInput {
state,
virtual_keycode: Some(virtual_keycode),
modifiers,
..
},
input,
..
},
..
} => self.handle_keyboard_input(state, virtual_keycode, modifiers),
} => self.handle_keyboard_input(input),
Event::WindowEvent {
event: winit::WindowEvent::MouseInput { state, button, .. },
..
@ -584,16 +564,6 @@ impl Window {
}
}
fn toggle_modifier(&self, modifier: KeyModifiers, pressed: bool) {
let mut modifiers = self.key_modifiers.get();
if pressed {
modifiers.insert(modifier);
} else {
modifiers.remove(modifier);
}
self.key_modifiers.set(modifiers);
}
/// Helper function to handle a click
fn handle_mouse(
&self,

View file

@ -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"))]

View file

@ -10,6 +10,6 @@ path = "lib.rs"
[dependencies]
euclid = "0.19"
msg = {path = "../../../components/msg"}
keyboard-types = "0.4.2-servo"
script = {path = "../../../components/script"}
servo_url = {path = "../../../components/url"}

View file

@ -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;

View file

@ -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");
}