Skip to main content

script/dom/event/
keyboardevent.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6use std::str::FromStr;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::rust::HandleObject;
11use keyboard_types::{Code, Key, Modifiers, NamedKey};
12use script_bindings::cell::DomRefCell;
13use script_bindings::domstring::TracedDOMString;
14use script_bindings::reflector::reflect_dom_object_with_proto;
15use style::Atom;
16
17use crate::dom::bindings::codegen::Bindings::KeyboardEventBinding;
18use crate::dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
19use crate::dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::inheritance::Castable;
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::bindings::str::DOMString;
24use crate::dom::event::Event;
25use crate::dom::uievent::UIEvent;
26use crate::dom::window::Window;
27
28#[dom_struct]
29pub(crate) struct KeyboardEvent {
30    uievent: UIEvent,
31    key: DomRefCell<TracedDOMString>,
32    #[no_trace]
33    typed_key: DomRefCell<Key>,
34    code: DomRefCell<TracedDOMString>,
35    #[no_trace]
36    original_code: DomRefCell<Option<Code>>,
37    location: Cell<u32>,
38    #[no_trace]
39    modifiers: Cell<Modifiers>,
40    repeat: Cell<bool>,
41    is_composing: Cell<bool>,
42    char_code: Cell<u32>,
43    key_code: Cell<u32>,
44}
45
46impl KeyboardEvent {
47    fn new_inherited() -> Self {
48        Self {
49            uievent: UIEvent::new_inherited(),
50            key: Default::default(),
51            typed_key: Default::default(),
52            code: Default::default(),
53            original_code: Default::default(),
54            location: Default::default(),
55            modifiers: Default::default(),
56            repeat: Default::default(),
57            is_composing: Default::default(),
58            char_code: Default::default(),
59            key_code: Default::default(),
60        }
61    }
62
63    pub(crate) fn new_uninitialized(cx: &mut JSContext, window: &Window) -> DomRoot<KeyboardEvent> {
64        Self::new_uninitialized_with_proto(cx, window, None)
65    }
66
67    fn new_uninitialized_with_proto(
68        cx: &mut JSContext,
69        window: &Window,
70        proto: Option<HandleObject>,
71    ) -> DomRoot<KeyboardEvent> {
72        reflect_dom_object_with_proto(cx, Box::new(KeyboardEvent::new_inherited()), window, proto)
73    }
74
75    pub(crate) fn new_with_platform_keyboard_event(
76        cx: &mut JSContext,
77        window: &Window,
78        event_type: Atom,
79        keyboard_event: &keyboard_types::KeyboardEvent,
80    ) -> DomRoot<KeyboardEvent> {
81        let keycode = legacy_keycode_for_keyboard_event(keyboard_event);
82        Self::new_with_proto(
83            cx,
84            window,
85            None,
86            event_type,
87            true,         /* can_bubble */
88            true,         /* cancelable */
89            Some(window), /* view */
90            0,            /* detail */
91            keyboard_event.key.clone(),
92            DOMString::from(keyboard_event.code.to_string()),
93            Some(keyboard_event.code),
94            keyboard_event.location as u32,
95            keyboard_event.repeat,
96            keyboard_event.is_composing,
97            keyboard_event.modifiers,
98            0, /* char_code */
99            keycode,
100        )
101    }
102
103    #[expect(clippy::too_many_arguments)]
104    fn new_with_proto(
105        cx: &mut JSContext,
106        window: &Window,
107        proto: Option<HandleObject>,
108        event_type: Atom,
109        can_bubble: bool,
110        cancelable: bool,
111        view: Option<&Window>,
112        _detail: i32,
113        key: Key,
114        code: DOMString,
115        original_code: Option<Code>,
116        location: u32,
117        repeat: bool,
118        is_composing: bool,
119        modifiers: Modifiers,
120        char_code: u32,
121        key_code: u32,
122    ) -> DomRoot<KeyboardEvent> {
123        let event = KeyboardEvent::new_uninitialized_with_proto(cx, window, proto);
124        event.init_event(
125            event_type,
126            can_bubble,
127            cancelable,
128            view,
129            DOMString::from(key.to_string()),
130            location,
131            repeat,
132        );
133        *event.typed_key.safe_borrow_mut(cx.no_gc()) = key;
134        *event.code.safe_borrow_mut(cx.no_gc()) = code.traced();
135        *event.original_code.safe_borrow_mut(cx.no_gc()) = original_code;
136        event.modifiers.set(modifiers);
137        event.is_composing.set(is_composing);
138        event.char_code.set(char_code);
139        event.key_code.set(key_code);
140        event.uievent.set_which(key_code);
141        event
142    }
143
144    pub(crate) fn key(&self) -> Key {
145        self.typed_key.borrow().clone()
146    }
147
148    pub(crate) fn original_code(&self) -> Option<Code> {
149        *self.original_code.borrow()
150    }
151
152    /// <https://w3c.github.io/uievents/#widl-KeyboardEvent-initKeyboardEvent>
153    #[expect(clippy::too_many_arguments)]
154    pub(crate) fn init_event(
155        &self,
156        event_type: Atom,
157        can_bubble_arg: bool,
158        cancelable_arg: bool,
159        view_arg: Option<&Window>,
160        key_arg: DOMString,
161        location_arg: u32,
162        repeat: bool,
163    ) {
164        if self.upcast::<Event>().dispatching() {
165            return;
166        }
167
168        self.upcast::<UIEvent>().init_event(
169            event_type,
170            can_bubble_arg,
171            cancelable_arg,
172            view_arg,
173            0,
174        );
175        *self.key.borrow_mut() = key_arg.traced();
176        self.location.set(location_arg);
177        self.repeat.set(repeat);
178    }
179}
180
181impl KeyboardEventMethods<crate::DomTypeHolder> for KeyboardEvent {
182    /// <https://w3c.github.io/uievents/#dom-keyboardevent-keyboardevent>
183    fn Constructor(
184        cx: &mut JSContext,
185        window: &Window,
186        proto: Option<HandleObject>,
187        event_type: DOMString,
188        init: &KeyboardEventBinding::KeyboardEventInit,
189    ) -> Fallible<DomRoot<KeyboardEvent>> {
190        let mut modifiers = Modifiers::empty();
191        modifiers.set(Modifiers::CONTROL, init.parent.ctrlKey);
192        modifiers.set(Modifiers::ALT, init.parent.altKey);
193        modifiers.set(Modifiers::SHIFT, init.parent.shiftKey);
194        modifiers.set(Modifiers::META, init.parent.metaKey);
195        let event = KeyboardEvent::new_with_proto(
196            cx,
197            window,
198            proto,
199            event_type.into(),
200            init.parent.parent.parent.bubbles,
201            init.parent.parent.parent.cancelable,
202            init.parent.parent.view.as_deref(),
203            init.parent.parent.detail,
204            Key::Named(NamedKey::Unidentified),
205            init.code.clone(),
206            Code::from_str(&init.code.str()).ok(),
207            init.location,
208            init.repeat,
209            init.isComposing,
210            modifiers,
211            init.charCode,
212            init.keyCode,
213        );
214        *event.key.safe_borrow_mut(cx.no_gc()) = init.key.clone().traced();
215        Ok(event)
216    }
217
218    /// <https://w3c.github.io/uievents/#widl-KeyboardEvent-initKeyboardEvent>
219    fn InitKeyboardEvent(
220        &self,
221        event_type: DOMString,
222        can_bubble_arg: bool,
223        cancelable_arg: bool,
224        view_arg: Option<&Window>,
225        key_arg: DOMString,
226        location_arg: u32,
227        _modifiers_list_arg: DOMString,
228        repeat: bool,
229        _locale: DOMString,
230    ) {
231        self.init_event(
232            event_type.into(),
233            can_bubble_arg,
234            cancelable_arg,
235            view_arg,
236            key_arg,
237            location_arg,
238            repeat,
239        );
240    }
241
242    /// <https://w3c.github.io/uievents/#dom-keyboardevent-initkeyboardevent>
243    fn Key(&self) -> DOMString {
244        self.key.borrow().clone().root()
245    }
246
247    /// <https://w3c.github.io/uievents/#dom-keyboardevent-code>
248    fn Code(&self) -> DOMString {
249        self.code.borrow().clone().root()
250    }
251
252    /// <https://w3c.github.io/uievents/#dom-keyboardevent-location>
253    fn Location(&self) -> u32 {
254        self.location.get()
255    }
256
257    /// <https://w3c.github.io/uievents/#dom-keyboardevent-ctrlkey>
258    fn CtrlKey(&self) -> bool {
259        self.modifiers.get().contains(Modifiers::CONTROL)
260    }
261
262    /// <https://w3c.github.io/uievents/#dom-keyboardevent-shiftkey>
263    fn ShiftKey(&self) -> bool {
264        self.modifiers.get().contains(Modifiers::SHIFT)
265    }
266
267    /// <https://w3c.github.io/uievents/#dom-keyboardevent-altkey>
268    fn AltKey(&self) -> bool {
269        self.modifiers.get().contains(Modifiers::ALT)
270    }
271
272    /// <https://w3c.github.io/uievents/#dom-keyboardevent-metakey>
273    fn MetaKey(&self) -> bool {
274        self.modifiers.get().contains(Modifiers::META)
275    }
276
277    /// <https://w3c.github.io/uievents/#dom-keyboardevent-repeat>
278    fn Repeat(&self) -> bool {
279        self.repeat.get()
280    }
281
282    /// <https://w3c.github.io/uievents/#dom-keyboardevent-iscomposing>
283    fn IsComposing(&self) -> bool {
284        self.is_composing.get()
285    }
286
287    /// <https://w3c.github.io/uievents/#dom-keyboardevent-getmodifierstate>
288    fn GetModifierState(&self, key_arg: DOMString) -> bool {
289        self.modifiers.get().contains(match &*key_arg.str() {
290            "Alt" => Modifiers::ALT,
291            "AltGraph" => Modifiers::ALT_GRAPH,
292            "CapsLock" => Modifiers::CAPS_LOCK,
293            "Control" => Modifiers::CONTROL,
294            "Fn" => Modifiers::FN,
295            "FnLock" => Modifiers::FN_LOCK,
296            "Meta" => Modifiers::META,
297            "NumLock" => Modifiers::NUM_LOCK,
298            "ScrollLock" => Modifiers::SCROLL_LOCK,
299            "Shift" => Modifiers::SHIFT,
300            "Symbol" => Modifiers::SYMBOL,
301            "SymbolLock" => Modifiers::SYMBOL_LOCK,
302            _ => return false,
303        })
304    }
305
306    /// <https://w3c.github.io/uievents/#dom-keyboardevent-charcode>
307    fn CharCode(&self) -> u32 {
308        self.char_code.get()
309    }
310
311    /// <https://w3c.github.io/uievents/#dom-keyboardevent-keycode>
312    fn KeyCode(&self) -> u32 {
313        self.key_code.get()
314    }
315
316    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
317    fn IsTrusted(&self) -> bool {
318        self.uievent.IsTrusted()
319    }
320}
321
322fn legacy_keycode_for_keyboard_event(keyboard_event: &keyboard_types::KeyboardEvent) -> u32 {
323    match keyboard_event.code {
324        Code::Backquote => 192,
325        Code::MetaLeft | Code::MetaRight => 224,
326        Code::ContextMenu => 93,
327        Code::Insert => 45,
328        Code::NumLock => 144,
329        Code::PrintScreen => 4,
330        Code::ScrollLock => 145,
331        Code::Pause => 19,
332        Code::F1 => 112,
333        Code::F2 => 113,
334        Code::F3 => 114,
335        Code::F4 => 115,
336        Code::F5 => 116,
337        Code::F6 => 117,
338        Code::F7 => 118,
339        Code::F8 => 119,
340        Code::F9 => 120,
341        Code::F10 => 121,
342        Code::F11 => 122,
343        Code::F12 => 123,
344        Code::F13 => 124,
345        Code::F14 => 125,
346        Code::F15 => 126,
347        Code::F16 => 127,
348        Code::F17 => 128,
349        Code::F18 => 129,
350        Code::F19 => 130,
351        Code::F20 => 131,
352        Code::F21 => 132,
353        Code::F22 => 133,
354        Code::F23 => 134,
355        Code::F24 => 135,
356        _ => keyboard_event.key.legacy_keycode(),
357    }
358}