2 * Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
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.
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.
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.
35 #include "HTMLPropertiesCollection.h"
37 #include "DOMSettableTokenList.h"
38 #include "DOMStringList.h"
39 #include "HTMLElement.h"
40 #include "HTMLNames.h"
42 #include "StaticNodeList.h"
46 using namespace HTMLNames;
48 PassRefPtr<HTMLPropertiesCollection> HTMLPropertiesCollection::create(Node* itemNode)
50 return adoptRef(new HTMLPropertiesCollection(itemNode));
53 HTMLPropertiesCollection::HTMLPropertiesCollection(Node* itemNode)
54 : HTMLCollection(itemNode, ItemProperties)
59 HTMLPropertiesCollection::~HTMLPropertiesCollection()
63 void HTMLPropertiesCollection::invalidateCacheIfNeeded() const
65 uint64_t docversion = base()->document()->domTreeVersion();
67 if (m_cache.version == docversion)
71 m_cache.version = docversion;
74 void HTMLPropertiesCollection::updateRefElements() const
76 if (m_cache.hasItemRefElements)
79 Vector<Element*> itemRefElements;
80 HTMLElement* baseElement = toHTMLElement(base());
82 if (!baseElement->fastHasAttribute(itemrefAttr)) {
83 itemRefElements.append(baseElement);
84 m_cache.setItemRefElements(itemRefElements);
88 DOMSettableTokenList* itemRef = baseElement->itemRef();
89 RefPtr<DOMSettableTokenList> processedItemRef = DOMSettableTokenList::create();
90 Node* rootNode = baseElement->treeScope()->rootNode();
92 for (Node* current = rootNode->firstChild(); current; current = current->traverseNextNode(rootNode)) {
93 if (!current->isHTMLElement())
95 HTMLElement* element = toHTMLElement(current);
97 if (element == baseElement) {
98 itemRefElements.append(element);
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);
110 m_cache.setItemRefElements(itemRefElements);
113 static Node* nextNodeWithProperty(Node* base, Node* node)
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);
123 Element* HTMLPropertiesCollection::itemAfter(Element* base, Element* previous) const
126 current = previous ? nextNodeWithProperty(base, previous) : base;
128 for (; current; current = nextNodeWithProperty(base, current)) {
129 if (!current->isHTMLElement())
131 HTMLElement* element = toHTMLElement(current);
132 if (element->fastHasAttribute(itempropAttr) && element->itemProp()->length()) {
140 unsigned HTMLPropertiesCollection::calcLength() const
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))
154 unsigned HTMLPropertiesCollection::length() const
156 if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
159 invalidateCacheIfNeeded();
161 if (!m_cache.hasLength)
162 m_cache.updateLength(calcLength());
164 return m_cache.length;
167 Element* HTMLPropertiesCollection::firstProperty() const
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);
175 m_cache.itemRefElementPosition = i;
183 Node* HTMLPropertiesCollection::item(unsigned index) const
185 if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
188 invalidateCacheIfNeeded();
189 if (m_cache.current && m_cache.position == index)
190 return m_cache.current;
192 if (m_cache.hasLength && m_cache.length <= index)
196 if (!m_cache.current || m_cache.position > index) {
197 m_cache.current = firstProperty();
198 if (!m_cache.current)
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();
207 bool found = (m_cache.position == index);
209 for (unsigned i = itemRefElementPos; i < itemRefElements.size() && !found; ++i) {
210 while (currentPosition < index) {
211 element = itemAfter(itemRefElements[i], element);
216 if (currentPosition == index) {
218 itemRefElementPos = i;
224 m_cache.updateCurrentItem(element, index, itemRefElementPos);
225 return m_cache.current;
228 void HTMLPropertiesCollection::findProperties(Element* base) const
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));
237 void HTMLPropertiesCollection::updateNameCache() const
239 invalidateCacheIfNeeded();
240 if (m_cache.hasNameCache)
245 const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
246 for (unsigned i = 0; i < itemRefElements.size(); ++i)
247 findProperties(itemRefElements[i]);
249 m_cache.hasNameCache = true;
252 PassRefPtr<DOMStringList> HTMLPropertiesCollection::names() const
254 if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
255 return DOMStringList::create();
259 return m_cache.propertyNames;
262 PassRefPtr<NodeList> HTMLPropertiesCollection::namedItem(const String& name) const
264 if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
267 Vector<RefPtr<Node> > namedItems;
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));
275 // FIXME: HTML5 specifies that this should return PropertyNodeList.
276 return namedItems.isEmpty() ? 0 : StaticNodeList::adopt(namedItems);
279 bool HTMLPropertiesCollection::hasNamedItem(const AtomicString& name) const
281 if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
286 if (Vector<Element*>* propertyCache = m_cache.propertyCache.get(name.impl())) {
287 if (!propertyCache->isEmpty())
294 } // namespace WebCore
296 #endif // ENABLE(MICRODATA)