1use 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 let keycode = legacy_keycode_for_keyboard_event(keyboard_event);
81 Self::new_with_proto(
82 cx,
83 window,
84 None,
85 event_type,
86 true, true, Some(window), 0, keyboard_event.key.clone(),
91 DOMString::from(keyboard_event.code.to_string()),
92 Some(keyboard_event.code),
93 keyboard_event.location as u32,
94 keyboard_event.repeat,
95 keyboard_event.is_composing,
96 keyboard_event.modifiers,
97 0, keycode,
99 )
100 }
101
102 #[expect(clippy::too_many_arguments)]
103 fn new_with_proto(
104 cx: &mut JSContext,
105 window: &Window,
106 proto: Option<HandleObject>,
107 event_type: Atom,
108 can_bubble: bool,
109 cancelable: bool,
110 view: Option<&Window>,
111 _detail: i32,
112 key: Key,
113 code: DOMString,
114 original_code: Option<Code>,
115 location: u32,
116 repeat: bool,
117 is_composing: bool,
118 modifiers: Modifiers,
119 char_code: u32,
120 key_code: u32,
121 ) -> DomRoot<KeyboardEvent> {
122 let event = KeyboardEvent::new_uninitialized_with_proto(cx, window, proto);
123 event.init_event(
124 event_type,
125 can_bubble,
126 cancelable,
127 view,
128 DOMString::from(key.to_string()),
129 location,
130 repeat,
131 );
132 *event.typed_key.safe_borrow_mut(cx.no_gc()) = key;
133 *event.code.safe_borrow_mut(cx.no_gc()) = code;
134 *event.original_code.safe_borrow_mut(cx.no_gc()) = original_code;
135 event.modifiers.set(modifiers);
136 event.is_composing.set(is_composing);
137 event.char_code.set(char_code);
138 event.key_code.set(key_code);
139 event.uievent.set_which(key_code);
140 event
141 }
142
143 pub(crate) fn key(&self) -> Key {
144 self.typed_key.borrow().clone()
145 }
146
147 pub(crate) fn original_code(&self) -> Option<Code> {
148 *self.original_code.borrow()
149 }
150
151 #[expect(clippy::too_many_arguments)]
153 pub(crate) fn init_event(
154 &self,
155 event_type: Atom,
156 can_bubble_arg: bool,
157 cancelable_arg: bool,
158 view_arg: Option<&Window>,
159 key_arg: DOMString,
160 location_arg: u32,
161 repeat: bool,
162 ) {
163 if self.upcast::<Event>().dispatching() {
164 return;
165 }
166
167 self.upcast::<UIEvent>().init_event(
168 event_type,
169 can_bubble_arg,
170 cancelable_arg,
171 view_arg,
172 0,
173 );
174 *self.key.borrow_mut() = key_arg;
175 self.location.set(location_arg);
176 self.repeat.set(repeat);
177 }
178}
179
180impl KeyboardEventMethods<crate::DomTypeHolder> for KeyboardEvent {
181 fn Constructor(
183 cx: &mut JSContext,
184 window: &Window,
185 proto: Option<HandleObject>,
186 event_type: DOMString,
187 init: &KeyboardEventBinding::KeyboardEventInit,
188 ) -> Fallible<DomRoot<KeyboardEvent>> {
189 let mut modifiers = Modifiers::empty();
190 modifiers.set(Modifiers::CONTROL, init.parent.ctrlKey);
191 modifiers.set(Modifiers::ALT, init.parent.altKey);
192 modifiers.set(Modifiers::SHIFT, init.parent.shiftKey);
193 modifiers.set(Modifiers::META, init.parent.metaKey);
194 let event = KeyboardEvent::new_with_proto(
195 cx,
196 window,
197 proto,
198 event_type.into(),
199 init.parent.parent.parent.bubbles,
200 init.parent.parent.parent.cancelable,
201 init.parent.parent.view.as_deref(),
202 init.parent.parent.detail,
203 Key::Named(NamedKey::Unidentified),
204 init.code.clone(),
205 Code::from_str(&init.code.str()).ok(),
206 init.location,
207 init.repeat,
208 init.isComposing,
209 modifiers,
210 init.charCode,
211 init.keyCode,
212 );
213 *event.key.safe_borrow_mut(cx.no_gc()) = init.key.clone();
214 Ok(event)
215 }
216
217 fn InitKeyboardEvent(
219 &self,
220 event_type: DOMString,
221 can_bubble_arg: bool,
222 cancelable_arg: bool,
223 view_arg: Option<&Window>,
224 key_arg: DOMString,
225 location_arg: u32,
226 _modifiers_list_arg: DOMString,
227 repeat: bool,
228 _locale: DOMString,
229 ) {
230 self.init_event(
231 event_type.into(),
232 can_bubble_arg,
233 cancelable_arg,
234 view_arg,
235 key_arg,
236 location_arg,
237 repeat,
238 );
239 }
240
241 fn Key(&self) -> DOMString {
243 self.key.borrow().clone()
244 }
245
246 fn Code(&self) -> DOMString {
248 self.code.borrow().clone()
249 }
250
251 fn Location(&self) -> u32 {
253 self.location.get()
254 }
255
256 fn CtrlKey(&self) -> bool {
258 self.modifiers.get().contains(Modifiers::CONTROL)
259 }
260
261 fn ShiftKey(&self) -> bool {
263 self.modifiers.get().contains(Modifiers::SHIFT)
264 }
265
266 fn AltKey(&self) -> bool {
268 self.modifiers.get().contains(Modifiers::ALT)
269 }
270
271 fn MetaKey(&self) -> bool {
273 self.modifiers.get().contains(Modifiers::META)
274 }
275
276 fn Repeat(&self) -> bool {
278 self.repeat.get()
279 }
280
281 fn IsComposing(&self) -> bool {
283 self.is_composing.get()
284 }
285
286 fn GetModifierState(&self, key_arg: DOMString) -> bool {
288 self.modifiers.get().contains(match &*key_arg.str() {
289 "Alt" => Modifiers::ALT,
290 "AltGraph" => Modifiers::ALT_GRAPH,
291 "CapsLock" => Modifiers::CAPS_LOCK,
292 "Control" => Modifiers::CONTROL,
293 "Fn" => Modifiers::FN,
294 "FnLock" => Modifiers::FN_LOCK,
295 "Meta" => Modifiers::META,
296 "NumLock" => Modifiers::NUM_LOCK,
297 "ScrollLock" => Modifiers::SCROLL_LOCK,
298 "Shift" => Modifiers::SHIFT,
299 "Symbol" => Modifiers::SYMBOL,
300 "SymbolLock" => Modifiers::SYMBOL_LOCK,
301 _ => return false,
302 })
303 }
304
305 fn CharCode(&self) -> u32 {
307 self.char_code.get()
308 }
309
310 fn KeyCode(&self) -> u32 {
312 self.key_code.get()
313 }
314
315 fn IsTrusted(&self) -> bool {
317 self.uievent.IsTrusted()
318 }
319}
320
321fn legacy_keycode_for_keyboard_event(keyboard_event: &keyboard_types::KeyboardEvent) -> u32 {
322 match keyboard_event.code {
323 Code::Backquote => 192,
324 Code::MetaLeft | Code::MetaRight => 224,
325 Code::ContextMenu => 93,
326 Code::Insert => 45,
327 Code::NumLock => 144,
328 Code::PrintScreen => 4,
329 Code::ScrollLock => 145,
330 Code::Pause => 19,
331 Code::F1 => 112,
332 Code::F2 => 113,
333 Code::F3 => 114,
334 Code::F4 => 115,
335 Code::F5 => 116,
336 Code::F6 => 117,
337 Code::F7 => 118,
338 Code::F8 => 119,
339 Code::F9 => 120,
340 Code::F10 => 121,
341 Code::F11 => 122,
342 Code::F12 => 123,
343 Code::F13 => 124,
344 Code::F14 => 125,
345 Code::F15 => 126,
346 Code::F16 => 127,
347 Code::F17 => 128,
348 Code::F18 => 129,
349 Code::F19 => 130,
350 Code::F20 => 131,
351 Code::F21 => 132,
352 Code::F22 => 133,
353 Code::F23 => 134,
354 Code::F24 => 135,
355 _ => keyboard_event.key.legacy_keycode(),
356 }
357}