diff --git a/Ladybird/AppKit/UI/Event.mm b/Ladybird/AppKit/UI/Event.mm index ef854714c11..3b91d90d581 100644 --- a/Ladybird/AppKit/UI/Event.mm +++ b/Ladybird/AppKit/UI/Event.mm @@ -297,6 +297,7 @@ Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type type, NSEvent* event) { auto modifiers = ns_modifiers_to_key_modifiers(event.modifierFlags); auto key_code = ns_key_code_to_key_code(event.keyCode, modifiers); + auto repeat = event.isARepeat; // FIXME: WebContent should really support multi-code point key events. u32 code_point = 0; @@ -312,7 +313,7 @@ Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type type, NSEvent* event) if (code_point >= 0xE000 && code_point <= 0xF8FF) code_point = 0; - return { type, key_code, modifiers, code_point, make(event) }; + return { type, key_code, modifiers, code_point, repeat, make(event) }; } NSEvent* key_event_to_ns_event(Web::KeyEvent const& event) diff --git a/Ladybird/Qt/WebContentView.cpp b/Ladybird/Qt/WebContentView.cpp index 50f526eec70..df1afbd0dbd 100644 --- a/Ladybird/Qt/WebContentView.cpp +++ b/Ladybird/Qt/WebContentView.cpp @@ -899,15 +899,15 @@ void WebContentView::enqueue_native_event(Web::KeyEvent::Type type, QKeyEvent co auto to_web_event = [&]() -> Web::KeyEvent { if (event.key() == Qt::Key_Backtab) { // Qt transforms Shift+Tab into a "Backtab", so we undo that transformation here. - return { type, Web::UIEvents::KeyCode::Key_Tab, Web::UIEvents::Mod_Shift, '\t', make(event) }; + return { type, Web::UIEvents::KeyCode::Key_Tab, Web::UIEvents::Mod_Shift, '\t', event.isAutoRepeat(), make(event) }; } if (event.key() == Qt::Key_Enter || event.key() == Qt::Key_Return) { // This ensures consistent behavior between systems that treat Enter as '\n' and '\r\n' - return { type, Web::UIEvents::KeyCode::Key_Return, Web::UIEvents::Mod_Shift, '\n', make(event) }; + return { type, Web::UIEvents::KeyCode::Key_Return, Web::UIEvents::Mod_Shift, '\n', event.isAutoRepeat(), make(event) }; } - return { type, keycode, modifiers, code_point, make(event) }; + return { type, keycode, modifiers, code_point, event.isAutoRepeat(), make(event) }; }; enqueue_input_event(to_web_event()); diff --git a/Userland/Libraries/LibWeb/Internals/Internals.cpp b/Userland/Libraries/LibWeb/Internals/Internals.cpp index eea2abad399..4eb9558e67c 100644 --- a/Userland/Libraries/LibWeb/Internals/Internals.cpp +++ b/Userland/Libraries/LibWeb/Internals/Internals.cpp @@ -79,7 +79,7 @@ void Internals::send_text(HTML::HTMLElement& target, String const& text, WebIDL: target.focus(); for (auto code_point : text.code_points()) - page.handle_keydown(UIEvents::code_point_to_key_code(code_point), modifiers, code_point); + page.handle_keydown(UIEvents::code_point_to_key_code(code_point), modifiers, code_point, false); } void Internals::send_key(HTML::HTMLElement& target, String const& key_name, WebIDL::UnsignedShort modifiers) @@ -87,12 +87,12 @@ void Internals::send_key(HTML::HTMLElement& target, String const& key_name, WebI auto key_code = UIEvents::key_code_from_string(key_name); target.focus(); - internals_page().handle_keydown(key_code, modifiers, 0); + internals_page().handle_keydown(key_code, modifiers, 0, false); } void Internals::commit_text() { - internals_page().handle_keydown(UIEvents::Key_Return, 0, 0); + internals_page().handle_keydown(UIEvents::Key_Return, 0, 0, false); } void Internals::click(double x, double y) diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp index 1a66629be83..dd7baf42215 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -840,7 +840,7 @@ constexpr bool should_ignore_keydown_event(u32 code_point, u32 modifiers) return code_point == 0 || code_point == 27; } -EventResult EventHandler::fire_keyboard_event(FlyString const& event_name, HTML::Navigable& navigable, UIEvents::KeyCode key, u32 modifiers, u32 code_point) +EventResult EventHandler::fire_keyboard_event(FlyString const& event_name, HTML::Navigable& navigable, UIEvents::KeyCode key, u32 modifiers, u32 code_point, bool repeat) { JS::GCPtr document = navigable.active_document(); if (!document) @@ -852,15 +852,15 @@ EventResult EventHandler::fire_keyboard_event(FlyString const& event_name, HTML: if (is(*focused_element)) { auto& navigable_container = verify_cast(*focused_element); if (navigable_container.content_navigable()) - return fire_keyboard_event(event_name, *navigable_container.content_navigable(), key, modifiers, code_point); + return fire_keyboard_event(event_name, *navigable_container.content_navigable(), key, modifiers, code_point, repeat); } - auto event = UIEvents::KeyboardEvent::create_from_platform_event(document->realm(), event_name, key, modifiers, code_point); + auto event = UIEvents::KeyboardEvent::create_from_platform_event(document->realm(), event_name, key, modifiers, code_point, repeat); return focused_element->dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled; } // FIXME: De-duplicate this. This is just to prevent wasting a KeyboardEvent allocation when recursing into an (i)frame. - auto event = UIEvents::KeyboardEvent::create_from_platform_event(document->realm(), event_name, key, modifiers, code_point); + auto event = UIEvents::KeyboardEvent::create_from_platform_event(document->realm(), event_name, key, modifiers, code_point, repeat); JS::GCPtr target = document->body() ?: &document->root(); return target->dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled; @@ -911,14 +911,14 @@ EventResult EventHandler::input_event(FlyString const& event_name, FlyString con return document->root().dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled; } -EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code_point) +EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code_point, bool repeat) { if (!m_navigable->active_document()) return EventResult::Dropped; if (!m_navigable->active_document()->is_fully_active()) return EventResult::Dropped; - auto dispatch_result = fire_keyboard_event(UIEvents::EventNames::keydown, m_navigable, key, modifiers, code_point); + auto dispatch_result = fire_keyboard_event(UIEvents::EventNames::keydown, m_navigable, key, modifiers, code_point, repeat); if (dispatch_result != EventResult::Accepted) return dispatch_result; @@ -926,7 +926,7 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u // 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)) { - dispatch_result = fire_keyboard_event(UIEvents::EventNames::keypress, m_navigable, key, modifiers, code_point); + dispatch_result = fire_keyboard_event(UIEvents::EventNames::keypress, m_navigable, key, modifiers, code_point, repeat); if (dispatch_result != EventResult::Accepted) return dispatch_result; } @@ -1171,9 +1171,9 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u return EventResult::Accepted; } -EventResult EventHandler::handle_keyup(UIEvents::KeyCode key, u32 modifiers, u32 code_point) +EventResult EventHandler::handle_keyup(UIEvents::KeyCode key, u32 modifiers, u32 code_point, [[maybe_unused]] bool repeat) { - return fire_keyboard_event(UIEvents::EventNames::keyup, m_navigable, key, modifiers, code_point); + return fire_keyboard_event(UIEvents::EventNames::keyup, m_navigable, key, modifiers, code_point, false); } void EventHandler::handle_paste(String const& text) diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.h b/Userland/Libraries/LibWeb/Page/EventHandler.h index 383fad584b8..e32091ed5dd 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.h +++ b/Userland/Libraries/LibWeb/Page/EventHandler.h @@ -34,8 +34,8 @@ public: EventResult handle_drag_and_drop_event(DragEvent::Type, CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, Vector files); - EventResult handle_keydown(UIEvents::KeyCode, unsigned modifiers, u32 code_point); - EventResult handle_keyup(UIEvents::KeyCode, unsigned modifiers, u32 code_point); + EventResult handle_keydown(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat); + EventResult handle_keyup(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat); void set_mouse_event_tracking_paintable(Painting::Paintable*); @@ -49,7 +49,7 @@ private: bool focus_next_element(); bool focus_previous_element(); - EventResult fire_keyboard_event(FlyString const& event_name, HTML::Navigable&, UIEvents::KeyCode, unsigned modifiers, u32 code_point); + EventResult fire_keyboard_event(FlyString const& event_name, HTML::Navigable&, UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat); [[nodiscard]] EventResult input_event(FlyString const& event_name, FlyString const& input_type, HTML::Navigable&, u32 code_point); CSSPixelPoint compute_mouse_event_client_offset(CSSPixelPoint event_page_position) const; CSSPixelPoint compute_mouse_event_page_offset(CSSPixelPoint event_client_offset) const; diff --git a/Userland/Libraries/LibWeb/Page/InputEvent.cpp b/Userland/Libraries/LibWeb/Page/InputEvent.cpp index d32784d481f..4a9baf8d6ad 100644 --- a/Userland/Libraries/LibWeb/Page/InputEvent.cpp +++ b/Userland/Libraries/LibWeb/Page/InputEvent.cpp @@ -12,7 +12,7 @@ namespace Web { KeyEvent KeyEvent::clone_without_chrome_data() const { - return { type, key, modifiers, code_point, nullptr }; + return { type, key, modifiers, code_point, repeat, nullptr }; } MouseEvent MouseEvent::clone_without_chrome_data() const @@ -34,6 +34,7 @@ ErrorOr IPC::encode(Encoder& encoder, Web::KeyEvent const& event) TRY(encoder.encode(event.key)); TRY(encoder.encode(event.modifiers)); TRY(encoder.encode(event.code_point)); + TRY(encoder.encode(event.repeat)); return {}; } @@ -44,8 +45,9 @@ ErrorOr IPC::decode(Decoder& decoder) auto key = TRY(decoder.decode()); auto modifiers = TRY(decoder.decode()); auto code_point = TRY(decoder.decode()); + auto repeat = TRY(decoder.decode()); - return Web::KeyEvent { type, key, modifiers, code_point, nullptr }; + return Web::KeyEvent { type, key, modifiers, code_point, repeat, nullptr }; } template<> diff --git a/Userland/Libraries/LibWeb/Page/InputEvent.h b/Userland/Libraries/LibWeb/Page/InputEvent.h index 20952fbd557..085e0562c02 100644 --- a/Userland/Libraries/LibWeb/Page/InputEvent.h +++ b/Userland/Libraries/LibWeb/Page/InputEvent.h @@ -34,6 +34,7 @@ struct KeyEvent { UIEvents::KeyCode key { UIEvents::KeyCode::Key_Invalid }; UIEvents::KeyModifier modifiers { UIEvents::KeyModifier::Mod_None }; u32 code_point { 0 }; + bool repeat { false }; OwnPtr chrome_data; }; diff --git a/Userland/Libraries/LibWeb/Page/Page.cpp b/Userland/Libraries/LibWeb/Page/Page.cpp index a526160aec1..134d94837d6 100644 --- a/Userland/Libraries/LibWeb/Page/Page.cpp +++ b/Userland/Libraries/LibWeb/Page/Page.cpp @@ -216,14 +216,14 @@ EventResult Page::handle_drag_and_drop_event(DragEvent::Type type, DevicePixelPo return top_level_traversable()->event_handler().handle_drag_and_drop_event(type, device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers, move(files)); } -EventResult Page::handle_keydown(UIEvents::KeyCode key, unsigned modifiers, u32 code_point) +EventResult Page::handle_keydown(UIEvents::KeyCode key, unsigned modifiers, u32 code_point, bool repeat) { - return focused_navigable().event_handler().handle_keydown(key, modifiers, code_point); + return focused_navigable().event_handler().handle_keydown(key, modifiers, code_point, repeat); } -EventResult Page::handle_keyup(UIEvents::KeyCode key, unsigned modifiers, u32 code_point) +EventResult Page::handle_keyup(UIEvents::KeyCode key, unsigned modifiers, u32 code_point, bool repeat) { - return focused_navigable().event_handler().handle_keyup(key, modifiers, code_point); + return focused_navigable().event_handler().handle_keyup(key, modifiers, code_point, repeat); } void Page::set_top_level_traversable(JS::NonnullGCPtr navigable) diff --git a/Userland/Libraries/LibWeb/Page/Page.h b/Userland/Libraries/LibWeb/Page/Page.h index fd838f712db..a656b36988a 100644 --- a/Userland/Libraries/LibWeb/Page/Page.h +++ b/Userland/Libraries/LibWeb/Page/Page.h @@ -100,8 +100,8 @@ public: EventResult handle_drag_and_drop_event(DragEvent::Type, DevicePixelPoint, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, Vector files); - EventResult handle_keydown(UIEvents::KeyCode, unsigned modifiers, u32 code_point); - EventResult handle_keyup(UIEvents::KeyCode, unsigned modifiers, u32 code_point); + EventResult handle_keydown(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat); + EventResult handle_keyup(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat); Gfx::Palette palette() const; CSSPixelRect web_exposed_screen_area() const; diff --git a/Userland/Libraries/LibWeb/UIEvents/KeyboardEvent.cpp b/Userland/Libraries/LibWeb/UIEvents/KeyboardEvent.cpp index 8ace358c77a..84187bfcf7c 100644 --- a/Userland/Libraries/LibWeb/UIEvents/KeyboardEvent.cpp +++ b/Userland/Libraries/LibWeb/UIEvents/KeyboardEvent.cpp @@ -668,7 +668,7 @@ static DOMKeyLocation get_event_location(KeyCode platform_key, unsigned modifier return DOMKeyLocation::Standard; } -JS::NonnullGCPtr KeyboardEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, KeyCode platform_key, unsigned modifiers, u32 code_point) +JS::NonnullGCPtr KeyboardEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, KeyCode platform_key, unsigned modifiers, u32 code_point, bool repeat) { auto event_key = MUST(get_event_key(platform_key, code_point)); auto event_code = MUST(get_event_code(platform_key, modifiers)); @@ -683,7 +683,7 @@ JS::NonnullGCPtr KeyboardEvent::create_from_platform_event(JS::Re 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.repeat = repeat; event_init.is_composing = false; event_init.key_code = key_code; event_init.char_code = char_code; diff --git a/Userland/Libraries/LibWeb/UIEvents/KeyboardEvent.h b/Userland/Libraries/LibWeb/UIEvents/KeyboardEvent.h index a4771d09bf5..c90978b7113 100644 --- a/Userland/Libraries/LibWeb/UIEvents/KeyboardEvent.h +++ b/Userland/Libraries/LibWeb/UIEvents/KeyboardEvent.h @@ -38,7 +38,7 @@ class KeyboardEvent final : public UIEvent { public: [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&, FlyString const& event_name, KeyboardEventInit const& = {}); - [[nodiscard]] static JS::NonnullGCPtr create_from_platform_event(JS::Realm&, FlyString const& event_name, KeyCode, unsigned modifiers, u32 code_point); + [[nodiscard]] static JS::NonnullGCPtr create_from_platform_event(JS::Realm&, FlyString const& event_name, KeyCode, unsigned modifiers, u32 code_point, bool repeat); static WebIDL::ExceptionOr> construct_impl(JS::Realm&, FlyString const& event_name, KeyboardEventInit const&); virtual ~KeyboardEvent() override; diff --git a/Userland/Libraries/LibWeb/WebDriver/Actions.cpp b/Userland/Libraries/LibWeb/WebDriver/Actions.cpp index 0e72dcf7029..76477a26cbd 100644 --- a/Userland/Libraries/LibWeb/WebDriver/Actions.cpp +++ b/Userland/Libraries/LibWeb/WebDriver/Actions.cpp @@ -902,7 +902,7 @@ static ErrorOr dispatch_key_down_action(ActionObject::Ke auto key = normalized_key_value(raw_key); // 3. If the source's pressed property contains key, let repeat be true, otherwise let repeat be false. - // FIXME: Add `repeat` support to Page::handle_keydown. + bool repeat = source.pressed.contains(key); // 4. Let code be the code for raw key. auto code = key_code_data(raw_key); @@ -945,7 +945,7 @@ static ErrorOr dispatch_key_down_action(ActionObject::Ke // keyboard in accordance with the requirements of [UI-EVENTS], and producing the following events, as appropriate, // with the specified properties. This will always produce events including at least a keyDown event. auto event = key_code_to_page_event(raw_key, modifiers, code); - browsing_context.page().handle_keydown(code.code, event.modifiers, event.code_point); + browsing_context.page().handle_keydown(code.code, event.modifiers, event.code_point, repeat); // 13. Return success with data null. return {}; @@ -1005,7 +1005,7 @@ static ErrorOr dispatch_key_up_action(ActionObject::KeyF // keyboard in accordance with the requirements of [UI-EVENTS], and producing at least the following events with // the specified properties: auto event = key_code_to_page_event(raw_key, modifiers, code); - browsing_context.page().handle_keyup(code.code, event.modifiers, event.code_point); + browsing_context.page().handle_keyup(code.code, event.modifiers, event.code_point, false); // 13. Return success with data null. return {}; diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index e8d0b51b9f3..5f313b0a31e 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -189,9 +189,9 @@ void ConnectionFromClient::process_next_input_event() [&](Web::KeyEvent const& event) { switch (event.type) { case Web::KeyEvent::Type::KeyDown: - return page->page().handle_keydown(event.key, event.modifiers, event.code_point); + return page->page().handle_keydown(event.key, event.modifiers, event.code_point, event.repeat); case Web::KeyEvent::Type::KeyUp: - return page->page().handle_keyup(event.key, event.modifiers, event.code_point); + return page->page().handle_keyup(event.key, event.modifiers, event.code_point, event.repeat); } VERIFY_NOT_REACHED(); },