HTMLFormElement should use WeakPtr to keep track of its associated elements
https://bugs.webkit.org/show_bug.cgi?id=209894

Reviewed by Wenson Hsieh.

Source/WebCore:

Replaced the vector of raw pointers to FormAssociatedElement in HTMLFormElement by a vector
of WeakPtr to the equivalent HTMLElement. Most of code changes below are due to type of elements
in the vector changing from FormAssociatedElement to HTMLElement and needing conversion.

This patch also moves clearing of m_form from ~FormAssociatedElement to its subclasses'
destructors since we need to make a virtual function call to get HTMLElement* out of
FormAssociatedElement, which would be too late inside ~FormAssociatedElement.

No new tests since there should be no behavioral change.

* html/FormAssociatedElement.cpp:
(WebCore::FormAssociatedElement::~FormAssociatedElement): Assert that m_form had been cleared
instead of clearing it here.
* html/FormAssociatedElement.h:
(WebCore::FormAssociatedElement::clearForm): Added.
* html/FormController.cpp:
(WebCore::recordFormStructure):
* html/HTMLFormControlElement.cpp:
(WebCore::HTMLFormControlElement::~HTMLFormControlElement): Now calls clearForm. Also removed
the redundant comment.
* html/HTMLFormControlsCollection.cpp:
(WebCore::findFormAssociatedElement):
(WebCore::HTMLFormControlsCollection::unsafeFormControlElements const): Deleted.
(WebCore::HTMLFormControlsCollection::copyFormControlElementsVector const): Deleted.
(WebCore::HTMLFormControlsCollection::customElementAfter const):
(WebCore::HTMLFormControlsCollection::updateNamedElementCache const):
* html/HTMLFormControlsCollection.h:
* html/HTMLFormElement.cpp:
(WebCore::HTMLFormElement::~HTMLFormElement):
(WebCore::HTMLFormElement::removedFromAncestor):
(WebCore::HTMLFormElement::length const):
(WebCore::HTMLFormElement::textFieldValues const):
(WebCore::HTMLFormElement::resetAssociatedFormControlElements):
(WebCore::HTMLFormElement::formElementIndexWithFormAttribute):
(WebCore::HTMLFormElement::registerFormElement):
(WebCore::HTMLFormElement::removeFormElement):
(WebCore::HTMLFormElement::checkInvalidControlsAndCollectUnhandled):
(WebCore::HTMLFormElement::assertItemCanBeInPastNamesMap const):
(WebCore::HTMLFormElement::unsafeAssociatedElements const):
(WebCore::HTMLFormElement::copyAssociatedElementsVector const):
* html/HTMLFormElement.h:
* html/HTMLObjectElement.cpp:
(WebCore::HTMLObjectElement::~HTMLObjectElement): Added. Calls clearForm.
* html/HTMLObjectElement.h:

Source/WebKitLegacy/mac:

* WebView/WebHTMLRepresentation.mm:
(-[WebHTMLRepresentation elementWithName:inForm:]):
(-[WebHTMLRepresentation controlsInForm:]):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@259393 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index e24cff7..c8374ad 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,55 @@
+2020-04-01  Ryosuke Niwa  <rniwa@webkit.org>
+
+        HTMLFormElement should use WeakPtr to keep track of its associated elements
+        https://bugs.webkit.org/show_bug.cgi?id=209894
+
+        Reviewed by Wenson Hsieh.
+
+        Replaced the vector of raw pointers to FormAssociatedElement in HTMLFormElement by a vector
+        of WeakPtr to the equivalent HTMLElement. Most of code changes below are due to type of elements
+        in the vector changing from FormAssociatedElement to HTMLElement and needing conversion.
+
+        This patch also moves clearing of m_form from ~FormAssociatedElement to its subclasses'
+        destructors since we need to make a virtual function call to get HTMLElement* out of
+        FormAssociatedElement, which would be too late inside ~FormAssociatedElement.
+
+        No new tests since there should be no behavioral change.
+
+        * html/FormAssociatedElement.cpp:
+        (WebCore::FormAssociatedElement::~FormAssociatedElement): Assert that m_form had been cleared
+        instead of clearing it here.
+        * html/FormAssociatedElement.h:
+        (WebCore::FormAssociatedElement::clearForm): Added.
+        * html/FormController.cpp:
+        (WebCore::recordFormStructure):
+        * html/HTMLFormControlElement.cpp:
+        (WebCore::HTMLFormControlElement::~HTMLFormControlElement): Now calls clearForm. Also removed
+        the redundant comment.
+        * html/HTMLFormControlsCollection.cpp:
+        (WebCore::findFormAssociatedElement):
+        (WebCore::HTMLFormControlsCollection::unsafeFormControlElements const): Deleted.
+        (WebCore::HTMLFormControlsCollection::copyFormControlElementsVector const): Deleted.
+        (WebCore::HTMLFormControlsCollection::customElementAfter const):
+        (WebCore::HTMLFormControlsCollection::updateNamedElementCache const):
+        * html/HTMLFormControlsCollection.h:
+        * html/HTMLFormElement.cpp:
+        (WebCore::HTMLFormElement::~HTMLFormElement):
+        (WebCore::HTMLFormElement::removedFromAncestor):
+        (WebCore::HTMLFormElement::length const):
+        (WebCore::HTMLFormElement::textFieldValues const):
+        (WebCore::HTMLFormElement::resetAssociatedFormControlElements):
+        (WebCore::HTMLFormElement::formElementIndexWithFormAttribute):
+        (WebCore::HTMLFormElement::registerFormElement):
+        (WebCore::HTMLFormElement::removeFormElement):
+        (WebCore::HTMLFormElement::checkInvalidControlsAndCollectUnhandled):
+        (WebCore::HTMLFormElement::assertItemCanBeInPastNamesMap const):
+        (WebCore::HTMLFormElement::unsafeAssociatedElements const):
+        (WebCore::HTMLFormElement::copyAssociatedElementsVector const):
+        * html/HTMLFormElement.h:
+        * html/HTMLObjectElement.cpp:
+        (WebCore::HTMLObjectElement::~HTMLObjectElement): Added. Calls clearForm.
+        * html/HTMLObjectElement.h:
+
 2020-04-02  Alex Christensen  <achristensen@webkit.org>
 
         Add SPI to restrict loading to main resources or non-network loads
diff --git a/Source/WebCore/html/FormAssociatedElement.cpp b/Source/WebCore/html/FormAssociatedElement.cpp
index 8fa2fff..a37afdb 100644
--- a/Source/WebCore/html/FormAssociatedElement.cpp
+++ b/Source/WebCore/html/FormAssociatedElement.cpp
@@ -58,7 +58,7 @@
 
 FormAssociatedElement::~FormAssociatedElement()
 {
-    setForm(nullptr);
+    RELEASE_ASSERT(!m_form);
 }
 
 void FormAssociatedElement::didMoveToNewDocument(Document&)
diff --git a/Source/WebCore/html/FormAssociatedElement.h b/Source/WebCore/html/FormAssociatedElement.h
index 922ed56..4461b42 100644
--- a/Source/WebCore/html/FormAssociatedElement.h
+++ b/Source/WebCore/html/FormAssociatedElement.h
@@ -97,6 +97,7 @@
     void removedFromAncestor(Node::RemovalType, ContainerNode&);
     void didMoveToNewDocument(Document& oldDocument);
 
+    void clearForm() { setForm(nullptr); }
     void setForm(HTMLFormElement*);
     void formAttributeChanged();
 
diff --git a/Source/WebCore/html/FormController.cpp b/Source/WebCore/html/FormController.cpp
index d0e03f5..79db341 100644
--- a/Source/WebCore/html/FormController.cpp
+++ b/Source/WebCore/html/FormController.cpp
@@ -286,9 +286,10 @@
     auto& controls = form.unsafeAssociatedElements();
     builder.appendLiteral(" [");
     for (size_t i = 0, namedControls = 0; i < controls.size() && namedControls < namedControlsToBeRecorded; ++i) {
-        if (!controls[i]->isFormControlElementWithState())
+        auto* formAssociatedElement = controls[i]->asFormAssociatedElement();
+        if (!formAssociatedElement->isFormControlElementWithState())
             continue;
-        RefPtr<HTMLFormControlElementWithState> control = static_cast<HTMLFormControlElementWithState*>(controls[i]);
+        RefPtr<HTMLFormControlElementWithState> control = static_cast<HTMLFormControlElementWithState*>(formAssociatedElement);
         if (!ownerFormForState(*control))
             continue;
         AtomString name = control->name();
diff --git a/Source/WebCore/html/HTMLFormControlElement.cpp b/Source/WebCore/html/HTMLFormControlElement.cpp
index fd77d7b..e0d7fe7 100644
--- a/Source/WebCore/html/HTMLFormControlElement.cpp
+++ b/Source/WebCore/html/HTMLFormControlElement.cpp
@@ -76,9 +76,7 @@
 
 HTMLFormControlElement::~HTMLFormControlElement()
 {
-    // The calls willChangeForm() and didChangeForm() are virtual, we want the
-    // form to be reset while this object still exists.
-    setForm(nullptr);
+    clearForm();
 }
 
 String HTMLFormControlElement::formEnctype() const
diff --git a/Source/WebCore/html/HTMLFormControlsCollection.cpp b/Source/WebCore/html/HTMLFormControlsCollection.cpp
index 84a1cb7..af1cd0e 100644
--- a/Source/WebCore/html/HTMLFormControlsCollection.cpp
+++ b/Source/WebCore/html/HTMLFormControlsCollection.cpp
@@ -66,21 +66,14 @@
     return Variant<RefPtr<RadioNodeList>, RefPtr<Element>> { RefPtr<RadioNodeList> { ownerNode().radioNodeList(name) } };
 }
 
-const Vector<FormAssociatedElement*>& HTMLFormControlsCollection::unsafeFormControlElements() const
-{
-    return ownerNode().unsafeAssociatedElements();
-}
-
-Vector<Ref<FormAssociatedElement>> HTMLFormControlsCollection::copyFormControlElementsVector() const
-{
-    return ownerNode().copyAssociatedElementsVector();
-}
-
-static unsigned findFormAssociatedElement(const Vector<FormAssociatedElement*>& elements, const Element& element)
+static unsigned findFormAssociatedElement(const Vector<WeakPtr<HTMLElement>>& elements, const Element& element)
 {
     for (unsigned i = 0; i < elements.size(); ++i) {
-        auto& associatedElement = *elements[i];
-        if (associatedElement.isEnumeratable() && &associatedElement.asHTMLElement() == &element)
+        auto currentElement = makeRefPtr(elements[i].get());
+        ASSERT(currentElement);
+        auto* associatedElement = currentElement->asFormAssociatedElement();
+        ASSERT(associatedElement);
+        if (associatedElement->isEnumeratable() && currentElement == &element)
             return i;
     }
     return elements.size();
@@ -89,7 +82,7 @@
 HTMLElement* HTMLFormControlsCollection::customElementAfter(Element* current) const
 {
     ScriptDisallowedScope::InMainThread scriptDisallowedScope;
-    auto& elements = unsafeFormControlElements();
+    auto& elements = ownerNode().unsafeAssociatedElements();
     unsigned start;
     if (!current)
         start = 0;
@@ -99,11 +92,13 @@
         start = findFormAssociatedElement(elements, *current) + 1;
 
     for (unsigned i = start; i < elements.size(); ++i) {
-        FormAssociatedElement& element = *elements[i];
-        if (element.isEnumeratable()) {
-            m_cachedElement = &element.asHTMLElement();
+        auto element = makeRefPtr(elements[i].get());
+        ASSERT(element);
+        ASSERT(element->asFormAssociatedElement());
+        if (element->asFormAssociatedElement()->isEnumeratable()) {
+            m_cachedElement = element.get();
             m_cachedElementOffsetInArray = i;
-            return &element.asHTMLElement();
+            return element.get();
         }
     }
     return nullptr;
@@ -124,18 +119,20 @@
     HashSet<AtomStringImpl*> foundInputElements;
 
     ScriptDisallowedScope::InMainThread scriptDisallowedScope;
-    for (auto& elementPtr : unsafeFormControlElements()) {
-        FormAssociatedElement& associatedElement = *elementPtr;
-        if (associatedElement.isEnumeratable()) {
-            HTMLElement& element = associatedElement.asHTMLElement();
-            const AtomString& id = element.getIdAttribute();
+    for (auto& weakElement : ownerNode().unsafeAssociatedElements()) {
+        auto element = makeRefPtr(weakElement.get());
+        ASSERT(element);
+        auto* associatedElement = element->asFormAssociatedElement();
+        ASSERT(associatedElement);
+        if (associatedElement->isEnumeratable()) {
+            const AtomString& id = element->getIdAttribute();
             if (!id.isEmpty()) {
-                cache->appendToIdCache(id, element);
+                cache->appendToIdCache(id, *element);
                 foundInputElements.add(id.impl());
             }
-            const AtomString& name = element.getNameAttribute();
+            const AtomString& name = element->getNameAttribute();
             if (!name.isEmpty() && id != name) {
-                cache->appendToNameCache(name, element);
+                cache->appendToNameCache(name, *element);
                 foundInputElements.add(name.impl());
             }
         }
diff --git a/Source/WebCore/html/HTMLFormControlsCollection.h b/Source/WebCore/html/HTMLFormControlsCollection.h
index 902730e..6c7c4b8 100644
--- a/Source/WebCore/html/HTMLFormControlsCollection.h
+++ b/Source/WebCore/html/HTMLFormControlsCollection.h
@@ -54,9 +54,6 @@
     void invalidateCacheForDocument(Document&) override;
     void updateNamedElementCache() const override;
 
-    const Vector<FormAssociatedElement*>& unsafeFormControlElements() const;
-    Vector<Ref<FormAssociatedElement>> copyFormControlElementsVector() const;
-
     mutable Element* m_cachedElement;
     mutable unsigned m_cachedElementOffsetInArray;
 };
diff --git a/Source/WebCore/html/HTMLFormElement.cpp b/Source/WebCore/html/HTMLFormElement.cpp
index 2d093ef..b4edf69 100644
--- a/Source/WebCore/html/HTMLFormElement.cpp
+++ b/Source/WebCore/html/HTMLFormElement.cpp
@@ -84,8 +84,13 @@
         document().unregisterForDocumentSuspensionCallbacks(*this);
 
     m_defaultButton = nullptr;
-    for (auto& associatedElement : m_associatedElements)
+    for (auto& weakElement : m_associatedElements) {
+        auto element = makeRefPtr(weakElement.get());
+        ASSERT(element);
+        auto* associatedElement = element->asFormAssociatedElement();
+        ASSERT(associatedElement);
         associatedElement->formWillBeDestroyed();
+    }
     for (auto& imageElement : m_imageElements)
         imageElement->m_form = nullptr;
 }
@@ -136,7 +141,7 @@
 void HTMLFormElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
 {
     Node& root = traverseToRootNode(); // Do not rely on rootNode() because our IsInTreeScope is outdated.
-    Vector<FormAssociatedElement*> associatedElements(m_associatedElements);
+    auto associatedElements = copyAssociatedElementsVector();
     for (auto& associatedElement : associatedElements)
         associatedElement->formOwnerRemovedFromTree(root);
     HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
@@ -154,7 +159,11 @@
 unsigned HTMLFormElement::length() const
 {
     unsigned length = 0;
-    for (auto& associatedElement : m_associatedElements) {
+    for (auto& weakElement : m_associatedElements) {
+        auto element = makeRefPtr(weakElement.get());
+        ASSERT(element);
+        auto* associatedElement = element->asFormAssociatedElement();
+        ASSERT(associatedElement);
         if (associatedElement->isEnumeratable())
             ++length;
     }
@@ -310,11 +319,11 @@
 {
     StringPairVector result;
     result.reserveInitialCapacity(m_associatedElements.size());
-    for (auto& associatedElement : m_associatedElements) {
-        auto& element = associatedElement->asHTMLElement();
+    for (auto& weakElement : m_associatedElements) {
+        auto element = makeRefPtr(weakElement.get());
         if (!is<HTMLInputElement>(element))
             continue;
-        auto& input = downcast<HTMLInputElement>(element);
+        auto& input = downcast<HTMLInputElement>(*element);
         if (!input.isTextField())
             continue;
         result.uncheckedAppend({ input.name().string(), input.value() });
@@ -397,9 +406,11 @@
     // the reset operation.
     Vector<Ref<HTMLFormControlElement>> associatedFormControlElements;
     associatedFormControlElements.reserveInitialCapacity(m_associatedElements.size());
-    for (auto* element : m_associatedElements) {
+    for (auto& weakElement : m_associatedElements) {
+        auto* element = weakElement.get();
+        ASSERT(element);
         if (is<HTMLFormControlElement>(element))
-            associatedFormControlElements.uncheckedAppend(*downcast<HTMLFormControlElement>(element));
+            associatedFormControlElements.uncheckedAppend(downcast<HTMLFormControlElement>(*element));
     }
     
     for (auto& associatedFormControlElement : associatedFormControlElements)
@@ -469,7 +480,7 @@
     while (left != right) {
         unsigned middle = left + ((right - left) / 2);
         ASSERT(middle < m_associatedElementsBeforeIndex || middle >= m_associatedElementsAfterIndex);
-        position = element->compareDocumentPosition(m_associatedElements[middle]->asHTMLElement());
+        position = element->compareDocumentPosition(*m_associatedElements[middle]);
         if (position & DOCUMENT_POSITION_FOLLOWING)
             right = middle;
         else
@@ -477,7 +488,7 @@
     }
     
     ASSERT(left < m_associatedElementsBeforeIndex || left >= m_associatedElementsAfterIndex);
-    position = element->compareDocumentPosition(m_associatedElements[left]->asHTMLElement());
+    position = element->compareDocumentPosition(*m_associatedElements[left]);
     if (position & DOCUMENT_POSITION_FOLLOWING)
         return left;
     return left + 1;
@@ -533,7 +544,7 @@
 
 void HTMLFormElement::registerFormElement(FormAssociatedElement* e)
 {
-    m_associatedElements.insert(formElementIndex(e), e);
+    m_associatedElements.insert(formElementIndex(e), makeWeakPtr(e->asHTMLElement()));
 
     if (is<HTMLFormControlElement>(e)) {
         HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*e);
@@ -548,7 +559,7 @@
 
 void HTMLFormElement::removeFormElement(FormAssociatedElement* e)
 {
-    unsigned index = m_associatedElements.find(e);
+    unsigned index = m_associatedElements.find(&e->asHTMLElement());
     ASSERT_WITH_SECURITY_IMPLICATION(index < m_associatedElements.size());
     if (index < m_associatedElementsBeforeIndex)
         --m_associatedElementsBeforeIndex;
@@ -730,14 +741,11 @@
     Ref<HTMLFormElement> protectedThis(*this);
     // Copy m_associatedElements because event handlers called from
     // HTMLFormControlElement::checkValidity() might change m_associatedElements.
-    Vector<RefPtr<FormAssociatedElement>> elements;
-    elements.reserveCapacity(m_associatedElements.size());
-    for (auto& associatedElement : m_associatedElements)
-        elements.append(associatedElement);
+    auto elements = copyAssociatedElementsVector();
     bool hasInvalidControls = false;
     for (auto& element : elements) {
-        if (element->form() == this && is<HTMLFormControlElement>(*element)) {
-            HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*element);
+        if (element->form() == this && is<HTMLFormControlElement>(element.get())) {
+            HTMLFormControlElement& control = downcast<HTMLFormControlElement>(element.get());
             if (!control.checkValidity(&unhandledInvalidControls) && control.form() == this)
                 hasInvalidControls = true;
         }
@@ -764,7 +772,7 @@
     ASSERT_WITH_SECURITY_IMPLICATION(element.form() == this);
 
     if (item->isFormAssociatedElement()) {
-        ASSERT_WITH_SECURITY_IMPLICATION(m_associatedElements.find(static_cast<FormAssociatedElement*>(item)) != notFound);
+        ASSERT_WITH_SECURITY_IMPLICATION(m_associatedElements.find(&element) != notFound);
         return;
     }
 
@@ -865,7 +873,7 @@
     document().formController().restoreControlStateIn(*this);
 }
 
-const Vector<FormAssociatedElement*>& HTMLFormElement::unsafeAssociatedElements() const
+const Vector<WeakPtr<HTMLElement>>& HTMLFormElement::unsafeAssociatedElements() const
 {
     ASSERT(ScriptDisallowedScope::InMainThread::hasDisallowedScope());
     return m_associatedElements;
@@ -873,8 +881,13 @@
 
 Vector<Ref<FormAssociatedElement>> HTMLFormElement::copyAssociatedElementsVector() const
 {
-    return WTF::map(m_associatedElements, [] (auto* rawElement) {
-        return Ref<FormAssociatedElement>(*rawElement);
+    return WTF::map(m_associatedElements, [] (auto& weakElement) {
+        auto element = makeRefPtr(weakElement.get());
+        ASSERT(element);
+        auto* formAssociatedElement = element->asFormAssociatedElement();
+        ASSERT(formAssociatedElement);
+        return Ref<FormAssociatedElement>(*formAssociatedElement);
+        
     });
 }
 
diff --git a/Source/WebCore/html/HTMLFormElement.h b/Source/WebCore/html/HTMLFormElement.h
index 47e39fd..5c5d65e 100644
--- a/Source/WebCore/html/HTMLFormElement.h
+++ b/Source/WebCore/html/HTMLFormElement.h
@@ -115,7 +115,7 @@
 
     RadioButtonGroups& radioButtonGroups() { return m_radioButtonGroups; }
 
-    WEBCORE_EXPORT const Vector<FormAssociatedElement*>& unsafeAssociatedElements() const;
+    WEBCORE_EXPORT const Vector<WeakPtr<HTMLElement>>& unsafeAssociatedElements() const;
     Vector<Ref<FormAssociatedElement>> copyAssociatedElementsVector() const;
     const Vector<WeakPtr<HTMLImageElement>>& imageElements() const { return m_imageElements; }
 
@@ -174,7 +174,7 @@
 
     unsigned m_associatedElementsBeforeIndex { 0 };
     unsigned m_associatedElementsAfterIndex { 0 };
-    Vector<FormAssociatedElement*> m_associatedElements;
+    Vector<WeakPtr<HTMLElement>> m_associatedElements;
     Vector<WeakPtr<HTMLImageElement>> m_imageElements;
     WeakHashSet<HTMLFormControlElement> m_invalidAssociatedFormControls;
     WeakPtr<FormSubmission> m_plannedFormSubmission;
diff --git a/Source/WebCore/html/HTMLObjectElement.cpp b/Source/WebCore/html/HTMLObjectElement.cpp
index d195741..9215c7e 100644
--- a/Source/WebCore/html/HTMLObjectElement.cpp
+++ b/Source/WebCore/html/HTMLObjectElement.cpp
@@ -76,6 +76,11 @@
     return result;
 }
 
+HTMLObjectElement::~HTMLObjectElement()
+{
+    clearForm();
+}
+
 int HTMLObjectElement::defaultTabIndex() const
 {
     return 0;
diff --git a/Source/WebCore/html/HTMLObjectElement.h b/Source/WebCore/html/HTMLObjectElement.h
index 312ec76..6f062aa 100644
--- a/Source/WebCore/html/HTMLObjectElement.h
+++ b/Source/WebCore/html/HTMLObjectElement.h
@@ -58,6 +58,7 @@
 
 private:
     HTMLObjectElement(const QualifiedName&, Document&, HTMLFormElement*);
+    ~HTMLObjectElement();
 
     int defaultTabIndex() const final;
 
diff --git a/Source/WebKitLegacy/mac/ChangeLog b/Source/WebKitLegacy/mac/ChangeLog
index b97b554..800ca13 100644
--- a/Source/WebKitLegacy/mac/ChangeLog
+++ b/Source/WebKitLegacy/mac/ChangeLog
@@ -1,3 +1,14 @@
+2020-04-01  Ryosuke Niwa  <rniwa@webkit.org>
+
+        HTMLFormElement should use WeakPtr to keep track of its associated elements
+        https://bugs.webkit.org/show_bug.cgi?id=209894
+
+        Reviewed by Wenson Hsieh.
+
+        * WebView/WebHTMLRepresentation.mm:
+        (-[WebHTMLRepresentation elementWithName:inForm:]):
+        (-[WebHTMLRepresentation controlsInForm:]):
+
 2020-03-31  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Datalist option's label not used
diff --git a/Source/WebKitLegacy/mac/WebView/WebHTMLRepresentation.mm b/Source/WebKitLegacy/mac/WebView/WebHTMLRepresentation.mm
index 76a575a..1033c9f 100644
--- a/Source/WebKitLegacy/mac/WebView/WebHTMLRepresentation.mm
+++ b/Source/WebKitLegacy/mac/WebView/WebHTMLRepresentation.mm
@@ -284,12 +284,11 @@
         return nil;
 
     ScriptDisallowedScope::InMainThread scriptDisallowedScope;
-    auto& elements = formElement->unsafeAssociatedElements();
     AtomString targetName = name;
-    for (unsigned i = 0; i < elements.size(); i++) {
-        FormAssociatedElement& element = *elements[i];
-        if (element.name() == targetName)
-            return kit(&element.asHTMLElement());
+    for (auto& weakElement : formElement->unsafeAssociatedElements()) {
+        auto element = makeRefPtr(weakElement.get());
+        if (element->asFormAssociatedElement()->name() == targetName)
+            return kit(element.get());
     }
     return nil;
 }
@@ -334,10 +333,10 @@
     NSMutableArray *results = nil;
 
     ScriptDisallowedScope::InMainThread scriptDisallowedScope;
-    auto& elements = formElement->unsafeAssociatedElements();
-    for (unsigned i = 0; i < elements.size(); i++) {
-        if (elements[i]->isEnumeratable()) { // Skip option elements, other duds
-            DOMElement *element = kit(&elements[i]->asHTMLElement());
+    for (auto& weakElement : formElement->unsafeAssociatedElements()) {
+        auto coreElement = makeRefPtr(weakElement.get());
+        if (coreElement->asFormAssociatedElement()->isEnumeratable()) { // Skip option elements, other duds
+            DOMElement *element = kit(coreElement.get());
             if (!results)
                 results = [NSMutableArray arrayWithObject:element];
             else