From 5b6f2bb23a60e34a1b392bc9c8c7e699e150f1f3 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Sun, 5 Jan 2025 20:50:09 +0000 Subject: [PATCH] LibWeb: Set dirty checkedness flag when setting `checked` IDL attribute This matches the behavior of other browsers, which always set the dirty checkedness flag when setting checkedness, except when setting the `checked` content attribute. --- Libraries/LibWeb/HTML/HTMLInputElement.cpp | 41 +++++------ Libraries/LibWeb/HTML/HTMLInputElement.h | 6 +- .../forms/the-input-element/cloning-steps.txt | 73 +++++++++++++++++++ .../the-input-element/cloning-steps.html | 64 ++++++++++++++++ .../forms/the-input-element/input-types.js | 24 ++++++ 5 files changed, 180 insertions(+), 28 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/cloning-steps.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/cloning-steps.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-types.js diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 369c980f9fd..e584051da2e 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -158,15 +158,13 @@ void HTMLInputElement::adjust_computed_style(CSS::ComputedProperties& style) style.set_property(CSS::PropertyID::LineHeight, CSS::CSSKeywordValue::create(CSS::Keyword::Normal)); } -void HTMLInputElement::set_checked(bool checked, ChangeSource change_source) +void HTMLInputElement::set_checked(bool checked) { - if (m_checked == checked) - return; - // The dirty checkedness flag must be initially set to false when the element is created, // and must be set to true whenever the user interacts with the control in a way that changes the checkedness. - if (change_source == ChangeSource::User) - m_dirty_checkedness = true; + m_dirty_checkedness = true; + if (m_checked == checked) + return; m_checked = checked; @@ -181,9 +179,9 @@ void HTMLInputElement::set_checked_binding(bool checked) if (checked) set_checked_within_group(); else - set_checked(false, ChangeSource::Programmatic); + set_checked(false); } else { - set_checked(checked, ChangeSource::Programmatic); + set_checked(checked); } } @@ -1249,16 +1247,13 @@ void HTMLInputElement::did_lose_focus() void HTMLInputElement::form_associated_element_attribute_changed(FlyString const& name, Optional const& value, Optional const&) { if (name == HTML::AttributeNames::checked) { - if (!value.has_value()) { - // 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); - } else { - // 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); + // https://html.spec.whatwg.org/multipage/input.html#the-input-element:concept-input-checked-dirty-2 + // 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; + // 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(value.has_value()); + // set_checked() sets the dirty checkedness flag. We reset it here sinceit shouldn't be set when updating the attribute value + m_dirty_checkedness = false; } } else if (name == HTML::AttributeNames::type) { auto new_type_attribute_state = parse_type_attribute(value.value_or(String {})); @@ -1757,7 +1752,7 @@ void HTMLInputElement::set_checked_within_group() if (checked()) return; - set_checked(true, ChangeSource::User); + set_checked(true); // No point iterating the tree if we have an empty name. if (!name().has_value() || name()->is_empty()) @@ -1765,7 +1760,7 @@ void HTMLInputElement::set_checked_within_group() root().for_each_in_inclusive_subtree_of_type([&](auto& element) { if (element.checked() && &element != this && is_in_same_radio_button_group(*this, element)) - element.set_checked(false, ChangeSource::User); + element.set_checked(false); return TraversalDecision::Continue; }); } @@ -1781,7 +1776,7 @@ void HTMLInputElement::legacy_pre_activation_behavior() // false, false if it is true) and set this element's indeterminate IDL // attribute to false. if (type_state() == TypeAttributeState::Checkbox) { - set_checked(!checked(), ChangeSource::User); + set_checked(!checked()); set_indeterminate(false); } @@ -1809,7 +1804,7 @@ void HTMLInputElement::legacy_cancelled_activation_behavior() // element's checkedness and the element's indeterminate IDL attribute back // to the values they had before the legacy-pre-activation behavior was run. if (type_state() == TypeAttributeState::Checkbox) { - set_checked(m_before_legacy_pre_activation_behavior_checked, ChangeSource::Programmatic); + set_checked(m_before_legacy_pre_activation_behavior_checked); set_indeterminate(m_before_legacy_pre_activation_behavior_indeterminate); } @@ -1834,7 +1829,7 @@ void HTMLInputElement::legacy_cancelled_activation_behavior() } if (!did_reselect_previous_element) - set_checked(false, ChangeSource::User); + set_checked(false); } } diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.h b/Libraries/LibWeb/HTML/HTMLInputElement.h index 1c1ae78def6..44538ed3e90 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -88,11 +88,7 @@ public: Optional placeholder_value() const; bool checked() const { return m_checked; } - enum class ChangeSource { - Programmatic, - User, - }; - void set_checked(bool, ChangeSource = ChangeSource::Programmatic); + void set_checked(bool); bool checked_binding() const { return checked(); } void set_checked_binding(bool); diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/cloning-steps.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/cloning-steps.txt new file mode 100644 index 00000000000..bcb6eae248f --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/cloning-steps.txt @@ -0,0 +1,73 @@ +Harness status: OK + +Found 68 tests + +68 Pass +Pass input element's value should be cloned +Pass input element's dirty value flag should be cloned, so setAttribute doesn't affect the cloned input's value +Pass input[type=button] element's indeterminateness should be cloned +Pass input[type=button] element's checkedness should be cloned +Pass input[type=button] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=checkbox] element's indeterminateness should be cloned +Pass input[type=checkbox] element's checkedness should be cloned +Pass input[type=checkbox] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=color] element's indeterminateness should be cloned +Pass input[type=color] element's checkedness should be cloned +Pass input[type=color] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=date] element's indeterminateness should be cloned +Pass input[type=date] element's checkedness should be cloned +Pass input[type=date] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=datetime-local] element's indeterminateness should be cloned +Pass input[type=datetime-local] element's checkedness should be cloned +Pass input[type=datetime-local] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=email] element's indeterminateness should be cloned +Pass input[type=email] element's checkedness should be cloned +Pass input[type=email] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=file] element's indeterminateness should be cloned +Pass input[type=file] element's checkedness should be cloned +Pass input[type=file] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=hidden] element's indeterminateness should be cloned +Pass input[type=hidden] element's checkedness should be cloned +Pass input[type=hidden] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=image] element's indeterminateness should be cloned +Pass input[type=image] element's checkedness should be cloned +Pass input[type=image] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=month] element's indeterminateness should be cloned +Pass input[type=month] element's checkedness should be cloned +Pass input[type=month] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=number] element's indeterminateness should be cloned +Pass input[type=number] element's checkedness should be cloned +Pass input[type=number] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=password] element's indeterminateness should be cloned +Pass input[type=password] element's checkedness should be cloned +Pass input[type=password] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=radio] element's indeterminateness should be cloned +Pass input[type=radio] element's checkedness should be cloned +Pass input[type=radio] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=range] element's indeterminateness should be cloned +Pass input[type=range] element's checkedness should be cloned +Pass input[type=range] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=reset] element's indeterminateness should be cloned +Pass input[type=reset] element's checkedness should be cloned +Pass input[type=reset] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=search] element's indeterminateness should be cloned +Pass input[type=search] element's checkedness should be cloned +Pass input[type=search] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=submit] element's indeterminateness should be cloned +Pass input[type=submit] element's checkedness should be cloned +Pass input[type=submit] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=tel] element's indeterminateness should be cloned +Pass input[type=tel] element's checkedness should be cloned +Pass input[type=tel] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=text] element's indeterminateness should be cloned +Pass input[type=text] element's checkedness should be cloned +Pass input[type=text] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=time] element's indeterminateness should be cloned +Pass input[type=time] element's checkedness should be cloned +Pass input[type=time] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=url] element's indeterminateness should be cloned +Pass input[type=url] element's checkedness should be cloned +Pass input[type=url] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness +Pass input[type=week] element's indeterminateness should be cloned +Pass input[type=week] element's checkedness should be cloned +Pass input[type=week] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/cloning-steps.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/cloning-steps.html new file mode 100644 index 00000000000..3e7ccb9b394 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/cloning-steps.html @@ -0,0 +1,64 @@ + + +Cloning of input elements + + + + + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-types.js b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-types.js new file mode 100644 index 00000000000..44567510523 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-types.js @@ -0,0 +1,24 @@ +export default [ + "button", + "checkbox", + "color", + "date", + "datetime-local", + "email", + "file", + "hidden", + "image", + "month", + "number", + "password", + "radio", + "range", + "reset", + "search", + "submit", + "tel", + "text", + "time", + "url", + "week", +];