• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * Copyright (C) 2014 Samsung Electronics. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23 
24 #include "config.h"
25 #include "core/html/HTMLFormControlsCollection.h"
26 
27 #include "core/HTMLNames.h"
28 #include "core/frame/UseCounter.h"
29 #include "core/html/HTMLFieldSetElement.h"
30 #include "core/html/HTMLFormElement.h"
31 #include "core/html/HTMLImageElement.h"
32 #include "wtf/HashSet.h"
33 
34 namespace WebCore {
35 
36 using namespace HTMLNames;
37 
38 // Since the collections are to be "live", we have to do the
39 // calculation every time if anything has changed.
40 
HTMLFormControlsCollection(ContainerNode & ownerNode)41 HTMLFormControlsCollection::HTMLFormControlsCollection(ContainerNode& ownerNode)
42     : HTMLCollection(ownerNode, FormControls, OverridesItemAfter)
43     , m_cachedElement(nullptr)
44     , m_cachedElementOffsetInArray(0)
45 {
46     ASSERT(isHTMLFormElement(ownerNode) || isHTMLFieldSetElement(ownerNode));
47     ScriptWrappable::init(this);
48 }
49 
create(ContainerNode & ownerNode,CollectionType type)50 PassRefPtrWillBeRawPtr<HTMLFormControlsCollection> HTMLFormControlsCollection::create(ContainerNode& ownerNode, CollectionType type)
51 {
52     ASSERT_UNUSED(type, type == FormControls);
53     return adoptRefWillBeNoop(new HTMLFormControlsCollection(ownerNode));
54 }
55 
~HTMLFormControlsCollection()56 HTMLFormControlsCollection::~HTMLFormControlsCollection()
57 {
58 }
59 
formControlElements() const60 const FormAssociatedElement::List& HTMLFormControlsCollection::formControlElements() const
61 {
62     ASSERT(isHTMLFormElement(ownerNode()) || isHTMLFieldSetElement(ownerNode()));
63     if (isHTMLFormElement(ownerNode()))
64         return toHTMLFormElement(ownerNode()).associatedElements();
65     return toHTMLFieldSetElement(ownerNode()).associatedElements();
66 }
67 
formImageElements() const68 const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >& HTMLFormControlsCollection::formImageElements() const
69 {
70     return toHTMLFormElement(ownerNode()).imageElements();
71 }
72 
findFormAssociatedElement(const FormAssociatedElement::List & associatedElements,Element * element)73 static unsigned findFormAssociatedElement(const FormAssociatedElement::List& associatedElements, Element* element)
74 {
75     unsigned i = 0;
76     for (; i < associatedElements.size(); ++i) {
77         FormAssociatedElement* associatedElement = associatedElements[i];
78         if (associatedElement->isEnumeratable() && toHTMLElement(associatedElement) == element)
79             break;
80     }
81     return i;
82 }
83 
virtualItemAfter(Element * previous) const84 Element* HTMLFormControlsCollection::virtualItemAfter(Element* previous) const
85 {
86     const FormAssociatedElement::List& associatedElements = formControlElements();
87     unsigned offset;
88     if (!previous)
89         offset = 0;
90     else if (m_cachedElement == previous)
91         offset = m_cachedElementOffsetInArray + 1;
92     else
93         offset = findFormAssociatedElement(associatedElements, previous) + 1;
94 
95     for (unsigned i = offset; i < associatedElements.size(); ++i) {
96         FormAssociatedElement* associatedElement = associatedElements[i];
97         if (associatedElement->isEnumeratable()) {
98             m_cachedElement = toHTMLElement(associatedElement);
99             m_cachedElementOffsetInArray = i;
100             return m_cachedElement;
101         }
102     }
103     return 0;
104 }
105 
invalidateCache(Document * oldDocument) const106 void HTMLFormControlsCollection::invalidateCache(Document* oldDocument) const
107 {
108     HTMLCollection::invalidateCache(oldDocument);
109     m_cachedElement = nullptr;
110     m_cachedElementOffsetInArray = 0;
111 }
112 
firstNamedItem(const FormAssociatedElement::List & elementsArray,const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement>> * imageElementsArray,const QualifiedName & attrName,const String & name)113 static HTMLElement* firstNamedItem(const FormAssociatedElement::List& elementsArray,
114     const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >* imageElementsArray, const QualifiedName& attrName, const String& name)
115 {
116     ASSERT(attrName == idAttr || attrName == nameAttr);
117 
118     for (unsigned i = 0; i < elementsArray.size(); ++i) {
119         HTMLElement* element = toHTMLElement(elementsArray[i]);
120         if (elementsArray[i]->isEnumeratable() && element->fastGetAttribute(attrName) == name)
121             return element;
122     }
123 
124     if (!imageElementsArray)
125         return 0;
126 
127     for (unsigned i = 0; i < imageElementsArray->size(); ++i) {
128         HTMLImageElement* element = (*imageElementsArray)[i];
129         if (element->fastGetAttribute(attrName) == name) {
130             UseCounter::count(element->document(), UseCounter::FormNameAccessForImageElement);
131             return element;
132         }
133     }
134 
135     return 0;
136 }
137 
namedItem(const AtomicString & name) const138 Element* HTMLFormControlsCollection::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     const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >* imagesElements = isHTMLFieldSetElement(ownerNode()) ? 0 : &formImageElements();
146     if (HTMLElement* item = firstNamedItem(formControlElements(), imagesElements, idAttr, name))
147         return item;
148 
149     return firstNamedItem(formControlElements(), imagesElements, nameAttr, name);
150 }
151 
updateIdNameCache() const152 void HTMLFormControlsCollection::updateIdNameCache() const
153 {
154     if (hasValidIdNameCache())
155         return;
156 
157     OwnPtrWillBeRawPtr<NamedItemCache> cache = NamedItemCache::create();
158     HashSet<StringImpl*> foundInputElements;
159 
160     const FormAssociatedElement::List& elementsArray = formControlElements();
161 
162     for (unsigned i = 0; i < elementsArray.size(); ++i) {
163         FormAssociatedElement* associatedElement = elementsArray[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                 cache->addElementWithId(idAttrVal, element);
170                 foundInputElements.add(idAttrVal.impl());
171             }
172             if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal) {
173                 cache->addElementWithName(nameAttrVal, element);
174                 foundInputElements.add(nameAttrVal.impl());
175             }
176         }
177     }
178 
179     if (isHTMLFormElement(ownerNode())) {
180         const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >& imageElementsArray = formImageElements();
181         for (unsigned i = 0; i < imageElementsArray.size(); ++i) {
182             HTMLImageElement* element = imageElementsArray[i];
183             const AtomicString& idAttrVal = element->getIdAttribute();
184             const AtomicString& nameAttrVal = element->getNameAttribute();
185             if (!idAttrVal.isEmpty() && !foundInputElements.contains(idAttrVal.impl()))
186                 cache->addElementWithId(idAttrVal, element);
187             if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && !foundInputElements.contains(nameAttrVal.impl()))
188                 cache->addElementWithName(nameAttrVal, element);
189         }
190     }
191 
192     // Set the named item cache last as traversing the tree may cause cache invalidation.
193     setNamedItemCache(cache.release());
194 }
195 
namedGetter(const AtomicString & name,bool & radioNodeListEnabled,RefPtrWillBeRawPtr<RadioNodeList> & radioNodeList,bool & elementEnabled,RefPtrWillBeRawPtr<Element> & element)196 void HTMLFormControlsCollection::namedGetter(const AtomicString& name, bool& radioNodeListEnabled, RefPtrWillBeRawPtr<RadioNodeList>& radioNodeList, bool& elementEnabled, RefPtrWillBeRawPtr<Element>& element)
197 {
198     WillBeHeapVector<RefPtrWillBeMember<Element> > namedItems;
199     this->namedItems(name, namedItems);
200 
201     if (namedItems.isEmpty())
202         return;
203 
204     if (namedItems.size() == 1) {
205         elementEnabled = true;
206         element = namedItems.at(0);
207         return;
208     }
209 
210     radioNodeListEnabled = true;
211     radioNodeList = ownerNode().radioNodeList(name);
212 }
213 
supportedPropertyNames(Vector<String> & names)214 void HTMLFormControlsCollection::supportedPropertyNames(Vector<String>& names)
215 {
216     // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#htmlformcontrolscollection-0:
217     // The supported property names consist of the non-empty values of all the id and name attributes
218     // of all the elements represented by the collection, in tree order, ignoring later duplicates,
219     // with the id of an element preceding its name if it contributes both, they differ from each
220     // other, and neither is the duplicate of an earlier entry.
221     HashSet<AtomicString> existingNames;
222     unsigned length = this->length();
223     for (unsigned i = 0; i < length; ++i) {
224         Element* element = item(i);
225         ASSERT(element);
226         const AtomicString& idAttribute = element->getIdAttribute();
227         if (!idAttribute.isEmpty()) {
228             HashSet<AtomicString>::AddResult addResult = existingNames.add(idAttribute);
229             if (addResult.isNewEntry)
230                 names.append(idAttribute);
231         }
232         const AtomicString& nameAttribute = element->getNameAttribute();
233         if (!nameAttribute.isEmpty()) {
234             HashSet<AtomicString>::AddResult addResult = existingNames.add(nameAttribute);
235             if (addResult.isNewEntry)
236                 names.append(nameAttribute);
237         }
238     }
239 }
240 
trace(Visitor * visitor)241 void HTMLFormControlsCollection::trace(Visitor* visitor)
242 {
243     visitor->trace(m_cachedElement);
244     HTMLCollection::trace(visitor);
245 }
246 
247 }
248