From 08ee48606d8ec40bd6e48165f990ebc9b495d7a9 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sun, 3 Dec 2023 08:24:16 -0500 Subject: [PATCH] LibWeb: Fire a change event on input elements in the focus update steps This ensures the change event is received before the blur event. --- .../Text/expected/input-commit-on-unfocus.txt | 2 + .../Text/input/input-commit-on-unfocus.html | 18 ++++++++ Userland/Libraries/LibWeb/HTML/Focus.cpp | 23 +++++++--- .../LibWeb/HTML/HTMLInputElement.cpp | 43 +++++++++++++++++++ .../Libraries/LibWeb/HTML/HTMLInputElement.h | 3 ++ 5 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/input-commit-on-unfocus.txt create mode 100644 Tests/LibWeb/Text/input/input-commit-on-unfocus.html diff --git a/Tests/LibWeb/Text/expected/input-commit-on-unfocus.txt b/Tests/LibWeb/Text/expected/input-commit-on-unfocus.txt new file mode 100644 index 0000000000..768633ebbd --- /dev/null +++ b/Tests/LibWeb/Text/expected/input-commit-on-unfocus.txt @@ -0,0 +1,2 @@ +wfh :^) wfh :^) +blur diff --git a/Tests/LibWeb/Text/input/input-commit-on-unfocus.html b/Tests/LibWeb/Text/input/input-commit-on-unfocus.html new file mode 100644 index 0000000000..a7ee207d0a --- /dev/null +++ b/Tests/LibWeb/Text/input/input-commit-on-unfocus.html @@ -0,0 +1,18 @@ + + + diff --git a/Userland/Libraries/LibWeb/HTML/Focus.cpp b/Userland/Libraries/LibWeb/HTML/Focus.cpp index 439bee40a0..6ceff5584b 100644 --- a/Userland/Libraries/LibWeb/HTML/Focus.cpp +++ b/Userland/Libraries/LibWeb/HTML/Focus.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -32,13 +33,21 @@ static void run_focus_update_steps(Vector> old_chain, Vect // 2. For each entry entry in old chain, in order, run these substeps: for (auto& entry : old_chain) { - // FIXME: 1. If entry is an input element, and the change event applies to the element, - // and the element does not have a defined activation behavior, - // and the user has changed the element's value or its list of selected files - // while the control was focused without committing that change - // (such that it is different to what it was when the control was first focused), - // then fire an event named change at the element, - // with the bubbles attribute initialized to true. + // 1. If entry is an input element, and the change event applies to the element, and the element does not have + // a defined activation behavior, and the user has changed the element's value or its list of selected files + // while the control was focused without committing that change (such that it is different to what it was + // when the control was first focused), then fire an event named change at the element, with the bubbles + // attribute initialized to true. + if (is(*entry)) { + auto& input_element = static_cast(*entry); + + // FIXME: Spec issue: It doesn't make sense to check if the element has a defined activation behavior, as + // that is always true. Instead, we check if it has an *input* activation behavior. + // https://github.com/whatwg/html/issues/9973 + if (input_element.change_event_applies() && !input_element.has_input_activation_behavior()) { + input_element.commit_pending_changes(); + } + } JS::GCPtr blur_event_target; if (is(*entry)) { diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index ef9fe46f93..925b7a585e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -1340,4 +1340,47 @@ void HTMLInputElement::activation_behavior(DOM::Event const&) run_input_activation_behavior().release_value_but_fixme_should_propagate_errors(); } +bool HTMLInputElement::has_input_activation_behavior() const +{ + switch (type_state()) { + case TypeAttributeState::Checkbox: + case TypeAttributeState::Color: + case TypeAttributeState::FileUpload: + case TypeAttributeState::ImageButton: + case TypeAttributeState::RadioButton: + case TypeAttributeState::ResetButton: + case TypeAttributeState::SubmitButton: + return true; + default: + return false; + } +} + +// https://html.spec.whatwg.org/multipage/input.html#the-input-element:event-change-2 +bool HTMLInputElement::change_event_applies() const +{ + switch (type_state()) { + case TypeAttributeState::Checkbox: + case TypeAttributeState::Color: + case TypeAttributeState::Date: + case TypeAttributeState::Email: + case TypeAttributeState::FileUpload: + case TypeAttributeState::LocalDateAndTime: + case TypeAttributeState::Month: + case TypeAttributeState::Number: + case TypeAttributeState::Password: + case TypeAttributeState::RadioButton: + case TypeAttributeState::Range: + case TypeAttributeState::Search: + case TypeAttributeState::Telephone: + case TypeAttributeState::Text: + case TypeAttributeState::Time: + case TypeAttributeState::URL: + case TypeAttributeState::Week: + return true; + default: + return false; + } +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index f205c4aa1b..0f8d6f640e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -158,6 +158,9 @@ public: virtual bool has_activation_behavior() const override; virtual void activation_behavior(DOM::Event const&) override; + bool has_input_activation_behavior() const; + bool change_event_applies() const; + private: HTMLInputElement(DOM::Document&, DOM::QualifiedName);