From 738cb246910a30a3ee4ade7a88746a8c27b5fe7a Mon Sep 17 00:00:00 2001 From: sideshowbarker Date: Fri, 24 Jan 2025 16:25:59 +0900 Subject: [PATCH] LibWeb: Fire keypress event for Enter, Shift+Enter, and Ctrl+Enter keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For web compat and interop with other engines, this change makes us fire “keypress” events for the Enter key and for the combination of the Enter key with the Shift or Ctrl keys — despite the fact the UI Events spec states at https://w3c.github.io/uievents/#event-type-keypress it must be fired “if and only if that key normally produces a character value”. See https://github.com/w3c/uievents/issues/183#issuecomment-448091687 and https://github.com/w3c/uievents/issues/266#issuecomment-1887917756. --- Libraries/LibWeb/Page/EventHandler.cpp | 17 ++++++++++- .../expected/UIEvents/KeyEvent-keypress.txt | 9 ++++++ .../input/UIEvents/KeyEvent-keypress.html | 28 +++++++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Libraries/LibWeb/Page/EventHandler.cpp b/Libraries/LibWeb/Page/EventHandler.cpp index d25f436e09c..e64658b93c9 100644 --- a/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Libraries/LibWeb/Page/EventHandler.cpp @@ -1068,6 +1068,18 @@ static bool produces_character_value(u32 code_point) || Unicode::code_point_has_symbol_general_category(code_point); } +// https://github.com/w3c/uievents/issues/183#issuecomment-448091687 +static bool is_enter_key_or_interoperable_enter_key_combo(UIEvents::KeyCode key, u32 modifiers) +{ + if (key != UIEvents::KeyCode::Key_Return) + return false; + if (!modifiers) + return true; + if (modifiers & (UIEvents::KeyModifier::Mod_Shift | UIEvents::KeyModifier::Mod_Ctrl)) + return true; + return false; +} + EventResult EventHandler::input_event(FlyString const& event_name, FlyString const& input_type, HTML::Navigable& navigable, u32 code_point) { auto document = navigable.active_document(); @@ -1115,7 +1127,10 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u // https://w3c.github.io/uievents/#event-type-keypress // If supported by a user agent, this event MUST be dispatched when a key is pressed down, if and only if that key // normally produces a character value. - if (produces_character_value(code_point)) { + // AD-HOC: For web compat and for interop with other engines, we make an exception here for the Enter key. See: + // https://github.com/w3c/uievents/issues/183#issuecomment-448091687 and + // https://github.com/w3c/uievents/issues/266#issuecomment-1887917756 + if (produces_character_value(code_point) || is_enter_key_or_interoperable_enter_key_combo(key, modifiers)) { dispatch_result = fire_keyboard_event(UIEvents::EventNames::keypress, m_navigable, key, modifiers, code_point, repeat); if (dispatch_result != EventResult::Accepted) return dispatch_result; diff --git a/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-keypress.txt b/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-keypress.txt index 4d4617ea838..5c74fa59aeb 100644 --- a/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-keypress.txt +++ b/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-keypress.txt @@ -3,3 +3,12 @@ keypress key=A charCode=65 keydown key=Shift charCode=0 keydown key=B charCode=0 keypress key=B charCode=66 +keydown key=Enter charCode=0 +keypress key=Enter charCode=0 +keydown key=Enter charCode=0 modifiers=Alt +keydown key=Enter charCode=0 modifiers=Control +keypress key=Enter charCode=0 modifiers=Control +keydown key=Enter charCode=0 modifiers=Shift +keypress key=Enter charCode=0 modifiers=Shift +keydown key=Enter charCode=0 +keydown key=Enter charCode=0 diff --git a/Tests/LibWeb/Text/input/UIEvents/KeyEvent-keypress.html b/Tests/LibWeb/Text/input/UIEvents/KeyEvent-keypress.html index 459f2cf66b0..4653c2259e7 100644 --- a/Tests/LibWeb/Text/input/UIEvents/KeyEvent-keypress.html +++ b/Tests/LibWeb/Text/input/UIEvents/KeyEvent-keypress.html @@ -4,15 +4,39 @@ test(() => { let input = document.getElementById("input"); + const modifierKeys = ['Alt', 'Control', 'Shift', 'Super', 'NumLock']; input.addEventListener("keydown", e => { - println(`keydown key=${e.key} charCode=${e.charCode}`); + const activeModifiers = modifierKeys.filter(modifier => e.getModifierState(modifier)); + if (activeModifiers.length > 0) { + println(`keydown key=${e.key} charCode=${e.charCode} modifiers=${activeModifiers.join(', ')}`); + } else { + println(`keydown key=${e.key} charCode=${e.charCode}`); + } }); input.addEventListener("keypress", e => { - println(`keypress key=${e.key} charCode=${e.charCode}`); + const activeModifiers = modifierKeys.filter(modifier => e.getModifierState(modifier)); + if (activeModifiers.length > 0) { + println(`keypress key=${e.key} charCode=${e.charCode} modifiers=${activeModifiers.join(', ')}`); + } else { + println(`keypress key=${e.key} charCode=${e.charCode}`); + } }); internals.sendText(input, "A"); internals.sendKey(input, "LeftShift"); internals.sendText(input, "B"); + + internals.sendKey(input, "Return"); + + let modifiers = 1 << 0; // Mod_Alt + internals.sendKey(input, "Return", modifiers); + modifiers = 1 << 1; // Mod_Ctrl + internals.sendKey(input, "Return", modifiers); + modifiers = 1 << 2; // Mod_Shift + internals.sendKey(input, "Return", modifiers); + modifiers = 1 << 3; // Mod_Super + internals.sendKey(input, "Return", modifiers); + modifiers = 1 << 4; // Mod_Keypad + internals.sendKey(input, "Return", modifiers); });