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