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();
+
+}