Title: [288033] trunk/Source/WebCore
Revision
288033
Author
cdu...@apple.com
Date
2022-01-14 14:46:39 -0800 (Fri, 14 Jan 2022)

Log Message

Clarify / Optimize <select> logic given that deeply nested <option> or <optgroup> are not supported
https://bugs.webkit.org/show_bug.cgi?id=235247

Reviewed by Geoffrey Garen.

Clarify / Optimize <select> logic given that deeply nested <option> or <optgroup> are not supported, as per the
specification.

An <option> is only associated with a <select> element if it is either a child of the <select> or a child of an
<optgroup> that is itself a child of the <select>:
- https://html.spec.whatwg.org/multipage/form-elements.html#concept-select-option-list

As a result, an <optgroup> is only associated with a <select> element if it is a child of that <select>.

No new tests, no Web-facing behavior change.

* html/HTMLOptGroupElement.cpp:
(WebCore::HTMLOptGroupElement::recalcSelectOptions):
(WebCore::HTMLOptGroupElement::ownerSelectElement const):
(WebCore::HTMLOptGroupElement::accessKeyAction):
* html/HTMLOptionElement.cpp:
(WebCore::HTMLOptionElement::setText):
(WebCore::HTMLOptionElement::accessKeyAction):
(WebCore::HTMLOptionElement::index const):
(WebCore::HTMLOptionElement::selected const):
(WebCore::HTMLOptionElement::setSelected):
(WebCore::HTMLOptionElement::childrenChanged):
(WebCore::HTMLOptionElement::ownerSelectElement const):
(WebCore::HTMLOptionElement::textIndentedToRespectGroupLabel const):
(WebCore::HTMLOptionElement::insertedIntoAncestor):
(WebCore::HTMLOptionElement::collectOptionInnerText const):
* html/HTMLSelectElement.cpp:
(WebCore::HTMLSelectElement::recalcListItems const):
(WebCore::HTMLSelectElement::listBoxDefaultEventHandler):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (288032 => 288033)


--- trunk/Source/WebCore/ChangeLog	2022-01-14 22:23:35 UTC (rev 288032)
+++ trunk/Source/WebCore/ChangeLog	2022-01-14 22:46:39 UTC (rev 288033)
@@ -1,3 +1,40 @@
+2022-01-14  Chris Dumez  <cdu...@apple.com>
+
+        Clarify / Optimize <select> logic given that deeply nested <option> or <optgroup> are not supported
+        https://bugs.webkit.org/show_bug.cgi?id=235247
+
+        Reviewed by Geoffrey Garen.
+
+        Clarify / Optimize <select> logic given that deeply nested <option> or <optgroup> are not supported, as per the
+        specification.
+
+        An <option> is only associated with a <select> element if it is either a child of the <select> or a child of an
+        <optgroup> that is itself a child of the <select>:
+        - https://html.spec.whatwg.org/multipage/form-elements.html#concept-select-option-list
+
+        As a result, an <optgroup> is only associated with a <select> element if it is a child of that <select>.
+
+        No new tests, no Web-facing behavior change.
+
+        * html/HTMLOptGroupElement.cpp:
+        (WebCore::HTMLOptGroupElement::recalcSelectOptions):
+        (WebCore::HTMLOptGroupElement::ownerSelectElement const):
+        (WebCore::HTMLOptGroupElement::accessKeyAction):
+        * html/HTMLOptionElement.cpp:
+        (WebCore::HTMLOptionElement::setText):
+        (WebCore::HTMLOptionElement::accessKeyAction):
+        (WebCore::HTMLOptionElement::index const):
+        (WebCore::HTMLOptionElement::selected const):
+        (WebCore::HTMLOptionElement::setSelected):
+        (WebCore::HTMLOptionElement::childrenChanged):
+        (WebCore::HTMLOptionElement::ownerSelectElement const):
+        (WebCore::HTMLOptionElement::textIndentedToRespectGroupLabel const):
+        (WebCore::HTMLOptionElement::insertedIntoAncestor):
+        (WebCore::HTMLOptionElement::collectOptionInnerText const):
+        * html/HTMLSelectElement.cpp:
+        (WebCore::HTMLSelectElement::recalcListItems const):
+        (WebCore::HTMLSelectElement::listBoxDefaultEventHandler):
+
 2022-01-14  Jer Noble  <jer.no...@apple.com>
 
         [Cocoa] rVFC() isn't called for initial video load

Modified: trunk/Source/WebCore/html/HTMLOptGroupElement.cpp (288032 => 288033)


--- trunk/Source/WebCore/html/HTMLOptGroupElement.cpp	2022-01-14 22:23:35 UTC (rev 288032)
+++ trunk/Source/WebCore/html/HTMLOptGroupElement.cpp	2022-01-14 22:46:39 UTC (rev 288033)
@@ -102,7 +102,7 @@
 
 void HTMLOptGroupElement::recalcSelectOptions()
 {
-    if (RefPtr selectElement = ancestorsOfType<HTMLSelectElement>(*this).first()) {
+    if (RefPtr selectElement = ownerSelectElement()) {
         selectElement->setRecalcListItems();
         selectElement->updateValidity();
     }
@@ -122,12 +122,13 @@
     
 HTMLSelectElement* HTMLOptGroupElement::ownerSelectElement() const
 {
-    return const_cast<HTMLSelectElement*>(ancestorsOfType<HTMLSelectElement>(*this).first());
+    auto* parent = parentNode();
+    return is<HTMLSelectElement>(parent) ? downcast<HTMLSelectElement>(parent) : nullptr;
 }
 
 bool HTMLOptGroupElement::accessKeyAction(bool)
 {
-    RefPtr<HTMLSelectElement> select = ownerSelectElement();
+    RefPtr select = ownerSelectElement();
     // send to the parent to bring focus to the list box
     if (select && !select->focused())
         return select->accessKeyAction(false);

Modified: trunk/Source/WebCore/html/HTMLOptionElement.cpp (288032 => 288033)


--- trunk/Source/WebCore/html/HTMLOptionElement.cpp	2022-01-14 22:23:35 UTC (rev 288032)
+++ trunk/Source/WebCore/html/HTMLOptionElement.cpp	2022-01-14 22:46:39 UTC (rev 288033)
@@ -114,17 +114,17 @@
 
 void HTMLOptionElement::setText(const String &text)
 {
-    Ref<HTMLOptionElement> protectedThis(*this);
+    Ref protectedThis { *this };
 
     // Changing the text causes a recalc of a select's items, which will reset the selected
     // index to the first item if the select is single selection with a menu list. We attempt to
     // preserve the selected item.
-    RefPtr<HTMLSelectElement> select = ownerSelectElement();
+    RefPtr select = ownerSelectElement();
     bool selectIsMenuList = select && select->usesMenuList();
     int oldSelectedIndex = selectIsMenuList ? select->selectedIndex() : -1;
 
     // Handle the common special case where there's exactly 1 child node, and it's a text node.
-    RefPtr<Node> child = firstChild();
+    RefPtr child = firstChild();
     if (is<Text>(child) && !child->nextSibling())
         downcast<Text>(*child).setData(text);
     else {
@@ -138,7 +138,7 @@
 
 bool HTMLOptionElement::accessKeyAction(bool)
 {
-    RefPtr<HTMLSelectElement> select = ownerSelectElement();
+    RefPtr select = ownerSelectElement();
     if (select) {
         select->accessKeySetSelectedIndex(index());
         return true;
@@ -150,7 +150,7 @@
 {
     // It would be faster to cache the index, but harder to get it right in all cases.
 
-    RefPtr<HTMLSelectElement> selectElement = ownerSelectElement();
+    RefPtr selectElement = ownerSelectElement();
     if (!selectElement)
         return 0;
 
@@ -212,7 +212,7 @@
 
 bool HTMLOptionElement::selected() const
 {
-    if (RefPtr<HTMLSelectElement> select = ownerSelectElement())
+    if (RefPtr select = ownerSelectElement())
         select->updateListItemSelectedStates();
     return m_isSelected;
 }
@@ -224,7 +224,7 @@
 
     setSelectedState(selected);
 
-    if (RefPtr<HTMLSelectElement> select = ownerSelectElement())
+    if (RefPtr select = ownerSelectElement())
         select->optionSelectionStateChanged(*this, selected);
 }
 
@@ -249,7 +249,7 @@
     for (auto& dataList : ancestorsOfType<HTMLDataListElement>(*this))
         dataList.optionElementChildrenChanged();
 #endif
-    if (RefPtr<HTMLSelectElement> select = ownerSelectElement())
+    if (RefPtr select = ownerSelectElement())
         select->optionElementChildrenChanged();
     HTMLElement::childrenChanged(change);
 }
@@ -256,7 +256,13 @@
 
 HTMLSelectElement* HTMLOptionElement::ownerSelectElement() const
 {
-    return const_cast<HTMLSelectElement*>(ancestorsOfType<HTMLSelectElement>(*this).first());
+    if (auto* parent = parentElement()) {
+        if (is<HTMLSelectElement>(*parent))
+            return downcast<HTMLSelectElement>(parent);
+        if (is<HTMLOptGroupElement>(*parent))
+            return downcast<HTMLOptGroupElement>(*parent).ownerSelectElement();
+    }
+    return nullptr;
 }
 
 String HTMLOptionElement::label() const
@@ -292,7 +298,7 @@
 
 String HTMLOptionElement::textIndentedToRespectGroupLabel() const
 {
-    RefPtr<ContainerNode> parent = parentNode();
+    RefPtr parent = parentNode();
     if (is<HTMLOptGroupElement>(parent))
         return "    " + displayLabel();
     return displayLabel();
@@ -311,7 +317,7 @@
 
 Node::InsertedIntoAncestorResult HTMLOptionElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
 {
-    if (RefPtr<HTMLSelectElement> select = ownerSelectElement()) {
+    if (RefPtr select = ownerSelectElement()) {
         select->setRecalcListItems();
         select->updateValidity();
         // Do not call selected() since calling updateListItemSelectedStates()
@@ -329,7 +335,7 @@
 String HTMLOptionElement::collectOptionInnerText() const
 {
     StringBuilder text;
-    for (RefPtr<Node> node = firstChild(); node; ) {
+    for (RefPtr node = firstChild(); node; ) {
         if (is<Text>(*node))
             text.append(node->nodeValue());
         // Text nodes inside script elements are not part of the option text.

Modified: trunk/Source/WebCore/html/HTMLSelectElement.cpp (288032 => 288033)


--- trunk/Source/WebCore/html/HTMLSelectElement.cpp	2022-01-14 22:23:35 UTC (rev 288032)
+++ trunk/Source/WebCore/html/HTMLSelectElement.cpp	2022-01-14 22:46:39 UTC (rev 288033)
@@ -770,50 +770,31 @@
 
     RefPtr<HTMLOptionElement> foundSelected;
     RefPtr<HTMLOptionElement> firstOption;
-    for (RefPtr<Element> currentElement = ElementTraversal::firstWithin(*this); currentElement; ) {
-        if (!is<HTMLElement>(*currentElement)) {
-            currentElement = ElementTraversal::nextSkippingChildren(*currentElement, this);
-            continue;
-        }
-        HTMLElement& current = downcast<HTMLElement>(*currentElement);
-
-        // Only consider optgroup elements that are direct children of the select element.
-        if (is<HTMLOptGroupElement>(current) && current.parentNode() == this) {
-            m_listItems.append(&current);
-            if (RefPtr<Element> nextElement = ElementTraversal::firstWithin(current)) {
-                currentElement = nextElement;
-                continue;
+    auto handleOptionElement = [&](HTMLOptionElement& option) {
+        m_listItems.append(&option);
+        if (updateSelectedStates && !m_multiple) {
+            if (!firstOption)
+                firstOption = &option;
+            if (option.selected()) {
+                if (foundSelected)
+                    foundSelected->setSelectedState(false);
+                foundSelected = &option;
+            } else if (m_size <= 1 && !foundSelected && !option.isDisabledFormControl()) {
+                foundSelected = &option;
+                foundSelected->setSelectedState(true);
             }
         }
+    };
 
-        if (is<HTMLOptionElement>(current)) {
-            m_listItems.append(&current);
-
-            if (updateSelectedStates && !m_multiple) {
-                HTMLOptionElement& option = downcast<HTMLOptionElement>(current);
-                if (!firstOption)
-                    firstOption = &option;
-                if (option.selected()) {
-                    if (foundSelected)
-                        foundSelected->setSelectedState(false);
-                    foundSelected = &option;
-                } else if (m_size <= 1 && !foundSelected && !option.isDisabledFormControl()) {
-                    foundSelected = &option;
-                    foundSelected->setSelectedState(true);
-                }
-            }
-        }
-
-        if (current.hasTagName(hrTag))
-            m_listItems.append(&current);
-
-        // In conforming HTML code, only <optgroup> and <option> will be found
-        // within a <select>. We call NodeTraversal::nextSkippingChildren so that we only step
-        // into those tags that we choose to. For web-compat, we should cope
-        // with the case where odd tags like a <div> have been added but we
-        // handle this because such tags have already been removed from the
-        // <select>'s subtree at this point.
-        currentElement = ElementTraversal::nextSkippingChildren(*currentElement, this);
+    for (auto& child : childrenOfType<HTMLElement>(*const_cast<HTMLSelectElement*>(this))) {
+        if (is<HTMLOptGroupElement>(child)) {
+            m_listItems.append(&child);
+            for (auto& option : childrenOfType<HTMLOptionElement>(child))
+                handleOptionElement(option);
+        } else if (is<HTMLOptionElement>(child)) {
+            handleOptionElement(downcast<HTMLOptionElement>(child));
+        } else if (is<HTMLHRElement>(child))
+            m_listItems.append(&child);
     }
 
     if (!foundSelected && m_size <= 1 && firstOption && !firstOption->selected())
@@ -1349,7 +1330,7 @@
                 updateSelectedState(listIndex, mouseEvent.ctrlKey(), mouseEvent.shiftKey());
 #endif
             }
-            if (RefPtr<Frame> frame = document().frame())
+            if (RefPtr frame = document().frame())
                 frame->eventHandler().setMouseDownMayStartAutoscroll();
 
             mouseEvent.setDefaultHandled();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to