7d453f757c1d71dfb1781c60e6b72420d49cf1d3
[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 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 "CollectionCache.h"
27 #include "HTMLFormControlElement.h"
28 #include "HTMLFormElement.h"
29 #include "HTMLImageElement.h"
30 #include "HTMLNames.h"
31
32 namespace WebCore {
33
34 using namespace HTMLNames;
35
36 // Since the collections are to be "live", we have to do the
37 // calculation every time if anything has changed.
38
39 HTMLFormCollection::HTMLFormCollection(HTMLFormElement* form)
40     : HTMLCollection(form, OtherCollection, 0, /* retainBaseNode */ false)
41 {
42 }
43
44 PassRefPtr<HTMLFormCollection> HTMLFormCollection::create(HTMLFormElement* form)
45 {
46     return adoptRef(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     resetCollectionInfo();
61
62     if (info()->current && info()->position == index)
63         return info()->current;
64
65     if (info()->hasLength && info()->length <= index)
66         return 0;
67
68     if (!info()->current || info()->position > index) {
69         info()->current = 0;
70         info()->position = 0;
71         info()->elementsArrayPosition = 0;
72     }
73
74     Vector<FormAssociatedElement*>& elementsArray = static_cast<HTMLFormElement*>(base())->m_associatedElements;
75     unsigned currentIndex = info()->position;
76     
77     for (unsigned i = info()->elementsArrayPosition; i < elementsArray.size(); i++) {
78         if (elementsArray[i]->isEnumeratable()) {
79             HTMLElement* element = toHTMLElement(elementsArray[i]);
80             if (index == currentIndex) {
81                 info()->position = index;
82                 info()->current = element;
83                 info()->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     info()->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     bool foundInputElements = false;
105     for (unsigned i = 0; i < form->m_associatedElements.size(); ++i) {
106         FormAssociatedElement* associatedElement = form->m_associatedElements[i];
107         HTMLElement* element = toHTMLElement(associatedElement);
108         if (associatedElement->isEnumeratable() && element->getAttribute(attrName) == name) {
109             foundInputElements = true;
110             if (!duplicateNumber)
111                 return element;
112             --duplicateNumber;
113         }
114     }
115
116     if (!foundInputElements) {
117         for (unsigned i = 0; i < form->m_imageElements.size(); ++i) {
118             HTMLImageElement* element = form->m_imageElements[i];
119             if (element->getAttribute(attrName) == name) {
120                 if (!duplicateNumber)
121                     return element;
122                 --duplicateNumber;
123             }
124         }
125     }
126
127     return 0;
128 }
129
130 Node* HTMLFormCollection::nextItem() const
131 {
132     return item(info()->position + 1);
133 }
134
135 Node* HTMLFormCollection::namedItem(const AtomicString& name) const
136 {
137     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
138     // This method first searches for an object with a matching id
139     // attribute. If a match is not found, the method then searches for an
140     // object with a matching name attribute, but only on those elements
141     // that are allowed a name attribute.
142     resetCollectionInfo();
143     info()->current = getNamedItem(idAttr, name);
144     if (info()->current)
145         return info()->current;
146     info()->current = getNamedItem(nameAttr, name);
147     return info()->current;
148 }
149
150 void HTMLFormCollection::updateNameCache() const
151 {
152     if (info()->hasNameCache)
153         return;
154
155     HashSet<AtomicStringImpl*> foundInputElements;
156
157     HTMLFormElement* f = static_cast<HTMLFormElement*>(base());
158
159     for (unsigned i = 0; i < f->m_associatedElements.size(); ++i) {
160         FormAssociatedElement* associatedElement = f->m_associatedElements[i];
161         if (associatedElement->isEnumeratable()) {
162             HTMLElement* element = toHTMLElement(associatedElement);
163             const AtomicString& idAttrVal = element->getIdAttribute();
164             const AtomicString& nameAttrVal = element->getAttribute(nameAttr);
165             if (!idAttrVal.isEmpty()) {
166                 append(info()->idCache, idAttrVal, element);
167                 foundInputElements.add(idAttrVal.impl());
168             }
169             if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal) {
170                 append(info()->nameCache, nameAttrVal, element);
171                 foundInputElements.add(nameAttrVal.impl());
172             }
173         }
174     }
175
176     for (unsigned i = 0; i < f->m_imageElements.size(); ++i) {
177         HTMLImageElement* element = f->m_imageElements[i];
178         const AtomicString& idAttrVal = element->getIdAttribute();
179         const AtomicString& nameAttrVal = element->getAttribute(nameAttr);
180         if (!idAttrVal.isEmpty() && !foundInputElements.contains(idAttrVal.impl()))
181             append(info()->idCache, idAttrVal, element);
182         if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && !foundInputElements.contains(nameAttrVal.impl()))
183             append(info()->nameCache, nameAttrVal, element);
184     }
185
186     info()->hasNameCache = true;
187 }
188
189 }