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);