mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
LibWeb: Implement dispatching of "input" event
This commit is contained in:
parent
d88e6bee5d
commit
0de61b0f65
Notes:
github-actions[bot]
2024-10-22 12:46:02 +00:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/0de61b0f65e Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1903 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/trflynn89 ✅
11 changed files with 139 additions and 3 deletions
6
Tests/LibWeb/Text/expected/Editing/input-event.txt
Normal file
6
Tests/LibWeb/Text/expected/Editing/input-event.txt
Normal file
|
@ -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
Tests/LibWeb/Text/input/Editing/input-event.html
Normal file
19
Tests/LibWeb/Text/input/Editing/input-event.html
Normal file
|
@ -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>
|
|
@ -47,6 +47,7 @@
|
||||||
#include <LibWeb/SVG/AttributeNames.h>
|
#include <LibWeb/SVG/AttributeNames.h>
|
||||||
#include <LibWeb/SVG/TagNames.h>
|
#include <LibWeb/SVG/TagNames.h>
|
||||||
#include <LibWeb/UIEvents/EventNames.h>
|
#include <LibWeb/UIEvents/EventNames.h>
|
||||||
|
#include <LibWeb/UIEvents/InputTypes.h>
|
||||||
#include <LibWeb/WebGL/EventNames.h>
|
#include <LibWeb/WebGL/EventNames.h>
|
||||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||||
#include <LibWeb/XHR/EventNames.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::AttributeNames::initialize_strings();
|
||||||
SVG::TagNames::initialize_strings();
|
SVG::TagNames::initialize_strings();
|
||||||
UIEvents::EventNames::initialize_strings();
|
UIEvents::EventNames::initialize_strings();
|
||||||
|
UIEvents::InputTypes::initialize_strings();
|
||||||
WebGL::EventNames::initialize_strings();
|
WebGL::EventNames::initialize_strings();
|
||||||
XHR::EventNames::initialize_strings();
|
XHR::EventNames::initialize_strings();
|
||||||
XLink::AttributeNames::initialize_strings();
|
XLink::AttributeNames::initialize_strings();
|
||||||
|
|
|
@ -708,6 +708,7 @@ set(SOURCES
|
||||||
UIEvents/EventNames.cpp
|
UIEvents/EventNames.cpp
|
||||||
UIEvents/FocusEvent.cpp
|
UIEvents/FocusEvent.cpp
|
||||||
UIEvents/InputEvent.cpp
|
UIEvents/InputEvent.cpp
|
||||||
|
UIEvents/InputTypes.cpp
|
||||||
UIEvents/KeyboardEvent.cpp
|
UIEvents/KeyboardEvent.cpp
|
||||||
UIEvents/MouseEvent.cpp
|
UIEvents/MouseEvent.cpp
|
||||||
UIEvents/PointerEvent.cpp
|
UIEvents/PointerEvent.cpp
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include <LibWeb/Painting/PaintableBox.h>
|
#include <LibWeb/Painting/PaintableBox.h>
|
||||||
#include <LibWeb/Painting/TextPaintable.h>
|
#include <LibWeb/Painting/TextPaintable.h>
|
||||||
#include <LibWeb/UIEvents/EventNames.h>
|
#include <LibWeb/UIEvents/EventNames.h>
|
||||||
|
#include <LibWeb/UIEvents/InputEvent.h>
|
||||||
|
#include <LibWeb/UIEvents/InputTypes.h>
|
||||||
#include <LibWeb/UIEvents/KeyboardEvent.h>
|
#include <LibWeb/UIEvents/KeyboardEvent.h>
|
||||||
#include <LibWeb/UIEvents/MouseButton.h>
|
#include <LibWeb/UIEvents/MouseButton.h>
|
||||||
#include <LibWeb/UIEvents/MouseEvent.h>
|
#include <LibWeb/UIEvents/MouseEvent.h>
|
||||||
|
@ -36,6 +38,10 @@
|
||||||
|
|
||||||
namespace Web {
|
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)
|
static JS::GCPtr<DOM::Node> dom_node_for_event_dispatch(Painting::Paintable& paintable)
|
||||||
{
|
{
|
||||||
if (auto node = paintable.mouse_event_target())
|
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);
|
|| 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)
|
EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code_point)
|
||||||
{
|
{
|
||||||
if (!m_navigable->active_document())
|
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());
|
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;
|
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());
|
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;
|
return EventResult::Handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1065,14 +1106,17 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u
|
||||||
}
|
}
|
||||||
|
|
||||||
input_element->commit_pending_changes();
|
input_element->commit_pending_changes();
|
||||||
|
FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::insertParagraph, m_navigable, code_point));
|
||||||
return EventResult::Handled;
|
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.
|
// FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
|
||||||
if (!should_ignore_keydown_event(code_point, modifiers) && node.is_editable()) {
|
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);
|
m_edit_event_handler->handle_insert(document, JS::NonnullGCPtr { *document->cursor_position() }, code_point);
|
||||||
document->increment_cursor_position_offset();
|
document->increment_cursor_position_offset();
|
||||||
|
FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::insertText, m_navigable, code_point));
|
||||||
return EventResult::Handled;
|
return EventResult::Handled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ private:
|
||||||
bool focus_previous_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);
|
||||||
|
[[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_client_offset(CSSPixelPoint event_page_position) const;
|
||||||
CSSPixelPoint compute_mouse_event_page_offset(CSSPixelPoint event_client_offset) const;
|
CSSPixelPoint compute_mouse_event_page_offset(CSSPixelPoint event_client_offset) const;
|
||||||
CSSPixelPoint compute_mouse_event_movement(CSSPixelPoint event_client_offset) const;
|
CSSPixelPoint compute_mouse_event_movement(CSSPixelPoint event_client_offset) const;
|
||||||
|
|
|
@ -19,6 +19,7 @@ namespace Web::UIEvents::EventNames {
|
||||||
__ENUMERATE_UI_EVENT(click) \
|
__ENUMERATE_UI_EVENT(click) \
|
||||||
__ENUMERATE_UI_EVENT(contextmenu) \
|
__ENUMERATE_UI_EVENT(contextmenu) \
|
||||||
__ENUMERATE_UI_EVENT(dblclick) \
|
__ENUMERATE_UI_EVENT(dblclick) \
|
||||||
|
__ENUMERATE_UI_EVENT(input) \
|
||||||
__ENUMERATE_UI_EVENT(keydown) \
|
__ENUMERATE_UI_EVENT(keydown) \
|
||||||
__ENUMERATE_UI_EVENT(keypress) \
|
__ENUMERATE_UI_EVENT(keypress) \
|
||||||
__ENUMERATE_UI_EVENT(keyup) \
|
__ENUMERATE_UI_EVENT(keyup) \
|
||||||
|
|
|
@ -12,6 +12,11 @@ namespace Web::UIEvents {
|
||||||
|
|
||||||
JS_DEFINE_ALLOCATOR(InputEvent);
|
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)
|
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);
|
return realm.heap().allocate<InputEvent>(realm, realm, event_name, event_init);
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Web::UIEvents {
|
||||||
struct InputEventInit : public UIEventInit {
|
struct InputEventInit : public UIEventInit {
|
||||||
Optional<String> data;
|
Optional<String> data;
|
||||||
bool is_composing { false };
|
bool is_composing { false };
|
||||||
String input_type {};
|
FlyString input_type {};
|
||||||
};
|
};
|
||||||
|
|
||||||
class InputEvent final : public UIEvent {
|
class InputEvent final : public UIEvent {
|
||||||
|
@ -21,6 +21,7 @@ class InputEvent final : public UIEvent {
|
||||||
JS_DECLARE_ALLOCATOR(InputEvent);
|
JS_DECLARE_ALLOCATOR(InputEvent);
|
||||||
|
|
||||||
public:
|
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);
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<InputEvent>> construct_impl(JS::Realm&, FlyString const& event_name, InputEventInit const& event_init);
|
||||||
|
|
||||||
virtual ~InputEvent() override;
|
virtual ~InputEvent() override;
|
||||||
|
@ -32,7 +33,7 @@ public:
|
||||||
bool is_composing() const { return m_is_composing; }
|
bool is_composing() const { return m_is_composing; }
|
||||||
|
|
||||||
// https://w3c.github.io/uievents/#dom-inputevent-inputtype
|
// 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:
|
private:
|
||||||
InputEvent(JS::Realm&, FlyString const& event_name, InputEventInit const&);
|
InputEvent(JS::Realm&, FlyString const& event_name, InputEventInit const&);
|
||||||
|
@ -41,7 +42,7 @@ private:
|
||||||
|
|
||||||
Optional<String> m_data;
|
Optional<String> m_data;
|
||||||
bool m_is_composing;
|
bool m_is_composing;
|
||||||
String m_input_type;
|
FlyString m_input_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
28
Userland/Libraries/LibWeb/UIEvents/InputTypes.cpp
Normal file
28
Userland/Libraries/LibWeb/UIEvents/InputTypes.cpp
Normal file
|
@ -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
Userland/Libraries/LibWeb/UIEvents/InputTypes.h
Normal file
28
Userland/Libraries/LibWeb/UIEvents/InputTypes.h
Normal file
|
@ -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();
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue