mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 18:32:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			733 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			733 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | ||
|  * Copyright (c) 2021-2022, Andreas Kling <kling@serenityos.org>
 | ||
|  *
 | ||
|  * SPDX-License-Identifier: BSD-2-Clause
 | ||
|  */
 | ||
| 
 | ||
| #include <AK/CharacterTypes.h>
 | ||
| #include <LibUnicode/CharacterTypes.h>
 | ||
| #include <LibWeb/Bindings/Intrinsics.h>
 | ||
| #include <LibWeb/UIEvents/KeyboardEvent.h>
 | ||
| 
 | ||
| namespace Web::UIEvents {
 | ||
| 
 | ||
| JS_DEFINE_ALLOCATOR(KeyboardEvent);
 | ||
| 
 | ||
| // https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode
 | ||
| static unsigned long determine_key_code(KeyCode platform_key, u32 code_point)
 | ||
| {
 | ||
|     // If input key when pressed without modifiers would insert a numerical character (0-9), return the ASCII code of that numerical character.
 | ||
|     if (is_ascii_digit(code_point))
 | ||
|         return code_point;
 | ||
| 
 | ||
|     switch (platform_key) {
 | ||
|     case KeyCode::Key_ExclamationPoint:
 | ||
|         return static_cast<unsigned long>('1');
 | ||
|     case KeyCode::Key_AtSign:
 | ||
|         return static_cast<unsigned long>('2');
 | ||
|     case KeyCode::Key_Hashtag:
 | ||
|         return static_cast<unsigned long>('3');
 | ||
|     case KeyCode::Key_Dollar:
 | ||
|         return static_cast<unsigned long>('4');
 | ||
|     case KeyCode::Key_Percent:
 | ||
|         return static_cast<unsigned long>('5');
 | ||
|     case KeyCode::Key_Circumflex:
 | ||
|         return static_cast<unsigned long>('6');
 | ||
|     case KeyCode::Key_Ampersand:
 | ||
|         return static_cast<unsigned long>('7');
 | ||
|     case KeyCode::Key_Asterisk:
 | ||
|         return static_cast<unsigned long>('8');
 | ||
|     case KeyCode::Key_LeftParen:
 | ||
|         return static_cast<unsigned long>('9');
 | ||
|     case KeyCode::Key_RightParen:
 | ||
|         return static_cast<unsigned long>('0');
 | ||
|     default:
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     // If input key when pressed without modifiers would insert a lower case character in the a-z alphabetical range, return the ASCII code of the upper case equivalent.
 | ||
|     if (is_ascii_lower_alpha(code_point))
 | ||
|         return to_ascii_uppercase(code_point);
 | ||
| 
 | ||
|     // If the key’s function, as determined in an implementation-specific way, corresponds to one of the keys in the §8.3.3 Fixed virtual key codes table, return the corresponding key code.
 | ||
|     // https://www.w3.org/TR/uievents/#fixed-virtual-key-codes
 | ||
|     switch (platform_key) {
 | ||
|     case KeyCode::Key_Backspace:
 | ||
|         return 8;
 | ||
|     case KeyCode::Key_Tab:
 | ||
|         return 9;
 | ||
|     case KeyCode::Key_Return:
 | ||
|         return 13;
 | ||
|     case KeyCode::Key_Shift:
 | ||
|         return 16;
 | ||
|     case KeyCode::Key_Control:
 | ||
|         return 17;
 | ||
|     case KeyCode::Key_Alt:
 | ||
|         return 18;
 | ||
|     case KeyCode::Key_CapsLock:
 | ||
|         return 20;
 | ||
|     case KeyCode::Key_Escape:
 | ||
|         return 27;
 | ||
|     case KeyCode::Key_Space:
 | ||
|         return 32;
 | ||
|     case KeyCode::Key_PageUp:
 | ||
|         return 33;
 | ||
|     case KeyCode::Key_PageDown:
 | ||
|         return 34;
 | ||
|     case KeyCode::Key_End:
 | ||
|         return 35;
 | ||
|     case KeyCode::Key_Home:
 | ||
|         return 36;
 | ||
|     case KeyCode::Key_Left:
 | ||
|         return 37;
 | ||
|     case KeyCode::Key_Up:
 | ||
|         return 38;
 | ||
|     case KeyCode::Key_Right:
 | ||
|         return 39;
 | ||
|     case KeyCode::Key_Down:
 | ||
|         return 40;
 | ||
|     default:
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     // https://www.w3.org/TR/uievents/#optionally-fixed-virtual-key-codes
 | ||
|     switch (platform_key) {
 | ||
|     case KeyCode::Key_Semicolon:
 | ||
|     case KeyCode::Key_Colon:
 | ||
|         return 186;
 | ||
|     case KeyCode::Key_Equal:
 | ||
|     case KeyCode::Key_Plus:
 | ||
|         return 187;
 | ||
|     case KeyCode::Key_Comma:
 | ||
|     case KeyCode::Key_LessThan:
 | ||
|         return 188;
 | ||
|     case KeyCode::Key_Minus:
 | ||
|     case KeyCode::Key_Underscore:
 | ||
|         return 189;
 | ||
|     case KeyCode::Key_Period:
 | ||
|     case KeyCode::Key_GreaterThan:
 | ||
|         return 190;
 | ||
|     case KeyCode::Key_Slash:
 | ||
|     case KeyCode::Key_QuestionMark:
 | ||
|         return 191;
 | ||
|     case KeyCode::Key_Backtick:
 | ||
|     case KeyCode::Key_Tilde:
 | ||
|         return 192;
 | ||
|     case KeyCode::Key_LeftBracket:
 | ||
|     case KeyCode::Key_LeftBrace:
 | ||
|         return 219;
 | ||
|     case KeyCode::Key_Backslash:
 | ||
|     case KeyCode::Key_Pipe:
 | ||
|         return 220;
 | ||
|     case KeyCode::Key_RightBracket:
 | ||
|     case KeyCode::Key_RightBrace:
 | ||
|         return 221;
 | ||
|     case KeyCode::Key_Apostrophe:
 | ||
|     case KeyCode::Key_DoubleQuote:
 | ||
|         return 222;
 | ||
|     default:
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     // Return the virtual key code from the operating system.
 | ||
|     return platform_key;
 | ||
| }
 | ||
| 
 | ||
| // 3. Named key Attribute Values, https://www.w3.org/TR/uievents-key/#named-key-attribute-values
 | ||
| static ErrorOr<Optional<String>> get_event_named_key(KeyCode platform_key)
 | ||
| {
 | ||
|     switch (platform_key) {
 | ||
|     // 3.1. Special Keys, https://www.w3.org/TR/uievents-key/#keys-special
 | ||
|     case KeyCode::Key_Invalid:
 | ||
|         return "Unidentified"_string;
 | ||
| 
 | ||
|     // 3.2. Modifier Keys, https://www.w3.org/TR/uievents-key/#keys-modifier
 | ||
|     case KeyCode::Key_Alt:
 | ||
|         return "AltLeft"_string;
 | ||
|     case KeyCode::Key_RightAlt:
 | ||
|         return "AltRight"_string;
 | ||
|     case KeyCode::Key_CapsLock:
 | ||
|         return "CapsLock"_string;
 | ||
|     case KeyCode::Key_Control:
 | ||
|         return "Control"_string;
 | ||
|     case KeyCode::Key_Super:
 | ||
|         return "Meta"_string;
 | ||
|     case KeyCode::Key_NumLock:
 | ||
|         return "NumLock"_string;
 | ||
|     case KeyCode::Key_ScrollLock:
 | ||
|         return "ScrollLock"_string;
 | ||
|     case KeyCode::Key_LeftShift:
 | ||
|     case KeyCode::Key_RightShift:
 | ||
|         return "Shift"_string;
 | ||
| 
 | ||
|     // 3.3. Whitespace Keys, https://www.w3.org/TR/uievents-key/#keys-whitespace
 | ||
|     case KeyCode::Key_Return:
 | ||
|         return "Enter"_string;
 | ||
|     case KeyCode::Key_Tab:
 | ||
|         return "Tab"_string;
 | ||
|     case KeyCode::Key_Space:
 | ||
|         return " "_string;
 | ||
| 
 | ||
|     // 3.4. Navigation Keys, https://www.w3.org/TR/uievents-key/#keys-navigation
 | ||
|     case KeyCode::Key_Down:
 | ||
|         return "ArrowDown"_string;
 | ||
|     case KeyCode::Key_Left:
 | ||
|         return "ArrowLeft"_string;
 | ||
|     case KeyCode::Key_Right:
 | ||
|         return "ArrowRight"_string;
 | ||
|     case KeyCode::Key_Up:
 | ||
|         return "ArrowUp"_string;
 | ||
|     case KeyCode::Key_End:
 | ||
|         return "End"_string;
 | ||
|     case KeyCode::Key_Home:
 | ||
|         return "Home"_string;
 | ||
|     case KeyCode::Key_PageDown:
 | ||
|         return "PageDown"_string;
 | ||
|     case KeyCode::Key_PageUp:
 | ||
|         return "PageUp"_string;
 | ||
| 
 | ||
|     // 3.5. Editing Keys, https://www.w3.org/TR/uievents-key/#keys-editing
 | ||
|     case KeyCode::Key_Backspace:
 | ||
|         return "Backspace"_string;
 | ||
|     case KeyCode::Key_Delete:
 | ||
|         return "Delete"_string;
 | ||
|     case KeyCode::Key_Insert:
 | ||
|         return "Insert"_string;
 | ||
| 
 | ||
|     // 3.6. UI Keys, https://www.w3.org/TR/uievents-key/#keys-ui
 | ||
|     case KeyCode::Key_Menu:
 | ||
|         return "ContextMenu"_string;
 | ||
|     case KeyCode::Key_Escape:
 | ||
|         return "Escape"_string;
 | ||
|     // FIXME: Help
 | ||
|     // FIXME: Pause
 | ||
| 
 | ||
|     // 3.7. Device Keys, https://www.w3.org/TR/uievents-key/#keys-device
 | ||
|     case KeyCode::Key_PrintScreen:
 | ||
|         return "PrintScreen"_string;
 | ||
| 
 | ||
|     // 3.9. General-Purpose Function Keys, https://www.w3.org/TR/uievents-key/#keys-function
 | ||
|     case KeyCode::Key_F1:
 | ||
|         return "F1"_string;
 | ||
|     case KeyCode::Key_F2:
 | ||
|         return "F2"_string;
 | ||
|     case KeyCode::Key_F3:
 | ||
|         return "F3"_string;
 | ||
|     case KeyCode::Key_F4:
 | ||
|         return "F4"_string;
 | ||
|     case KeyCode::Key_F5:
 | ||
|         return "F5"_string;
 | ||
|     case KeyCode::Key_F6:
 | ||
|         return "F6"_string;
 | ||
|     case KeyCode::Key_F7:
 | ||
|         return "F7"_string;
 | ||
|     case KeyCode::Key_F8:
 | ||
|         return "F8"_string;
 | ||
|     case KeyCode::Key_F9:
 | ||
|         return "F9"_string;
 | ||
|     case KeyCode::Key_F10:
 | ||
|         return "F10"_string;
 | ||
|     case KeyCode::Key_F11:
 | ||
|         return "F11"_string;
 | ||
|     case KeyCode::Key_F12:
 | ||
|         return "F12"_string;
 | ||
| 
 | ||
|     default:
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     return OptionalNone {};
 | ||
| }
 | ||
| 
 | ||
| // 2.1. Unicode Values, https://www.w3.org/TR/uievents-key/#keys-unicode
 | ||
| static ErrorOr<Optional<String>> get_event_key_string(u32 code_point)
 | ||
| {
 | ||
|     auto is_non_control_character = [&]() {
 | ||
|         // A non-control character is any valid Unicode character except those that are part of the "Other, Control"
 | ||
|         // ("Cc") General Category.
 | ||
|         static auto control_general_category = Unicode::general_category_from_string("Cc"sv);
 | ||
|         if (!control_general_category.has_value())
 | ||
|             return true;
 | ||
| 
 | ||
|         return !Unicode::code_point_has_general_category(code_point, *control_general_category);
 | ||
|     };
 | ||
| 
 | ||
|     // A key string is a string containing a 0 or 1 non-control characters ("base" characters) followed by 0 or more
 | ||
|     // combining characters. The string MUST be in Normalized Form C (NFC) as described in [UAX15].
 | ||
|     // FIXME: Our key events are currently set up to provide one code point at a time. We will need to handle multi-
 | ||
|     //        code point events and NFC normalize that string.
 | ||
|     if (is_non_control_character())
 | ||
|         return String::from_code_point(code_point);
 | ||
| 
 | ||
|     return OptionalNone {};
 | ||
| }
 | ||
| 
 | ||
| // 2.2. Selecting key Attribute Values, https://www.w3.org/TR/uievents-key/#selecting-key-attribute-values
 | ||
| static ErrorOr<String> get_event_key(KeyCode platform_key, u32 code_point)
 | ||
| {
 | ||
|     // 1. Let key be a DOMString initially set to "Unidentified".
 | ||
|     // NOTE: We return "Unidentified" at the end to avoid needlessly allocating it here.
 | ||
|     Optional<String> key;
 | ||
| 
 | ||
|     // 2. If there exists an appropriate named key attribute value for this key event, then
 | ||
|     if (auto named_key = TRY(get_event_named_key(platform_key)); named_key.has_value()) {
 | ||
|         // 1. Set key to that named key attribute value.
 | ||
|         key = named_key.release_value();
 | ||
|     }
 | ||
| 
 | ||
|     // 3. Else, if the key event generates a valid key string, then
 | ||
|     else if (auto key_string = TRY(get_event_key_string(code_point)); key_string.has_value()) {
 | ||
|         // 1. Set key to that key string value.
 | ||
|         key = key_string.release_value();
 | ||
|     }
 | ||
| 
 | ||
|     // FIXME: 4. Else, if the key event has any modifier keys other than glyph modifier keys, then
 | ||
|     // FIXME:     1. Set key to the key string that would have been generated by this event if it had been typed with all
 | ||
|     //               modifer keys removed except for glyph modifier keys.
 | ||
| 
 | ||
|     // 5. Return key as the key attribute value for this key event.
 | ||
|     if (key.has_value())
 | ||
|         return key.release_value();
 | ||
|     return "Unidentified"_string;
 | ||
| }
 | ||
| 
 | ||
| // 3. Keyboard Event code Value Tables, https://www.w3.org/TR/uievents-code/#code-value-tables
 | ||
| static ErrorOr<String> get_event_code(KeyCode platform_key, unsigned modifiers)
 | ||
| {
 | ||
|     // 3.4. Numpad Section, https://www.w3.org/TR/uievents-code/#key-numpad-section
 | ||
|     if ((modifiers & Mod_Keypad) != 0) {
 | ||
|         switch (platform_key) {
 | ||
|         case KeyCode::Key_0:
 | ||
|             return "Numpad0"_string;
 | ||
|         case KeyCode::Key_1:
 | ||
|             return "Numpad1"_string;
 | ||
|         case KeyCode::Key_2:
 | ||
|             return "Numpad2"_string;
 | ||
|         case KeyCode::Key_3:
 | ||
|             return "Numpad3"_string;
 | ||
|         case KeyCode::Key_4:
 | ||
|             return "Numpad4"_string;
 | ||
|         case KeyCode::Key_5:
 | ||
|             return "Numpad5"_string;
 | ||
|         case KeyCode::Key_6:
 | ||
|             return "Numpad6"_string;
 | ||
|         case KeyCode::Key_7:
 | ||
|             return "Numpad7"_string;
 | ||
|         case KeyCode::Key_8:
 | ||
|             return "Numpad8"_string;
 | ||
|         case KeyCode::Key_9:
 | ||
|             return "Numpad9"_string;
 | ||
|         case KeyCode::Key_Plus:
 | ||
|             return "NumpadAdd"_string;
 | ||
|         case KeyCode::Key_Period:
 | ||
|         case KeyCode::Key_Delete:
 | ||
|             return "NumpadDecimal"_string;
 | ||
|         case KeyCode::Key_Slash:
 | ||
|             return "NumpadDivide"_string;
 | ||
|         case KeyCode::Key_Return:
 | ||
|             return "NumpadEnter"_string;
 | ||
|         case KeyCode::Key_Asterisk:
 | ||
|             return "NumpadAsterisk"_string;
 | ||
|         case KeyCode::Key_Minus:
 | ||
|             return "NumpadSubtract"_string;
 | ||
|         default:
 | ||
|             break;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     switch (platform_key) {
 | ||
|     // 3.1.1. Writing System Keys, https://www.w3.org/TR/uievents-code/#key-alphanumeric-writing-system
 | ||
|     case KeyCode::Key_Backtick:
 | ||
|     case KeyCode::Key_Tilde:
 | ||
|         return "Backquote"_string;
 | ||
|     case KeyCode::Key_Backslash:
 | ||
|     case KeyCode::Key_Pipe:
 | ||
|         return "Backslash"_string;
 | ||
|     case KeyCode::Key_LeftBrace:
 | ||
|     case KeyCode::Key_LeftBracket:
 | ||
|         return "BracketLeft"_string;
 | ||
|     case KeyCode::Key_RightBrace:
 | ||
|     case KeyCode::Key_RightBracket:
 | ||
|         return "BracketRight"_string;
 | ||
|     case KeyCode::Key_Comma:
 | ||
|     case KeyCode::Key_LessThan:
 | ||
|         return "Comma"_string;
 | ||
|     case KeyCode::Key_0:
 | ||
|     case KeyCode::Key_RightParen:
 | ||
|         return "Digit0"_string;
 | ||
|     case KeyCode::Key_1:
 | ||
|     case KeyCode::Key_ExclamationPoint:
 | ||
|         return "Digit1"_string;
 | ||
|     case KeyCode::Key_2:
 | ||
|     case KeyCode::Key_AtSign:
 | ||
|         return "Digit2"_string;
 | ||
|     case KeyCode::Key_3:
 | ||
|     case KeyCode::Key_Hashtag:
 | ||
|         return "Digit3"_string;
 | ||
|     case KeyCode::Key_4:
 | ||
|     case KeyCode::Key_Dollar:
 | ||
|         return "Digit4"_string;
 | ||
|     case KeyCode::Key_5:
 | ||
|     case KeyCode::Key_Percent:
 | ||
|         return "Digit5"_string;
 | ||
|     case KeyCode::Key_6:
 | ||
|     case KeyCode::Key_Circumflex:
 | ||
|         return "Digit6"_string;
 | ||
|     case KeyCode::Key_7:
 | ||
|     case KeyCode::Key_Ampersand:
 | ||
|         return "Digit7"_string;
 | ||
|     case KeyCode::Key_8:
 | ||
|     case KeyCode::Key_Asterisk:
 | ||
|         return "Digit8"_string;
 | ||
|     case KeyCode::Key_9:
 | ||
|     case KeyCode::Key_LeftParen:
 | ||
|         return "Digit9"_string;
 | ||
|     case KeyCode::Key_Equal:
 | ||
|     case KeyCode::Key_Plus:
 | ||
|         return "Equal"_string;
 | ||
|     // FIXME: IntlBackslash
 | ||
|     // FIXME: IntlRo
 | ||
|     // FIXME: IntlYen
 | ||
|     case KeyCode::Key_A:
 | ||
|         return "KeyA"_string;
 | ||
|     case KeyCode::Key_B:
 | ||
|         return "KeyB"_string;
 | ||
|     case KeyCode::Key_C:
 | ||
|         return "KeyC"_string;
 | ||
|     case KeyCode::Key_D:
 | ||
|         return "KeyD"_string;
 | ||
|     case KeyCode::Key_E:
 | ||
|         return "KeyE"_string;
 | ||
|     case KeyCode::Key_F:
 | ||
|         return "KeyF"_string;
 | ||
|     case KeyCode::Key_G:
 | ||
|         return "KeyG"_string;
 | ||
|     case KeyCode::Key_H:
 | ||
|         return "KeyH"_string;
 | ||
|     case KeyCode::Key_I:
 | ||
|         return "KeyI"_string;
 | ||
|     case KeyCode::Key_J:
 | ||
|         return "KeyJ"_string;
 | ||
|     case KeyCode::Key_K:
 | ||
|         return "KeyK"_string;
 | ||
|     case KeyCode::Key_L:
 | ||
|         return "KeyL"_string;
 | ||
|     case KeyCode::Key_M:
 | ||
|         return "KeyM"_string;
 | ||
|     case KeyCode::Key_N:
 | ||
|         return "KeyN"_string;
 | ||
|     case KeyCode::Key_O:
 | ||
|         return "KeyO"_string;
 | ||
|     case KeyCode::Key_P:
 | ||
|         return "KeyP"_string;
 | ||
|     case KeyCode::Key_Q:
 | ||
|         return "KeyQ"_string;
 | ||
|     case KeyCode::Key_R:
 | ||
|         return "KeyR"_string;
 | ||
|     case KeyCode::Key_S:
 | ||
|         return "KeyS"_string;
 | ||
|     case KeyCode::Key_T:
 | ||
|         return "KeyT"_string;
 | ||
|     case KeyCode::Key_U:
 | ||
|         return "KeyU"_string;
 | ||
|     case KeyCode::Key_V:
 | ||
|         return "KeyV"_string;
 | ||
|     case KeyCode::Key_W:
 | ||
|         return "KeyW"_string;
 | ||
|     case KeyCode::Key_X:
 | ||
|         return "KeyX"_string;
 | ||
|     case KeyCode::Key_Y:
 | ||
|         return "KeyY"_string;
 | ||
|     case KeyCode::Key_Z:
 | ||
|         return "KeyZ"_string;
 | ||
|     case KeyCode::Key_Minus:
 | ||
|     case KeyCode::Key_Underscore:
 | ||
|         return "Minus"_string;
 | ||
|     case KeyCode::Key_Period:
 | ||
|     case KeyCode::Key_GreaterThan:
 | ||
|         return "Period"_string;
 | ||
|     case KeyCode::Key_Apostrophe:
 | ||
|     case KeyCode::Key_DoubleQuote:
 | ||
|         return "Quote"_string;
 | ||
|     case KeyCode::Key_Semicolon:
 | ||
|     case KeyCode::Key_Colon:
 | ||
|         return "Semicolon"_string;
 | ||
|     case KeyCode::Key_Slash:
 | ||
|     case KeyCode::Key_QuestionMark:
 | ||
|         return "Slash"_string;
 | ||
| 
 | ||
|     // 3.1.2. Functional Keys, https://www.w3.org/TR/uievents-code/#key-alphanumeric-functional
 | ||
|     case KeyCode::Key_Alt:
 | ||
|         return "AltLeft"_string;
 | ||
|     case KeyCode::Key_RightAlt:
 | ||
|         return "AltRight"_string;
 | ||
|     case KeyCode::Key_Backspace:
 | ||
|         return "Backspace"_string;
 | ||
|     case KeyCode::Key_CapsLock:
 | ||
|         return "CapsLock"_string;
 | ||
|     case KeyCode::Key_Menu:
 | ||
|         return "ContextMenu"_string;
 | ||
|     case KeyCode::Key_Control:
 | ||
|         return "ControlLeft"_string;
 | ||
|     case KeyCode::Key_RightControl:
 | ||
|         return "ControlRight"_string;
 | ||
|     case KeyCode::Key_Return:
 | ||
|         return "Enter"_string;
 | ||
|     case KeyCode::Key_Super:
 | ||
|         return "Meta"_string; // FIXME: Detect left vs. right key.
 | ||
|     case KeyCode::Key_LeftShift:
 | ||
|         return "ShiftLeft"_string;
 | ||
|     case KeyCode::Key_RightShift:
 | ||
|         return "ShiftRight"_string;
 | ||
|     case KeyCode::Key_Space:
 | ||
|         return "Space"_string;
 | ||
|     case KeyCode::Key_Tab:
 | ||
|         return "Tab"_string;
 | ||
| 
 | ||
|     // 3.2. Control Pad Section, https://www.w3.org/TR/uievents-code/#key-controlpad-section
 | ||
|     case KeyCode::Key_Delete:
 | ||
|         return "Delete"_string;
 | ||
|     case KeyCode::Key_End:
 | ||
|         return "End"_string;
 | ||
|     // FIXME: Help
 | ||
|     case KeyCode::Key_Home:
 | ||
|         return "Home"_string;
 | ||
|     case KeyCode::Key_Insert:
 | ||
|         return "Insert"_string;
 | ||
|     case KeyCode::Key_PageDown:
 | ||
|         return "PageDown"_string;
 | ||
|     case KeyCode::Key_PageUp:
 | ||
|         return "PageUp"_string;
 | ||
| 
 | ||
|     // 3.3. Arrow Pad Section, https://www.w3.org/TR/uievents-code/#key-arrowpad-section
 | ||
|     case KeyCode::Key_Down:
 | ||
|         return "ArrowDown"_string;
 | ||
|     case KeyCode::Key_Left:
 | ||
|         return "ArrowLeft"_string;
 | ||
|     case KeyCode::Key_Right:
 | ||
|         return "ArrowRight"_string;
 | ||
|     case KeyCode::Key_Up:
 | ||
|         return "ArrowUp"_string;
 | ||
| 
 | ||
|     // 3.4. Numpad Section, https://www.w3.org/TR/uievents-code/#key-numpad-section
 | ||
|     case KeyCode::Key_NumLock:
 | ||
|         return "NumLock"_string;
 | ||
| 
 | ||
|     // 3.5. Function Section, https://www.w3.org/TR/uievents-code/#key-function-section
 | ||
|     case KeyCode::Key_Escape:
 | ||
|         return "Escape"_string;
 | ||
|     case KeyCode::Key_F1:
 | ||
|         return "F1"_string;
 | ||
|     case KeyCode::Key_F2:
 | ||
|         return "F2"_string;
 | ||
|     case KeyCode::Key_F3:
 | ||
|         return "F3"_string;
 | ||
|     case KeyCode::Key_F4:
 | ||
|         return "F4"_string;
 | ||
|     case KeyCode::Key_F5:
 | ||
|         return "F5"_string;
 | ||
|     case KeyCode::Key_F6:
 | ||
|         return "F6"_string;
 | ||
|     case KeyCode::Key_F7:
 | ||
|         return "F7"_string;
 | ||
|     case KeyCode::Key_F8:
 | ||
|         return "F8"_string;
 | ||
|     case KeyCode::Key_F9:
 | ||
|         return "F9"_string;
 | ||
|     case KeyCode::Key_F10:
 | ||
|         return "F10"_string;
 | ||
|     case KeyCode::Key_F11:
 | ||
|         return "F11"_string;
 | ||
|     case KeyCode::Key_F12:
 | ||
|         return "F12"_string;
 | ||
|     case KeyCode::Key_PrintScreen:
 | ||
|     case KeyCode::Key_SysRq:
 | ||
|         return "PrintScreen"_string;
 | ||
|     case KeyCode::Key_ScrollLock:
 | ||
|         return "ScrollLock"_string;
 | ||
|     case KeyCode::Key_PauseBreak:
 | ||
|         return "Pause"_string;
 | ||
| 
 | ||
|     // 3.6. Media Section, https://www.w3.org/TR/uievents-code/#media-keys
 | ||
|     case KeyCode::Key_BrowserSearch:
 | ||
|         return "BrowserSearch"_string;
 | ||
|     case KeyCode::Key_BrowserFavorites:
 | ||
|         return "BrowserFavorites"_string;
 | ||
|     case KeyCode::Key_BrowserHome:
 | ||
|         return "BrowserHome"_string;
 | ||
|     case KeyCode::Key_PreviousTrack:
 | ||
|         return "PreviousTrack"_string;
 | ||
|     case KeyCode::Key_BrowserBack:
 | ||
|         return "BrowserBack"_string;
 | ||
|     case KeyCode::Key_BrowserForward:
 | ||
|         return "BrowserForward"_string;
 | ||
|     case KeyCode::Key_BrowserRefresh:
 | ||
|         return "BrowserRefresh"_string;
 | ||
|     case KeyCode::Key_BrowserStop:
 | ||
|         return "BrowserStop"_string;
 | ||
|     case KeyCode::Key_VolumeDown:
 | ||
|         return "AudioVolumeDown"_string;
 | ||
|     case KeyCode::Key_VolumeUp:
 | ||
|         return "AudioVolumeUp"_string;
 | ||
|     case KeyCode::Key_Wake:
 | ||
|         return "WakeUp"_string;
 | ||
|     case KeyCode::Key_Sleep:
 | ||
|         return "Sleep"_string;
 | ||
|     case KeyCode::Key_NextTrack:
 | ||
|         return "NextTrack"_string;
 | ||
|     case KeyCode::Key_MediaSelect:
 | ||
|         return "MediaSelect"_string;
 | ||
|     case KeyCode::Key_Email:
 | ||
|         return "LaunchMail"_string;
 | ||
| 
 | ||
|     case KeyCode::Key_Power:
 | ||
|         return "Power"_string;
 | ||
|     case KeyCode::Key_Stop:
 | ||
|         return "MediaStop"_string;
 | ||
|     case KeyCode::Key_PlayPause:
 | ||
|         return "MediaPlayPause"_string;
 | ||
|     case KeyCode::Key_Mute:
 | ||
|         return "AudioVolumeMute"_string;
 | ||
|     case KeyCode::Key_Calculator:
 | ||
|         return "LaunchApp2"_string;
 | ||
|     case KeyCode::Key_MyComputer:
 | ||
|         return "LaunchApp1"_string;
 | ||
| 
 | ||
|     // FIXME: Are these correct?
 | ||
|     case KeyCode::Key_LeftGUI:
 | ||
|         return "LaunchApp2"_string;
 | ||
|     case KeyCode::Key_RightGUI:
 | ||
|     case KeyCode::Key_Apps:
 | ||
|         return "LaunchApp1"_string;
 | ||
| 
 | ||
|     // 3.7. Legacy, Non-Standard and Special Keys, https://www.w3.org/TR/uievents-code/#key-legacy
 | ||
|     case KeyCode::Key_Invalid:
 | ||
|         return "Unidentified"_string;
 | ||
|     }
 | ||
| 
 | ||
|     VERIFY_NOT_REACHED();
 | ||
| }
 | ||
| 
 | ||
| // 5.6.2. Keyboard Event Key Location, https://www.w3.org/TR/uievents/#events-keyboard-key-location
 | ||
| static DOMKeyLocation get_event_location(KeyCode platform_key, unsigned modifiers)
 | ||
| {
 | ||
|     if ((modifiers & Mod_Keypad) != 0)
 | ||
|         return DOMKeyLocation::Numpad;
 | ||
| 
 | ||
|     // FIXME: Detect left vs. right for Control and Alt keys.
 | ||
|     switch (platform_key) {
 | ||
|     case KeyCode::Key_LeftShift:
 | ||
|         return DOMKeyLocation::Left;
 | ||
|     case KeyCode::Key_RightShift:
 | ||
|         return DOMKeyLocation::Right;
 | ||
|     default:
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     return DOMKeyLocation::Standard;
 | ||
| }
 | ||
| 
 | ||
| JS::NonnullGCPtr<KeyboardEvent> KeyboardEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, KeyCode platform_key, unsigned modifiers, u32 code_point)
 | ||
| {
 | ||
|     auto event_key = MUST(get_event_key(platform_key, code_point));
 | ||
|     auto event_code = MUST(get_event_code(platform_key, modifiers));
 | ||
| 
 | ||
|     auto key_code = determine_key_code(platform_key, code_point);
 | ||
|     KeyboardEventInit event_init {};
 | ||
|     event_init.key = move(event_key);
 | ||
|     event_init.code = move(event_code);
 | ||
|     event_init.location = to_underlying(get_event_location(platform_key, modifiers));
 | ||
|     event_init.ctrl_key = modifiers & Mod_Ctrl;
 | ||
|     event_init.shift_key = modifiers & Mod_Shift;
 | ||
|     event_init.alt_key = modifiers & Mod_Alt;
 | ||
|     event_init.meta_key = modifiers & Mod_Super;
 | ||
|     event_init.repeat = false;
 | ||
|     event_init.is_composing = false;
 | ||
|     event_init.key_code = key_code;
 | ||
|     event_init.char_code = code_point;
 | ||
|     event_init.bubbles = true;
 | ||
|     event_init.cancelable = true;
 | ||
|     event_init.composed = true;
 | ||
|     auto event = KeyboardEvent::create(realm, event_name, event_init);
 | ||
|     event->set_is_trusted(true);
 | ||
|     return event;
 | ||
| }
 | ||
| 
 | ||
| bool KeyboardEvent::get_modifier_state(String const& key_arg) const
 | ||
| {
 | ||
|     if (key_arg == "Control")
 | ||
|         return m_ctrl_key;
 | ||
|     if (key_arg == "Shift")
 | ||
|         return m_shift_key;
 | ||
|     if (key_arg == "Alt")
 | ||
|         return m_alt_key;
 | ||
|     if (key_arg == "Meta")
 | ||
|         return m_meta_key;
 | ||
|     if (key_arg == "AltGraph")
 | ||
|         return m_modifier_alt_graph;
 | ||
|     if (key_arg == "CapsLock")
 | ||
|         return m_modifier_caps_lock;
 | ||
|     if (key_arg == "Fn")
 | ||
|         return m_modifier_fn;
 | ||
|     if (key_arg == "FnLock")
 | ||
|         return m_modifier_fn_lock;
 | ||
|     if (key_arg == "Hyper")
 | ||
|         return m_modifier_hyper;
 | ||
|     if (key_arg == "NumLock")
 | ||
|         return m_modifier_num_lock;
 | ||
|     if (key_arg == "ScrollLock")
 | ||
|         return m_modifier_scroll_lock;
 | ||
|     if (key_arg == "Super")
 | ||
|         return m_modifier_super;
 | ||
|     if (key_arg == "Symbol")
 | ||
|         return m_modifier_symbol;
 | ||
|     if (key_arg == "SymbolLock")
 | ||
|         return m_modifier_symbol_lock;
 | ||
|     return false;
 | ||
| }
 | ||
| 
 | ||
| JS::NonnullGCPtr<KeyboardEvent> KeyboardEvent::create(JS::Realm& realm, FlyString const& event_name, KeyboardEventInit const& event_init)
 | ||
| {
 | ||
|     return realm.heap().allocate<KeyboardEvent>(realm, realm, event_name, event_init);
 | ||
| }
 | ||
| 
 | ||
| WebIDL::ExceptionOr<JS::NonnullGCPtr<KeyboardEvent>> KeyboardEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, KeyboardEventInit const& event_init)
 | ||
| {
 | ||
|     return create(realm, event_name, event_init);
 | ||
| }
 | ||
| 
 | ||
| KeyboardEvent::KeyboardEvent(JS::Realm& realm, FlyString const& event_name, KeyboardEventInit const& event_init)
 | ||
|     : UIEvent(realm, event_name, event_init)
 | ||
|     , m_key(event_init.key)
 | ||
|     , m_code(event_init.code)
 | ||
|     , m_location(event_init.location)
 | ||
|     , m_ctrl_key(event_init.ctrl_key)
 | ||
|     , m_shift_key(event_init.shift_key)
 | ||
|     , m_alt_key(event_init.alt_key)
 | ||
|     , m_meta_key(event_init.meta_key)
 | ||
|     , m_modifier_alt_graph(event_init.modifier_alt_graph)
 | ||
|     , m_modifier_caps_lock(event_init.modifier_caps_lock)
 | ||
|     , m_modifier_fn(event_init.modifier_fn)
 | ||
|     , m_modifier_fn_lock(event_init.modifier_fn_lock)
 | ||
|     , m_modifier_hyper(event_init.modifier_hyper)
 | ||
|     , m_modifier_num_lock(event_init.modifier_num_lock)
 | ||
|     , m_modifier_scroll_lock(event_init.modifier_scroll_lock)
 | ||
|     , m_modifier_super(event_init.modifier_super)
 | ||
|     , m_modifier_symbol(event_init.modifier_symbol)
 | ||
|     , m_modifier_symbol_lock(event_init.modifier_symbol_lock)
 | ||
|     , m_repeat(event_init.repeat)
 | ||
|     , m_is_composing(event_init.is_composing)
 | ||
|     , m_key_code(event_init.key_code)
 | ||
|     , m_char_code(event_init.char_code)
 | ||
| {
 | ||
| }
 | ||
| 
 | ||
| KeyboardEvent::~KeyboardEvent() = default;
 | ||
| 
 | ||
| void KeyboardEvent::initialize(JS::Realm& realm)
 | ||
| {
 | ||
|     Base::initialize(realm);
 | ||
|     set_prototype(&Bindings::ensure_web_prototype<Bindings::KeyboardEventPrototype>(realm, "KeyboardEvent"_fly_string));
 | ||
| }
 | ||
| 
 | ||
| }
 | 
