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