Build fix after r122115 and some cleanups.
[WebKit-https.git] / Source / WebCore / html / HTMLPropertiesCollection.cpp
1 /*
2  * Copyright (c) 2011 Motorola Mobility, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification,
5  * are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation and/or
12  * other materials provided with the distribution.
13  *
14  * Neither the name of Motorola Mobility, Inc. nor the names of its contributors may
15  * be used to endorse or promote products derived from this software without
16  * specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #if ENABLE(MICRODATA)
34
35 #include "HTMLPropertiesCollection.h"
36
37 #include "DOMSettableTokenList.h"
38 #include "DOMStringList.h"
39 #include "HTMLElement.h"
40 #include "HTMLNames.h"
41 #include "Node.h"
42 #include "StaticNodeList.h"
43
44 namespace WebCore {
45
46 using namespace HTMLNames;
47
48 PassRefPtr<HTMLPropertiesCollection> HTMLPropertiesCollection::create(Node* itemNode)
49 {
50     return adoptRef(new HTMLPropertiesCollection(itemNode));
51 }
52
53 HTMLPropertiesCollection::HTMLPropertiesCollection(Node* itemNode)
54     : HTMLCollection(itemNode, ItemProperties)
55 {
56     m_cache.clear();
57 }
58
59 HTMLPropertiesCollection::~HTMLPropertiesCollection()
60 {
61 }
62
63 void HTMLPropertiesCollection::invalidateCacheIfNeeded() const
64 {
65     uint64_t docversion = base()->document()->domTreeVersion();
66
67     if (m_cache.version == docversion)
68         return;
69
70     m_cache.clear();
71     m_cache.version = docversion;
72 }
73
74 void HTMLPropertiesCollection::updateRefElements() const
75 {
76     if (m_cache.hasItemRefElements)
77         return;
78
79     Vector<Element*> itemRefElements;
80     HTMLElement* baseElement = toHTMLElement(base());
81
82     if (!baseElement->fastHasAttribute(itemrefAttr)) {
83         itemRefElements.append(baseElement);
84         m_cache.setItemRefElements(itemRefElements);
85         return;
86     }
87
88     DOMSettableTokenList* itemRef = baseElement->itemRef();
89     RefPtr<DOMSettableTokenList> processedItemRef = DOMSettableTokenList::create();
90     Node* rootNode = baseElement->treeScope()->rootNode();
91
92     for (Node* current = rootNode->firstChild(); current; current = current->traverseNextNode(rootNode)) {
93         if (!current->isHTMLElement())
94             continue;
95         HTMLElement* element = toHTMLElement(current);
96
97         if (element == baseElement) {
98             itemRefElements.append(element);
99             continue;
100         }
101
102         const AtomicString& id = element->getIdAttribute();
103         if (!processedItemRef->tokens().contains(id) && itemRef->tokens().contains(id)) {
104             processedItemRef->setValue(id);
105             if (!element->isDescendantOf(baseElement))
106                 itemRefElements.append(element);
107         }
108     }
109
110     m_cache.setItemRefElements(itemRefElements);
111 }
112
113 static Node* nextNodeWithProperty(Node* base, Node* node)
114 {
115     // An Microdata item may contain properties which in turn are items themselves. Properties can
116     // also themselves be groups of name-value pairs, by putting the itemscope attribute on the element
117     // that declares the property. If the property has an itemscope attribute specified then we need
118     // to traverse the next sibling.
119     return node == base || (node->isHTMLElement() && !toHTMLElement(node)->fastHasAttribute(itemscopeAttr))
120             ? node->traverseNextNode(base) : node->traverseNextSibling(base);
121 }
122
123 Element* HTMLPropertiesCollection::itemAfter(Element* base, Element* previous) const
124 {
125     Node* current;
126     current = previous ? nextNodeWithProperty(base, previous) : base;
127
128     for (; current; current = nextNodeWithProperty(base, current)) {
129         if (!current->isHTMLElement())
130             continue;
131         HTMLElement* element = toHTMLElement(current);
132         if (element->fastHasAttribute(itempropAttr) && element->itemProp()->length()) {
133             return element;
134         }
135     }
136
137     return 0;
138 }
139
140 unsigned HTMLPropertiesCollection::calcLength() const
141 {
142     unsigned length = 0;
143     updateRefElements();
144
145     const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
146     for (unsigned i = 0; i < itemRefElements.size(); ++i) {
147         for (Element* element = itemAfter(itemRefElements[i], 0); element; element = itemAfter(itemRefElements[i], element))
148             ++length;
149     }
150
151     return length;
152 }
153
154 unsigned HTMLPropertiesCollection::length() const
155 {
156     if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
157         return 0;
158
159     invalidateCacheIfNeeded();
160
161     if (!m_cache.hasLength)
162         m_cache.updateLength(calcLength());
163
164     return m_cache.length;
165 }
166
167 Element* HTMLPropertiesCollection::firstProperty() const
168 {
169     Element* element = 0;
170     m_cache.resetPosition();
171     const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
172     for (unsigned i = 0; i < itemRefElements.size(); ++i) {
173         element = itemAfter(itemRefElements[i], 0);
174         if (element) {
175             m_cache.itemRefElementPosition = i;
176             break;
177         }
178     }
179
180     return element;
181 }
182
183 Node* HTMLPropertiesCollection::item(unsigned index) const
184 {
185     if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
186         return 0;
187
188     invalidateCacheIfNeeded();
189     if (m_cache.current && m_cache.position == index)
190         return m_cache.current;
191
192     if (m_cache.hasLength && m_cache.length <= index)
193         return 0;
194
195     updateRefElements();
196     if (!m_cache.current || m_cache.position > index) {
197         m_cache.current = firstProperty();
198         if (!m_cache.current)
199             return 0;
200     }
201
202     unsigned currentPosition = m_cache.position;
203     Element* element = m_cache.current;
204     unsigned itemRefElementPos = m_cache.itemRefElementPosition;
205     const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
206
207     bool found = (m_cache.position == index);
208
209     for (unsigned i = itemRefElementPos; i < itemRefElements.size() && !found; ++i) {
210         while (currentPosition < index) {
211             element = itemAfter(itemRefElements[i], element);
212             if (!element)
213                 break;
214             currentPosition++;
215
216             if (currentPosition == index) {
217                 found = true;
218                 itemRefElementPos = i;
219                 break;
220             }
221         }
222     }
223
224     m_cache.updateCurrentItem(element, index, itemRefElementPos);
225     return m_cache.current;
226 }
227
228 void HTMLPropertiesCollection::findProperties(Element* base) const
229 {
230     for (Element* element = itemAfter(base, 0); element; element = itemAfter(base, element)) {
231         DOMSettableTokenList* itemProperty = element->itemProp();
232         for (unsigned i = 0; i < itemProperty->length(); ++i)
233             m_cache.updatePropertyCache(element, itemProperty->item(i));
234     }
235 }
236
237 void HTMLPropertiesCollection::updateNameCache() const
238 {
239     invalidateCacheIfNeeded();
240     if (m_cache.hasNameCache)
241         return;
242
243     updateRefElements();
244
245     const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
246     for (unsigned i = 0; i < itemRefElements.size(); ++i)
247         findProperties(itemRefElements[i]);
248
249     m_cache.hasNameCache = true;
250 }
251
252 PassRefPtr<DOMStringList> HTMLPropertiesCollection::names() const
253 {
254     if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
255         return DOMStringList::create();
256
257     updateNameCache();
258
259     return m_cache.propertyNames;
260 }
261
262 PassRefPtr<NodeList> HTMLPropertiesCollection::namedItem(const String& name) const
263 {
264     if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
265       return 0;
266
267     Vector<RefPtr<Node> > namedItems;
268
269     updateNameCache();
270
271     Vector<Element*>* propertyResults = m_cache.propertyCache.get(AtomicString(name).impl());
272     for (unsigned i = 0; propertyResults && i < propertyResults->size(); ++i)
273         namedItems.append(propertyResults->at(i));
274
275     // FIXME: HTML5 specifies that this should return PropertyNodeList.
276     return namedItems.isEmpty() ? 0 : StaticNodeList::adopt(namedItems);
277 }
278
279 bool HTMLPropertiesCollection::hasNamedItem(const AtomicString& name) const
280 {
281     if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
282         return false;
283
284     updateNameCache();
285
286     if (Vector<Element*>* propertyCache = m_cache.propertyCache.get(name.impl())) {
287         if (!propertyCache->isEmpty())
288             return true;
289     }
290
291     return false;
292 }
293
294 } // namespace WebCore
295
296 #endif // ENABLE(MICRODATA)