diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 727519c3b1..e88f92b470 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -791,16 +791,40 @@ void HTMLInputElement::form_associated_element_was_inserted() create_shadow_tree_if_needed(); } +// https://html.spec.whatwg.org/multipage/input.html#radio-button-group +static bool is_in_same_radio_button_group(HTML::HTMLInputElement const& a, HTML::HTMLInputElement const& b) +{ + // The radio button group that contains an input element a also contains all the + // other input elements b that fulfill all of the following conditions: + return ( + // - Both a and b are in the same tree. + // - The input element b's type attribute is in the Radio Button state. + a.type_state() == b.type_state() + && b.type_state() == HTMLInputElement::TypeAttributeState::RadioButton + // - Either a and b have the same form owner, or they both have no form owner. + && a.form() == b.form() + // - They both have a name attribute, their name attributes are not empty, and the + // value of a's name attribute equals the value of b's name attribute. + && a.has_attribute(HTML::AttributeNames::name) + && b.has_attribute(HTML::AttributeNames::name) + && a.name() == b.name()); +} + +// https://html.spec.whatwg.org/multipage/input.html#radio-button-state-(type=radio) void HTMLInputElement::set_checked_within_group() { if (checked()) return; set_checked(true, ChangeSource::User); - DeprecatedString name = this->name(); + + // No point iterating the tree if we have an empty name. + auto name = this->name(); + if (name.is_empty()) + return; document().for_each_in_inclusive_subtree_of_type([&](auto& element) { - if (element.checked() && &element != this && element.name() == name) + if (element.checked() && &element != this && is_in_same_radio_button_group(*this, element)) element.set_checked(false, ChangeSource::User); return IterationDecision::Continue; }); @@ -829,7 +853,7 @@ void HTMLInputElement::legacy_pre_activation_behavior() DeprecatedString name = this->name(); document().for_each_in_inclusive_subtree_of_type([&](auto& element) { - if (element.checked() && element.name() == name) { + if (element.checked() && is_in_same_radio_button_group(*this, element)) { m_legacy_pre_activation_behavior_checked_element_in_group = &element; return IterationDecision::Break; } @@ -860,11 +884,10 @@ void HTMLInputElement::legacy_cancelled_activation_behavior() // group, or if this element no longer has a radio button group, setting // this element's checkedness to false. if (type_state() == TypeAttributeState::RadioButton) { - DeprecatedString name = this->name(); bool did_reselect_previous_element = false; if (m_legacy_pre_activation_behavior_checked_element_in_group) { auto& element_in_group = *m_legacy_pre_activation_behavior_checked_element_in_group; - if (name == element_in_group.name()) { + if (is_in_same_radio_button_group(*this, element_in_group)) { element_in_group.set_checked_within_group(); did_reselect_previous_element = true; }