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
33namespace WebCore {
34
35using 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
40WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLFormControlsCollection);
41
42HTMLFormControlsCollection::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
50Ref<HTMLFormControlsCollection> HTMLFormControlsCollection::create(ContainerNode& ownerNode, CollectionType)
51{
52 return adoptRef(*new HTMLFormControlsCollection(ownerNode));
53}
54
55HTMLFormControlsCollection::~HTMLFormControlsCollection() = default;
56
57Optional<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
69const Vector<FormAssociatedElement*>& HTMLFormControlsCollection::unsafeFormControlElements() const
70{
71 return ownerNode().unsafeAssociatedElements();
72}
73
74Vector<Ref<FormAssociatedElement>> HTMLFormControlsCollection::copyFormControlElementsVector() const
75{
76 return ownerNode().copyAssociatedElementsVector();
77}
78
79static 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
89HTMLElement* 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
112HTMLFormElement& HTMLFormControlsCollection::ownerNode() const
113{
114 return downcast<HTMLFormElement>(CachedHTMLCollection<HTMLFormControlsCollection, CollectionTypeTraits<FormControls>::traversalType>::ownerNode());
115}
116
117void HTMLFormControlsCollection::updateNamedElementCache() const
118{
119 if (hasNamedElementCache())
120 return;
121
122 auto cache = std::make_unique<CollectionNamedElementCache>();
123
124 HashSet<AtomicStringImpl*> 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 AtomicString& id = element.getIdAttribute();
132 if (!id.isEmpty()) {
133 cache->appendToIdCache(id, element);
134 foundInputElements.add(id.impl());
135 }
136 const AtomicString& 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 AtomicString& id = element.getIdAttribute();
149 if (!id.isEmpty() && !foundInputElements.contains(id.impl()))
150 cache->appendToIdCache(id, element);
151 const AtomicString& 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
159void HTMLFormControlsCollection::invalidateCacheForDocument(Document& document)
160{
161 CachedHTMLCollection<HTMLFormControlsCollection, CollectionTypeTraits<FormControls>::traversalType>::invalidateCacheForDocument(document);
162 m_cachedElement = nullptr;
163 m_cachedElementOffsetInArray = 0;
164}
165
166}
167