/* This Source Code Form is subject to the terms of the Mozilla Public * 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 dom::bindings::codegen::Bindings::KeyboardEventBinding; use dom::bindings::codegen::Bindings::KeyboardEventBinding::{KeyboardEventMethods, KeyboardEventConstants}; use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; use dom::bindings::codegen::InheritTypes::{EventCast, UIEventCast, KeyboardEventDerived}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::global; use dom::bindings::js::{JSRef, Temporary, RootedReference}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::event::{Event, KeyboardEventTypeId}; use dom::uievent::UIEvent; use dom::window::Window; use servo_msg::constellation_msg; use servo_util::str::DOMString; use std::cell::{RefCell, Cell}; #[jstraceable] #[must_root] pub struct KeyboardEvent { uievent: UIEvent, key: RefCell, code: RefCell, location: Cell, ctrl: Cell, alt: Cell, shift: Cell, meta: Cell, repeat: Cell, is_composing: Cell, char_code: Cell>, key_code: Cell, } impl KeyboardEventDerived for Event { fn is_keyboardevent(&self) -> bool { *self.type_id() == KeyboardEventTypeId } } impl KeyboardEvent { fn new_inherited() -> KeyboardEvent { KeyboardEvent { uievent: UIEvent::new_inherited(KeyboardEventTypeId), key: RefCell::new("".to_string()), code: RefCell::new("".to_string()), location: Cell::new(0), ctrl: Cell::new(false), alt: Cell::new(false), shift: Cell::new(false), meta: Cell::new(false), repeat: Cell::new(false), is_composing: Cell::new(false), char_code: Cell::new(None), key_code: Cell::new(0), } } pub fn new_uninitialized(window: JSRef) -> Temporary { reflect_dom_object(box KeyboardEvent::new_inherited(), global::Window(window), KeyboardEventBinding::Wrap) } pub fn new(window: JSRef, type_: DOMString, canBubble: bool, cancelable: bool, view: Option>, _detail: i32, key: DOMString, code: DOMString, location: u32, repeat: bool, isComposing: bool, ctrlKey: bool, altKey: bool, shiftKey: bool, metaKey: bool, char_code: Option, key_code: u32) -> Temporary { let ev = KeyboardEvent::new_uninitialized(window).root(); ev.deref().InitKeyboardEvent(type_, canBubble, cancelable, view, key, location, "".to_string(), repeat, "".to_string()); *ev.code.borrow_mut() = code; ev.ctrl.set(ctrlKey); ev.alt.set(altKey); ev.shift.set(shiftKey); ev.meta.set(metaKey); ev.char_code.set(char_code); ev.key_code.set(key_code); ev.is_composing.set(isComposing); Temporary::from_rooted(*ev) } pub fn Constructor(global: &GlobalRef, type_: DOMString, init: &KeyboardEventBinding::KeyboardEventInit) -> Fallible> { let event = KeyboardEvent::new(global.as_window(), type_, init.parent.parent.parent.bubbles, init.parent.parent.parent.cancelable, init.parent.parent.view.root_ref(), init.parent.parent.detail, init.key.clone(), init.code.clone(), init.location, init.repeat, init.isComposing, init.parent.ctrlKey, init.parent.altKey, init.parent.shiftKey, init.parent.metaKey, None, 0); Ok(event) } pub fn key_properties(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> KeyEventProperties { KeyEventProperties { key: key_value(key, mods), code: code_value(key), location: key_location(key), char_code: key_charcode(key, mods), key_code: key_keycode(key), } } } // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html fn key_value(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> &'static str { let shift = mods.contains(constellation_msg::SHIFT); match key { constellation_msg::KeySpace => " ", constellation_msg::KeyApostrophe if shift => "\"", constellation_msg::KeyApostrophe => "'", constellation_msg::KeyComma if shift => "<", constellation_msg::KeyComma => ",", constellation_msg::KeyMinus if shift => "_", constellation_msg::KeyMinus => "-", constellation_msg::KeyPeriod if shift => ">", constellation_msg::KeyPeriod => ".", constellation_msg::KeySlash if shift => "?", constellation_msg::KeySlash => "/", constellation_msg::Key0 if shift => ")", constellation_msg::Key0 => "0", constellation_msg::Key1 if shift => "!", constellation_msg::Key1 => "1", constellation_msg::Key2 if shift => "@", constellation_msg::Key2 => "2", constellation_msg::Key3 if shift => "#", constellation_msg::Key3 => "3", constellation_msg::Key4 if shift => "$", constellation_msg::Key4 => "4", constellation_msg::Key5 if shift => "%", constellation_msg::Key5 => "5", constellation_msg::Key6 if shift => "^", constellation_msg::Key6 => "6", constellation_msg::Key7 if shift => "&", constellation_msg::Key7 => "7", constellation_msg::Key8 if shift => "*", constellation_msg::Key8 => "8", constellation_msg::Key9 if shift => "(", constellation_msg::Key9 => "9", constellation_msg::KeySemicolon if shift => ":", constellation_msg::KeySemicolon => ";", constellation_msg::KeyEqual if shift => "+", constellation_msg::KeyEqual => "=", constellation_msg::KeyA if shift => "A", constellation_msg::KeyA => "a", constellation_msg::KeyB if shift => "B", constellation_msg::KeyB => "b", constellation_msg::KeyC if shift => "C", constellation_msg::KeyC => "c", constellation_msg::KeyD if shift => "D", constellation_msg::KeyD => "d", constellation_msg::KeyE if shift => "E", constellation_msg::KeyE => "e", constellation_msg::KeyF if shift => "F", constellation_msg::KeyF => "f", constellation_msg::KeyG if shift => "G", constellation_msg::KeyG => "g", constellation_msg::KeyH if shift => "H", constellation_msg::KeyH => "h", constellation_msg::KeyI if shift => "I", constellation_msg::KeyI => "i", constellation_msg::KeyJ if shift => "J", constellation_msg::KeyJ => "j", constellation_msg::KeyK if shift => "K", constellation_msg::KeyK => "k", constellation_msg::KeyL if shift => "L", constellation_msg::KeyL => "l", constellation_msg::KeyM if shift => "M", constellation_msg::KeyM => "m", constellation_msg::KeyN if shift => "N", constellation_msg::KeyN => "n", constellation_msg::KeyO if shift => "O", constellation_msg::KeyO => "o", constellation_msg::KeyP if shift => "P", constellation_msg::KeyP => "p", constellation_msg::KeyQ if shift => "Q", constellation_msg::KeyQ => "q", constellation_msg::KeyR if shift => "R", constellation_msg::KeyR => "r", constellation_msg::KeyS if shift => "S", constellation_msg::KeyS => "s", constellation_msg::KeyT if shift => "T", constellation_msg::KeyT => "t", constellation_msg::KeyU if shift => "U", constellation_msg::KeyU => "u", constellation_msg::KeyV if shift => "V", constellation_msg::KeyV => "v", constellation_msg::KeyW if shift => "W", constellation_msg::KeyW => "w", constellation_msg::KeyX if shift => "X", constellation_msg::KeyX => "x", constellation_msg::KeyY if shift => "Y", constellation_msg::KeyY => "y", constellation_msg::KeyZ if shift => "Z", constellation_msg::KeyZ => "z", constellation_msg::KeyLeftBracket if shift => "{", constellation_msg::KeyLeftBracket => "[", constellation_msg::KeyBackslash if shift => "|", constellation_msg::KeyBackslash => "\\", constellation_msg::KeyRightBracket if shift => "}", constellation_msg::KeyRightBracket => "]", constellation_msg::KeyGraveAccent => "Dead", constellation_msg::KeyWorld1 => "Unidentified", constellation_msg::KeyWorld2 => "Unidentified", constellation_msg::KeyEscape => "Escape", constellation_msg::KeyEnter => "Enter", constellation_msg::KeyTab => "Tab", constellation_msg::KeyBackspace => "Backspace", constellation_msg::KeyInsert => "Insert", constellation_msg::KeyDelete => "Delete", constellation_msg::KeyRight => "ArrowRight", constellation_msg::KeyLeft => "ArrowLeft", constellation_msg::KeyDown => "ArrowDown", constellation_msg::KeyUp => "ArrowUp", constellation_msg::KeyPageUp => "PageUp", constellation_msg::KeyPageDown => "PageDown", constellation_msg::KeyHome => "Home", constellation_msg::KeyEnd => "End", constellation_msg::KeyCapsLock => "CapsLock", constellation_msg::KeyScrollLock => "ScrollLock", constellation_msg::KeyNumLock => "NumLock", constellation_msg::KeyPrintScreen => "PrintScreen", constellation_msg::KeyPause => "Pause", constellation_msg::KeyF1 => "F1", constellation_msg::KeyF2 => "F2", constellation_msg::KeyF3 => "F3", constellation_msg::KeyF4 => "F4", constellation_msg::KeyF5 => "F5", constellation_msg::KeyF6 => "F6", constellation_msg::KeyF7 => "F7", constellation_msg::KeyF8 => "F8", constellation_msg::KeyF9 => "F9", constellation_msg::KeyF10 => "F10", constellation_msg::KeyF11 => "F11", constellation_msg::KeyF12 => "F12", constellation_msg::KeyF13 => "F13", constellation_msg::KeyF14 => "F14", constellation_msg::KeyF15 => "F15", constellation_msg::KeyF16 => "F16", constellation_msg::KeyF17 => "F17", constellation_msg::KeyF18 => "F18", constellation_msg::KeyF19 => "F19", constellation_msg::KeyF20 => "F20", constellation_msg::KeyF21 => "F21", constellation_msg::KeyF22 => "F22", constellation_msg::KeyF23 => "F23", constellation_msg::KeyF24 => "F24", constellation_msg::KeyF25 => "F25", constellation_msg::KeyKp0 => "0", constellation_msg::KeyKp1 => "1", constellation_msg::KeyKp2 => "2", constellation_msg::KeyKp3 => "3", constellation_msg::KeyKp4 => "4", constellation_msg::KeyKp5 => "5", constellation_msg::KeyKp6 => "6", constellation_msg::KeyKp7 => "7", constellation_msg::KeyKp8 => "8", constellation_msg::KeyKp9 => "9", constellation_msg::KeyKpDecimal => ".", constellation_msg::KeyKpDivide => "/", constellation_msg::KeyKpMultiply => "*", constellation_msg::KeyKpSubtract => "-", constellation_msg::KeyKpAdd => "+", constellation_msg::KeyKpEnter => "Enter", constellation_msg::KeyKpEqual => "=", constellation_msg::KeyLeftShift => "Shift", constellation_msg::KeyLeftControl => "Control", constellation_msg::KeyLeftAlt => "Alt", constellation_msg::KeyLeftSuper => "Super", constellation_msg::KeyRightShift => "Shift", constellation_msg::KeyRightControl => "Control", constellation_msg::KeyRightAlt => "Alt", constellation_msg::KeyRightSuper => "Super", constellation_msg::KeyMenu => "ContextMenu", } } // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html fn code_value(key: constellation_msg::Key) -> &'static str { match key { constellation_msg::KeySpace => "Space", constellation_msg::KeyApostrophe => "Quote", constellation_msg::KeyComma => "Comma", constellation_msg::KeyMinus => "Minus", constellation_msg::KeyPeriod => "Period", constellation_msg::KeySlash => "Slash", constellation_msg::Key0 => "Digit0", constellation_msg::Key1 => "Digit1", constellation_msg::Key2 => "Digit2", constellation_msg::Key3 => "Digit3", constellation_msg::Key4 => "Digit4", constellation_msg::Key5 => "Digit5", constellation_msg::Key6 => "Digit6", constellation_msg::Key7 => "Digit7", constellation_msg::Key8 => "Digit8", constellation_msg::Key9 => "Digit9", constellation_msg::KeySemicolon => "Semicolon", constellation_msg::KeyEqual => "Equals", constellation_msg::KeyA => "KeyA", constellation_msg::KeyB => "KeyB", constellation_msg::KeyC => "KeyC", constellation_msg::KeyD => "KeyD", constellation_msg::KeyE => "KeyE", constellation_msg::KeyF => "KeyF", constellation_msg::KeyG => "KeyG", constellation_msg::KeyH => "KeyH", constellation_msg::KeyI => "KeyI", constellation_msg::KeyJ => "KeyJ", constellation_msg::KeyK => "KeyK", constellation_msg::KeyL => "KeyL", constellation_msg::KeyM => "KeyM", constellation_msg::KeyN => "KeyN", constellation_msg::KeyO => "KeyO", constellation_msg::KeyP => "KeyP", constellation_msg::KeyQ => "KeyQ", constellation_msg::KeyR => "KeyR", constellation_msg::KeyS => "KeyS", constellation_msg::KeyT => "KeyT", constellation_msg::KeyU => "KeyU", constellation_msg::KeyV => "KeyV", constellation_msg::KeyW => "KeyW", constellation_msg::KeyX => "KeyX", constellation_msg::KeyY => "KeyY", constellation_msg::KeyZ => "KeyZ", constellation_msg::KeyLeftBracket => "BracketLeft", constellation_msg::KeyBackslash => "Backslash", constellation_msg::KeyRightBracket => "BracketRight", constellation_msg::KeyGraveAccent | constellation_msg::KeyWorld1 | constellation_msg::KeyWorld2 => panic!("unknown char code for {}", key), constellation_msg::KeyEscape => "Escape", constellation_msg::KeyEnter => "Enter", constellation_msg::KeyTab => "Tab", constellation_msg::KeyBackspace => "Backspace", constellation_msg::KeyInsert => "Insert", constellation_msg::KeyDelete => "Delete", constellation_msg::KeyRight => "ArrowRight", constellation_msg::KeyLeft => "ArrowLeft", constellation_msg::KeyDown => "ArrowDown", constellation_msg::KeyUp => "ArrowUp", constellation_msg::KeyPageUp => "PageUp", constellation_msg::KeyPageDown => "PageDown", constellation_msg::KeyHome => "Home", constellation_msg::KeyEnd => "End", constellation_msg::KeyCapsLock => "CapsLock", constellation_msg::KeyScrollLock => "ScrollLock", constellation_msg::KeyNumLock => "NumLock", constellation_msg::KeyPrintScreen => "PrintScreen", constellation_msg::KeyPause => "Pause", constellation_msg::KeyF1 => "F1", constellation_msg::KeyF2 => "F2", constellation_msg::KeyF3 => "F3", constellation_msg::KeyF4 => "F4", constellation_msg::KeyF5 => "F5", constellation_msg::KeyF6 => "F6", constellation_msg::KeyF7 => "F7", constellation_msg::KeyF8 => "F8", constellation_msg::KeyF9 => "F9", constellation_msg::KeyF10 => "F10", constellation_msg::KeyF11 => "F11", constellation_msg::KeyF12 => "F12", constellation_msg::KeyF13 => "F13", constellation_msg::KeyF14 => "F14", constellation_msg::KeyF15 => "F15", constellation_msg::KeyF16 => "F16", constellation_msg::KeyF17 => "F17", constellation_msg::KeyF18 => "F18", constellation_msg::KeyF19 => "F19", constellation_msg::KeyF20 => "F20", constellation_msg::KeyF21 => "F21", constellation_msg::KeyF22 => "F22", constellation_msg::KeyF23 => "F23", constellation_msg::KeyF24 => "F24", constellation_msg::KeyF25 => "F25", constellation_msg::KeyKp0 => "Numpad0", constellation_msg::KeyKp1 => "Numpad1", constellation_msg::KeyKp2 => "Numpad2", constellation_msg::KeyKp3 => "Numpad3", constellation_msg::KeyKp4 => "Numpad4", constellation_msg::KeyKp5 => "Numpad5", constellation_msg::KeyKp6 => "Numpad6", constellation_msg::KeyKp7 => "Numpad7", constellation_msg::KeyKp8 => "Numpad8", constellation_msg::KeyKp9 => "Numpad9", constellation_msg::KeyKpDecimal => "NumpadDecimal", constellation_msg::KeyKpDivide => "NumpadDivide", constellation_msg::KeyKpMultiply => "NumpadMultiply", constellation_msg::KeyKpSubtract => "NumpadSubtract", constellation_msg::KeyKpAdd => "NumpadAdd", constellation_msg::KeyKpEnter => "NumpadEnter", constellation_msg::KeyKpEqual => "NumpadEquals", constellation_msg::KeyLeftShift | constellation_msg::KeyRightShift => "Shift", constellation_msg::KeyLeftControl | constellation_msg::KeyRightControl => "Control", constellation_msg::KeyLeftAlt | constellation_msg::KeyRightAlt => "Alt", constellation_msg::KeyLeftSuper | constellation_msg::KeyRightSuper => "Super", constellation_msg::KeyMenu => "Menu", } } fn key_location(key: constellation_msg::Key) -> u32 { match key { constellation_msg::KeyKp0 | constellation_msg::KeyKp1 | constellation_msg::KeyKp2 | constellation_msg::KeyKp3 | constellation_msg::KeyKp4 | constellation_msg::KeyKp5 | constellation_msg::KeyKp6 | constellation_msg::KeyKp7 | constellation_msg::KeyKp8 | constellation_msg::KeyKp9 | constellation_msg::KeyKpDecimal | constellation_msg::KeyKpDivide | constellation_msg::KeyKpMultiply | constellation_msg::KeyKpSubtract | constellation_msg::KeyKpAdd | constellation_msg::KeyKpEnter | constellation_msg::KeyKpEqual => KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD, constellation_msg::KeyLeftShift | constellation_msg::KeyLeftAlt | constellation_msg::KeyLeftControl | constellation_msg::KeyLeftSuper => KeyboardEventConstants::DOM_KEY_LOCATION_LEFT, constellation_msg::KeyRightShift | constellation_msg::KeyRightAlt | constellation_msg::KeyRightControl | constellation_msg::KeyRightSuper => KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT, _ => KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD, } } // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#widl-KeyboardEvent-charCode fn key_charcode(key: constellation_msg::Key, mods: constellation_msg::KeyModifiers) -> Option { let key = key_value(key, mods); if key.len() == 1 { Some(key.char_at(0) as u32) } else { None } } // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#legacy-key-models fn key_keycode(key: constellation_msg::Key) -> u32 { match key { // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#legacy-key-models constellation_msg::KeyBackspace => 8, constellation_msg::KeyTab => 9, constellation_msg::KeyEnter => 13, constellation_msg::KeyLeftShift | constellation_msg::KeyRightShift => 16, constellation_msg::KeyLeftControl | constellation_msg::KeyRightControl => 17, constellation_msg::KeyLeftAlt | constellation_msg::KeyRightAlt => 18, constellation_msg::KeyCapsLock => 20, constellation_msg::KeyEscape => 27, constellation_msg::KeySpace => 32, constellation_msg::KeyPageUp => 33, constellation_msg::KeyPageDown => 34, constellation_msg::KeyEnd => 35, constellation_msg::KeyHome => 36, constellation_msg::KeyLeft => 37, constellation_msg::KeyUp => 38, constellation_msg::KeyRight => 39, constellation_msg::KeyDown => 40, constellation_msg::KeyDelete => 46, // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#optionally-fixed-virtual-key-codes constellation_msg::KeySemicolon => 186, constellation_msg::KeyEqual => 187, constellation_msg::KeyComma => 188, constellation_msg::KeyMinus => 189, constellation_msg::KeyPeriod => 190, constellation_msg::KeySlash => 191, constellation_msg::KeyLeftBracket => 219, constellation_msg::KeyBackslash => 220, constellation_msg::KeyRightBracket => 221, constellation_msg::KeyApostrophe => 222, //§ B.2.1.3 constellation_msg::Key0 | constellation_msg::Key1 | constellation_msg::Key2 | constellation_msg::Key3 | constellation_msg::Key4 | constellation_msg::Key5 | constellation_msg::Key6 | constellation_msg::Key7 | constellation_msg::Key8 | constellation_msg::Key9 => key as u32 - constellation_msg::Key0 as u32 + '0' as u32, //§ B.2.1.4 constellation_msg::KeyA | constellation_msg::KeyB | constellation_msg::KeyC | constellation_msg::KeyD | constellation_msg::KeyE | constellation_msg::KeyF | constellation_msg::KeyG | constellation_msg::KeyH | constellation_msg::KeyI | constellation_msg::KeyJ | constellation_msg::KeyK | constellation_msg::KeyL | constellation_msg::KeyM | constellation_msg::KeyN | constellation_msg::KeyO | constellation_msg::KeyP | constellation_msg::KeyQ | constellation_msg::KeyR | constellation_msg::KeyS | constellation_msg::KeyT | constellation_msg::KeyU | constellation_msg::KeyV | constellation_msg::KeyW | constellation_msg::KeyX | constellation_msg::KeyY | constellation_msg::KeyZ => key as u32 - constellation_msg::KeyA as u32 + 'A' as u32, //§ B.2.1.8 _ => 0 } } pub struct KeyEventProperties { pub key: &'static str, pub code: &'static str, pub location: u32, pub char_code: Option, pub key_code: u32, } impl KeyEventProperties { pub fn is_printable(&self) -> bool { self.char_code.is_some() } } impl<'a> KeyboardEventMethods for JSRef<'a, KeyboardEvent> { fn InitKeyboardEvent(self, typeArg: DOMString, canBubbleArg: bool, cancelableArg: bool, viewArg: Option>, keyArg: DOMString, locationArg: u32, _modifiersListArg: DOMString, repeat: bool, _locale: DOMString) { let event: JSRef = EventCast::from_ref(self); if event.dispatching() { return; } let uievent: JSRef = UIEventCast::from_ref(self); uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, 0); *self.key.borrow_mut() = keyArg; self.location.set(locationArg); self.repeat.set(repeat); } fn Key(self) -> DOMString { self.key.borrow().clone() } fn Code(self) -> DOMString { self.code.borrow().clone() } fn Location(self) -> u32 { self.location.get() } fn CtrlKey(self) -> bool { self.ctrl.get() } fn ShiftKey(self) -> bool { self.shift.get() } fn AltKey(self) -> bool { self.alt.get() } fn MetaKey(self) -> bool { self.meta.get() } fn Repeat(self) -> bool { self.repeat.get() } fn IsComposing(self) -> bool { self.is_composing.get() } // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#widl-KeyboardEvent-getModifierState fn GetModifierState(self, keyArg: DOMString) -> bool { match keyArg.as_slice() { "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, } } fn CharCode(self) -> u32 { self.char_code.get().unwrap_or(0) } fn KeyCode(self) -> u32 { self.key_code.get() } fn Which(self) -> u32 { self.char_code.get().unwrap_or(self.KeyCode()) } } impl Reflectable for KeyboardEvent { fn reflector<'a>(&'a self) -> &'a Reflector { self.uievent.reflector() } }