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