diff --git a/Tests/LibWeb/Text/expected/textarea-value.txt b/Tests/LibWeb/Text/expected/textarea-value.txt index 0d549da05b..aa11dd9807 100644 --- a/Tests/LibWeb/Text/expected/textarea-value.txt +++ b/Tests/LibWeb/Text/expected/textarea-value.txt @@ -2,3 +2,4 @@ 2. "PASS" 3. "PASS" 4. 4 +5. "PASS" diff --git a/Tests/LibWeb/Text/input/textarea-value.html b/Tests/LibWeb/Text/input/textarea-value.html index c5977c9509..2189534bac 100644 --- a/Tests/LibWeb/Text/input/textarea-value.html +++ b/Tests/LibWeb/Text/input/textarea-value.html @@ -35,5 +35,19 @@ textarea.innerHTML = 'PASS'; return textarea.textLength; }); + + // 5. Textarea input via keyboard events. + testPart(() => { + const textarea = document.createElement('textarea'); + + // The element currently has to be part of a document to be able to receive keyboard events. + document.body.appendChild(textarea); + + internals.sendText(textarea, 'PASS'); + const value = textarea.value; + + document.body.removeChild(textarea); + return value; + }); }); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index 74eff6e4fa..3de54081b8 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -24,6 +24,7 @@ JS_DEFINE_ALLOCATOR(HTMLTextAreaElement); HTMLTextAreaElement::HTMLTextAreaElement(DOM::Document& document, DOM::QualifiedName qualified_name) : HTMLElement(document, move(qualified_name)) + , m_input_event_timer(Web::Platform::Timer::create_single_shot(0, [this]() { queue_firing_input_event(); })) { } @@ -333,10 +334,28 @@ void HTMLTextAreaElement::form_associated_element_attribute_changed(FlyString co void HTMLTextAreaElement::did_edit_text_node(Badge) { + VERIFY(m_text_node); + m_raw_value = m_text_node->data(); + + // Any time the user causes the element's raw value to change, the user agent must queue an element task on the user + // interaction task source given the textarea element to fire an event named input at the textarea element, with the + // bubbles and composed attributes initialized to true. User agents may wait for a suitable break in the user's + // interaction before queuing the task; for example, a user agent could wait for the user to have not hit a key for + // 100ms, so as to only fire the event when the user pauses, instead of continuously for each keystroke. + m_input_event_timer->restart(100); + // A textarea element's dirty value flag must be set to true whenever the user interacts with the control in a way that changes the raw value. m_dirty_value = true; update_placeholder_visibility(); } +void HTMLTextAreaElement::queue_firing_input_event() +{ + queue_an_element_task(HTML::Task::Source::UserInteraction, [this]() { + auto change_event = DOM::Event::create(realm(), HTML::EventNames::input, { .bubbles = true, .composed = true }); + dispatch_event(change_event); + }); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h index 3e296b8820..c13475168c 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace Web::HTML { @@ -109,6 +110,8 @@ private: void handle_readonly_attribute(Optional const& value); void handle_maxlength_attribute(); + void queue_firing_input_event(); + void update_placeholder_visibility(); JS::GCPtr m_placeholder_element; JS::GCPtr m_placeholder_text_node; @@ -116,6 +119,8 @@ private: JS::GCPtr m_inner_text_element; JS::GCPtr m_text_node; + RefPtr m_input_event_timer; + // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-dirty bool m_dirty_value { false };