mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-06 08:10:02 +00:00
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.
This commit is contained in:
parent
c87bc78d5d
commit
5b6f2bb23a
Notes:
github-actions[bot]
2025-01-11 10:25:26 +00:00
Author: https://github.com/tcl3
Commit: 5b6f2bb23a
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3154
5 changed files with 180 additions and 28 deletions
|
@ -158,15 +158,13 @@ void HTMLInputElement::adjust_computed_style(CSS::ComputedProperties& style)
|
||||||
style.set_property(CSS::PropertyID::LineHeight, CSS::CSSKeywordValue::create(CSS::Keyword::Normal));
|
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,
|
// 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.
|
// 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;
|
m_checked = checked;
|
||||||
|
|
||||||
|
@ -181,9 +179,9 @@ void HTMLInputElement::set_checked_binding(bool checked)
|
||||||
if (checked)
|
if (checked)
|
||||||
set_checked_within_group();
|
set_checked_within_group();
|
||||||
else
|
else
|
||||||
set_checked(false, ChangeSource::Programmatic);
|
set_checked(false);
|
||||||
} else {
|
} 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<String> const& value, Optional<FlyString> const&)
|
void HTMLInputElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& value, Optional<FlyString> const&)
|
||||||
{
|
{
|
||||||
if (name == HTML::AttributeNames::checked) {
|
if (name == HTML::AttributeNames::checked) {
|
||||||
if (!value.has_value()) {
|
// https://html.spec.whatwg.org/multipage/input.html#the-input-element:concept-input-checked-dirty-2
|
||||||
// When the checked content attribute is removed, if the control does not have dirty checkedness,
|
// 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;
|
||||||
// the user agent must set the checkedness of the element to false.
|
// 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)
|
if (!m_dirty_checkedness) {
|
||||||
set_checked(false, ChangeSource::Programmatic);
|
set_checked(value.has_value());
|
||||||
} else {
|
// set_checked() sets the dirty checkedness flag. We reset it here sinceit shouldn't be set when updating the attribute value
|
||||||
// When the checked content attribute is added, if the control does not have dirty checkedness,
|
m_dirty_checkedness = false;
|
||||||
// the user agent must set the checkedness of the element to true
|
|
||||||
if (!m_dirty_checkedness)
|
|
||||||
set_checked(true, ChangeSource::Programmatic);
|
|
||||||
}
|
}
|
||||||
} else if (name == HTML::AttributeNames::type) {
|
} else if (name == HTML::AttributeNames::type) {
|
||||||
auto new_type_attribute_state = parse_type_attribute(value.value_or(String {}));
|
auto new_type_attribute_state = parse_type_attribute(value.value_or(String {}));
|
||||||
|
@ -1757,7 +1752,7 @@ void HTMLInputElement::set_checked_within_group()
|
||||||
if (checked())
|
if (checked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
set_checked(true, ChangeSource::User);
|
set_checked(true);
|
||||||
|
|
||||||
// No point iterating the tree if we have an empty name.
|
// No point iterating the tree if we have an empty name.
|
||||||
if (!name().has_value() || name()->is_empty())
|
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<HTML::HTMLInputElement>([&](auto& element) {
|
root().for_each_in_inclusive_subtree_of_type<HTML::HTMLInputElement>([&](auto& element) {
|
||||||
if (element.checked() && &element != this && is_in_same_radio_button_group(*this, 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;
|
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
|
// false, false if it is true) and set this element's indeterminate IDL
|
||||||
// attribute to false.
|
// attribute to false.
|
||||||
if (type_state() == TypeAttributeState::Checkbox) {
|
if (type_state() == TypeAttributeState::Checkbox) {
|
||||||
set_checked(!checked(), ChangeSource::User);
|
set_checked(!checked());
|
||||||
set_indeterminate(false);
|
set_indeterminate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1809,7 +1804,7 @@ void HTMLInputElement::legacy_cancelled_activation_behavior()
|
||||||
// element's checkedness and the element's indeterminate IDL attribute back
|
// element's checkedness and the element's indeterminate IDL attribute back
|
||||||
// to the values they had before the legacy-pre-activation behavior was run.
|
// to the values they had before the legacy-pre-activation behavior was run.
|
||||||
if (type_state() == TypeAttributeState::Checkbox) {
|
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);
|
set_indeterminate(m_before_legacy_pre_activation_behavior_indeterminate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1834,7 +1829,7 @@ void HTMLInputElement::legacy_cancelled_activation_behavior()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!did_reselect_previous_element)
|
if (!did_reselect_previous_element)
|
||||||
set_checked(false, ChangeSource::User);
|
set_checked(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,11 +88,7 @@ public:
|
||||||
Optional<String> placeholder_value() const;
|
Optional<String> placeholder_value() const;
|
||||||
|
|
||||||
bool checked() const { return m_checked; }
|
bool checked() const { return m_checked; }
|
||||||
enum class ChangeSource {
|
void set_checked(bool);
|
||||||
Programmatic,
|
|
||||||
User,
|
|
||||||
};
|
|
||||||
void set_checked(bool, ChangeSource = ChangeSource::Programmatic);
|
|
||||||
|
|
||||||
bool checked_binding() const { return checked(); }
|
bool checked_binding() const { return checked(); }
|
||||||
void set_checked_binding(bool);
|
void set_checked_binding(bool);
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,64 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Cloning of input elements</title>
|
||||||
|
<link rel="help" href="https://dom.spec.whatwg.org/#dom-node-clonenode">
|
||||||
|
<link rel="help" href="https://dom.spec.whatwg.org/#concept-node-clone">
|
||||||
|
<link rel="help" href="https://dom.spec.whatwg.org/#concept-node-clone-ext">
|
||||||
|
<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-node-clone-ext">
|
||||||
|
<link rel="author" title="Matthew Phillips" href="mailto:matthew@matthewphillips.info">
|
||||||
|
|
||||||
|
<script src="../../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<script type=module>
|
||||||
|
import inputTypes from "./input-types.js";
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.value = "foo bar";
|
||||||
|
|
||||||
|
var copy = input.cloneNode();
|
||||||
|
assert_equals(copy.value, "foo bar");
|
||||||
|
}, "input element's value should be cloned");
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.value = "foo bar";
|
||||||
|
|
||||||
|
var copy = input.cloneNode();
|
||||||
|
copy.setAttribute("value", "something else");
|
||||||
|
|
||||||
|
assert_equals(copy.value, "foo bar");
|
||||||
|
}, "input element's dirty value flag should be cloned, so setAttribute doesn't affect the cloned input's value");
|
||||||
|
|
||||||
|
for (const inputType of inputTypes) {
|
||||||
|
test(function() {
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.setAttribute("type", inputType);
|
||||||
|
input.indeterminate = true;
|
||||||
|
|
||||||
|
var copy = input.cloneNode();
|
||||||
|
assert_equals(copy.indeterminate, true);
|
||||||
|
}, `input[type=${inputType}] element's indeterminateness should be cloned`);
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.setAttribute("type", inputType);
|
||||||
|
input.checked = true;
|
||||||
|
|
||||||
|
var copy = input.cloneNode();
|
||||||
|
assert_equals(copy.checked, true);
|
||||||
|
}, `input[type=${inputType}] element's checkedness should be cloned`);
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.setAttribute("type", inputType);
|
||||||
|
input.checked = false;
|
||||||
|
|
||||||
|
var copy = input.cloneNode();
|
||||||
|
copy.setAttribute("checked", "checked");
|
||||||
|
|
||||||
|
assert_equals(copy.checked, false);
|
||||||
|
}, `input[type=${inputType}] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness`);
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -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",
|
||||||
|
];
|
Loading…
Add table
Add a link
Reference in a new issue