diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
index 2e548cfdd2..4a59c0460a 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
+++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2021, Andreas Kling
+ * Copyright (c) 2018-2022, Andreas Kling
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -61,14 +61,37 @@ RefPtr HTMLInputElement::create_layout_node(NonnullRefPtrset_needs_display();
+ if (should_run_activation_behavior == ShouldRunActivationBehavior::Yes)
+ run_activation_behavior();
+}
+
+void HTMLInputElement::run_activation_behavior()
+{
+ // The activation behavior for input elements are these steps:
+
+ // FIXME: 1. If this element is not mutable and is not in the Checkbox state and is not in the Radio state, then return.
+
+ // 2. Run this element's input activation behavior, if any, and do nothing otherwise.
+ run_input_activation_behavior();
+}
+
+// https://html.spec.whatwg.org/multipage/input.html#input-activation-behavior
+void HTMLInputElement::run_input_activation_behavior()
+{
dispatch_event(DOM::Event::create(EventNames::change));
}
@@ -139,4 +162,26 @@ void HTMLInputElement::removed_from(DOM::Node* old_parent)
set_form(nullptr);
}
+void HTMLInputElement::parse_attribute(FlyString const& name, String const& value)
+{
+ FormAssociatedElement::parse_attribute(name, value);
+ if (name == HTML::AttributeNames::checked) {
+ // When the checked content attribute is added, if the control does not have dirty checkedness,
+ // the user agent must set the checkedness of the element to true
+ if (!m_dirty_checkedness)
+ set_checked(true, ChangeSource::Programmatic, ShouldRunActivationBehavior::No);
+ }
+}
+
+void HTMLInputElement::did_remove_attribute(FlyString const& name)
+{
+ FormAssociatedElement::did_remove_attribute(name);
+ if (name == HTML::AttributeNames::checked) {
+ // When the checked content attribute is removed, if the control does not have dirty checkedness,
+ // the user agent must set the checkedness of the element to false.
+ if (!m_dirty_checkedness)
+ set_checked(false, ChangeSource::Programmatic, ShouldRunActivationBehavior::No);
+ }
+}
+
}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
index acf3577166..248c968061 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2021, Andreas Kling
+ * Copyright (c) 2018-2022, Andreas Kling
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -28,7 +28,15 @@ public:
void set_value(String);
bool checked() const { return m_checked; }
- void set_checked(bool);
+ enum class ChangeSource {
+ Programmatic,
+ User,
+ };
+ enum class ShouldRunActivationBehavior {
+ No,
+ Yes,
+ };
+ void set_checked(bool, ChangeSource = ChangeSource::Programmatic, ShouldRunActivationBehavior = ShouldRunActivationBehavior::Yes);
bool enabled() const;
@@ -36,6 +44,9 @@ public:
virtual bool is_focusable() const override;
+ virtual void parse_attribute(FlyString const&, String const&) override;
+ virtual void did_remove_attribute(FlyString const&) override;
+
private:
// ^DOM::Node
virtual void inserted() override;
@@ -43,11 +54,16 @@ private:
// ^DOM::EventTarget
virtual void did_receive_focus() override;
+ virtual void run_activation_behavior() override;
void create_shadow_tree_if_needed();
+ void run_input_activation_behavior();
RefPtr m_text_node;
bool m_checked { false };
+
+ // https://html.spec.whatwg.org/multipage/input.html#concept-input-checked-dirty-flag
+ bool m_dirty_checkedness { false };
};
}
diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp
index 5d043e0198..c171899834 100644
--- a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp
+++ b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
@@ -62,7 +63,7 @@ void CheckBox::handle_mouseup(Badge, const Gfx::IntPoint& position
is_inside_node_or_label = Label::is_inside_associated_label(*this, position);
if (is_inside_node_or_label)
- dom_node().set_checked(!dom_node().checked());
+ dom_node().set_checked(!dom_node().checked(), HTML::HTMLInputElement::ChangeSource::User);
m_being_pressed = false;
m_tracking_mouse = false;
@@ -102,7 +103,7 @@ void CheckBox::handle_associated_label_mouseup(Badge