From 0de61b0f65ee472d3cf4853b108eb1f9501e6275 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sat, 24 Feb 2024 06:08:26 +0100 Subject: [PATCH] LibWeb: Implement dispatching of "input" event --- .../Text/expected/Editing/input-event.txt | 6 +++ .../Text/input/Editing/input-event.html | 19 ++++++++ .../LibWeb/Bindings/MainThreadVM.cpp | 2 + Userland/Libraries/LibWeb/CMakeLists.txt | 1 + .../Libraries/LibWeb/Page/EventHandler.cpp | 44 +++++++++++++++++++ Userland/Libraries/LibWeb/Page/EventHandler.h | 1 + .../Libraries/LibWeb/UIEvents/EventNames.h | 1 + .../Libraries/LibWeb/UIEvents/InputEvent.cpp | 5 +++ .../Libraries/LibWeb/UIEvents/InputEvent.h | 7 +-- .../Libraries/LibWeb/UIEvents/InputTypes.cpp | 28 ++++++++++++ .../Libraries/LibWeb/UIEvents/InputTypes.h | 28 ++++++++++++ 11 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/Editing/input-event.txt create mode 100644 Tests/LibWeb/Text/input/Editing/input-event.html create mode 100644 Userland/Libraries/LibWeb/UIEvents/InputTypes.cpp create mode 100644 Userland/Libraries/LibWeb/UIEvents/InputTypes.h diff --git a/Tests/LibWeb/Text/expected/Editing/input-event.txt b/Tests/LibWeb/Text/expected/Editing/input-event.txt new file mode 100644 index 00000000000..5149a6228d9 --- /dev/null +++ b/Tests/LibWeb/Text/expected/Editing/input-event.txt @@ -0,0 +1,6 @@ +input data=(h) intputType=(insertText) +input data=(e) intputType=(insertText) +input data=(l) intputType=(insertText) +input data=(l) intputType=(insertText) +input data=(o) intputType=(insertText) +input data=(null) intputType=(insertParagraph) diff --git a/Tests/LibWeb/Text/input/Editing/input-event.html b/Tests/LibWeb/Text/input/Editing/input-event.html new file mode 100644 index 00000000000..864130debc0 --- /dev/null +++ b/Tests/LibWeb/Text/input/Editing/input-event.html @@ -0,0 +1,19 @@ + + +
+ diff --git a/Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp b/Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp index 00010d17f34..d63181d20bd 100644 --- a/Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp +++ b/Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +106,7 @@ ErrorOr initialize_main_thread_vm(HTML::EventLoop::Type type) SVG::AttributeNames::initialize_strings(); SVG::TagNames::initialize_strings(); UIEvents::EventNames::initialize_strings(); + UIEvents::InputTypes::initialize_strings(); WebGL::EventNames::initialize_strings(); XHR::EventNames::initialize_strings(); XLink::AttributeNames::initialize_strings(); diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 91492f7696a..894238c0fad 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -708,6 +708,7 @@ set(SOURCES UIEvents/EventNames.cpp UIEvents/FocusEvent.cpp UIEvents/InputEvent.cpp + UIEvents/InputTypes.cpp UIEvents/KeyboardEvent.cpp UIEvents/MouseEvent.cpp UIEvents/PointerEvent.cpp diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp index 6e53ad918fa..a979633e1cd 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,6 +38,10 @@ namespace Web { +#define FIRE(event_result) \ + if (event_result == EventResult::Cancelled) \ + return event_result; + static JS::GCPtr dom_node_for_event_dispatch(Painting::Paintable& paintable) { if (auto node = paintable.mouse_event_target()) @@ -872,6 +878,39 @@ static bool produces_character_value(u32 code_point) || Unicode::code_point_has_symbol_general_category(code_point); } +EventResult EventHandler::input_event(FlyString const& event_name, FlyString const& input_type, HTML::Navigable& navigable, u32 code_point) +{ + auto document = navigable.active_document(); + if (!document) + return EventResult::Dropped; + if (!document->is_fully_active()) + return EventResult::Dropped; + + UIEvents::InputEventInit input_event_init; + if (!is_unicode_control(code_point)) { + input_event_init.data = String::from_code_point(code_point); + } + input_event_init.input_type = input_type; + + if (auto* focused_element = document->focused_element()) { + if (is(*focused_element)) { + auto& navigable_container = verify_cast(*focused_element); + if (navigable_container.content_navigable()) + return input_event(event_name, input_type, *navigable_container.content_navigable(), code_point); + } + + auto event = UIEvents::InputEvent::create_from_platform_event(document->realm(), event_name, input_event_init); + return focused_element->dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled; + } + + auto event = UIEvents::InputEvent::create_from_platform_event(document->realm(), event_name, input_event_init); + + if (auto* body = document->body()) + return body->dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled; + + return document->root().dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled; +} + EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code_point) { if (!m_navigable->active_document()) @@ -964,6 +1003,7 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u } m_edit_event_handler->handle_delete_character_after(document, *document->cursor_position()); + FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::deleteContentBackward, m_navigable, code_point)); return EventResult::Handled; } @@ -974,6 +1014,7 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u } m_edit_event_handler->handle_delete_character_after(document, *document->cursor_position()); + FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::deleteContentForward, m_navigable, code_point)); return EventResult::Handled; } @@ -1065,14 +1106,17 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u } input_element->commit_pending_changes(); + FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::insertParagraph, m_navigable, code_point)); return EventResult::Handled; } + FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::insertParagraph, m_navigable, code_point)); } // FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here. if (!should_ignore_keydown_event(code_point, modifiers) && node.is_editable()) { m_edit_event_handler->handle_insert(document, JS::NonnullGCPtr { *document->cursor_position() }, code_point); document->increment_cursor_position_offset(); + FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::insertText, m_navigable, code_point)); return EventResult::Handled; } } diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.h b/Userland/Libraries/LibWeb/Page/EventHandler.h index d0e1b2bcdac..383fad584b8 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.h +++ b/Userland/Libraries/LibWeb/Page/EventHandler.h @@ -50,6 +50,7 @@ private: bool focus_previous_element(); EventResult fire_keyboard_event(FlyString const& event_name, HTML::Navigable&, UIEvents::KeyCode, unsigned modifiers, u32 code_point); + [[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; CSSPixelPoint compute_mouse_event_movement(CSSPixelPoint event_client_offset) const; diff --git a/Userland/Libraries/LibWeb/UIEvents/EventNames.h b/Userland/Libraries/LibWeb/UIEvents/EventNames.h index 029baab1807..09b8d884597 100644 --- a/Userland/Libraries/LibWeb/UIEvents/EventNames.h +++ b/Userland/Libraries/LibWeb/UIEvents/EventNames.h @@ -19,6 +19,7 @@ namespace Web::UIEvents::EventNames { __ENUMERATE_UI_EVENT(click) \ __ENUMERATE_UI_EVENT(contextmenu) \ __ENUMERATE_UI_EVENT(dblclick) \ + __ENUMERATE_UI_EVENT(input) \ __ENUMERATE_UI_EVENT(keydown) \ __ENUMERATE_UI_EVENT(keypress) \ __ENUMERATE_UI_EVENT(keyup) \ diff --git a/Userland/Libraries/LibWeb/UIEvents/InputEvent.cpp b/Userland/Libraries/LibWeb/UIEvents/InputEvent.cpp index 77e79eca52a..b131693720b 100644 --- a/Userland/Libraries/LibWeb/UIEvents/InputEvent.cpp +++ b/Userland/Libraries/LibWeb/UIEvents/InputEvent.cpp @@ -12,6 +12,11 @@ namespace Web::UIEvents { JS_DEFINE_ALLOCATOR(InputEvent); +JS::NonnullGCPtr InputEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, InputEventInit const& event_init) +{ + return realm.heap().allocate(realm, realm, event_name, event_init); +} + WebIDL::ExceptionOr> InputEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, InputEventInit const& event_init) { return realm.heap().allocate(realm, realm, event_name, event_init); diff --git a/Userland/Libraries/LibWeb/UIEvents/InputEvent.h b/Userland/Libraries/LibWeb/UIEvents/InputEvent.h index 7fa2c0d01cf..3dd08518edf 100644 --- a/Userland/Libraries/LibWeb/UIEvents/InputEvent.h +++ b/Userland/Libraries/LibWeb/UIEvents/InputEvent.h @@ -13,7 +13,7 @@ namespace Web::UIEvents { struct InputEventInit : public UIEventInit { Optional data; bool is_composing { false }; - String input_type {}; + FlyString input_type {}; }; class InputEvent final : public UIEvent { @@ -21,6 +21,7 @@ class InputEvent final : public UIEvent { JS_DECLARE_ALLOCATOR(InputEvent); public: + [[nodiscard]] static JS::NonnullGCPtr create_from_platform_event(JS::Realm&, FlyString const& type, InputEventInit const& event_init); static WebIDL::ExceptionOr> construct_impl(JS::Realm&, FlyString const& event_name, InputEventInit const& event_init); virtual ~InputEvent() override; @@ -32,7 +33,7 @@ public: bool is_composing() const { return m_is_composing; } // https://w3c.github.io/uievents/#dom-inputevent-inputtype - String input_type() const { return m_input_type; } + FlyString input_type() const { return m_input_type; } private: InputEvent(JS::Realm&, FlyString const& event_name, InputEventInit const&); @@ -41,7 +42,7 @@ private: Optional m_data; bool m_is_composing; - String m_input_type; + FlyString m_input_type; }; } diff --git a/Userland/Libraries/LibWeb/UIEvents/InputTypes.cpp b/Userland/Libraries/LibWeb/UIEvents/InputTypes.cpp new file mode 100644 index 00000000000..a97e69d71c4 --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/InputTypes.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Web::UIEvents::InputTypes { + +#define __ENUMERATE_INPUT_TYPE(name) FlyString name; +ENUMERATE_INPUT_TYPES +#undef __ENUMERATE_INPUT_TYPE + +void initialize_strings() +{ + static bool s_initialized = false; + VERIFY(!s_initialized); + +#define __ENUMERATE_INPUT_TYPE(name) \ + name = #name##_fly_string; + ENUMERATE_INPUT_TYPES +#undef __ENUMERATE_INPUT_TYPE + + s_initialized = true; +} + +} diff --git a/Userland/Libraries/LibWeb/UIEvents/InputTypes.h b/Userland/Libraries/LibWeb/UIEvents/InputTypes.h new file mode 100644 index 00000000000..8ee68a01d38 --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/InputTypes.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::UIEvents::InputTypes { + +// https://w3c.github.io/input-events/#interface-InputEvent-Attributes + +#define ENUMERATE_INPUT_TYPES \ + __ENUMERATE_INPUT_TYPE(insertText) \ + __ENUMERATE_INPUT_TYPE(insertParagraph) \ + __ENUMERATE_INPUT_TYPE(deleteContentBackward) \ + __ENUMERATE_INPUT_TYPE(deleteContentForward) + +#define __ENUMERATE_INPUT_TYPE(name) extern FlyString name; +ENUMERATE_INPUT_TYPES +#undef __ENUMERATE_INPUT_TYPE + +void initialize_strings(); + +}