Title: [286855] trunk
Revision
286855
Author
cdu...@apple.com
Date
2021-12-10 09:16:51 -0800 (Fri, 10 Dec 2021)

Log Message

Radio buttons with no form owner are not grouped
https://bugs.webkit.org/show_bug.cgi?id=220502
<rdar://problem/73300895>

Reviewed by Darin Adler.

LayoutTests/imported/w3c:

Rebaseline WPT tests now that more checks are passing.

* web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt:
* web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt:
* web-platform-tests/html/semantics/forms/the-input-element/radio-expected.txt:

Source/WebCore:

Per the HTML specification and to match the behavior of both Gecko and Blink,
radio buttons should still be grouped, even if they are disconnected and not
owned by a form.

This patch aligns our behavior with Gecko and Blink and is based on the following
Blink commit:
- https://chromium-review.googlesource.com/c/chromium/src/+/1988087

No new tests, rebaselined existing tests.

* dom/ContainerNode.h:
(WebCore::ContainerNode::rootNode const):
* dom/ElementTraversal.h:
(WebCore::Traversal<ElementType>::inclusiveFirstWithin):
* html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::setChecked):
(WebCore::HTMLInputElement::didChangeForm):
(WebCore::HTMLInputElement::insertedIntoAncestor):
(WebCore::HTMLInputElement::removedFromAncestor):
(WebCore::HTMLInputElement::checkedRadioButtonForGroup const):
* html/InputType.h:
(WebCore::InputType::willUpdateCheckedness):
* html/RadioInputType.cpp:
(WebCore::RadioInputType::valueMissing const):
(WebCore::RadioInputType::willUpdateCheckedness):
* html/RadioInputType.h:

LayoutTests:

* fast/forms/radio/ValidityState-valueMissing-radio-expected.txt:
* fast/forms/radio/ValidityState-valueMissing-radio.html:
* fast/forms/radio/radio-live-validation-style-expected.txt:
* fast/forms/radio/radio-live-validation-style.html:
Update existing tests to reflect behavior change. I have verified that our behavior on those tests
is aligned with both Firefox and Chrome.

* platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt:
* platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt:
Rebaseline WPT tests now that more checks are passing.

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (286854 => 286855)


--- trunk/LayoutTests/ChangeLog	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/ChangeLog	2021-12-10 17:16:51 UTC (rev 286855)
@@ -1,3 +1,22 @@
+2021-12-10  Chris Dumez  <cdu...@apple.com>
+
+        Radio buttons with no form owner are not grouped
+        https://bugs.webkit.org/show_bug.cgi?id=220502
+        <rdar://problem/73300895>
+
+        Reviewed by Darin Adler.
+
+        * fast/forms/radio/ValidityState-valueMissing-radio-expected.txt:
+        * fast/forms/radio/ValidityState-valueMissing-radio.html:
+        * fast/forms/radio/radio-live-validation-style-expected.txt:
+        * fast/forms/radio/radio-live-validation-style.html:
+        Update existing tests to reflect behavior change. I have verified that our behavior on those tests
+        is aligned with both Firefox and Chrome.
+
+        * platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt:
+        * platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt:
+        Rebaseline WPT tests now that more checks are passing.
+
 2021-12-10  Youenn Fablet  <you...@apple.com>
 
         [ Monterey ] http/tests/workers/service/serviceworker-websocket.https.html (layout-test) is constant text failure

Modified: trunk/LayoutTests/fast/forms/radio/ValidityState-valueMissing-radio-expected.txt (286854 => 286855)


--- trunk/LayoutTests/fast/forms/radio/ValidityState-valueMissing-radio-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/fast/forms/radio/ValidityState-valueMissing-radio-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -40,11 +40,11 @@
 PASS inputs[2].validity.valueMissing is false
 
 Not in a radio button group
-PASS requiredButton.validity.valueMissing is false
 PASS requiredButton.validity.valueMissing is true
+PASS requiredButton.validity.valueMissing is true
 PASS button.validity.valueMissing is true
 PASS button.validity.valueMissing is false
-PASS requiredButton.validity.valueMissing is false
+PASS requiredButton.validity.valueMissing is true
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/forms/radio/ValidityState-valueMissing-radio.html (286854 => 286855)


--- trunk/LayoutTests/fast/forms/radio/ValidityState-valueMissing-radio.html	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/fast/forms/radio/ValidityState-valueMissing-radio.html	2021-12-10 17:16:51 UTC (rev 286855)
@@ -71,7 +71,7 @@
 requiredButton.type = 'radio';
 requiredButton.name = 'victim';
 requiredButton.required = true;
-shouldBeFalse('requiredButton.validity.valueMissing');
+shouldBeTrue('requiredButton.validity.valueMissing');
 
 parent.innerHTML = '<input name=victim type=radio required><input name=victim type=radio>';
 requiredButton = document.getElementsByName('victim')[0];
@@ -81,7 +81,7 @@
 parent.removeChild(button);
 shouldBeFalse('button.validity.valueMissing');
 parent.removeChild(requiredButton);
-shouldBeFalse('requiredButton.validity.valueMissing');
+shouldBeTrue('requiredButton.validity.valueMissing');
 
 </script>
 <script src=""

Modified: trunk/LayoutTests/fast/forms/radio/radio-live-validation-style-expected.txt (286854 => 286855)


--- trunk/LayoutTests/fast/forms/radio/radio-live-validation-style-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/fast/forms/radio/radio-live-validation-style-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -7,7 +7,7 @@
 PASS backgroundOf($("radio1")) is validColor
 PASS parent.removeChild($("radio2")); backgroundOf($("radio1")) is invalidColor
 PASS $("radio1").remove(); radio2.matches(":valid") is false
-PASS radio2.remove(); radio2.matches(":valid") is true
+PASS radio2.remove(); radio2.matches(":valid") is false
 
 Removing a checked radio button from a required radio button group by name attribute change:
 PASS $("radio2").name = "group2"; backgroundOf($("radio1")) is invalidColor

Modified: trunk/LayoutTests/fast/forms/radio/radio-live-validation-style.html (286854 => 286855)


--- trunk/LayoutTests/fast/forms/radio/radio-live-validation-style.html	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/fast/forms/radio/radio-live-validation-style.html	2021-12-10 17:16:51 UTC (rev 286855)
@@ -35,7 +35,7 @@
     '<input type=radio name=group1 required id=radio3>';
 var radio2 = $('radio2');
 shouldBeFalse('$("radio1").remove(); radio2.matches(":valid")');
-shouldBeTrue('radio2.remove(); radio2.matches(":valid")');
+shouldBeFalse('radio2.remove(); radio2.matches(":valid")');
 debug('');
 
 debug('Removing a checked radio button from a required radio button group by name attribute change:');

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (286854 => 286855)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2021-12-10 17:16:51 UTC (rev 286855)
@@ -1,3 +1,17 @@
+2021-12-10  Chris Dumez  <cdu...@apple.com>
+
+        Radio buttons with no form owner are not grouped
+        https://bugs.webkit.org/show_bug.cgi?id=220502
+        <rdar://problem/73300895>
+
+        Reviewed by Darin Adler.
+
+        Rebaseline WPT tests now that more checks are passing.
+
+        * web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt:
+        * web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt:
+        * web-platform-tests/html/semantics/forms/the-input-element/radio-expected.txt:
+
 2021-12-10  Patrick Griffis  <pgrif...@igalia.com>
 
         CSP: Allow external scripts with SRI hashes matching CSP

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt (286854 => 286855)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -23,7 +23,7 @@
 PASS [INPUT in NUMBER status] validity.valid must be false if validity.stepMismatch is true
 PASS [INPUT in NUMBER status] validity.valid must be false if validity.valueMissing is true
 PASS [INPUT in CHECKBOX status] validity.valid must be false if validity.valueMissing is true
-FAIL [INPUT in RADIO status] validity.valid must be false if validity.valueMissing is true assert_false: The validity.valid should be false. expected false got true
+PASS [INPUT in RADIO status] validity.valid must be false if validity.valueMissing is true
 PASS [INPUT in FILE status] validity.valid must be false if validity.valueMissing is true
 PASS [select]  validity.valid must be false if validity.valueMissing is true
 PASS [textarea]  validity.valid must be false if validity.valueMissing is true

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt (286854 => 286855)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -38,7 +38,7 @@
 PASS [INPUT in CHECKBOX status] The checked attribute is false
 PASS [INPUT in RADIO status] The required attribute is not set
 PASS [INPUT in RADIO status] The checked attribute is true
-FAIL [INPUT in RADIO status] The checked attribute is false assert_true: The validity.valueMissing should be true. expected true got false
+PASS [INPUT in RADIO status] The checked attribute is false
 PASS [INPUT in RADIO status] The checked attribute is false and the name attribute is empty
 PASS [INPUT in FILE status] The required attribute is not set
 PASS [INPUT in FILE status] The Files attribute is null

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/the-input-element/radio-expected.txt (286854 => 286855)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/the-input-element/radio-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/the-input-element/radio-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -7,8 +7,8 @@
 PASS radio inputs with non-ASCII name attributes belong to the same radio button group
 PASS changing the name of a radio input element and setting its checkedness to true makes all the other elements' checkedness in the same radio button group be set to false
 PASS moving radio input element out of or into a form should still work as expected
-FAIL Radio buttons in an orphan tree should make a group assert_false: The second radio should be unchecked after setting checked expected false got true
-FAIL Radio buttons in different groups (because they have different form owners or no form owner) do not affect each other's checkedness assert_false: radio5 should be unchecked expected false got true
+PASS Radio buttons in an orphan tree should make a group
+PASS Radio buttons in different groups (because they have different form owners or no form owner) do not affect each other's checkedness
 PASS Radio buttons in different groups (because they are not in the same tree) do not affect each other's checkedness
 PASS Radio buttons in different groups (because they have different name attribute values, or no name attribute) do not affect each other's checkedness
 

Modified: trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt (286854 => 286855)


--- trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -38,7 +38,7 @@
 PASS [INPUT in NUMBER status] validity.valid must be false if validity.stepMismatch is true
 PASS [INPUT in NUMBER status] validity.valid must be false if validity.valueMissing is true
 PASS [INPUT in CHECKBOX status] validity.valid must be false if validity.valueMissing is true
-FAIL [INPUT in RADIO status] validity.valid must be false if validity.valueMissing is true assert_false: The validity.valid should be false. expected false got true
+PASS [INPUT in RADIO status] validity.valid must be false if validity.valueMissing is true
 PASS [INPUT in FILE status] validity.valid must be false if validity.valueMissing is true
 PASS [select]  validity.valid must be false if validity.valueMissing is true
 PASS [textarea]  validity.valid must be false if validity.valueMissing is true

Modified: trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt (286854 => 286855)


--- trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -84,7 +84,7 @@
 PASS [INPUT in CHECKBOX status] The checked attribute is false
 PASS [INPUT in RADIO status] The required attribute is not set
 PASS [INPUT in RADIO status] The checked attribute is true
-FAIL [INPUT in RADIO status] The checked attribute is false assert_true: The validity.valueMissing should be true. expected true got false
+PASS [INPUT in RADIO status] The checked attribute is false
 PASS [INPUT in RADIO status] The checked attribute is false and the name attribute is empty
 PASS [INPUT in FILE status] The required attribute is not set
 PASS [INPUT in FILE status] The Files attribute is null

Modified: trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt (286854 => 286855)


--- trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valid-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -38,7 +38,7 @@
 PASS [INPUT in NUMBER status] validity.valid must be false if validity.stepMismatch is true
 PASS [INPUT in NUMBER status] validity.valid must be false if validity.valueMissing is true
 PASS [INPUT in CHECKBOX status] validity.valid must be false if validity.valueMissing is true
-FAIL [INPUT in RADIO status] validity.valid must be false if validity.valueMissing is true assert_false: The validity.valid should be false. expected false got true
+PASS [INPUT in RADIO status] validity.valid must be false if validity.valueMissing is true
 PASS [INPUT in FILE status] validity.valid must be false if validity.valueMissing is true
 PASS [select]  validity.valid must be false if validity.valueMissing is true
 PASS [textarea]  validity.valid must be false if validity.valueMissing is true

Modified: trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt (286854 => 286855)


--- trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt	2021-12-10 17:16:51 UTC (rev 286855)
@@ -84,7 +84,7 @@
 PASS [INPUT in CHECKBOX status] The checked attribute is false
 PASS [INPUT in RADIO status] The required attribute is not set
 PASS [INPUT in RADIO status] The checked attribute is true
-FAIL [INPUT in RADIO status] The checked attribute is false assert_true: The validity.valueMissing should be true. expected true got false
+PASS [INPUT in RADIO status] The checked attribute is false
 PASS [INPUT in RADIO status] The checked attribute is false and the name attribute is empty
 PASS [INPUT in FILE status] The required attribute is not set
 PASS [INPUT in FILE status] The Files attribute is null

Modified: trunk/Source/WebCore/ChangeLog (286854 => 286855)


--- trunk/Source/WebCore/ChangeLog	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/ChangeLog	2021-12-10 17:16:51 UTC (rev 286855)
@@ -1,3 +1,38 @@
+2021-12-10  Chris Dumez  <cdu...@apple.com>
+
+        Radio buttons with no form owner are not grouped
+        https://bugs.webkit.org/show_bug.cgi?id=220502
+        <rdar://problem/73300895>
+
+        Reviewed by Darin Adler.
+
+        Per the HTML specification and to match the behavior of both Gecko and Blink,
+        radio buttons should still be grouped, even if they are disconnected and not
+        owned by a form.
+
+        This patch aligns our behavior with Gecko and Blink and is based on the following
+        Blink commit:
+        - https://chromium-review.googlesource.com/c/chromium/src/+/1988087
+
+        No new tests, rebaselined existing tests.
+
+        * dom/ContainerNode.h:
+        (WebCore::ContainerNode::rootNode const):
+        * dom/ElementTraversal.h:
+        (WebCore::Traversal<ElementType>::inclusiveFirstWithin):
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::setChecked):
+        (WebCore::HTMLInputElement::didChangeForm):
+        (WebCore::HTMLInputElement::insertedIntoAncestor):
+        (WebCore::HTMLInputElement::removedFromAncestor):
+        (WebCore::HTMLInputElement::checkedRadioButtonForGroup const):
+        * html/InputType.h:
+        (WebCore::InputType::willUpdateCheckedness):
+        * html/RadioInputType.cpp:
+        (WebCore::RadioInputType::valueMissing const):
+        (WebCore::RadioInputType::willUpdateCheckedness):
+        * html/RadioInputType.h:
+
 2021-12-10  Patrick Griffis  <pgrif...@igalia.com>
 
         CSP: Allow external scripts with SRI hashes matching CSP

Modified: trunk/Source/WebCore/dom/ContainerNode.h (286854 => 286855)


--- trunk/Source/WebCore/dom/ContainerNode.h	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/dom/ContainerNode.h	2021-12-10 17:16:51 UTC (rev 286855)
@@ -60,6 +60,8 @@
     void stringReplaceAll(const String&);
     void replaceAll(Node*);
 
+    ContainerNode& rootNode() const { return downcast<ContainerNode>(Node::rootNode()); }
+
     // These methods are only used during parsing.
     // They don't send DOM mutation events or handle reparenting.
     // However, arbitrary code may be run by beforeload handlers.

Modified: trunk/Source/WebCore/dom/ElementTraversal.h (286854 => 286855)


--- trunk/Source/WebCore/dom/ElementTraversal.h	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/dom/ElementTraversal.h	2021-12-10 17:16:51 UTC (rev 286855)
@@ -43,6 +43,10 @@
     static ElementType* firstWithin(const ContainerNode&);
     static ElementType* lastWithin(const Node&);
     static ElementType* lastWithin(const ContainerNode&);
+    static ElementType* inclusiveFirstWithin(Node&);
+    static ElementType* inclusiveFirstWithin(ContainerNode&);
+    static ElementType* inclusiveLastWithin(Node&);
+    static ElementType* inclusiveLastWithin(ContainerNode&);
 
     // Pre-order traversal skipping non-ElementType nodes.
     static ElementType* next(const Node&);
@@ -235,6 +239,38 @@
 inline ElementType* Traversal<ElementType>::lastChild(const Node& current) { return lastChildTemplate(current); }
 
 template <typename ElementType>
+inline ElementType* Traversal<ElementType>::inclusiveFirstWithin(ContainerNode& current)
+{
+    if (is<ElementType>(current))
+        return &downcast<ElementType>(current);
+    return firstWithin(current);
+}
+
+template <typename ElementType>
+inline ElementType* Traversal<ElementType>::inclusiveFirstWithin(Node& current)
+{
+    if (is<ElementType>(current))
+        return &downcast<ElementType>(current);
+    return firstWithin(current);
+}
+
+template <typename ElementType>
+inline ElementType* Traversal<ElementType>::inclusiveLastWithin(ContainerNode& current)
+{
+    if (is<ElementType>(current))
+        return &downcast<ElementType>(current);
+    return lastWithin(current);
+}
+
+template <typename ElementType>
+inline ElementType* Traversal<ElementType>::inclusiveLastWithin(Node& current)
+{
+    if (is<ElementType>(current))
+        return &downcast<ElementType>(current);
+    return lastWithin(current);
+}
+
+template <typename ElementType>
 inline ElementType* Traversal<ElementType>::firstWithin(const ContainerNode& current) { return firstWithinTemplate(current); }
 template <typename ElementType>
 inline ElementType* Traversal<ElementType>::firstWithin(const Node& current) { return firstWithinTemplate(current); }

Modified: trunk/Source/WebCore/dom/TypedElementDescendantIterator.h (286854 => 286855)


--- trunk/Source/WebCore/dom/TypedElementDescendantIterator.h	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/dom/TypedElementDescendantIterator.h	2021-12-10 17:16:51 UTC (rev 286855)
@@ -68,6 +68,21 @@
     const ContainerNode& m_root;
 };
 
+template<typename ElementType> class InclusiveElementDescendantRange {
+public:
+    InclusiveElementDescendantRange(const ContainerNode& root);
+    ElementDescendantIterator<ElementType> begin() const;
+    static constexpr std::nullptr_t end() { return nullptr; }
+    ElementDescendantIterator<ElementType> beginAt(ElementType&) const;
+    ElementDescendantIterator<ElementType> from(Element&) const;
+
+    ElementType* first() const;
+    ElementType* last() const;
+
+private:
+    const ContainerNode& m_root;
+};
+
 template<typename ElementType> class DoubleElementDescendantRange {
 public:
     typedef ElementDescendantRange<ElementType> SingleAdapter;
@@ -171,6 +186,43 @@
     return Traversal<ElementType>::lastWithin(m_root);
 }
 
+// InclusiveElementDescendantRange
+
+template<typename ElementType> InclusiveElementDescendantRange<ElementType>::InclusiveElementDescendantRange(const ContainerNode& root)
+    : m_root(root)
+{
+}
+
+template<typename ElementType> ElementDescendantIterator<ElementType> InclusiveElementDescendantRange<ElementType>::begin() const
+{
+    return ElementDescendantIterator<ElementType>(m_root, Traversal<ElementType>::inclusiveFirstWithin(const_cast<ContainerNode&>(m_root)));
+}
+
+template<typename ElementType> ElementDescendantIterator<ElementType> InclusiveElementDescendantRange<ElementType>::beginAt(ElementType& descendant) const
+{
+    ASSERT(&m_root == &descendant || descendant.isDescendantOf(m_root));
+    return ElementDescendantIterator<ElementType>(m_root, &descendant);
+}
+
+template<typename ElementType> ElementDescendantIterator<ElementType> InclusiveElementDescendantRange<ElementType>::from(Element& descendant) const
+{
+    ASSERT(&m_root == &descendant || descendant.isDescendantOf(m_root));
+    if (is<ElementType>(descendant))
+        return ElementDescendantIterator<ElementType>(m_root, downcast<ElementType>(&descendant));
+    ElementType* next = Traversal<ElementType>::next(descendant, &m_root);
+    return ElementDescendantIterator<ElementType>(m_root, next);
+}
+
+template<typename ElementType> ElementType* InclusiveElementDescendantRange<ElementType>::first() const
+{
+    return Traversal<ElementType>::inclusiveFirstWithin(m_root);
+}
+
+template<typename ElementType> ElementType* InclusiveElementDescendantRange<ElementType>::last() const
+{
+    return Traversal<ElementType>::inclusiveLastWithin(m_root);
+}
+
 // DoubleElementDescendantRange
 
 template<typename ElementType> DoubleElementDescendantRange<ElementType>::DoubleElementDescendantRange(SingleAdapter&& first, SingleAdapter&& second)
@@ -251,6 +303,11 @@
     return ElementDescendantRange<ElementType>(root);
 }
 
+template<typename ElementType> InclusiveElementDescendantRange<ElementType> inclusiveDescendantsOfType(ContainerNode& root)
+{
+    return InclusiveElementDescendantRange<ElementType>(root);
+}
+
 template<typename ElementType> ElementDescendantRange<const ElementType> descendantsOfType(const ContainerNode& root)
 {
     return ElementDescendantRange<const ElementType>(root);

Modified: trunk/Source/WebCore/html/HTMLFormControlElement.cpp (286854 => 286855)


--- trunk/Source/WebCore/html/HTMLFormControlElement.cpp	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.cpp	2021-12-10 17:16:51 UTC (rev 286855)
@@ -301,11 +301,6 @@
     dispatchInputEvent();
 }
 
-bool HTMLFormControlElement::isRequired() const
-{
-    return m_isRequired;
-}
-
 void HTMLFormControlElement::didRecalcStyle(Style::Change)
 {
     // updateFromElement() can cause the selection to change, and in turn

Modified: trunk/Source/WebCore/html/HTMLFormControlElement.h (286854 => 286855)


--- trunk/Source/WebCore/html/HTMLFormControlElement.h	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.h	2021-12-10 17:16:51 UTC (rev 286855)
@@ -76,7 +76,7 @@
 
     bool isEnumeratable() const override { return false; }
 
-    bool isRequired() const;
+    bool isRequired() const { return m_isRequired; }
 
     const AtomString& type() const { return formControlType(); }
 

Modified: trunk/Source/WebCore/html/HTMLInputElement.cpp (286854 => 286855)


--- trunk/Source/WebCore/html/HTMLInputElement.cpp	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/html/HTMLInputElement.cpp	2021-12-10 17:16:51 UTC (rev 286855)
@@ -68,6 +68,7 @@
 #include "StepRange.h"
 #include "StyleGeneratedImage.h"
 #include "TextControlInnerElements.h"
+#include "TypedElementDescendantIterator.h"
 #include <wtf/IsoMallocInlines.h>
 #include <wtf/Language.h>
 #include <wtf/MathExtras.h>
@@ -979,6 +980,8 @@
     if (checked() == nowChecked)
         return;
 
+    m_inputType->willUpdateCheckedness(nowChecked);
+
     m_dirtyCheckednessFlag = true;
     m_isChecked = nowChecked;
     invalidateStyleForSubtree();
@@ -1576,12 +1579,14 @@
 
 void HTMLInputElement::didChangeForm()
 {
+    addToRadioButtonGroup();
     HTMLTextFormControlElement::didChangeForm();
-    addToRadioButtonGroup();
 }
 
 Node::InsertedIntoAncestorResult HTMLInputElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
 {
+    if (isRadioButton())
+        updateValidity();
     HTMLTextFormControlElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
 #if ENABLE(DATALIST_ELEMENT)
     resetListAttributeTargetObserver();
@@ -1608,6 +1613,8 @@
         removeFromRadioButtonGroup();
     HTMLTextFormControlElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
     ASSERT(!isConnected());
+    if (removalType.disconnectedFromDocument && !form() && isRadioButton())
+        updateValidity();
 #if ENABLE(DATALIST_ELEMENT)
     resetListAttributeTargetObserver();
 #endif
@@ -1956,8 +1963,21 @@
 
 RefPtr<HTMLInputElement> HTMLInputElement::checkedRadioButtonForGroup() const
 {
+    if (checked())
+        return const_cast<HTMLInputElement*>(this);
+
+    auto& name = this->name();
     if (RadioButtonGroups* buttons = radioButtonGroups())
-        return buttons->checkedButtonForGroup(name());
+        return buttons->checkedButtonForGroup(name);
+
+    if (name.isEmpty())
+        return nullptr;
+
+    // The input is not managed by a RadioButtonGroups, we'll need to traverse the tree.
+    for (auto& descendant : descendantsOfType<HTMLInputElement>(rootNode())) {
+        if (descendant.checked() && descendant.isRadioButton() && !descendant.form() && name == descendant.name())
+            return &descendant;
+    }
     return nullptr;
 }
 

Modified: trunk/Source/WebCore/html/InputType.h (286854 => 286855)


--- trunk/Source/WebCore/html/InputType.h	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/html/InputType.h	2021-12-10 17:16:51 UTC (rev 286855)
@@ -363,6 +363,7 @@
 #if ENABLE(DATALIST_ELEMENT)
     virtual bool isFocusingWithDataListDropdown() const { return false; };
 #endif
+    virtual void willUpdateCheckedness(bool /*nowChecked*/) { }
 
     // Parses the specified string for the type, and return
     // the Decimal value for the parsing result if the parsing

Modified: trunk/Source/WebCore/html/RadioInputType.cpp (286854 => 286855)


--- trunk/Source/WebCore/html/RadioInputType.cpp	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/html/RadioInputType.cpp	2021-12-10 17:16:51 UTC (rev 286855)
@@ -32,6 +32,7 @@
 #include "MouseEvent.h"
 #include "NodeTraversal.h"
 #include "SpatialNavigation.h"
+#include "TypedElementDescendantIterator.h"
 
 namespace WebCore {
 
@@ -45,9 +46,37 @@
 bool RadioInputType::valueMissing(const String&) const
 {
     ASSERT(element());
-    return element()->isInRequiredRadioButtonGroup() && !element()->checkedRadioButtonForGroup();
+    auto& name = element()->name();
+    if (auto* buttons = element()->radioButtonGroups())
+        return !buttons->checkedButtonForGroup(name) && buttons->isInRequiredGroup(*element());
+
+    if (name.isEmpty())
+        return false;
+
+    bool isRequired = false;
+    for (auto& input : inclusiveDescendantsOfType<HTMLInputElement>(element()->rootNode())) {
+        if (!input.isRadioButton() || input.form() || input.name() != name)
+            continue;
+        if (input.checked())
+            return false;
+        if (input.isRequired())
+            isRequired = true;
+    }
+    return isRequired;
 }
 
+void RadioInputType::willUpdateCheckedness(bool nowChecked)
+{
+    if (!nowChecked)
+        return;
+    if (element()->radioButtonGroups()) {
+        // Buttons in RadioButtonGroups are handled in HTMLInputElement::setChecked().
+        return;
+    }
+    if (auto input = element()->checkedRadioButtonForGroup())
+        input->setChecked(false);
+}
+
 String RadioInputType::valueMissingText() const
 {
     return validationMessageValueMissingForRadioText();

Modified: trunk/Source/WebCore/html/RadioInputType.h (286854 => 286855)


--- trunk/Source/WebCore/html/RadioInputType.h	2021-12-10 17:07:12 UTC (rev 286854)
+++ trunk/Source/WebCore/html/RadioInputType.h	2021-12-10 17:16:51 UTC (rev 286855)
@@ -52,6 +52,7 @@
     void willDispatchClick(InputElementClickState&) final;
     void didDispatchClick(Event&, const InputElementClickState&) final;
     bool matchesIndeterminatePseudoClass() const final;
+    void willUpdateCheckedness(bool nowChecked) final;
 };
 
 } // namespace WebCore
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to