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