2021-09-27 23:22:21 +02:00
/*
2022-08-08 22:29:40 +02:00
* Copyright ( c ) 2021 - 2022 , Andreas Kling < kling @ serenityos . org >
2021-09-27 23:22:21 +02:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/CharacterTypes.h>
2023-07-08 19:19:26 -04:00
# include <LibUnicode/CharacterTypes.h>
2022-09-25 18:06:11 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2021-09-27 23:22:21 +02:00
# include <LibWeb/UIEvents/KeyboardEvent.h>
namespace Web : : UIEvents {
// 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 ;
// 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 ;
}
// Return the virtual key code from the operating system.
return platform_key ;
}
2023-07-08 19:19:26 -04:00
// 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 " Alt " _string ;
// FIXME: AltGraph
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
2023-05-23 02:33:57 +00:00
static ErrorOr < String > get_event_key ( KeyCode platform_key , u32 code_point )
{
2023-07-08 19:19:26 -04:00
// 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 ( ) ;
2023-05-23 02:33:57 +00:00
}
2023-07-08 19:19:26 -04:00
// 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 ( ) ;
2023-05-23 02:33:57 +00:00
}
2023-07-08 19:19:26 -04:00
// 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 ( ) ;
2023-08-07 11:12:38 +02:00
return " Unidentified " _string ;
2023-05-23 02:33:57 +00:00
}
2023-07-08 18:20:02 -04:00
// 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 " Alt " _string ; // FIXME: Detect left vs. right key.
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 " Control " _string ; // FIXME: Detect left vs. right key.
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 ;
// FIXME: Pause
// 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 ( ) ;
}
2023-07-08 19:30:36 -04:00
// 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 ;
}
2023-03-09 21:35:15 +01:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < KeyboardEvent > > KeyboardEvent : : create_from_platform_event ( JS : : Realm & realm , FlyString const & event_name , KeyCode platform_key , unsigned modifiers , u32 code_point )
2021-09-27 23:22:21 +02:00
{
2023-03-09 21:35:15 +01:00
auto & vm = realm . vm ( ) ;
2023-05-23 02:33:57 +00:00
auto event_key = TRY_OR_THROW_OOM ( vm , get_event_key ( platform_key , code_point ) ) ;
2023-07-08 18:20:02 -04:00
auto event_code = TRY_OR_THROW_OOM ( vm , get_event_code ( platform_key , modifiers ) ) ;
2021-09-27 23:22:21 +02:00
auto key_code = determine_key_code ( platform_key , code_point ) ;
2021-10-01 20:25:37 +03:00
KeyboardEventInit event_init { } ;
event_init . key = move ( event_key ) ;
event_init . code = move ( event_code ) ;
2023-07-08 19:30:36 -04:00
event_init . location = to_underlying ( get_event_location ( platform_key , modifiers ) ) ;
2021-10-01 20:25:37 +03:00
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 = false ;
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 ;
2022-09-25 18:06:11 -06:00
return KeyboardEvent : : create ( realm , event_name , event_init ) ;
}
2023-03-09 21:35:15 +01:00
bool KeyboardEvent : : get_modifier_state ( String const & key_arg )
2021-09-27 23:22:21 +02:00
{
if ( key_arg = = " Alt " )
return m_alt_key ;
if ( key_arg = = " Control " )
return m_ctrl_key ;
if ( key_arg = = " Shift " )
return m_shift_key ;
if ( key_arg = = " Meta " )
return m_meta_key ;
return false ;
}
2022-08-08 22:29:40 +02:00
2023-03-09 21:35:15 +01:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < KeyboardEvent > > KeyboardEvent : : create ( JS : : Realm & realm , FlyString const & event_name , KeyboardEventInit const & event_init )
2022-08-08 22:29:40 +02:00
{
2023-02-19 10:25:32 +01:00
return MUST_OR_THROW_OOM ( realm . heap ( ) . allocate < KeyboardEvent > ( realm , realm , event_name , event_init ) ) ;
2022-08-08 22:29:40 +02:00
}
2023-03-09 21:35:15 +01:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < KeyboardEvent > > KeyboardEvent : : construct_impl ( JS : : Realm & realm , FlyString const & event_name , KeyboardEventInit const & event_init )
2022-08-08 22:29:40 +02:00
{
2022-09-25 18:06:11 -06:00
return create ( realm , event_name , event_init ) ;
2022-08-08 22:29:40 +02:00
}
2023-03-09 21:35:15 +01:00
KeyboardEvent : : KeyboardEvent ( JS : : Realm & realm , FlyString const & event_name , KeyboardEventInit const & event_init )
2023-04-06 16:12:33 +02:00
: UIEvent ( realm , event_name , event_init )
2022-08-08 22:29:40 +02:00
, 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_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 ;
2023-08-07 08:41:28 +02:00
void KeyboardEvent : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2023-01-10 06:28:20 -05:00
set_prototype ( & Bindings : : ensure_web_prototype < Bindings : : KeyboardEventPrototype > ( realm , " KeyboardEvent " ) ) ;
}
2021-09-27 23:22:21 +02:00
}