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.
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.
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.
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.
25 #include "core/html/HTMLFormControlsCollection.h"
27 #include "bindings/core/v8/UnionTypesCore.h"
28 #include "core/HTMLNames.h"
29 #include "core/frame/UseCounter.h"
30 #include "core/html/HTMLFieldSetElement.h"
31 #include "core/html/HTMLFormElement.h"
32 #include "core/html/HTMLImageElement.h"
33 #include "wtf/HashSet.h"
37 using namespace HTMLNames
;
39 // Since the collections are to be "live", we have to do the
40 // calculation every time if anything has changed.
42 HTMLFormControlsCollection::HTMLFormControlsCollection(ContainerNode
& ownerNode
)
43 : HTMLCollection(ownerNode
, FormControls
, OverridesItemAfter
)
44 , m_cachedElement(nullptr)
45 , m_cachedElementOffsetInArray(0)
47 ASSERT(isHTMLFormElement(ownerNode
) || isHTMLFieldSetElement(ownerNode
));
50 PassRefPtrWillBeRawPtr
<HTMLFormControlsCollection
> HTMLFormControlsCollection::create(ContainerNode
& ownerNode
, CollectionType type
)
52 ASSERT_UNUSED(type
, type
== FormControls
);
53 return adoptRefWillBeNoop(new HTMLFormControlsCollection(ownerNode
));
56 HTMLFormControlsCollection::~HTMLFormControlsCollection()
60 const FormAssociatedElement::List
& HTMLFormControlsCollection::formControlElements() const
62 ASSERT(isHTMLFormElement(ownerNode()) || isHTMLFieldSetElement(ownerNode()));
63 if (isHTMLFormElement(ownerNode()))
64 return toHTMLFormElement(ownerNode()).associatedElements();
65 return toHTMLFieldSetElement(ownerNode()).associatedElements();
68 const WillBeHeapVector
<RawPtrWillBeMember
<HTMLImageElement
>>& HTMLFormControlsCollection::formImageElements() const
70 return toHTMLFormElement(ownerNode()).imageElements();
73 static unsigned findFormAssociatedElement(const FormAssociatedElement::List
& associatedElements
, Element
* element
)
76 for (; i
< associatedElements
.size(); ++i
) {
77 FormAssociatedElement
* associatedElement
= associatedElements
[i
];
78 if (associatedElement
->isEnumeratable() && toHTMLElement(associatedElement
) == element
)
84 HTMLElement
* HTMLFormControlsCollection::virtualItemAfter(Element
* previous
) const
86 const FormAssociatedElement::List
& associatedElements
= formControlElements();
90 else if (m_cachedElement
== previous
)
91 offset
= m_cachedElementOffsetInArray
+ 1;
93 offset
= findFormAssociatedElement(associatedElements
, previous
) + 1;
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
;
106 void HTMLFormControlsCollection::invalidateCache(Document
* oldDocument
) const
108 HTMLCollection::invalidateCache(oldDocument
);
109 m_cachedElement
= nullptr;
110 m_cachedElementOffsetInArray
= 0;
113 static HTMLElement
* firstNamedItem(const FormAssociatedElement::List
& elementsArray
,
114 const WillBeHeapVector
<RawPtrWillBeMember
<HTMLImageElement
>>* imageElementsArray
, const QualifiedName
& attrName
, const String
& name
)
116 ASSERT(attrName
== idAttr
|| attrName
== nameAttr
);
118 for (unsigned i
= 0; i
< elementsArray
.size(); ++i
) {
119 HTMLElement
* element
= toHTMLElement(elementsArray
[i
]);
120 if (elementsArray
[i
]->isEnumeratable() && element
->fastGetAttribute(attrName
) == name
)
124 if (!imageElementsArray
)
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
);
138 HTMLElement
* HTMLFormControlsCollection::namedItem(const AtomicString
& name
) const
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
))
149 return firstNamedItem(formControlElements(), imagesElements
, nameAttr
, name
);
152 void HTMLFormControlsCollection::updateIdNameCache() const
154 if (hasValidIdNameCache())
157 OwnPtrWillBeRawPtr
<NamedItemCache
> cache
= NamedItemCache::create();
158 HashSet
<StringImpl
*> foundInputElements
;
160 const FormAssociatedElement::List
& elementsArray
= formControlElements();
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());
172 if (!nameAttrVal
.isEmpty() && idAttrVal
!= nameAttrVal
) {
173 cache
->addElementWithName(nameAttrVal
, element
);
174 foundInputElements
.add(nameAttrVal
.impl());
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
);
192 // Set the named item cache last as traversing the tree may cause cache invalidation.
193 setNamedItemCache(cache
.release());
196 void HTMLFormControlsCollection::namedGetter(const AtomicString
& name
, RadioNodeListOrElement
& returnValue
)
198 WillBeHeapVector
<RefPtrWillBeMember
<Element
>> namedItems
;
199 this->namedItems(name
, namedItems
);
201 if (namedItems
.isEmpty())
204 if (namedItems
.size() == 1) {
205 returnValue
.setElement(namedItems
.at(0));
209 returnValue
.setRadioNodeList(ownerNode().radioNodeList(name
));
212 void HTMLFormControlsCollection::supportedPropertyNames(Vector
<String
>& names
)
214 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#htmlformcontrolscollection-0:
215 // The supported property names consist of the non-empty values of all the id and name attributes
216 // of all the elements represented by the collection, in tree order, ignoring later duplicates,
217 // with the id of an element preceding its name if it contributes both, they differ from each
218 // other, and neither is the duplicate of an earlier entry.
219 HashSet
<AtomicString
> existingNames
;
220 unsigned length
= this->length();
221 for (unsigned i
= 0; i
< length
; ++i
) {
222 HTMLElement
* element
= item(i
);
224 const AtomicString
& idAttribute
= element
->getIdAttribute();
225 if (!idAttribute
.isEmpty()) {
226 HashSet
<AtomicString
>::AddResult addResult
= existingNames
.add(idAttribute
);
227 if (addResult
.isNewEntry
)
228 names
.append(idAttribute
);
230 const AtomicString
& nameAttribute
= element
->getNameAttribute();
231 if (!nameAttribute
.isEmpty()) {
232 HashSet
<AtomicString
>::AddResult addResult
= existingNames
.add(nameAttribute
);
233 if (addResult
.isNewEntry
)
234 names
.append(nameAttribute
);
239 DEFINE_TRACE(HTMLFormControlsCollection
)
241 visitor
->trace(m_cachedElement
);
242 HTMLCollection::trace(visitor
);