mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 03:55:24 +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: https://github.com/LadybirdBrowser/ladybird/commit/5b6f2bb23a6 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));
|
||||
}
|
||||
|
||||
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<String> const& value, Optional<FlyString> 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<HTML::HTMLInputElement>([&](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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,11 +88,7 @@ public:
|
|||
Optional<String> 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);
|
||||
|
|
|
@ -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
Reference in a new issue