RadioNodeList support in HTMLFormElement::elements
[WebKit-https.git] / Source / WebCore / html / HTMLFormCollection.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 "HTMLFormCollection.h"
25
26 #include "HTMLFormControlElement.h"
27 #include "HTMLFormElement.h"
28 #include "HTMLImageElement.h"
29 #include "HTMLNames.h"
30
31 namespace WebCore {
32
33 using namespace HTMLNames;
34
35 // Since the collections are to be "live", we have to do the
36 // calculation every time if anything has changed.
37
38 HTMLFormCollection::HTMLFormCollection(HTMLFormElement* form)
39     : HTMLCollection(form, FormControls)
40     , currentPos(0)
41 {
42 }
43
44 PassOwnPtr<HTMLFormCollection> HTMLFormCollection::create(HTMLFormElement* form)
45 {
46     return adoptPtr(new HTMLFormCollection(form));
47 }
48
49 HTMLFormCollection::~HTMLFormCollection()
50 {
51 }
52
53 unsigned HTMLFormCollection::calcLength() const
54 {
55     return static_cast<HTMLFormElement*>(base())->length();
56 }
57
58 Node* HTMLFormCollection::item(unsigned index) const
59 {
60     invalidateCacheIfNeeded();
61
62     if (m_cache.current && m_cache.position == index)
63         return m_cache.current;
64
65     if (m_cache.hasLength && m_cache.length <= index)
66         return 0;
67
68     if (!m_cache.current || m_cache.position > index) {
69         m_cache.current = 0;
70         m_cache.position = 0;
71         m_cache.elementsArrayPosition = 0;
72     }
73
74     Vector<FormAssociatedElement*>& elementsArray = static_cast<HTMLFormElement*>(base())->m_associatedElements;
75     unsigned currentIndex = m_cache.position;
76     
77     for (unsigned i = m_cache.elementsArrayPosition; i < elementsArray.size(); i++) {
78         if (elementsArray[i]->isEnumeratable()) {
79             HTMLElement* element = toHTMLElement(elementsArray[i]);
80             if (index == currentIndex) {
81                 m_cache.position = index;
82                 m_cache.current = element;
83                 m_cache.elementsArrayPosition = i;
84                 return element;
85             }
86
87             currentIndex++;
88         }
89     }
90
91     return 0;
92 }
93
94 Element* HTMLFormCollection::getNamedItem(const QualifiedName& attrName, const AtomicString& name) const
95 {
96     m_cache.position = 0;
97     return getNamedFormItem(attrName, name, 0);
98 }
99
100 Element* HTMLFormCollection::getNamedFormItem(const QualifiedName& attrName, const String& name, int duplicateNumber) const
101 {
102     HTMLFormElement* form = static_cast<HTMLFormElement*>(base());
103
104     if (!form)
105         return 0;
106
107     bool foundInputElements = false;
108     for (unsigned i = 0; i < form->m_associatedElements.size(); ++i) {
109         FormAssociatedElement* associatedElement = form->m_associatedElements[i];
110         HTMLElement* element = toHTMLElement(associatedElement);
111         if (associatedElement->isEnumeratable() && element->getAttribute(attrName) == name) {
112             foundInputElements = true;
113             if (!duplicateNumber)
114                 return element;
115             --duplicateNumber;
116         }
117     }
118
119     if (!foundInputElements) {
120         for (unsigned i = 0; i < form->m_imageElements.size(); ++i) {
121             HTMLImageElement* element = form->m_imageElements[i];
122             if (element->getAttribute(attrName) == name) {
123                 if (!duplicateNumber)
124                     return element;
125                 --duplicateNumber;
126             }
127         }
128     }
129
130     return 0;
131 }
132
133 Node* HTMLFormCollection::nextItem() const
134 {
135     return item(m_cache.position + 1);
136 }
137
138 Node* HTMLFormCollection::namedItem(const AtomicString& name) const
139 {
140     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
141     // This method first searches for an object with a matching id
142     // attribute. If a match is not found, the method then searches for an
143     // object with a matching name attribute, but only on those elements
144     // that are allowed a name attribute.
145     invalidateCacheIfNeeded();
146     m_cache.current = getNamedItem(idAttr, name);
147     if (m_cache.current)
148         return m_cache.current;
149     m_cache.current = getNamedItem(nameAttr, name);
150     return m_cache.current;
151 }
152
153 void HTMLFormCollection::updateNameCache() const
154 {
155     if (m_cache.hasNameCache)
156         return;
157
158     HashSet<AtomicStringImpl*> foundInputElements;
159
160     HTMLFormElement* f = static_cast<HTMLFormElement*>(base());
161
162     for (unsigned i = 0; i < f->m_associatedElements.size(); ++i) {
163         FormAssociatedElement* associatedElement = f->m_associatedElements[i];
164         if (associatedElement->isEnumeratable()) {
165             HTMLElement* element = toHTMLElement(associatedElement);
166             const AtomicString& idAttrVal = element->getIdAttribute();
167             const AtomicString& nameAttrVal = element->getNameAttribute();
168             if (!idAttrVal.isEmpty()) {
169                 append(m_cache.idCache, idAttrVal, element);
170                 foundInputElements.add(idAttrVal.impl());
171             }
172             if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal) {
173                 append(m_cache.nameCache, nameAttrVal, element);
174                 foundInputElements.add(nameAttrVal.impl());
175             }
176         }
177     }
178
179     for (unsigned i = 0; i < f->m_imageElements.size(); ++i) {
180         HTMLImageElement* element = f->m_imageElements[i];
181         const AtomicString& idAttrVal = element->getIdAttribute();
182         const AtomicString& nameAttrVal = element->getNameAttribute();
183         if (!idAttrVal.isEmpty() && !foundInputElements.contains(idAttrVal.impl()))
184             append(m_cache.idCache, idAttrVal, element);
185         if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && !foundInputElements.contains(nameAttrVal.impl()))
186             append(m_cache.nameCache, nameAttrVal, element);
187     }
188
189     m_cache.hasNameCache = true;
190 }
191
192 }