[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / html / HTMLFormControlsCollection.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010, 2011, 2012 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "HTMLFormControlsCollection.h"
25
26 #include "FormAssociatedElement.h"
27 #include "HTMLFormElement.h"
28 #include "HTMLImageElement.h"
29 #include "HTMLNames.h"
30 #include "ScriptDisallowedScope.h"
31 #include <wtf/IsoMallocInlines.h>
32
33 namespace WebCore {
34
35 using namespace HTMLNames;
36
37 // Since the collections are to be "live", we have to do the
38 // calculation every time if anything has changed.
39
40 WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLFormControlsCollection);
41
42 HTMLFormControlsCollection::HTMLFormControlsCollection(ContainerNode& ownerNode)
43     : CachedHTMLCollection<HTMLFormControlsCollection, CollectionTypeTraits<FormControls>::traversalType>(ownerNode, FormControls)
44     , m_cachedElement(nullptr)
45     , m_cachedElementOffsetInArray(0)
46 {
47     ASSERT(is<HTMLFormElement>(ownerNode));
48 }
49
50 Ref<HTMLFormControlsCollection> HTMLFormControlsCollection::create(ContainerNode& ownerNode, CollectionType)
51 {
52     return adoptRef(*new HTMLFormControlsCollection(ownerNode));
53 }
54
55 HTMLFormControlsCollection::~HTMLFormControlsCollection() = default;
56
57 Optional<Variant<RefPtr<RadioNodeList>, RefPtr<Element>>> HTMLFormControlsCollection::namedItemOrItems(const String& name) const
58 {
59     auto namedItems = this->namedItems(name);
60
61     if (namedItems.isEmpty())
62         return WTF::nullopt;
63     if (namedItems.size() == 1)
64         return Variant<RefPtr<RadioNodeList>, RefPtr<Element>> { RefPtr<Element> { WTFMove(namedItems[0]) } };
65
66     return Variant<RefPtr<RadioNodeList>, RefPtr<Element>> { RefPtr<RadioNodeList> { ownerNode().radioNodeList(name) } };
67 }
68
69 const Vector<FormAssociatedElement*>& HTMLFormControlsCollection::unsafeFormControlElements() const
70 {
71     return ownerNode().unsafeAssociatedElements();
72 }
73
74 Vector<Ref<FormAssociatedElement>> HTMLFormControlsCollection::copyFormControlElementsVector() const
75 {
76     return ownerNode().copyAssociatedElementsVector();
77 }
78
79 static unsigned findFormAssociatedElement(const Vector<FormAssociatedElement*>& elements, const Element& element)
80 {
81     for (unsigned i = 0; i < elements.size(); ++i) {
82         auto& associatedElement = *elements[i];
83         if (associatedElement.isEnumeratable() && &associatedElement.asHTMLElement() == &element)
84             return i;
85     }
86     return elements.size();
87 }
88
89 HTMLElement* HTMLFormControlsCollection::customElementAfter(Element* current) const
90 {
91     ScriptDisallowedScope::InMainThread scriptDisallowedScope;
92     auto& elements = unsafeFormControlElements();
93     unsigned start;
94     if (!current)
95         start = 0;
96     else if (m_cachedElement == current)
97         start = m_cachedElementOffsetInArray + 1;
98     else
99         start = findFormAssociatedElement(elements, *current) + 1;
100
101     for (unsigned i = start; i < elements.size(); ++i) {
102         FormAssociatedElement& element = *elements[i];
103         if (element.isEnumeratable()) {
104             m_cachedElement = &element.asHTMLElement();
105             m_cachedElementOffsetInArray = i;
106             return &element.asHTMLElement();
107         }
108     }
109     return nullptr;
110 }
111
112 HTMLFormElement& HTMLFormControlsCollection::ownerNode() const
113 {
114     return downcast<HTMLFormElement>(CachedHTMLCollection<HTMLFormControlsCollection, CollectionTypeTraits<FormControls>::traversalType>::ownerNode());
115 }
116
117 void HTMLFormControlsCollection::updateNamedElementCache() const
118 {
119     if (hasNamedElementCache())
120         return;
121
122     auto cache = makeUnique<CollectionNamedElementCache>();
123
124     HashSet<AtomStringImpl*> foundInputElements;
125
126     ScriptDisallowedScope::InMainThread scriptDisallowedScope;
127     for (auto& elementPtr : unsafeFormControlElements()) {
128         FormAssociatedElement& associatedElement = *elementPtr;
129         if (associatedElement.isEnumeratable()) {
130             HTMLElement& element = associatedElement.asHTMLElement();
131             const AtomString& id = element.getIdAttribute();
132             if (!id.isEmpty()) {
133                 cache->appendToIdCache(id, element);
134                 foundInputElements.add(id.impl());
135             }
136             const AtomString& name = element.getNameAttribute();
137             if (!name.isEmpty() && id != name) {
138                 cache->appendToNameCache(name, element);
139                 foundInputElements.add(name.impl());
140             }
141         }
142     }
143
144     for (auto& elementPtr : ownerNode().imageElements()) {
145         if (!elementPtr)
146             continue;
147         HTMLImageElement& element = *elementPtr;
148         const AtomString& id = element.getIdAttribute();
149         if (!id.isEmpty() && !foundInputElements.contains(id.impl()))
150             cache->appendToIdCache(id, element);
151         const AtomString& name = element.getNameAttribute();
152         if (!name.isEmpty() && id != name && !foundInputElements.contains(name.impl()))
153             cache->appendToNameCache(name, element);
154     }
155
156     setNamedItemCache(WTFMove(cache));
157 }
158
159 void HTMLFormControlsCollection::invalidateCacheForDocument(Document& document)
160 {
161     CachedHTMLCollection<HTMLFormControlsCollection, CollectionTypeTraits<FormControls>::traversalType>::invalidateCacheForDocument(document);
162     m_cachedElement = nullptr;
163     m_cachedElementOffsetInArray = 0;
164 }
165
166 }