Rewild Your Web
web browser dweb
at main 177 lines 6.4 kB view raw
1--- original 2+++ modified 3@@ -0,0 +1,174 @@ 4+/* SPDX Id: AGPL-3.0-or-later */ 5+ 6+//! The `Keyboard` interface provides virtual keyboard input injection. 7+//! This is a Servo-specific, non-standard API for privileged pages. 8+ 9+use constellation_traits::ScriptToConstellationMessage; 10+use dom_struct::dom_struct; 11+use embedder_traits::{ 12+ ImeEvent, InputEvent, InputEventAndId, KeyboardEvent as EmbedderKeyboardEvent, 13+}; 14+use keyboard_types::{ 15+ Code, CompositionEvent, CompositionState, Key, KeyState, Location, Modifiers, 16+}; 17+ 18+use crate::dom::bindings::codegen::Bindings::KeyboardBinding::{ 19+ KeyboardMethods, ServoCompositionEventInit, ServoKeyboardEventInit, 20+}; 21+use crate::dom::bindings::error::{Error, Fallible}; 22+use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; 23+use crate::dom::bindings::root::DomRoot; 24+use crate::dom::bindings::str::DOMString; 25+use crate::dom::globalscope::GlobalScope; 26+use crate::script_runtime::CanGc; 27+ 28+#[dom_struct] 29+pub(crate) struct Keyboard { 30+ reflector_: Reflector, 31+} 32+ 33+impl Keyboard { 34+ fn new_inherited() -> Keyboard { 35+ Keyboard { 36+ reflector_: Reflector::new(), 37+ } 38+ } 39+ 40+ pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Keyboard> { 41+ reflect_dom_object(Box::new(Keyboard::new_inherited()), global, can_gc) 42+ } 43+ 44+ fn send_to_active_ime(&self, event: InputEvent) { 45+ let global = self.global(); 46+ let event_and_id = InputEventAndId::from(event); 47+ let _ = global.script_to_constellation_chan().send( 48+ ScriptToConstellationMessage::InjectInputToActiveIme(event_and_id), 49+ ); 50+ } 51+ 52+ fn parse_key_state(state: &DOMString) -> Fallible<KeyState> { 53+ match &*state.str() { 54+ "down" => Ok(KeyState::Down), 55+ "up" => Ok(KeyState::Up), 56+ _ => Err(Error::Type(format!( 57+ "Invalid key state '{}': expected 'down' or 'up'", 58+ state 59+ ))), 60+ } 61+ } 62+ 63+ fn parse_key(key: &DOMString) -> Key { 64+ let key_str = key.str(); 65+ // Try to parse as a named key first 66+ if key_str.len() > 1 { 67+ // This could be a named key like "Backspace", "Enter", etc. 68+ match &*key_str { 69+ "Backspace" => Key::Named(keyboard_types::NamedKey::Backspace), 70+ "Tab" => Key::Named(keyboard_types::NamedKey::Tab), 71+ "Enter" => Key::Named(keyboard_types::NamedKey::Enter), 72+ "Escape" => Key::Named(keyboard_types::NamedKey::Escape), 73+ "Delete" => Key::Named(keyboard_types::NamedKey::Delete), 74+ "ArrowUp" => Key::Named(keyboard_types::NamedKey::ArrowUp), 75+ "ArrowDown" => Key::Named(keyboard_types::NamedKey::ArrowDown), 76+ "ArrowLeft" => Key::Named(keyboard_types::NamedKey::ArrowLeft), 77+ "ArrowRight" => Key::Named(keyboard_types::NamedKey::ArrowRight), 78+ "Home" => Key::Named(keyboard_types::NamedKey::Home), 79+ "End" => Key::Named(keyboard_types::NamedKey::End), 80+ "PageUp" => Key::Named(keyboard_types::NamedKey::PageUp), 81+ "PageDown" => Key::Named(keyboard_types::NamedKey::PageDown), 82+ "Space" => Key::Character(" ".to_string()), 83+ _ => Key::Character(key_str.to_string()), 84+ } 85+ } else { 86+ Key::Character(key_str.to_string()) 87+ } 88+ } 89+ 90+ fn parse_code(code: &DOMString) -> Code { 91+ let code_str = code.str(); 92+ if code_str.is_empty() { 93+ Code::Unidentified 94+ } else { 95+ // Try to parse the code string 96+ code_str.to_string().parse().unwrap_or(Code::Unidentified) 97+ } 98+ } 99+ 100+ fn parse_location(location: u32) -> Location { 101+ match location { 102+ 0 => Location::Standard, 103+ 1 => Location::Left, 104+ 2 => Location::Right, 105+ 3 => Location::Numpad, 106+ _ => Location::Standard, 107+ } 108+ } 109+ 110+ fn build_modifiers(init: &ServoKeyboardEventInit) -> Modifiers { 111+ let mut modifiers = Modifiers::empty(); 112+ if init.shift { 113+ modifiers.insert(Modifiers::SHIFT); 114+ } 115+ if init.ctrl { 116+ modifiers.insert(Modifiers::CONTROL); 117+ } 118+ if init.alt { 119+ modifiers.insert(Modifiers::ALT); 120+ } 121+ if init.meta { 122+ modifiers.insert(Modifiers::META); 123+ } 124+ modifiers 125+ } 126+ 127+ fn parse_composition_state(state: &DOMString) -> Fallible<CompositionState> { 128+ match &*state.str() { 129+ "start" => Ok(CompositionState::Start), 130+ "update" => Ok(CompositionState::Update), 131+ "end" => Ok(CompositionState::End), 132+ _ => Err(Error::Type(format!( 133+ "Invalid composition state '{}': expected 'start', 'update', or 'end'", 134+ state 135+ ))), 136+ } 137+ } 138+} 139+ 140+impl KeyboardMethods<crate::DomTypeHolder> for Keyboard { 141+ /// Send a keyboard event (keydown/keyup) to the active IME input. 142+ fn SendKeyboardEvent(&self, init: &ServoKeyboardEventInit) -> Fallible<()> { 143+ let state = Self::parse_key_state(&init.state)?; 144+ let key = Self::parse_key(&init.key); 145+ let code = Self::parse_code(&init.code); 146+ let location = Self::parse_location(init.location); 147+ let modifiers = Self::build_modifiers(init); 148+ 149+ let keyboard_event = EmbedderKeyboardEvent::new_without_event( 150+ state, 151+ key, 152+ code, 153+ location, 154+ modifiers, 155+ init.repeat, 156+ init.isComposing, 157+ ); 158+ 159+ let event = InputEvent::Keyboard(keyboard_event); 160+ self.send_to_active_ime(event); 161+ Ok(()) 162+ } 163+ 164+ /// Send a composition event to the active IME input. 165+ fn SendCompositionEvent(&self, init: &ServoCompositionEventInit) -> Fallible<()> { 166+ let state = Self::parse_composition_state(&init.state)?; 167+ 168+ let composition_event = CompositionEvent { 169+ state, 170+ data: init.data.to_string(), 171+ }; 172+ 173+ let event = InputEvent::Ime(ImeEvent::Composition(composition_event)); 174+ self.send_to_active_ime(event); 175+ Ok(()) 176+ } 177+}