Explorar el Código

LibWeb: Implement dispatching of "input" event

Aliaksandr Kalenik hace 1 año
padre
commit
0de61b0f65

+ 6 - 0
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)

+ 19 - 0
Tests/LibWeb/Text/input/Editing/input-event.html

@@ -0,0 +1,19 @@
+<script src="../include.js"></script>
+<style>
+    #input {
+        width: 100px;
+        height: 100px;
+        border: 1px solid black;
+    }
+</style>
+<div id="input" contenteditable="true"></div>
+<script>
+    test(() => {
+        const input = document.getElementById("input");
+        input.addEventListener("input", (e) => {
+            println(`input data=(${e.data}) intputType=(${e.inputType})`);
+        });
+        internals.sendText(input, "hello");
+        internals.commitText();
+    });
+</script>

+ 2 - 0
Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp

@@ -47,6 +47,7 @@
 #include <LibWeb/SVG/AttributeNames.h>
 #include <LibWeb/SVG/TagNames.h>
 #include <LibWeb/UIEvents/EventNames.h>
+#include <LibWeb/UIEvents/InputTypes.h>
 #include <LibWeb/WebGL/EventNames.h>
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/XHR/EventNames.h>
@@ -105,6 +106,7 @@ ErrorOr<void> 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();

+ 1 - 0
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

+ 44 - 0
Userland/Libraries/LibWeb/Page/EventHandler.cpp

@@ -29,6 +29,8 @@
 #include <LibWeb/Painting/PaintableBox.h>
 #include <LibWeb/Painting/TextPaintable.h>
 #include <LibWeb/UIEvents/EventNames.h>
+#include <LibWeb/UIEvents/InputEvent.h>
+#include <LibWeb/UIEvents/InputTypes.h>
 #include <LibWeb/UIEvents/KeyboardEvent.h>
 #include <LibWeb/UIEvents/MouseButton.h>
 #include <LibWeb/UIEvents/MouseEvent.h>
@@ -36,6 +38,10 @@
 
 namespace Web {
 
+#define FIRE(event_result)                      \
+    if (event_result == EventResult::Cancelled) \
+        return event_result;
+
 static JS::GCPtr<DOM::Node> 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<HTML::NavigableContainer>(*focused_element)) {
+            auto& navigable_container = verify_cast<HTML::NavigableContainer>(*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;
         }
     }

+ 1 - 0
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;

+ 1 - 0
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)       \

+ 5 - 0
Userland/Libraries/LibWeb/UIEvents/InputEvent.cpp

@@ -12,6 +12,11 @@ namespace Web::UIEvents {
 
 JS_DEFINE_ALLOCATOR(InputEvent);
 
+JS::NonnullGCPtr<InputEvent> InputEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, InputEventInit const& event_init)
+{
+    return realm.heap().allocate<InputEvent>(realm, realm, event_name, event_init);
+}
+
 WebIDL::ExceptionOr<JS::NonnullGCPtr<InputEvent>> InputEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, InputEventInit const& event_init)
 {
     return realm.heap().allocate<InputEvent>(realm, realm, event_name, event_init);

+ 4 - 3
Userland/Libraries/LibWeb/UIEvents/InputEvent.h

@@ -13,7 +13,7 @@ namespace Web::UIEvents {
 struct InputEventInit : public UIEventInit {
     Optional<String> 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<InputEvent> create_from_platform_event(JS::Realm&, FlyString const& type, InputEventInit const& event_init);
     static WebIDL::ExceptionOr<JS::NonnullGCPtr<InputEvent>> 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<String> m_data;
     bool m_is_composing;
-    String m_input_type;
+    FlyString m_input_type;
 };
 
 }

+ 28 - 0
Userland/Libraries/LibWeb/UIEvents/InputTypes.cpp

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/UIEvents/InputTypes.h>
+
+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;
+}
+
+}

+ 28 - 0
Userland/Libraries/LibWeb/UIEvents/InputTypes.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Error.h>
+#include <AK/FlyString.h>
+
+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();
+
+}