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#include "HTMLPropertiesCollection.h"
35
36#include "DOMSettableTokenList.h"
37#include "DOMStringList.h"
38#include "HTMLElement.h"
39#include "HTMLNames.h"
40#include "Node.h"
41
42namespace WebCore {
43
44using namespace HTMLNames;
45
46static inline bool compareTreeOrder(Node* node1, Node* node2)
47{
48 return (node2->compareDocumentPosition(node1) & (Node::DOCUMENT_POSITION_PRECEDING | Node::DOCUMENT_POSITION_DISCONNECTED)) == Node::DOCUMENT_POSITION_PRECEDING;
49}
50
51PassRefPtr<HTMLPropertiesCollection> HTMLPropertiesCollection::create(PassRefPtr<Node> itemNode)
52{
53 return adoptRef(new HTMLPropertiesCollection(itemNode));
54}
55
56HTMLPropertiesCollection::HTMLPropertiesCollection(PassRefPtr<Node> itemNode)
57 : HTMLCollection(itemNode, ItemProperties)
58 , m_propertyNames(DOMStringList::create())
59{
60}
61
62HTMLPropertiesCollection::~HTMLPropertiesCollection()
63{
64}
65
66void HTMLPropertiesCollection::findPropetiesOfAnItem(Node* root) const
67{
68 // 5.2.5 Associating names with items.
69 Vector<Node*> memory, pending;
70
71 memory.append(root);
72
73 // Add the child elements of root, if any, to pending.
74 for (Node* child = root->firstChild(); child; child = child->nextSibling())
75 if (child->isHTMLElement())
76 pending.append(child);
77
78 // If root has an itemref attribute, split the value of that itemref attribute on spaces.
79 // For each resulting token ID, if there is an element in the home subtree of root with the ID ID,
80 // then add the first such element to pending.
81 if (toHTMLElement(root)->fastHasAttribute(itemrefAttr)) {
82 DOMSettableTokenList* itemRef = root->itemRef();
83
84 for (size_t i = 0; i < itemRef->length(); ++i) {
85 AtomicString id = itemRef->item(i);
86
87 Element* element = root->document()->getElementById(id);
88 if (element && element->isHTMLElement())
89 pending.append(element);
90 }
91 }
92
93 // Loop till we have processed all pending elements
94 while (!pending.isEmpty()) {
95
96 // Remove first element from pending and let current be that element.
97 Node* current = pending[0];
98 pending.remove(0);
99
100 // If current is already in memory, there is a microdata error;
101 if (memory.contains(current)) {
102 // microdata error;
103 continue;
104 }
105
106 memory.append(current);
107
108 // If current does not have an itemscope attribute, then: add all the child elements of current to pending.
109 HTMLElement* element = toHTMLElement(current);
110 if (!element->fastHasAttribute(itemscopeAttr)) {
111 for (Node* child = current->firstChild(); child; child = child->nextSibling())
112 if (child->isHTMLElement())
113 pending.append(child);
114 }
115
116 // If current has an itemprop attribute specified, add it to results.
117 if (element->fastHasAttribute(itempropAttr))
118 m_properties.append(current);
119 }
120}
121
122unsigned HTMLPropertiesCollection::length() const
123{
124 if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
125 return 0;
126
127 m_properties.clear();
128 findPropetiesOfAnItem(base());
129 return m_properties.size();
130}
131
132Node* HTMLPropertiesCollection::item(unsigned index) const
133{
134 if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
135 return 0;
136
137 m_properties.clear();
138 findPropetiesOfAnItem(base());
139
140 if (m_properties.size() <= index)
141 return 0;
142
143 std::sort(m_properties.begin(), m_properties.end(), compareTreeOrder);
144 return m_properties[index];
145}
146
147PassRefPtr<DOMStringList> HTMLPropertiesCollection::names() const
148{
149 m_properties.clear();
150 m_propertyNames->clear();
151
152 if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
153 return m_propertyNames;
154
155 findPropetiesOfAnItem(base());
156
157 std::sort(m_properties.begin(), m_properties.end(), compareTreeOrder);
158
159 for (size_t i = 0; i < m_properties.size(); ++i) {
160 // For each item properties, split the value of that itemprop attribute on spaces.
161 // Add all tokens to property names, with the order preserved but with duplicates removed.
162 DOMSettableTokenList* itemProperty = m_properties[i]->itemProp();
163 for (size_t i = 0; i < itemProperty->length(); ++i) {
164 AtomicString propertyName = itemProperty->item(i);
165 if (m_propertyNames->isEmpty() || !m_propertyNames->contains(propertyName))
166 m_propertyNames->append(propertyName);
167 }
168 }
169
170 return m_propertyNames;
171}
172
173} // namespace WebCore
174
175#endif // ENABLE(MICRODATA)