diff --git a/Libraries/LibWeb/Internals/Internals.cpp b/Libraries/LibWeb/Internals/Internals.cpp index f53e88f9706..a890ba5a5f0 100644 --- a/Libraries/LibWeb/Internals/Internals.cpp +++ b/Libraries/LibWeb/Internals/Internals.cpp @@ -155,6 +155,14 @@ void Internals::send_key(HTML::HTMLElement& target, String const& key_name, WebI page().handle_keydown(key_code, modifiers, 0, false); } +void Internals::paste(HTML::HTMLElement& target, String const& text) +{ + auto& page = this->page(); + target.focus(); + + page.focused_navigable().paste(text); +} + void Internals::commit_text() { page().handle_keydown(UIEvents::Key_Return, 0, 0x0d, false); diff --git a/Libraries/LibWeb/Internals/Internals.h b/Libraries/LibWeb/Internals/Internals.h index 48d9f178f76..806a69f5105 100644 --- a/Libraries/LibWeb/Internals/Internals.h +++ b/Libraries/LibWeb/Internals/Internals.h @@ -31,6 +31,7 @@ public: void send_text(HTML::HTMLElement&, String const&, WebIDL::UnsignedShort modifiers); void send_key(HTML::HTMLElement&, String const&, WebIDL::UnsignedShort modifiers); + void paste(HTML::HTMLElement& target, String const& text); void commit_text(); void click(double x, double y); diff --git a/Libraries/LibWeb/Internals/Internals.idl b/Libraries/LibWeb/Internals/Internals.idl index 5348f4aebcf..2efb9cc9dfb 100644 --- a/Libraries/LibWeb/Internals/Internals.idl +++ b/Libraries/LibWeb/Internals/Internals.idl @@ -23,6 +23,7 @@ interface Internals { undefined sendText(HTMLElement target, DOMString text, optional unsigned short modifiers = 0); undefined sendKey(HTMLElement target, DOMString keyName, optional unsigned short modifiers = 0); + undefined paste(HTMLElement target, DOMString text); undefined commitText(); undefined click(double x, double y); diff --git a/Libraries/LibWeb/Page/EventHandler.cpp b/Libraries/LibWeb/Page/EventHandler.cpp index 7af8af46284..69b6f74196a 100644 --- a/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Libraries/LibWeb/Page/EventHandler.cpp @@ -1129,7 +1129,7 @@ static GC::RootVector> target_ranges_for_input_event(D return target_ranges; } -EventResult EventHandler::input_event(FlyString const& event_name, FlyString const& input_type, HTML::Navigable& navigable, u32 code_point) +EventResult EventHandler::input_event(FlyString const& event_name, FlyString const& input_type, HTML::Navigable& navigable, Variant code_point_or_string) { auto document = navigable.active_document(); if (!document) @@ -1138,16 +1138,23 @@ EventResult EventHandler::input_event(FlyString const& event_name, FlyString con return EventResult::Dropped; UIEvents::InputEventInit input_event_init; - if (!is_unicode_control(code_point)) { - input_event_init.data = String::from_code_point(code_point); + + if (code_point_or_string.has()) { + auto code_point = code_point_or_string.get(); + if (!is_unicode_control(code_point)) { + input_event_init.data = String::from_code_point(code_point); + } + } else { + input_event_init.data = code_point_or_string.get(); } + input_event_init.input_type = input_type; if (auto* focused_element = document->focused_element()) { if (is(*focused_element)) { auto& navigable_container = as(*focused_element); if (navigable_container.content_navigable()) - return input_event(event_name, input_type, *navigable_container.content_navigable(), code_point); + return input_event(event_name, input_type, *navigable_container.content_navigable(), code_point_or_string); } auto event = UIEvents::InputEvent::create_from_platform_event(document->realm(), event_name, input_event_init, target_ranges_for_input_event(*document)); @@ -1366,18 +1373,21 @@ EventResult EventHandler::handle_keyup(UIEvents::KeyCode key, u32 modifiers, u32 return fire_keyboard_event(UIEvents::EventNames::keyup, m_navigable, key, modifiers, code_point, false); } -void EventHandler::handle_paste(String const& text) +EventResult EventHandler::handle_paste(String const& text) { auto active_document = m_navigable->active_document(); if (!active_document) - return; + return EventResult::Dropped; if (!active_document->is_fully_active()) - return; + return EventResult::Dropped; auto* target = active_document->active_input_events_target(); if (!target) - return; + return EventResult::Dropped; + + FIRE(input_event(UIEvents::EventNames::beforeinput, UIEvents::InputTypes::insertFromPaste, m_navigable, text)); target->handle_insert(text); + return EventResult::Handled; } void EventHandler::set_mouse_event_tracking_paintable(Painting::Paintable* paintable) diff --git a/Libraries/LibWeb/Page/EventHandler.h b/Libraries/LibWeb/Page/EventHandler.h index 283207c7a0f..f6b959ffeaa 100644 --- a/Libraries/LibWeb/Page/EventHandler.h +++ b/Libraries/LibWeb/Page/EventHandler.h @@ -39,7 +39,7 @@ public: void set_mouse_event_tracking_paintable(Painting::Paintable*); - void handle_paste(String const& text); + EventResult handle_paste(String const& text); void visit_edges(JS::Cell::Visitor& visitor) const; @@ -50,7 +50,7 @@ private: EventResult focus_previous_element(); 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); + [[nodiscard]] EventResult input_event(FlyString const& event_name, FlyString const& input_type, HTML::Navigable&, Variant code_point_or_string); 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/Libraries/LibWeb/UIEvents/InputTypes.h b/Libraries/LibWeb/UIEvents/InputTypes.h index 7d62b6c5214..f532efa3e64 100644 --- a/Libraries/LibWeb/UIEvents/InputTypes.h +++ b/Libraries/LibWeb/UIEvents/InputTypes.h @@ -14,6 +14,7 @@ namespace Web::UIEvents::InputTypes { #define ENUMERATE_INPUT_TYPES \ __ENUMERATE_INPUT_TYPE(deleteContentBackward) \ __ENUMERATE_INPUT_TYPE(deleteContentForward) \ + __ENUMERATE_INPUT_TYPE(insertFromPaste) \ __ENUMERATE_INPUT_TYPE(insertLineBreak) \ __ENUMERATE_INPUT_TYPE(insertParagraph) \ __ENUMERATE_INPUT_TYPE(insertText) diff --git a/Tests/LibWeb/Text/expected/Editing/beforeinput-event.txt b/Tests/LibWeb/Text/expected/Editing/beforeinput-event.txt index 0c70195b70f..89f9157ce15 100644 --- a/Tests/LibWeb/Text/expected/Editing/beforeinput-event.txt +++ b/Tests/LibWeb/Text/expected/Editing/beforeinput-event.txt @@ -6,4 +6,10 @@ beforeinput data=(e) inputType=(insertText) beforeinput data=(c) inputType=(insertText) beforeinput data=(e) inputType=(insertText) beforeinput data=(null) inputType=(insertParagraph) -Text in input: reee +Text in input before any paste: reee +beforeinput data=(non-cancelled paste) inputType=(insertFromPaste) +beforeinput data=(null) inputType=(insertParagraph) +Text in input after non-cancelled paste: reeenon-cancelled paste +beforeinput data=(cancelled paste) inputType=(insertFromPaste) +beforeinput data=(null) inputType=(insertParagraph) +Text in input after cancelled paste: reeenon-cancelled paste diff --git a/Tests/LibWeb/Text/input/Editing/beforeinput-event.html b/Tests/LibWeb/Text/input/Editing/beforeinput-event.html index ab473a81f57..10cbdc9e32e 100644 --- a/Tests/LibWeb/Text/input/Editing/beforeinput-event.html +++ b/Tests/LibWeb/Text/input/Editing/beforeinput-event.html @@ -13,13 +13,19 @@ const input = document.getElementById("input"); input.addEventListener("beforeinput", (e) => { println(`beforeinput data=(${e.data}) inputType=(${e.inputType})`); - if (e.data !== 'r' && e.data !== 'e') { + if (e.data !== 'r' && e.data !== 'e' && e.data !== "non-cancelled paste") { e.preventDefault(); return; } }); internals.sendText(input, "raebece"); internals.commitText(); - println(`Text in input: ${input.textContent}`); + println(`Text in input before any paste: ${input.textContent}`); + internals.paste(input, "non-cancelled paste"); + internals.commitText(); + println(`Text in input after non-cancelled paste: ${input.textContent}`); + internals.paste(input, "cancelled paste"); + internals.commitText(); + println(`Text in input after cancelled paste: ${input.textContent}`); });